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

152 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-06 10:20 +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 # -- This is a slow test. Skip it if running with --fast-only flag. 

16 apio_runner.skip_test_if_fast_only() 

17 

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

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

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

21 with apio_runner.in_sandbox() as sb: 

22 

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

24 result = sb.invoke_apio_cmd( 

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

26 ) 

27 sb.assert_result_ok(result) 

28 

29 # -- Run 'apio build' 

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

31 sb.assert_result_ok(result) 

32 

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

34 sb.write_apio_ini( 

35 { 

36 "[env:default]": { 

37 "board": "iCE40-HX8K", 

38 "top-module": "leds", 

39 } 

40 } 

41 ) 

42 

43 # -- Run 'apio clean' 

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

45 sb.assert_result_ok(result) 

46 

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

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

49 sb.assert_result_ok(result) 

50 

51 

52def _test_project( 

53 apio_runner: ApioRunner, 

54 *, 

55 remote_proj_dir: bool, 

56 example: str, 

57 testbench_file: str, 

58 bitstream: str, 

59 report_item: str, 

60): 

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

62 architecture. 

63 """ 

64 

65 # pylint: disable=too-many-arguments 

66 # pylint: disable=too-many-statements 

67 

68 # -- This is a slow test. Skip it if running with --fast-only flag. 

69 apio_runner.skip_test_if_fast_only() 

70 

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

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

73 

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

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

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

77 with apio_runner.in_sandbox() as sb: 

78 

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

80 # -- use -p proj_dir arg. 

81 if remote_proj_dir: 

82 os.chdir(sb.sandbox_dir) 

83 sb.proj_dir.rmdir() 

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

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

86 else: 

87 proj_arg = [] 

88 dst_arg = [] 

89 

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

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

92 if remote_proj_dir: 

93 assert not sb.proj_dir.exists() 

94 else: 

95 assert not os.listdir(sb.proj_dir) 

96 

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

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

99 result = sb.invoke_apio_cmd(apio, args) 

100 sb.assert_result_ok(result) 

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

102 assert "fetched successfully" in result.output 

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

104 

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

106 project_files = os.listdir(sb.proj_dir) 

107 

108 # -- 'apio build' 

109 args = ["build"] + proj_arg 

110 result = sb.invoke_apio_cmd(apio, args) 

111 sb.assert_result_ok(result) 

112 assert "SUCCESS" in result.output 

113 assert "yosys -p" in result.output 

114 

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

116 

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

118 args = ["build"] + proj_arg 

119 result = sb.invoke_apio_cmd(apio, args) 

120 sb.assert_result_ok(result) 

121 assert "SUCCESS" in result.output 

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

123 

124 # -- Modify apio.ini 

125 apio_ini_lines = sb.read_file( 

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

127 ) 

128 apio_ini_lines.append(" ") 

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

130 

131 # -- 'apio build' 

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

133 args = ["build"] + proj_arg 

134 result = sb.invoke_apio_cmd(apio, args) 

135 sb.assert_result_ok(result) 

136 assert "SUCCESS" in result.output 

137 assert "yosys -p" in result.output 

138 

139 # -- 'apio lint' 

140 args = ["lint"] + proj_arg 

141 result = sb.invoke_apio_cmd(apio, args) 

142 sb.assert_result_ok(result) 

143 assert "SUCCESS" in result.output 

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

145 

146 # -- 'apio format' 

147 args = ["format"] + proj_arg 

148 result = sb.invoke_apio_cmd(apio, args) 

149 sb.assert_result_ok(result) 

150 

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

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

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

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

155 result = sb.invoke_apio_cmd(apio, args) 

156 sb.assert_result_ok(result) 

157 

158 # -- 'apio test' 

159 args = ["test"] + proj_arg 

160 result = sb.invoke_apio_cmd(apio, args) 

161 sb.assert_result_ok(result) 

162 assert "SUCCESS" in result.output 

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

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

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

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

167 

168 # -- 'apio clean' 

169 args = ["clean"] + proj_arg 

170 result = sb.invoke_apio_cmd(apio, args) 

171 sb.assert_result_ok(result) 

172 assert "Cleanup completed" in result.output 

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

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

175 

176 # -- 'apio sim --no-gtkwave' 

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

178 result = sb.invoke_apio_cmd(apio, args) 

179 sb.assert_result_ok(result) 

180 assert "SUCCESS" in result.output 

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

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

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

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

185 

186 # -- 'apio clean' 

187 args = ["clean"] + proj_arg 

188 result = sb.invoke_apio_cmd(apio, args) 

189 sb.assert_result_ok(result) 

190 assert "Cleanup completed" in result.output 

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

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

193 

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

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

196 result = sb.invoke_apio_cmd(apio, args) 

197 sb.assert_result_ok(result) 

198 assert "SUCCESS" in result.output 

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

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

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

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

203 

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

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

206 result = sb.invoke_apio_cmd(apio, args) 

207 sb.assert_result_ok(result) 

208 assert "SUCCESS" in result.output 

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

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

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

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

213 

214 # -- 'apio clean' 

215 args = ["clean"] + proj_arg 

216 result = sb.invoke_apio_cmd(apio, args) 

217 sb.assert_result_ok(result) 

218 assert "Cleanup completed" in result.output 

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

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

221 

222 # -- 'apio report' 

223 args = ["report"] + proj_arg 

224 result = sb.invoke_apio_cmd(apio, args) 

225 sb.assert_result_ok(result) 

226 assert "SUCCESS" in result.output 

227 assert report_item in result.output 

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

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

230 

231 # -- 'apio graph -n' 

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

233 result = sb.invoke_apio_cmd(apio, args) 

234 sb.assert_result_ok(result) 

235 assert "SUCCESS" in result.output 

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

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

238 

239 # -- 'apio clean' 

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

241 args = ["clean"] + proj_arg 

242 result = sb.invoke_apio_cmd(apio, args) 

243 sb.assert_result_ok(result) 

244 assert "Cleanup completed" in result.output 

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

246 

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

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

249 

250 

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

252# subdirectories. This increases the test coverage. 

253def test_project_ice40_local_dir(apio_runner: ApioRunner): 

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

255 dir.""" 

