Coverage for tests / integration_tests / test_projects.py: 100%

150 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-24 01:53 +0000

1""" 

2Test various "apio" commands. 

3""" 

4 

5import os 

6from os.path import getsize 

7from pathlib import Path 

8from tests.conftest import ApioRunner 

9from apio.commands.apio import apio_top_cli as apio 

10 

11 

12def test_project_with_legacy_board_id(apio_runner: ApioRunner): 

13 """Test a project that uses a legacy board id.""" 

14 

15 # -- We shared the apio home with the other tests in this file to speed 

16 # -- up apio package installation. Tests should not mutate the shared home 

17 # -- to avoid cross-interference between tests in this file. 

18 with apio_runner.in_sandbox() as sb: 

19 

20 # -- Fetch an example of a board that has a legacy name. 

21 result = sb.invoke_apio_cmd( 

22 apio, ["examples", "fetch", "ice40-hx8k/leds"] 

23 ) 

24 sb.assert_result_ok(result) 

25 

26 # -- Run 'apio build' 

27 result = sb.invoke_apio_cmd(apio, ["build"]) 

28 sb.assert_result_ok(result) 

29 

30 # -- Modify the apio.ini to have the legacy board id 

31 sb.write_apio_ini( 

32 { 

33 "[env:default]": { 

34 "board": "iCE40-HX8K", 

35 "top-module": "leds", 

36 } 

37 } 

38 ) 

39 

40 # -- Run 'apio clean' 

41 result = sb.invoke_apio_cmd(apio, ["clean"]) 

42 sb.assert_result_ok(result) 

43 

44 # -- Run 'apio build' again. It should also succeed. 

45 result = sb.invoke_apio_cmd(apio, ["build"]) 

46 sb.assert_result_ok(result) 

47 

48 

49def _test_project( 

50 apio_runner: ApioRunner, 

51 *, 

52 remote_proj_dir: bool, 

53 example: str, 

54 testbench_file: str, 

55 bitstream: str, 

56 report_item: str, 

57): 

58 """A common project integration test. Invoked per each tested 

59 architecture. 

60 """ 

61 

62 # pylint: disable=too-many-arguments 

63 # pylint: disable=too-many-statements 

64 

65 # -- Extract the base name of the testbench file 

66 testbench, _ = os.path.splitext(testbench_file) 

67 

68 # -- We shared the apio home with the other tests in this file to speed 

69 # -- up apio package installation. Tests should not mutate the shared home 

70 # -- to avoid cross-interference between tests in this file. 

71 with apio_runner.in_sandbox() as sb: 

72 

73 # -- If testing from a remote dir, step out of the proj dir, and 

74 # -- use -p proj_dir arg. 

75 if remote_proj_dir: 

76 os.chdir(sb.sandbox_dir) 

77 sb.proj_dir.rmdir() 

78 proj_arg = ["-p", str(sb.proj_dir)] 

79 dst_arg = ["-d", str(sb.proj_dir)] 

80 else: 

81 proj_arg = [] 

82 dst_arg = [] 

83 

84 # -- If testing from a remote dir, the proj dir should not exist yet, 

85 # -- else, the project dir should be empty. 

86 if remote_proj_dir: 

87 assert not sb.proj_dir.exists() 

88 else: 

89 assert not os.listdir(sb.proj_dir) 

90 

91 # -- 'apio examples fetch <example> -d <proj_dir>' 

92 args = ["examples", "fetch", example] + dst_arg 

93 result = sb.invoke_apio_cmd(apio, args) 

94 sb.assert_result_ok(result) 

95 assert f"Copying {example} example files" in result.output 

96 assert "fetched successfully" in result.output 

97 assert getsize(sb.proj_dir / "apio.ini") 

98 

99 # -- Remember the original list of project files. 

100 project_files = os.listdir(sb.proj_dir) 

101 

102 # -- 'apio build' 

103 args = ["build"] + proj_arg 

104 result = sb.invoke_apio_cmd(apio, args) 

105 sb.assert_result_ok(result) 

106 assert "SUCCESS" in result.output 

107 assert "yosys -p" in result.output 

108 

109 assert getsize(sb.proj_dir / "_build/default" / bitstream) 

110 

111 # -- 'apio build' (no change) 

112 args = ["build"] + proj_arg 

113 result = sb.invoke_apio_cmd(apio, args) 

114 sb.assert_result_ok(result) 

115 assert "SUCCESS" in result.output 

116 assert "yosys -p" not in result.output 

117 

118 # -- Modify apio.ini 

119 apio_ini_lines = sb.read_file( 

120 sb.proj_dir / "apio.ini", lines_mode=True 

121 ) 

122 apio_ini_lines.append(" ") 

123 sb.write_file(sb.proj_dir / "apio.ini", apio_ini_lines, exists_ok=True) 

124 

125 # -- 'apio build' 

126 # -- Apio.ini modification should triggers a new build. 

127 args = ["build"] + proj_arg 

128 result = sb.invoke_apio_cmd(apio, args) 

129 sb.assert_result_ok(result) 

