Coverage for tests/unit_tests/commands/test_apio_api.py: 100%

163 statements  

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

1"""Test for the "apio api" command.""" 

2 

3# NOTE: 'apio api scan-devices' require apio packages (for libusb1 lib) 

4# and thus, is tested in the integration tests. 

5 

6import json 

7import os 

8from tests.conftest import ApioRunner 

9from apio.commands.apio import apio_top_cli as apio 

10 

11# TODO: Add more tests 

12 

13 

14def test_apio_api_get_boards(apio_runner: ApioRunner): 

15 """Test "apio api get-boards" """ 

16 

17 with apio_runner.in_sandbox() as sb: 

18 

19 # -- Execute "apio api get-boards -t xyz" (stdout) 

20 result = sb.invoke_apio_cmd(apio, ["api", "get-boards", "-t", "xyz"]) 

21 sb.assert_result_ok(result) 

22 assert "xyz" in result.output 

23 assert "alhambra-ii" in result.output 

24 

25 # -- Execute "apio api get-boards -t xyz -o <dir>" (file) 

26 path = sb.proj_dir / "apio.json" 

27 result = sb.invoke_apio_cmd( 

28 apio, ["api", "get-boards", "-t", "xyz", "-o", str(path)] 

29 ) 

30 sb.assert_result_ok(result) 

31 

32 # -- Read and verify the file. 

33 text = sb.read_file_text(path) 

34 data = json.loads(text) 

35 assert data["timestamp"] == "xyz" 

36 assert data["boards"]["alhambra-ii"] == { 

37 "description": "Alhambra II", 

38 "fpga": { 

39 "id": "ice40hx4k-tq144-8k", 

40 "part-num": "ICE40HX4K-TQ144", 

41 "arch": "ice40", 

42 "size": "8k", 

43 "ice40-params": { 

44 "package": "tq144:4k", 

45 "type": "hx8k", 

46 }, 

47 }, 

48 "programmer": {"id": "openfpgaloader"}, 

49 } 

50 

51 

52def test_apio_api_get_fpgas(apio_runner: ApioRunner): 

53 """Test "apio api get-fpgas" """ 

54 

55 with apio_runner.in_sandbox() as sb: 

56 

57 # -- Execute "apio api get-fpgas -t xyz" (stdout) 

58 result = sb.invoke_apio_cmd(apio, ["api", "get-fpgas", "-t", "xyz"]) 

59 sb.assert_result_ok(result) 

60 assert "xyz" in result.output 

61 assert "ice40hx4k-tq144-8k" in result.output 

62 

63 # -- Execute "apio api get-fpgas -t xyz -o <dir>" (file) 

64 path = sb.proj_dir / "apio.json" 

65 result = sb.invoke_apio_cmd( 

66 apio, ["api", "get-fpgas", "-t", "xyz", "-o", str(path)] 

67 ) 

68 sb.assert_result_ok(result) 

69 

70 # -- Read and verify the file. 

71 text = sb.read_file_text(path) 

72 data = json.loads(text) 

73 assert data["timestamp"] == "xyz" 

74 assert data["fpgas"]["ice40hx4k-tq144-8k"] == { 

75 "part-num": "ICE40HX4K-TQ144", 

76 "arch": "ice40", 

77 "size": "8k", 

78 "ice40-params": { 

79 "package": "tq144:4k", 

80 "type": "hx8k", 

81 }, 

82 } 

83 

84 

85def test_apio_api_get_programmers(apio_runner: ApioRunner): 

86 """Test "apio api get-programmers" """ 

87 

88 with apio_runner.in_sandbox() as sb: 

89 

90 # -- Execute "apio api get-programmers -t xyz" (stdout) 

91 result = sb.invoke_apio_cmd( 

92 apio, ["api", "get-programmers", "-t", "xyz"] 

93 ) 

94 sb.assert_result_ok(result) 

95 assert "xyz" in result.output 

96 assert "openfpgaloader" in result.output 

97 

98 # -- Execute "apio api get-programmers -t xyz -o <dir>" (file) 

99 path = sb.proj_dir / "apio.json" 

100 result = sb.invoke_apio_cmd( 

101 apio, ["api", "get-programmers", "-t", "xyz", "-o", str(path)] 

102 ) 

