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

186 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-08 02:47 +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 assert "-DSYNTHESIZE" in result.output 

109 assert "-DAPIO_SIM" not in result.output 

110 

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

112 

113 # -- 'apio build' (no change, yosys is not invoked) 

114 args = ["build"] + proj_arg 

115 result = sb.invoke_apio_cmd(apio, args) 

116 sb.assert_result_ok(result) 

117 assert "SUCCESS" in result.output 

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

119 

120 # -- Modify apio.ini 

121 apio_ini_lines = sb.read_file( 

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

123 ) 

124 apio_ini_lines.append(" ") 

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

126 

127 # -- 'apio build' 

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

129 args = ["build"] + proj_arg 

130 result = sb.invoke_apio_cmd(apio, args) 

131 sb.assert_result_ok(result) 

132 assert "SUCCESS" in result.output 

133 assert "yosys -p" in result.output 

134 assert "-DSYNTHESIZE" in result.output 

135 assert "-DAPIO_SIM" not in result.output 

136 

137 # -- 'apio lint' 

138 args = ["lint"] + proj_arg 

139 result = sb.invoke_apio_cmd(apio, args) 

140 sb.assert_result_ok(result) 

141 assert "SUCCESS" in result.output 

142 assert "-DSYNTHESIZE" in result.output 

143 assert "-DAPIO_SIM=0" in result.output 

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

145 

146 # -- 'apio lint --nosynth' 

147 args = ["lint", "--nosynth"] + proj_arg 

148 result = sb.invoke_apio_cmd(apio, args) 

149 sb.assert_result_ok(result) 

150 assert "SUCCESS" in result.output 

151 assert "-DSYNTHESIZE" not in result.output 

152 assert "-DAPIO_SIM=0" in result.output 

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

154 

155 # -- 'apio format' 

156 args = ["format"] + proj_arg 

157 result = sb.invoke_apio_cmd(apio, args) 

158 sb.assert_result_ok(result) 

159 assert "-DSYNTHESIZE" not in result.output 

160 assert "-DAPIO_SIM" not in result.output 

161 

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

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

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

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

166 result = sb.invoke_apio_cmd(apio, args) 

167 sb.assert_result_ok(result) 

168 

169 # -- 'apio test' 

170 args = ["test"] + proj_arg 

171 result = sb.invoke_apio_cmd(apio, args) 

172 sb.assert_result_ok(result) 

173 assert "SUCCESS" in result.output 

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

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

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

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

178 assert "-DSYNTHESIZE" not in result.output 

179 assert "-DAPIO_SIM=0" in result.output 

180 

181 # -- 'apio clean' 

182 args = ["clean"] + proj_arg 

183 result = sb.invoke_apio_cmd(apio, args) 

184 sb.assert_result_ok(result) 

185 assert "Cleanup completed" in result.output 

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

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

188 

189 # -- 'apio test --default' 

190 args = ["test", "--default"] + proj_arg 

191 result = sb.invoke_apio_cmd(apio, args) 

192 sb.assert_result_ok(result) 

193 assert "SUCCESS" in result.output 

194 assert "-DAPIO_SIM=0" in result.output 

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

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

197 

198 # -- 'apio clean' 

199 args = ["clean"] + proj_arg 

200 result = sb.invoke_apio_cmd(apio, args) 

201 sb.assert_result_ok(result) 

202 assert "Cleanup completed" in result.output 

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

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

205 

206 # -- 'apio sim --no-gtkwave' 

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

208 result = sb.invoke_apio_cmd(apio, args) 

209 sb.assert_result_ok(result) 

210 assert "SUCCESS" in result.output 

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

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

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

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

215 assert "-DSYNTHESIZE" not in result.output 

216 assert "-DAPIO_SIM=1" in result.output 

217 

218 # -- 'apio clean' 

219 args = ["clean"] + proj_arg 

220 result = sb.invoke_apio_cmd(apio, args) 

221 sb.assert_result_ok(result) 

222 assert "Cleanup completed" in result.output 

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

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

225 

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

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

228 result = sb.invoke_apio_cmd(apio, args) 

229 sb.assert_result_ok(result) 

230 assert "SUCCESS" in result.output 

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

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

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

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

235 

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

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

238 result = sb.invoke_apio_cmd(apio, args) 

239 sb.assert_result_ok(result) 

240 assert "SUCCESS" in result.output 

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

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

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

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