256 _test_project( 

257 apio_runner, 

258 remote_proj_dir=False, 

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

260 testbench_file="main_tb.v", 

261 bitstream="hardware.bin", 

262 report_item="ICESTORM_LC", 

263 ) 

264 

265 

266def test_project_ice40_remote_dir(apio_runner: ApioRunner): 

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

268 the -p option.""" 

269 _test_project( 

270 apio_runner, 

271 remote_proj_dir=True, 

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

273 testbench_file="main_tb.v", 

274 bitstream="hardware.bin", 

275 report_item="ICESTORM_LC", 

276 ) 

277 

278 

279def test_project_ice40_system_verilog(apio_runner: ApioRunner): 

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

281 verilog files.""" 

282 _test_project( 

283 apio_runner, 

284 remote_proj_dir=False, 

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

286 testbench_file="main_tb.sv", 

287 bitstream="hardware.bin", 

288 report_item="ICESTORM_LC", 

289 ) 

290 

291 

292def test_project_ecp5_local_dir(apio_runner: ApioRunner): 

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

294 _test_project( 

295 apio_runner, 

296 remote_proj_dir=False, 

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

298 testbench_file="ledon_tb.v", 

299 bitstream="hardware.bit", 

300 report_item="ALU54B", 

301 ) 

302 

303 

304def test_project_ecp5_remote_dir(apio_runner: ApioRunner): 

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

306 _test_project( 

307 apio_runner, 

308 remote_proj_dir=True, 

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

310 testbench_file="ledon_tb.v", 

311 bitstream="hardware.bit", 

312 report_item="ALU54B", 

313 ) 

314 

315 

316def test_project_ecp5_system_verilog(apio_runner: ApioRunner): 

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

318 verilog files.""" 

319 _test_project( 

320 apio_runner, 

321 remote_proj_dir=False, 

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

323 testbench_file="ledon_tb.sv", 

324 bitstream="hardware.bit", 

325 report_item="ALU54B", 

326 ) 

327 

328 

329def test_project_gowin_local_dir(apio_runner: ApioRunner): 

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

331 _test_project( 

332 apio_runner, 

333 remote_proj_dir=False, 

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

335 testbench_file="blinky_tb.v", 

336 bitstream="hardware.fs", 

337 report_item="LUT4", 

338 ) 

339 

340 

341def test_project_gowin_remote_dir(apio_runner: ApioRunner): 

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

343 _test_project( 

344 apio_runner, 

345 remote_proj_dir=True, 

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

347 testbench_file="blinky_tb.v", 

348 bitstream="hardware.fs", 

349 report_item="LUT4", 

350 ) 

351 

352 

353def test_project_gowin_system_verilog(apio_runner: ApioRunner): 

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

355 verilog files.""" 

356 _test_project( 

357 apio_runner, 

358 remote_proj_dir=False, 

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

360 testbench_file="blinky_tb.sv", 

361 bitstream="hardware.fs", 

362 report_item="LUT4", 

363 )