103 sb.assert_result_ok(result) 

104 

105 # -- Read and verify the file. 

106 text = sb.read_file_text(path) 

107 data = json.loads(text) 

108 assert data["timestamp"] == "xyz" 

109 assert data["programmers"]["openfpgaloader"] == { 

110 "command": "openFPGALoader", 

111 "args": "--force-terminal-mode --verify", 

112 } 

113 

114 

115def test_apio_api_get_commands(apio_runner: ApioRunner): 

116 """Test "apio api get-commands" """ 

117 

118 with apio_runner.in_sandbox() as sb: 

119 

120 # -- Execute "apio api get-commands -t xyz" (stdout) 

121 result = sb.invoke_apio_cmd(apio, ["api", "get-commands", "-t", "xyz"]) 

122 sb.assert_result_ok(result) 

123 assert "xyz" in result.output 

124 assert '"apio"' in result.output 

125 assert '"api"' in result.output 

126 assert '"get-boards"' in result.output 

127 

128 # -- Execute "apio api get-commands -t xyz -o <dir>" (file) 

129 path = sb.proj_dir / "apio.json" 

130 result = sb.invoke_apio_cmd( 

131 apio, ["api", "get-commands", "-t", "xyz", "-o", str(path)] 

132 ) 

133 sb.assert_result_ok(result) 

134 

135 # -- Read and verify the file. 

136 text = sb.read_file_text(path) 

137 data = json.loads(text) 

138 assert data["timestamp"] == "xyz" 

139 assert ( 

140 data["commands"]["apio"]["commands"]["api"]["commands"][ 

141 "get-boards" 

142 ] 

143 == {} 

144 ) 

145 

146 

147def test_apio_api_get_packages(apio_runner: ApioRunner): 

148 """Test "apio api get-packages" """ 

149 

150 with apio_runner.in_sandbox() as sb: 

151 

152 # -- Execute "apio api get-packages -t xyz" (stdout) 

153 result = sb.invoke_apio_cmd(apio, ["api", "get-packages", "-t", "xyz"]) 

154 sb.assert_result_ok(result) 

155 assert '"packages":' in result.output 

156 assert '"oss-cad-suite":' in result.output 

157 

158 # -- Execute "apio api get-packages -t xyz -o <dir>" (file) 

159 path = sb.proj_dir / "apio.json" 

160 result = sb.invoke_apio_cmd( 

161 apio, ["api", "get-packages", "-t", "xyz", "-o", str(path)] 

162 ) 

163 sb.assert_result_ok(result) 

164 

165 # -- Read and verify the file. 

166 text = sb.read_file_text(path) 

167 data = json.loads(text) 

168 assert data["timestamp"] == "xyz" 

169 assert ( 

170 "yosys-release-tag" 

171 in data["packages"]["oss-cad-suite"]["build-info"] 

172 ) 

173 

174 

175def test_apio_api_get_system(apio_runner: ApioRunner): 

176 """Test "apio api get-system" """ 

177 

178 with apio_runner.in_sandbox() as sb: 

179 

180 # -- Execute "apio api get-system -t xyz" (stdout) 

181 result = sb.invoke_apio_cmd(apio, ["api", "get-system", "-t", "xyz"]) 

182 sb.assert_result_ok(result) 

183 assert "xyz" in result.output 

184 assert '"apio-cli-version"' in result.output 

185 assert '"python-version"' in result.output 

186 

187 # -- Execute "apio api get-system -t xyz -o <dir>" (file) 

188 path = sb.proj_dir / "apio.json" 

189 result = sb.invoke_apio_cmd( 

190 apio, ["api", "get-system", "-t", "xyz", "-o", str(path)] 

191 ) 

192 sb.assert_result_ok(result) 

193 

194 # -- Read and verify the file. 

195 text = sb.read_file_text(path) 

196 data = json.loads(text) 

197 assert data["timestamp"] == "xyz" 

198 assert data["system"]["remote-config-url"].endswith(".jsonc") 

199 

200 

201def test_apio_api_get_project(apio_runner: ApioRunner): 

202 """Test "apio api get-project" """ 

203 

204 with apio_runner.in_sandbox() as sb: 

205 

206 # -- Create a fake apio project 