130 assert "SUCCESS" in result.output 

131 assert "yosys -p" in result.output 

132 

133 # -- 'apio lint' 

134 args = ["lint"] + proj_arg 

135 result = sb.invoke_apio_cmd(apio, args) 

136 sb.assert_result_ok(result) 

137 assert "SUCCESS" in result.output 

138 assert getsize(sb.proj_dir / "_build/default/hardware.vlt") 

139 

140 # -- 'apio format' 

141 args = ["format"] + proj_arg 

142 result = sb.invoke_apio_cmd(apio, args) 

143 sb.assert_result_ok(result) 

144 

145 # -- 'apio format <testbench-file>' 

146 # -- This tests the project relative specification even when 

147 # -- the option --project-dir is used. 

148 args = ["format", testbench_file] + proj_arg 

149 result = sb.invoke_apio_cmd(apio, args) 

150 sb.assert_result_ok(result) 

151 

152 # -- 'apio test' 

153 args = ["test"] + proj_arg 

154 result = sb.invoke_apio_cmd(apio, args) 

155 sb.assert_result_ok(result) 

156 assert "SUCCESS" in result.output 

157 assert getsize(sb.proj_dir / f"_build/default/{testbench}.out") 

158 assert getsize(sb.proj_dir / f"_build/default/{testbench}.vcd") 

159 # -- For issue https://github.com/FPGAwars/apio/issues/557 

160 assert "warning: Timing checks are not supported" not in result.output 

161 

162 # -- 'apio clean' 

163 args = ["clean"] + proj_arg 

164 result = sb.invoke_apio_cmd(apio, args) 

165 sb.assert_result_ok(result) 

166 assert "Cleanup completed" in result.output 

167 assert not (sb.proj_dir / f"_build/default/{testbench}.out").exists() 

168 assert not (sb.proj_dir / f"_build/default/{testbench}.vcd").exists() 

169 

170 # -- 'apio sim --no-gtkwave' 

171 args = ["sim", "--no-gtkwave"] + proj_arg 

172 result = sb.invoke_apio_cmd(apio, args) 

173 sb.assert_result_ok(result) 

174 assert "SUCCESS" in result.output 

175 assert getsize(sb.proj_dir / f"_build/default/{testbench}.out") 

176 assert getsize(sb.proj_dir / f"_build/default/{testbench}.vcd") 

177 # -- For issue https://github.com/FPGAwars/apio/issues/557 

178 assert "warning: Timing checks are not supported" not in result.output 

179 

180 # -- 'apio clean' 

181 args = ["clean"] + proj_arg 

182 result = sb.invoke_apio_cmd(apio, args) 

183 sb.assert_result_ok(result) 

184 assert "Cleanup completed" in result.output 

185 assert not (sb.proj_dir / f"_build/default/{testbench}.out").exists() 

186 assert not (sb.proj_dir / f"_build/default/{testbench}.vcd").exists() 

187 

188 # -- 'apio test <testbench-file>' 

189 args = ["test", testbench_file] + proj_arg 

190 result = sb.invoke_apio_cmd(apio, args) 

191 sb.assert_result_ok(result) 

192 assert "SUCCESS" in result.output 

193 assert getsize(sb.proj_dir / f"_build/default/{testbench}.out") 

194 assert getsize(sb.proj_dir / f"_build/default/{testbench}.vcd") 

195 # -- For issue https://github.com/FPGAwars/apio/issues/557 

196 assert "warning: Timing checks are not supported" not in result.output 

197 

198 # -- 'apio sim --no-gtkw <testbench-file>' 

199 args = ["sim", "--no-gtkwave", testbench_file] + proj_arg 

200 result = sb.invoke_apio_cmd(apio, args) 

201 sb.assert_result_ok(result) 

202 assert "SUCCESS" in result.output 

203 assert getsize(sb.proj_dir / f"_build/default/{testbench}.out") 

204 assert getsize(sb.proj_dir / f"_build/default/{testbench}.vcd") 

205 # -- For issue https://github.com/FPGAwars/apio/issues/557 

206 assert "warning: Timing checks are not supported" not in result.output 

207 

208 # -- 'apio clean' 

209 args = ["clean"] + proj_arg 

210 result = sb.invoke_apio_cmd(apio, args) 

211 sb.assert_result_ok(result) 

212 assert "Cleanup completed" in result.output 

213 assert not (sb.proj_dir / f"_build/default/{testbench}.out").exists() 

214 assert not (sb.proj_dir / f"_build/default/{testbench}.vcd").exists() 

215 

216 # -- 'apio report' 

217 args = ["report"] + proj_arg 

218 result = sb.invoke_apio_cmd(apio, args) 

219 sb.assert_result_ok(result) 

220 assert "SUCCESS" in result.output 

221 assert report_item in result.output 

222 assert "─────┐" in result.output # Graphical table border 

223 assert getsize(sb.proj_dir / "_build/default/hardware.pnr") 

224 

225 # -- 'apio graph -n' 

226 args = ["graph", "-n"] + proj_arg 

