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

147 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-09 01:55 +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(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(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(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-boards -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(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_system(apio_runner: ApioRunner): 

148 """Test "apio api get-system" """ 

149 

150 with apio_runner.in_sandbox() as sb: 

151 

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

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

154 sb.assert_result_ok(result) 

155 assert "xyz" in result.output 

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

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

158 

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

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

161 result = sb.invoke_apio_cmd( 

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

163 ) 

164 sb.assert_result_ok(result) 

165 

166 # -- Read and verify the file. 

167 text = sb.read_file(path) 

168 data = json.loads(text) 

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

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

171 

172 

173def test_apio_api_get_project(apio_runner: ApioRunner): 

174 """Test "apio api get-project" """ 

175 

176 with apio_runner.in_sandbox() as sb: 

177 

178 # -- Create a fake apio project 

179 sb.write_default_apio_ini() 

180 

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

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

183 

184 os.makedirs("src1") 

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

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

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

188 

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

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

191 sb.assert_result_ok(result) 

192 assert '"default"' in result.output 

193 assert '"envs"' in result.output 

194 

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

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

197 result = sb.invoke_apio_cmd( 

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

199 ) 

200 sb.assert_result_ok(result) 

201 

202 # -- Read and verify the file. 

203 text = sb.read_file(path) 

204 data = json.loads(text) 

205 

206 assert data == { 

207 "timestamp": "xyz", 

208 "project": { 

209 "active-env": { 

210 "name": "default", 

211 "options": { 

212 "board": "alhambra-ii", 

213 "top-module": "main", 

214 }, 

215 }, 

216 "envs": [ 

217 "default", 

218 ], 

219 "synth-files": [ 

220 "synth0.v", 

221 "tb_0.sv", 

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

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

224 ], 

225 "test-benches": [ 

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

227 ], 

228 "board": { 

229 "id": "alhambra-ii", 

230 "description": "Alhambra II", 

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

232 "programmer": { 

233 "extra-args": "-b ice40_generic" 

234 + " --vid ${VID} --pid ${PID} " 

235 "--busdev-num ${BUS}:${DEV}", 

236 "id": "openfpgaloader", 

237 }, 

238 "usb": { 

239 "pid": "6010", 

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

241 "vid": "0403", 

242 }, 

243 }, 

244 "fpga": { 

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

246 "arch": "ice40", 

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

248 "size": "8k", 

249 "ice40-params": { 

250 "package": "tq144:4k", 

251 "type": "hx8k", 

252 }, 

253 }, 

254 "programmer": { 

255 "id": "openfpgaloader", 

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

257 "command": "openFPGALoader", 

258 }, 

259 }, 

260 } 

261 

262 

263def test_apio_api_get_build_report(apio_runner: ApioRunner): 

264 """Test "apio api get-project" """ 

265 

266 with apio_runner.in_sandbox() as sb: 

267 

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

269 result = sb.invoke_apio_cmd( 

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

271 ) 

272 sb.assert_result_ok(result) 

273 

274 # -- Execute "apio build" 

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

276 sb.assert_result_ok(result) 

277 

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

279 result = sb.invoke_apio_cmd( 

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

281 ) 

282 sb.assert_result_ok(result) 

283 

284 # -- Sanity check the output 

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

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

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

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

289 

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

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

292 result = sb.invoke_apio_cmd( 

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

294 ) 

295 sb.assert_result_ok(result) 

296 

297 # -- Read and sanity check the file. 

298 text = sb.read_file(path) 

299 data = json.loads(text) 

300 print(data) 

301 

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

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

304 assert ( 

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

306 == 7680 

307 ) 

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

309 

310 

311def test_apio_api_get_examples(apio_runner: ApioRunner): 

312 """Test "apio api get-examples" """ 

313 

314 with apio_runner.in_sandbox() as sb: 

315 

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

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

318 sb.assert_result_ok(result) 

319 assert "xyz" in result.output 

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

321 assert '"blinky"' in result.output 

322 

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

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

325 result = sb.invoke_apio_cmd( 

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

327 ) 

328 sb.assert_result_ok(result) 

329 

330 # -- Read and verify the file. 

331 text = sb.read_file(path) 

332 data = json.loads(text) 

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

334 assert ( 

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

336 == "Blinking led" 

337 ) 

338 

339 

340def test_apio_api_scan_devices(apio_runner: ApioRunner): 

341 """Test "apio api scan-devices" """ 

342 

343 with apio_runner.in_sandbox() as sb: 

344 

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

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

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

348 # -- coverage report. 

349 result = sb.invoke_apio_cmd( 

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

351 ) 

352 sb.assert_result_ok(result) 

353 

354 assert "xyz" in result.output 

355 assert "usb-devices" in result.output 

356 assert "serial-devices" in result.output 

357 

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

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

360 

361 result = sb.invoke_apio_cmd( 

362 apio, 

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

364 in_subprocess=True, 

365 ) 

366 sb.assert_result_ok(result) 

367 

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

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

370 text = sb.read_file(path) 

371 data = json.loads(text) 

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

373 assert "usb-devices" in data 

374 assert "serial-devices" in data 

375 

376 

377def test_apio_api_echo(apio_runner: ApioRunner): 

378 """Test "apio api echo" """ 

379 

380 with apio_runner.in_sandbox() as sb: 

381 

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

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

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

385 # -- coverage report. 

386 result = sb.invoke_apio_cmd( 

387 apio, 

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

389 in_subprocess=True, 

390 ) 

391 sb.assert_result_ok(result) 

392 

393 assert "Hello world" in result.output