207 sb.write_default_apio_ini() 

208 

209 sb.write_file("synth0.v", "") 

210 sb.write_file("tb_0.sv", "") 

211 

212 os.makedirs("src1") 

213 sb.write_file("src1/synth1.sv", "") 

214 sb.write_file("src1/synth2.sv", "") 

215 sb.write_file("src1/tb1_tb.sv", "") 

216 

217 # -- Execute "apio api get-project -t xyz" (stdout) 

218 result = sb.invoke_apio_cmd(apio, ["api", "get-project", "-t", "xyz"]) 

219 sb.assert_result_ok(result) 

220 assert '"default"' in result.output 

221 assert '"envs"' in result.output 

222 

223 # -- Execute "apio api get-project -t xyz -o <dir>" (file) 

224 path = sb.proj_dir / "apio.json" 

225 result = sb.invoke_apio_cmd( 

226 apio, ["api", "get-project", "-t", "xyz", "-o", str(path)] 

227 ) 

228 sb.assert_result_ok(result) 

229 

230 # -- Read and verify the file. 

231 text = sb.read_file_text(path) 

232 data = json.loads(text) 

233 

234 print("\nActual data:") 

235 print(json.dumps(data, indent=2)) 

236 print() 

237 

238 assert data == { 

239 "timestamp": "xyz", 

240 "project": { 

241 "active-env": { 

242 "name": "default", 

243 "options": { 

244 "board": "alhambra-ii", 

245 "top-module": "main", 

246 }, 

247 }, 

248 "envs": [ 

249 "default", 

250 ], 

251 "synth-files": [ 

252 "synth0.v", 

253 "tb_0.sv", 

254 f"src1{os.sep}synth1.sv", 

255 f"src1{os.sep}synth2.sv", 

256 ], 

257 "test-benches": [ 

258 f"src1{os.sep}tb1_tb.sv", 

259 ], 

260 "board": { 

261 "id": "alhambra-ii", 

262 "is-custom": False, 

263 "definition": { 

264 "description": "Alhambra II", 

265 "fpga-id": "ice40hx4k-tq144-8k", 

266 "programmer": { 

267 "id": "openfpgaloader", 

268 "extra-args": "-b ice40_generic --vid ${VID} " 

269 + "--pid ${PID} --busdev-num ${BUS}:${DEV}", 

270 }, 

271 "usb": { 

272 "vid": "0403", 

273 "pid": "6010", 

274 "product-regex": "^Alhambra II.*", 

275 }, 

276 }, 

277 }, 

278 "fpga": { 

279 "id": "ice40hx4k-tq144-8k", 

280 "is-custom": False, 

281 "definition": { 

282 "part-num": "ICE40HX4K-TQ144", 

283 "arch": "ice40", 

284 "size": "8k", 

285 "ice40-params": { 

286 "type": "hx8k", 

287 "package": "tq144:4k", 

288 }, 

289 }, 

290 }, 

291 "programmer": { 

292 "id": "openfpgaloader", 

293 "is-custom": False, 

294 "definition": { 

295 "command": "openFPGALoader", 

296 "args": "--force-terminal-mode --verify", 

297 }, 

298 }, 

299 }, 

300 } 

301 

302 

303def test_apio_api_get_build_report(apio_runner: ApioRunner): 

304 """Test "apio api get-project" """ 

305 

306 with apio_runner.in_sandbox() as sb: 

307 

308 # -- Execute "apio examples fetch alhambra-ii/blinky" 

309 result = sb.invoke_apio_cmd( 

310 apio, ["examples", "fetch", "alhambra-ii/blinky"] 

311 ) 

312 sb.assert_result_ok(result) 

313 

314 # -- Execute "apio build" 

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

316 sb.assert_result_ok(result) 

317 

318 # -- Execute "apio api get-build-report -t xyz" (stdout) 

319 result = sb.invoke_apio_cmd( 

320 apio, ["api", "get-build-report", "-t", "xyz"] 

321 ) 

322 sb.assert_result_ok(result) 

323 

324 # -- Sanity check the output 

325 assert '"env": "default"' in result.output 

326 assert '"ICESTORM_LC":' in result.output 

327 assert '"CLK":' in result.output 

328 assert '"fmax_mhz":' in result.output 

329 