245 

246 # -- 'apio clean' 

247 args = ["clean"] + proj_arg 

248 result = sb.invoke_apio_cmd(apio, args) 

249 sb.assert_result_ok(result) 

250 assert "Cleanup completed" in result.output 

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

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

253 

254 # -- 'apio report' 

255 args = ["report"] + proj_arg 

256 result = sb.invoke_apio_cmd(apio, args) 

257 sb.assert_result_ok(result) 

258 assert "SUCCESS" in result.output 

259 assert "-DSYNTHESIZE" in result.output 

260 assert "-DAPIO_SIM" not in result.output 

261 assert report_item in result.output 

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

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

264 

265 # -- 'apio graph -n' 

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

267 result = sb.invoke_apio_cmd(apio, args) 

268 sb.assert_result_ok(result) 

269 assert "SUCCESS" in result.output 

270 assert "-DSYNTHESIZE" in result.output 

271 assert "-DAPIO_SIM" not in result.output 

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

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

274 

275 # -- 'apio clean' 

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

277 args = ["clean"] + proj_arg 

278 result = sb.invoke_apio_cmd(apio, args) 

279 sb.assert_result_ok(result) 

280 assert "Cleanup completed" in result.output 

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

282 

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

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

285 

286 

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

288# subdirectories. This increases the test coverage. 

289def test_project_ice40_local_dir(apio_runner: ApioRunner): 

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

291 dir.""" 

292 _test_project( 

293 apio_runner, 

294 remote_proj_dir=False, 

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

296 testbench_file="main_tb.v", 

297 bitstream="hardware.bin", 

298 report_item="ICESTORM_LC", 

299 ) 

300 

301 

302def test_project_ice40_remote_dir(apio_runner: ApioRunner): 

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

304 the -p option.""" 

305 _test_project( 

306 apio_runner, 

307 remote_proj_dir=True, 

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

309 testbench_file="main_tb.v", 

310 bitstream="hardware.bin", 

311 report_item="ICESTORM_LC", 

312 ) 

313 

314 

315def test_project_ice40_system_verilog(apio_runner: ApioRunner): 

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

317 verilog files.""" 

318 _test_project( 

319 apio_runner, 

320 remote_proj_dir=False, 

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

322 testbench_file="main_tb.sv", 

323 bitstream="hardware.bin", 

324 report_item="ICESTORM_LC", 

325 ) 

326 

327 

328def test_project_ecp5_local_dir(apio_runner: ApioRunner): 

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

330 _test_project( 

331 apio_runner, 

332 remote_proj_dir=False, 

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

334 testbench_file="ledon_tb.v", 

335 bitstream="hardware.bit", 

336 report_item="ALU54B", 

337 ) 

338 

339 

340def test_project_ecp5_remote_dir(apio_runner: ApioRunner): 

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

342 _test_project( 

343 apio_runner, 

344 remote_proj_dir=True, 

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

346 testbench_file="ledon_tb.v", 

347 bitstream="hardware.bit", 

348 report_item="ALU54B", 

349 ) 

350 

351 

352def test_project_ecp5_system_verilog(apio_runner: ApioRunner): 

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

354 verilog files.""" 

355 _test_project( 

356 apio_runner, 

357 remote_proj_dir=False, 

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

359 testbench_file="ledon_tb.sv", 

360 bitstream="hardware.bit", 

361 report_item="ALU54B", 

362 ) 

363 

364 

365def test_project_gowin_local_dir(apio_runner: ApioRunner): 

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

367 _test_project( 

368 apio_runner, 

369 remote_proj_dir=False, 

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

371 testbench_file="blinky_tb.v", 

372 bitstream="hardware.fs", 

373 report_item="LUT4", 

374 ) 

375 

376 

377def test_project_gowin_remote_dir(apio_runner: ApioRunner): 

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

379 _test_project( 

380 apio_runner, 

381 remote_proj_dir=True, 

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

383 testbench_file="blinky_tb.v", 

384 bitstream="hardware.fs", 

385 report_item="LUT4", 

386 ) 

387 

388 

389def test_project_gowin_system_verilog(apio_runner: ApioRunner): 

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

391 verilog files.""" 

392 _test_project( 

393 apio_runner, 

394 remote_proj_dir=False, 

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

396 testbench_file="blinky_tb.sv", 

397 bitstream="hardware.fs", 

398 report_item="LUT4", 

399 )