Coverage for tests/unit_tests/common/test_build_report.py: 100%

39 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-09-23 03:53 +0000

1"""Test for build_report.py.""" 

2 

3from pathlib import Path 

4import pytest 

5from tests.conftest import ApioRunner 

6from apio.common.build_report import ( 

7 ResourceReport, 

8 ClockReport, 

9 BuildReport, 

10 read_build_report, 

11) 

12 

13# -- Test json for ECP5. Having 'TRELLIS' in a utilization key indicates 

14# -- that's it's ECP5. 

15ECP5_TEST_SUMMARY = """ 

16{ 

17 "other-stuff": { 

18 "bla-bla": "bla-bla" 

19 }, 

20 "utilization": { 

21 "TRELLIS_ECLKBUF": { 

22 "available": 8, 

23 "used": 0 

24 }, 

25 "SIOLOGIC": { 

26 "available": 69, 

27 "used": 0 

28 }, 

29 "TRELLIS_COMB": { 

30 "available": 24288, 

31 "used": 12144 

32 } 

33 }, 

34 "fmax": { 

35 "$glbnet$MY_CLK$TRELLIS_IO_IN": { 

36 "achieved": 295.420, 

37 "constraint": 12 

38 } 

39 } 

40} 

41""" 

42 

43 

44# -- Test json for non ECP5. The utilization keys do not contain "TRELLIS". 

45# -- Note that the user clock name "CLK" is at different part of the fmax keys 

46# -- compared to ECP5 above. 

47NON_ECP5_TEST_SUMMARY = """ 

48{ 

49 "other-stuff": { 

50 "bla-bla": "bla-bla" 

51 }, 

52 "utilization": { 

53 "ICESTORM_PLL": { 

54 "available": 2, 

55 "used": 0 

56 }, 

57 "ICESTORM_LC": { 

58 "available": 7680, 

59 "used": 27 

60 } 

61 }, 

62 "fmax": { 

63 "MY_CLK$SB_IO_IN_$glb_clk": { 

64 "achieved": 194.363, 

65 "constraint": 12 

66 } 

67 } 

68} 

69""" 

70 

71 

72def test_ecp5_read_build_report(apio_runner): 

73 """Tests the read_build_report() function for ECP 5 hardware.pnr.""" 

74 

75 with apio_runner.in_sandbox() as sb: 

76 

77 file_path = Path("_build/default/hardware.pnr") 

78 

79 sb.write_file(file_path, ECP5_TEST_SUMMARY) 

80 

81 build_report = read_build_report(file_path) 

82 assert isinstance(build_report, BuildReport) 

83 

84 print(build_report) 

85 

86 assert build_report == BuildReport( 

87 resources=[ 

88 ResourceReport( 

89 name="SIOLOGIC", available=69, used=0, percentage=0.0 

90 ), 

91 ResourceReport( 

92 name="TRELLIS_COMB", 

93 available=24288, 

94 used=12144, 

95 percentage=50.0, 

96 ), 

97 ResourceReport( 

98 name="TRELLIS_ECLKBUF", available=8, used=0, percentage=0.0 

99 ), 

100 ], 

101 clocks=[ClockReport(name="MY_CLK", fmax_mhz=295.420)], 

102 ) 

103 

104 

105def test_non_ecp5_read_build_report(apio_runner: ApioRunner): 

106 """Tests the read_build_report() function for non ECP 5 hardware.pnr.""" 

107 

108 with apio_runner.in_sandbox() as sb: 

109 

110 file_path = Path("_build/default/hardware.pnr") 

111 

112 sb.write_file(file_path, NON_ECP5_TEST_SUMMARY) 

113 

114 build_report = read_build_report(file_path) 

115 assert isinstance(build_report, BuildReport) 

116 

117 print(build_report) 

118 

119 assert build_report == BuildReport( 

120 resources=[ 

121 ResourceReport( 

122 name="ICESTORM_LC", 

123 available=7680, 

124 used=27, 

125 percentage=0.3515625, 

126 ), 

127 ResourceReport( 

128 name="ICESTORM_PLL", available=2, used=0, percentage=0.0 

129 ), 

130 ], 

131 clocks=[ClockReport(name="MY_CLK", fmax_mhz=194.363)], 

132 ) 

133 

134 

135def test_hardware_pnr_reading_failure(apio_runner: ApioRunner): 

136 """Tests the case where reading hardware.pne fails.""" 

137 with apio_runner.in_sandbox(): 

138 file_path = Path("_build/default/hardware.pnr") 

139 with apio_runner.with_logger() as log: 

140 with pytest.raises(SystemExit) as e: 

141 # -- Since we didn't create hardware.pnr, reading should fail. 

142 read_build_report(file_path) 

143 assert e.value.code == 1 

144 assert "Error: Failed to read" in log.out 

145 

146 

147def test_hardware_pnr_parsing_failure(apio_runner: ApioRunner): 

148 """Tests the case where reading hardware.pne fails.""" 

149 with apio_runner.in_sandbox() as sb: 

150 file_path = Path("_build/default/hardware.pnr") 

151 sb.write_file(file_path, "Broken JSON file") 

152 with apio_runner.with_logger() as log: 

153 with pytest.raises(SystemExit) as e: 

154 # -- Since we didn't create hardware.pnr, reading should fail. 

155 read_build_report(file_path) 

156 assert e.value.code == 1 

157 assert "Error: Failed parsing json file" in log.out