330 # -- Execute "apio api get-build-report -t xyz -o <dir>" (file) 

331 path = sb.proj_dir / "apio.json" 

332 result = sb.invoke_apio_cmd( 

333 apio, ["api", "get-build-report", "-t", "xyz", "-o", str(path)] 

334 ) 

335 sb.assert_result_ok(result) 

336 

337 # -- Read and sanity check the file. 

338 text = sb.read_file_text(path) 

339 data = json.loads(text) 

340 print(data) 

341 

342 assert data["timestamp"] == "xyz" 

343 assert data["build-report"]["env"] == "default" 

344 assert ( 

345 data["build-report"]["resources"]["ICESTORM_LC"]["available"] 

346 == 7680 

347 ) 

348 assert "fmax_mhz" in data["build-report"]["clocks"]["CLK"] 

349 

350 

351def test_apio_api_get_examples(apio_runner: ApioRunner): 

352 """Test "apio api get-examples" """ 

353 

354 with apio_runner.in_sandbox() as sb: 

355 

356 # -- Execute "apio api get-examples -t xyz" (stdout) 

357 result = sb.invoke_apio_cmd(apio, ["api", "get-examples", "-t", "xyz"]) 

358 sb.assert_result_ok(result) 

359 assert "xyz" in result.output 

360 assert '"alhambra-ii"' in result.output 

361 assert '"blinky"' in result.output 

362 

363 # -- Execute "apio api get-examples -t xyz -s boards -o <dir>" (file) 

364 path = sb.proj_dir / "apio.json" 

365 result = sb.invoke_apio_cmd( 

366 apio, ["api", "get-examples", "-t", "xyz", "-o", str(path)] 

367 ) 

368 sb.assert_result_ok(result) 

369 

370 # -- Read and verify the file. 

371 text = sb.read_file_text(path) 

372 data = json.loads(text) 

373 assert data["timestamp"] == "xyz" 

374 assert ( 

375 data["examples"]["alhambra-ii"]["blinky"]["description"] 

376 == "Blinking led" 

377 ) 

378 

379 

380def test_apio_api_scan_devices(apio_runner: ApioRunner): 

381 """Test "apio api scan-devices" """ 

382 

383 with apio_runner.in_sandbox() as sb: 

384 

385 # -- Execute "apio api scan-devices -t xyz". We run it in a 

386 # -- subprocess such that it releases the libusb1 file it uses. 

387 # -- This also means that it's not included in the pytest test 

388 # -- coverage report. 

389 result = sb.invoke_apio_cmd( 

390 apio, ["api", "scan-devices", "-t", "xyz"], in_subprocess=True 

391 ) 

392 sb.assert_result_ok(result) 

393 

394 assert "xyz" in result.output 

395 assert "usb-devices" in result.output 

396 assert "serial-devices" in result.output 

397 

398 # -- Execute "apio api get-boards -t xyz -s boards -o <dir>" (file) 

399 path = sb.proj_dir / "apio.json" 

400 

401 result = sb.invoke_apio_cmd( 

402 apio, 

403 ["api", "scan-devices", "-t", "xyz", "-o", str(path)], 

404 in_subprocess=True, 

405 ) 

406 sb.assert_result_ok(result) 

407 

408 # -- Read and verify the output file. Since we don't know what 

409 # -- devices the platform has, we just check for the section keys. 

410 text = sb.read_file_text(path) 

411 data = json.loads(text) 

412 assert data["timestamp"] == "xyz" 

413 assert "usb-devices" in data 

414 assert "serial-devices" in data 

415 

416 

417def test_apio_api_echo(apio_runner: ApioRunner): 

418 """Test "apio api echo" """ 

419 

420 with apio_runner.in_sandbox() as sb: 

421 

422 # -- Execute "apio api scan-devices -t xyz". We run it in a 

423 # -- subprocess such that it releases the libusb1 file it uses. 

424 # -- This also means that it's not included in the pytest test 

425 # -- coverage report. 

426 result = sb.invoke_apio_cmd( 

427 apio, 

428 ["api", "echo", "-t", "Hello world", "-s", "OK"], 

429 in_subprocess=True, 

430 ) 

431 sb.assert_result_ok(result) 

432 

433 assert "Hello world" in result.output