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

125 statements  

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

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

45 } 

46 

47 

48def test_apio_api_get_fpgas(apio_runner: ApioRunner): 

49 """Test "apio api get-fpgas" """ 

50 

51 with apio_runner.in_sandbox() as sb: 

52 

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

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

55 sb.assert_result_ok(result) 

56 assert "xyz" in result.output 

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

58 

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

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

61 result = sb.invoke_apio_cmd( 

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

63 ) 

64 sb.assert_result_ok(result) 

65 

66 # -- Read and verify the file. 

67 text = sb.read_file(path) 

68 data = json.loads(text) 

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

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

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

72 "arch": "ice40", 

73 "size": "8k", 

74 } 

75 

76 

77def test_apio_api_get_programmers(apio_runner: ApioRunner): 

78 """Test "apio api get-programmers" """ 

79 

80 with apio_runner.in_sandbox() as sb: 

81 

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

83 result = sb.invoke_apio_cmd( 

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

85 ) 

86 sb.assert_result_ok(result) 

87 assert "xyz" in result.output 

88 assert "openfpgaloader" in result.output 

89 

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

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

92 result = sb.invoke_apio_cmd( 

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

94 ) 

95 sb.assert_result_ok(result) 

96 

97 # -- Read and verify the file. 

98 text = sb.read_file(path) 

99 data = json.loads(text) 

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

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

102 "command": "openFPGALoader", 

103 "args": "", 

104 } 

105 

106 

107def test_apio_api_get_commands(apio_runner: ApioRunner): 

108 """Test "apio api get-commands" """ 

109 

110 with apio_runner.in_sandbox() as sb: 

111 

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

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

114 sb.assert_result_ok(result) 

115 assert "xyz" in result.output 

116 assert '"apio"' in result.output 

117 assert '"api"' in result.output 

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

119 

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

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

122 result = sb.invoke_apio_cmd( 

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

124 ) 

125 sb.assert_result_ok(result) 

126 

127 # -- Read and verify the file. 

128 text = sb.read_file(path) 

129 data = json.loads(text) 

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

131 assert ( 

132 data["commands"]["apio"]["commands"]["api"]["commands"][ 

133 "get-boards" 

134 ] 

135 == {} 

136 ) 

137 

138 

139def test_apio_api_get_system(apio_runner: ApioRunner): 

140 """Test "apio api get-system" """ 

141 

142 with apio_runner.in_sandbox() as sb: 

143 

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

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

146 sb.assert_result_ok(result) 

147 assert "xyz" in result.output 

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

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

150 

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

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

153 result = sb.invoke_apio_cmd( 

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

155 ) 

156 sb.assert_result_ok(result) 

157 

158 # -- Read and verify the file. 

159 text = sb.read_file(path) 

160 data = json.loads(text) 

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

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

163 

164 

165def test_apio_api_get_project(apio_runner: ApioRunner): 

166 """Test "apio api get-project" """ 

167 

168 with apio_runner.in_sandbox() as sb: 

169 

170 # -- Create a fake apio project 

171 sb.write_default_apio_ini() 

172 

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

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

175 

176 os.makedirs("src1") 

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

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

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

180 

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

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

183 sb.assert_result_ok(result) 

184 assert '"default"' in result.output 

185 assert '"envs"' in result.output 

186 

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

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

189 result = sb.invoke_apio_cmd( 

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

191 ) 

192 sb.assert_result_ok(result) 

193 

194 # -- Read and verify the file. 

195 text = sb.read_file(path) 

196 data = json.loads(text) 

197 

198 assert data == { 

199 "timestamp": "xyz", 

200 "project": { 

201 "active-env": { 

202 "name": "default", 

203 "options": { 

204 "board": "alhambra-ii", 

205 "top-module": "main", 

206 }, 

207 }, 

208 "envs": [ 

209 "default", 

210 ], 

211 "synth-files": [ 

212 "synth0.v", 

213 "tb_0.sv", 

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

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

216 ], 

217 "test-benches": [ 

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

219 ], 

220 "board": { 

221 "id": "alhambra-ii", 

222 "description": "Alhambra II", 

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

224 "programmer": { 

225 "extra-args": "--verify -b ice40_generic" 

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

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

228 "id": "openfpgaloader", 

229 }, 

230 "usb": { 

231 "pid": "6010", 

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

233 "vid": "0403", 

234 }, 

235 }, 

236 "fpga": { 

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

238 "arch": "ice40", 

239 "pack": "tq144:4k", 

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

241 "size": "8k", 

242 "type": "hx8k", 

243 }, 

244 "programmer": { 

245 "id": "openfpgaloader", 

246 "args": "", 

247 "command": "openFPGALoader", 

248 }, 

249 }, 

250 } 

251 

252 

253def test_apio_api_get_examples(apio_runner: ApioRunner): 

254 """Test "apio api get-examples" """ 

255 

256 with apio_runner.in_sandbox() as sb: 

257 

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

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

260 sb.assert_result_ok(result) 

261 assert "xyz" in result.output 

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

263 assert '"blinky"' in result.output 

264 

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

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

267 result = sb.invoke_apio_cmd( 

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

269 ) 

270 sb.assert_result_ok(result) 

271 

272 # -- Read and verify the file. 

273 text = sb.read_file(path) 

274 data = json.loads(text) 

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

276 assert ( 

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

278 == "Blinking led" 

279 ) 

280 

281 

282def test_apio_api_scan_devices(apio_runner: ApioRunner): 

283 """Test "apio api scan-devices" """ 

284 

285 with apio_runner.in_sandbox() as sb: 

286 

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

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

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

290 # -- coverage report. 

291 result = sb.invoke_apio_cmd( 

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

293 ) 

294 sb.assert_result_ok(result) 

295 

296 assert "xyz" in result.output 

297 assert "usb-devices" in result.output 

298 assert "serial-devices" in result.output 

299 

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

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

302 

303 result = sb.invoke_apio_cmd( 

304 apio, 

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

306 in_subprocess=True, 

307 ) 

308 sb.assert_result_ok(result) 

309 

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

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

312 text = sb.read_file(path) 

313 data = json.loads(text) 

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

315 assert "usb-devices" in data 

316 assert "serial-devices" in data 

317 

318 

319def test_apio_api_echo(apio_runner: ApioRunner): 

320 """Test "apio api echo" """ 

321 

322 with apio_runner.in_sandbox() as sb: 

323 

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

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

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

327 # -- coverage report. 

328 result = sb.invoke_apio_cmd( 

329 apio, 

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

331 in_subprocess=True, 

332 ) 

333 sb.assert_result_ok(result) 

334 

335 assert "Hello world" in result.output