227 result = sb.invoke_apio_cmd(apio, args) 

228 sb.assert_result_ok(result) 

229 assert "SUCCESS" in result.output 

230 assert getsize(sb.proj_dir / "_build/default/graph.dot") 

231 assert getsize(sb.proj_dir / "_build/default/graph.svg") 

232 

233 # -- 'apio clean' 

234 assert Path(sb.proj_dir / "_build/default").exists() 

235 args = ["clean"] + proj_arg 

236 result = sb.invoke_apio_cmd(apio, args) 

237 sb.assert_result_ok(result) 

238 assert "Cleanup completed" in result.output 

239 assert not Path(sb.proj_dir / "_build/default").exists() 

240 

241 # -- Here we should have only the original project files. 

242 assert set(os.listdir(sb.proj_dir)) == set(project_files) 

243 

244 

245# NOTE: We use the alhambra-ii/bcd-counter example because it uses 

246# subdirectories. This increases the test coverage. 

247def test_project_ice40_local_dir(apio_runner: ApioRunner): 

248 """Tests building and testing an ice40 project as the current working 

249 dir.""" 

250 _test_project( 

251 apio_runner, 

252 remote_proj_dir=False, 

253 example="alhambra-ii/bcd-counter", 

254 testbench_file="main_tb.v", 

255 bitstream="hardware.bin", 

256 report_item="ICESTORM_LC", 

257 ) 

258 

259 

260def test_project_ice40_remote_dir(apio_runner: ApioRunner): 

261 """Tests building and testing an ice40 project from a remote dir, using 

262 the -p option.""" 

263 _test_project( 

264 apio_runner, 

265 remote_proj_dir=True, 

266 example="alhambra-ii/bcd-counter", 

267 testbench_file="main_tb.v", 

268 bitstream="hardware.bin", 

269 report_item="ICESTORM_LC", 

270 ) 

271 

272 

273def test_project_ice40_system_verilog(apio_runner: ApioRunner): 

274 """Tests building and testing an ice40 project that contains system 

275 verilog files.""" 

276 _test_project( 

277 apio_runner, 

278 remote_proj_dir=False, 

279 example="alhambra-ii/bcd-counter-sv", 

280 testbench_file="main_tb.sv", 

281 bitstream="hardware.bin", 

282 report_item="ICESTORM_LC", 

283 ) 

284 

285 

286def test_project_ecp5_local_dir(apio_runner: ApioRunner): 

287 """Tests building and testing an ecp5 project as the current working dir""" 

288 _test_project( 

289 apio_runner, 

290 remote_proj_dir=False, 

291 example="colorlight-5a-75b-v8/ledon", 

292 testbench_file="ledon_tb.v", 

293 bitstream="hardware.bit", 

294 report_item="ALU54B", 

295 ) 

296 

297 

298def test_project_ecp5_remote_dir(apio_runner: ApioRunner): 

299 """Tests building and testing an ecp5 project from a remote directory.""" 

300 _test_project( 

301 apio_runner, 

302 remote_proj_dir=True, 

303 example="colorlight-5a-75b-v8/ledon", 

304 testbench_file="ledon_tb.v", 

305 bitstream="hardware.bit", 

306 report_item="ALU54B", 

307 ) 

308 

309 

310def test_project_ecp5_system_verilog(apio_runner: ApioRunner): 

311 """Tests building and testing an ecp5 project that contains system 

312 verilog files.""" 

313 _test_project( 

314 apio_runner, 

315 remote_proj_dir=False, 

316 example="colorlight-5a-75b-v8/ledon-sv", 

317 testbench_file="ledon_tb.sv", 

318 bitstream="hardware.bit", 

319 report_item="ALU54B", 

320 ) 

321 

322 

323def test_project_gowin_local_dir(apio_runner: ApioRunner): 

324 """Tests building and testing a gowin project as the current working dir""" 

325 _test_project( 

326 apio_runner, 

327 remote_proj_dir=False, 

328 example="sipeed-tang-nano-9k/blinky", 

329 testbench_file="blinky_tb.v", 

330 bitstream="hardware.fs", 

331 report_item="LUT4", 

332 ) 

333 

334 

335def test_project_gowin_remote_dir(apio_runner: ApioRunner): 

336 """Tests building and testing a gowin project from a remote directory.""" 

337 _test_project( 

338 apio_runner, 

339 remote_proj_dir=True, 

340 example="sipeed-tang-nano-9k/blinky", 

341 testbench_file="blinky_tb.v", 

342 bitstream="hardware.fs", 

343 report_item="LUT4", 

344 ) 

345 

346 

347def test_project_gowin_system_verilog(apio_runner: ApioRunner): 

348 """Tests building and testing an gowin project that contains system 

349 verilog files.""" 

350 _test_project( 

351 apio_runner, 

352 remote_proj_dir=False, 

353 example="sipeed-tang-nano-9k/blinky-sv", 

354 testbench_file="blinky_tb.sv", 

355 bitstream="hardware.fs", 

356 report_item="LUT4", 

357 )