VirtualBox

source: vbox/trunk/src/VBox/Devices/testcase/tstDeviceCfg.cpp@ 83062

最後變更 在這個檔案從83062是 83062,由 vboxsync 提交於 5 年 前

Devices/testcase/tstDevice: Implement JSON based configuration loader for the device testbench

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 21.2 KB
 
1/* $Id: tstDeviceCfg.cpp 83062 2020-02-12 17:45:51Z vboxsync $ */
2/** @file
3 * tstDevice - Configuration loader.
4 */
5
6/*
7 * Copyright (C) 2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DEFAULT /** @todo */
23#include <VBox/types.h>
24#include <iprt/errcore.h>
25#include <iprt/json.h>
26#include <iprt/mem.h>
27#include <iprt/message.h>
28#include <iprt/string.h>
29
30#include "tstDeviceCfg.h"
31
32
33/*********************************************************************************************************************************
34* Defined Constants And Macros *
35*********************************************************************************************************************************/
36
37
38/*********************************************************************************************************************************
39* Structures and Typedefs *
40*********************************************************************************************************************************/
41
42
43/**
44 * Wrapper around RTErrInfoSetV / RTMsgErrorV.
45 *
46 * @returns @a rc
47 * @param pErrInfo Extended error info.
48 * @param rc The return code.
49 * @param pszFormat The message format.
50 * @param ... The message format arguments.
51 */
52static int tstDevCfgErrorRc(PRTERRINFO pErrInfo, int rc, const char *pszFormat, ...)
53{
54 va_list va;
55 va_start(va, pszFormat);
56 if (pErrInfo)
57 RTErrInfoSetV(pErrInfo, rc, pszFormat, va);
58 else
59 RTMsgErrorV(pszFormat, va);
60 va_end(va);
61 return rc;
62}
63
64
65/**
66 * Destroys the given configuration item array freeing all allocated resources.
67 *
68 * @returns nothing.
69 * @param paCfg The configuration item array to destroy.
70 * @param cCfgItems Number of items in the array.
71 */
72static void tstDevCfgItemsDestroy(PTSTDEVCFGITEM paCfg, uint32_t cCfgItems)
73{
74 RT_NOREF(paCfg, cCfgItems);
75}
76
77
78/**
79 * Loads the given string from the config, creating a duplicate.
80 *
81 * @returns VBox status code.
82 * @param hJsonTop The JSON top value handle containing the value to load.
83 * @param pszValName The value name.
84 * @param ppszValCopy Where to store the pointer to the value on success, must be freed with RTStrFree().
85 * @param fMissingOk Flag whether it is considered success if the value does not exist.
86 * @param pErrInfo Pointer to the error info to fill on error.
87 */
88static int tstDevCfgLoadString(RTJSONVAL hJsonTop, const char *pszValName, char **ppszValCopy, bool fMissingOk, PRTERRINFO pErrInfo)
89{
90 RTJSONVAL hJsonVal;
91 int rc = RTJsonValueQueryByName(hJsonTop, pszValName, &hJsonVal);
92 if (RT_SUCCESS(rc))
93 {
94 const char *pszVal = RTJsonValueGetString(hJsonVal);
95 if (RT_LIKELY(pszVal))
96 {
97 *ppszValCopy = RTStrDup(pszVal);
98 if (RT_UNLIKELY(!*ppszValCopy))
99 rc = tstDevCfgErrorRc(pErrInfo, VERR_NO_STR_MEMORY, "tstDevCfg/JSON: Out of memory allocating memory for value of \"%s\" ", pszValName);
100 }
101 else
102 rc = tstDevCfgErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "tstDevCfg/JSON: \"%s\" is not a string", pszValName);
103
104 RTJsonValueRelease(hJsonVal);
105 }
106 else if ( rc == VERR_NOT_FOUND
107 && fMissingOk)
108 {
109 *ppszValCopy = NULL;
110 rc = VINF_SUCCESS;
111 }
112 else
113 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"%s\"", pszValName);
114
115 return rc;
116}
117
118
119/**
120 * Loads a bool value using the given value name from the config.
121 *
122 * @returns VBox status code.
123 * @param hJsonTop The JSON top value handle containing the value to load.
124 * @param pszValName The value name.
125 * @param pf Where to store the value on success.
126 * @param pErrInfo Pointer to the error info to fill on error.
127 */
128static int tstDevCfgLoadBool(RTJSONVAL hJsonTop, const char *pszValName, bool *pf, PRTERRINFO pErrInfo)
129{
130 int rc = RTJsonValueQueryBooleanByName(hJsonTop, pszValName, pf);
131 if (RT_FAILURE(rc))
132 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query boolean value of \"%s\"", pszValName);
133
134 return rc;
135}
136
137
138/**
139 * Determines the config item type from the given.value.
140 *
141 * @returns VBox status code.
142 * @param hJsonTop The JSON top value handle containing the value to load.
143 * @param pszValName The value name.
144 * @param penmCfgItemType Where to store the determined config item type on success.
145 * @param pErrInfo Pointer to the error info to fill on error.
146 */
147static int tstDevCfgLoadCfgItemType(RTJSONVAL hJsonTop, const char *pszValName, PTSTDEVCFGITEMTYPE penmCfgItemType, PRTERRINFO pErrInfo)
148{
149 RTJSONVAL hJsonVal;
150 int rc = RTJsonValueQueryByName(hJsonTop, pszValName, &hJsonVal);
151 if (RT_SUCCESS(rc))
152 {
153 const char *pszVal = RTJsonValueGetString(hJsonVal);
154 if (!RTStrCmp(pszVal, "Integer"))
155 *penmCfgItemType = TSTDEVCFGITEMTYPE_INTEGER;
156 else if (!RTStrCmp(pszVal, "String"))
157 *penmCfgItemType = TSTDEVCFGITEMTYPE_STRING;
158 else
159 rc = tstDevCfgErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "tstDevCfg/JSON: \"%s\" is not a valid config item type", pszVal);
160
161 RTJsonValueRelease(hJsonVal);
162 }
163 else
164 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"%s\"", pszValName);
165
166 return rc;
167}
168
169
170/**
171 * Loads the config item value from the given config based on the earlier determined type.
172 *
173 * @returns VBox status code.
174 * @param hJsonTop The JSON top value handle containing the value to load.
175 * @param pszValName The value name.
176 * @param pCfg Where to store the retrieved config value.
177 * @param enmCfgItemType The earlier determined config item type.
178 * @param pErrInfo Pointer to the error info to fill on error.
179 */
180static int tstDevCfgLoadCfgItemValue(RTJSONVAL hJsonTop, const char *pszValName, PTSTDEVCFGITEM pCfg, TSTDEVCFGITEMTYPE enmCfgItemType, PRTERRINFO pErrInfo)
181{
182 RTJSONVAL hJsonVal;
183
184 int rc = RTJsonValueQueryByName(hJsonTop, pszValName, &hJsonVal);
185 if (RT_SUCCESS(rc))
186 {
187 RTJSONVALTYPE enmJsonType = RTJsonValueGetType(hJsonVal);
188
189 if ( ( enmJsonType == RTJSONVALTYPE_INTEGER
190 && enmCfgItemType == TSTDEVCFGITEMTYPE_INTEGER)
191 || ( enmJsonType == RTJSONVALTYPE_STRING
192 && enmCfgItemType == TSTDEVCFGITEMTYPE_STRING))
193 {
194 switch (enmCfgItemType)
195 {
196 case TSTDEVCFGITEMTYPE_INTEGER:
197 {
198 rc = RTJsonValueQueryInteger(hJsonVal, &pCfg->u.i64);
199 break;
200 }
201 case TSTDEVCFGITEMTYPE_STRING:
202 {
203 const char *psz = RTJsonValueGetString(hJsonVal);
204 AssertPtr(psz);
205
206 pCfg->u.psz = RTStrDup(psz);
207 if (RT_UNLIKELY(!pCfg->u.psz))
208 rc = VERR_NO_STR_MEMORY;
209 break;
210 }
211 default:
212 AssertFailed(); /* Should never ever get here. */
213 rc = tstDevCfgErrorRc(pErrInfo, VERR_INTERNAL_ERROR, "tstDevCfg/JSON: Invalid config item type %u", enmCfgItemType);
214 }
215
216 if (RT_SUCCESS(rc))
217 pCfg->enmType = enmCfgItemType;
218 else
219 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query config item value");
220 }
221 else
222 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: JSON value type doesn't match config item type (got %u, expected %u)", enmJsonType, enmCfgItemType);
223
224 RTJsonValueRelease(hJsonVal);
225 }
226 else
227 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"%s\"", pszValName);
228
229 return rc;
230}
231
232
233/**
234 * Loads the test configuration from the given JSON value.
235 *
236 * @returns VBox status code.
237 * @param paCfg The configuration array to fill.
238 * @param cCfgItems Number of configuration items.
239 * @param hJsonValCfg The JSON value to gather the config items from.
240 * @param pErrInfo Pointer to error info.
241 */
242static int tstDevCfgLoadTestCfgWorker(PTSTDEVCFGITEM paCfg, uint32_t cCfgItems, RTJSONVAL hJsonValCfg, PRTERRINFO pErrInfo)
243{
244 int rc = VINF_SUCCESS;
245
246 for (uint32_t i = 0; i < cCfgItems && RT_SUCCESS(rc); i++)
247 {
248 PTSTDEVCFGITEM pCfg = &paCfg[i];
249 RTJSONVAL hJsonCfg;
250
251 rc = RTJsonValueQueryByIndex(hJsonValCfg, i, &hJsonCfg);
252 if (RT_SUCCESS(rc))
253 {
254 TSTDEVCFGITEMTYPE enmCfgItemType;
255
256 rc = tstDevCfgLoadString(hJsonCfg, "Key", (char **)&pCfg->pszKey, false /*fMissingOk*/, pErrInfo);
257 if (RT_SUCCESS(rc))
258 rc = tstDevCfgLoadCfgItemType(hJsonCfg, "Type", &enmCfgItemType, pErrInfo);
259 if (RT_SUCCESS(rc))
260 rc = tstDevCfgLoadCfgItemValue(hJsonCfg, "Value", pCfg, enmCfgItemType, pErrInfo);
261 }
262 else
263 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query config item %u", i);
264 }
265
266 return rc;
267}
268
269
270/**
271 * Loads a single testcase from the given JSON config value.
272 *
273 * @returns VBox status code.
274 * @param ppszTestcaseId Where to store the testcase ID on success.
275 * @param pcTestcaseCfgItems Where to store the number of testcase config items on success.
276 * @param ppTestcaseCfg Where to store the testcase config on success.
277 * @param pErrInfo Pointer to error info.
278 */
279static int tstDevCfgLoadTestcase(RTJSONVAL hJsonTestcase, const char **ppszTestcaseId, uint32_t *pcTestcaseCfgItems, PCTSTDEVCFGITEM *ppTestcaseCfg, PRTERRINFO pErrInfo)
280{
281 char *pszTestcaseId = NULL;
282 int rc = tstDevCfgLoadString(hJsonTestcase, "Testcase", &pszTestcaseId, false /*fMissingOk*/, pErrInfo);
283 if (RT_SUCCESS(rc))
284 {
285 RTJSONVAL hJsonValCfg;
286 rc = RTJsonValueQueryByName(hJsonTestcase, "Config", &hJsonValCfg);
287 if (RT_SUCCESS(rc))
288 {
289 unsigned cCfgItems = 0;
290 rc = RTJsonValueQueryArraySize(hJsonValCfg, &cCfgItems);
291 if (RT_SUCCESS(rc))
292 {
293 if (cCfgItems > 0)
294 {
295 size_t cbCfg = sizeof(TSTDEVCFGITEM) * cCfgItems;
296 PTSTDEVCFGITEM paCfg = (PTSTDEVCFGITEM)RTMemAllocZ(cbCfg);
297 if (paCfg)
298 {
299 rc = tstDevCfgLoadTestCfgWorker(paCfg, cCfgItems, hJsonValCfg, pErrInfo);
300 if (RT_SUCCESS(rc))
301 {
302 *ppszTestcaseId = pszTestcaseId;
303 *pcTestcaseCfgItems = cCfgItems;
304 *ppTestcaseCfg = paCfg;
305 }
306 else /* Error already set, free test config structure. */
307 tstDevCfgItemsDestroy(paCfg, cCfgItems);
308 }
309 else
310 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to allocate %zu bytes for the test config structure", cbCfg);
311 }
312 }
313 else
314 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"Config\" is not an array");
315
316 RTJsonValueRelease(hJsonValCfg);
317 }
318 else
319 tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"Config\" value");
320
321 if (RT_FAILURE(rc))
322 RTStrFree(pszTestcaseId);
323 }
324
325 return rc;
326}
327
328
329/**
330 * Loads the testcase descriptions from the config.
331 *
332 * @returns VBox status code.
333 * @param pDevTest Where to store the testcases config on success.
334 * @param hJsonValTest Where to load the testcases config from.
335 * @param pErrInfo Pointer to error info.
336 */
337static int tstDevCfgLoadTestcases(PTSTDEVTEST pDevTest, RTJSONVAL hJsonValTest, PRTERRINFO pErrInfo)
338{
339 RTJSONVAL hJsonValTestcases;
340 int rc = RTJsonValueQueryByName(hJsonValTest, "Testcases", &hJsonValTestcases);
341 if (RT_SUCCESS(rc))
342 {
343 unsigned cTestcases = 0;
344 rc = RTJsonValueQueryArraySize(hJsonValTestcases, &cTestcases);
345 if (RT_SUCCESS(rc))
346 {
347 pDevTest->cTestcases = cTestcases;
348 if (cTestcases > 0)
349 {
350 size_t cbArray = sizeof(void *) * 2 * cTestcases + cTestcases * sizeof(uint32_t); /* One for the testcase ID and one for the associated configuration. */
351 uint8_t *pbTmp = (uint8_t *)RTMemAllocZ(cbArray);
352 if (pbTmp)
353 {
354 pDevTest->papszTestcaseIds = (const char **)pbTmp;
355 pDevTest->pacTestcaseCfgItems = (uint32_t *)&pDevTest->papszTestcaseIds[cTestcases];
356 pDevTest->papTestcaseCfg = (PCTSTDEVCFGITEM *)&pDevTest->pacTestcaseCfgItems[cTestcases];
357
358 for (uint32_t i = 0; i < cTestcases; i++)
359 {
360 RTJSONVAL hJsonTestcase;
361
362 rc = RTJsonValueQueryByIndex(hJsonValTestcases, i, &hJsonTestcase);
363 if (RT_SUCCESS(rc))
364 {
365 rc = tstDevCfgLoadTestcase(hJsonTestcase, &pDevTest->papszTestcaseIds[i],
366 &pDevTest->pacTestcaseCfgItems[i], &pDevTest->papTestcaseCfg[i], pErrInfo);
367 RTJsonValueRelease(hJsonTestcase);
368 }
369 else
370 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query testcase item %u", i);
371 }
372 }
373 else
374 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to allocate %zu bytes for the testcases", cbArray);
375 }
376 else
377 rc = tstDevCfgErrorRc(pErrInfo, VERR_INVALID_PARAMETER, "tstDevCfg/JSON: \"Testcases\" doesn't contain anything");
378 }
379 else
380 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"Testcases\" is not an array");
381
382 RTJsonValueRelease(hJsonValTestcases);
383 }
384 else
385 tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"Testcases\" value");
386
387 return rc;
388}
389
390
391/**
392 * Loads a test config from the given JSON object.
393 *
394 * @returns VBox status code.
395 * @param pDevTest Where to store the test config on success.
396 * @param hJsonValTest Where to load the test config from.
397 * @param pErrInfo Pointer to error info.
398 */
399static int tstDevCfgLoadTest(PTSTDEVTEST pDevTest, RTJSONVAL hJsonValTest, PRTERRINFO pErrInfo)
400{
401 int rc = tstDevCfgLoadBool(hJsonValTest, "R0Enabled", &pDevTest->fR0Enabled, pErrInfo);
402 if (RT_SUCCESS(rc))
403 rc = tstDevCfgLoadBool(hJsonValTest, "RCEnabled", &pDevTest->fRCEnabled, pErrInfo);
404
405 if (RT_SUCCESS(rc))
406 {
407 RTJSONVAL hJsonValCfg;
408 rc = RTJsonValueQueryByName(hJsonValTest, "Config", &hJsonValCfg);
409 if (RT_SUCCESS(rc))
410 {
411 unsigned cCfgItems = 0;
412 rc = RTJsonValueQueryArraySize(hJsonValCfg, &cCfgItems);
413 if (RT_SUCCESS(rc))
414 {
415 pDevTest->cCfgItems = cCfgItems;
416 if (cCfgItems > 0)
417 {
418 size_t cbCfg = sizeof(TSTDEVCFGITEM) * cCfgItems;
419 PTSTDEVCFGITEM paCfg = (PTSTDEVCFGITEM)RTMemAllocZ(cbCfg);
420 if (paCfg)
421 {
422 rc = tstDevCfgLoadTestCfgWorker(paCfg, cCfgItems, hJsonValCfg, pErrInfo);
423 if (RT_SUCCESS(rc))
424 pDevTest->paCfgItems = paCfg;
425 else /* Error already set, free test config structure. */
426 tstDevCfgItemsDestroy(paCfg, cCfgItems);
427 }
428 else
429 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to allocate %zu bytes for the test config structure", cbCfg);
430 }
431 }
432 else
433 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"Config\" is not an array");
434
435 RTJsonValueRelease(hJsonValCfg);
436 }
437 else
438 tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"Config\" value");
439 }
440
441 /* Load the test configs. */
442 if (RT_SUCCESS(rc))
443 rc = tstDevCfgLoadTestcases(pDevTest, hJsonValTest, pErrInfo);
444
445 return rc;
446}
447
448
449/**
450 * Configuration loader worker.
451 *
452 * @returns VBox status code.
453 * @param pDevTstCfg The test config structure to fill.
454 * @param hJsonRoot Handle of the root JSON value.
455 * @param hJsonValDeviceTests Handle to the test JSON array.
456 * @param pErrInfo Pointer to the error info.
457 */
458static int tstDevCfgLoadWorker(PTSTDEVCFG pDevTstCfg, RTJSONVAL hJsonRoot, RTJSONVAL hJsonValDeviceTests, PRTERRINFO pErrInfo)
459{
460 int rc = tstDevCfgLoadString(hJsonRoot, "PdmR3Module", (char **)&pDevTstCfg->pszPdmR3Mod, false /*fMissingOk*/, pErrInfo);
461 if (RT_SUCCESS(rc))
462 rc = tstDevCfgLoadString(hJsonRoot, "PdmR0Module", (char **)&pDevTstCfg->pszPdmR0Mod, true /*fMissingOk*/, pErrInfo);
463 if (RT_SUCCESS(rc))
464 rc = tstDevCfgLoadString(hJsonRoot, "PdmRCModule", (char **)&pDevTstCfg->pszPdmRCMod, true /*fMissingOk*/, pErrInfo);
465 if (RT_SUCCESS(rc))
466 rc = tstDevCfgLoadString(hJsonRoot, "TestcaseModule", (char **)&pDevTstCfg->pszTstDevMod, true /*fMissingOk*/, pErrInfo);
467 if (RT_SUCCESS(rc))
468 rc = tstDevCfgLoadString(hJsonRoot, "Device", (char **)&pDevTstCfg->pszDevName, false /*fMissingOk*/, pErrInfo);
469
470 if (RT_SUCCESS(rc))
471 {
472 /* Load the individual test configs. */
473 for (uint32_t idx = 0; idx < pDevTstCfg->cTests && RT_SUCCESS(rc); idx++)
474 {
475 RTJSONVAL hJsonValTest;
476
477 rc = RTJsonValueQueryByIndex(hJsonValDeviceTests, idx, &hJsonValTest);
478 if (RT_SUCCESS(rc))
479 {
480 rc = tstDevCfgLoadTest(&pDevTstCfg->aTests[idx], hJsonValTest, pErrInfo);
481 RTJsonValueRelease(hJsonValTest);
482 }
483 else
484 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query test %u from \"DeviceTests\"", idx);
485 }
486 }
487
488 return rc;
489}
490
491
492DECLHIDDEN(int) tstDevCfgLoad(const char *pszCfgFilename, PRTERRINFO pErrInfo, PCTSTDEVCFG *ppDevTstCfg)
493{
494 RTJSONVAL hJsonRoot;
495 int rc = RTJsonParseFromFile(&hJsonRoot, pszCfgFilename, pErrInfo);
496 if (RT_SUCCESS(rc))
497 {
498 RTJSONVAL hJsonValDeviceTests;
499
500 rc = RTJsonValueQueryByName(hJsonRoot, "DeviceTests", &hJsonValDeviceTests);
501 if (RT_SUCCESS(rc))
502 {
503 unsigned cTests = 0;
504 rc = RTJsonValueQueryArraySize(hJsonValDeviceTests, &cTests);
505 if (RT_SUCCESS(rc))
506 {
507 if (cTests > 0)
508 {
509 size_t cbTestCfg = RT_UOFFSETOF_DYN(TSTDEVCFG, aTests[cTests]);
510 PTSTDEVCFG pDevTstCfg = (PTSTDEVCFG)RTMemAllocZ(cbTestCfg);
511 if (pDevTstCfg)
512 {
513 pDevTstCfg->cTests = cTests;
514 rc = tstDevCfgLoadWorker(pDevTstCfg, hJsonRoot, hJsonValDeviceTests, pErrInfo);
515 if (RT_SUCCESS(rc))
516 *ppDevTstCfg = pDevTstCfg;
517 else /* Error already set, free test config structure. */
518 tstDevCfgDestroy(pDevTstCfg);
519 }
520 else
521 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to allocate %zu bytes for the test config structure", cbTestCfg);
522 }
523 else
524 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"DeviceTests\" is empty");
525 }
526 else
527 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"DeviceTests\" is not an array");
528
529 RTJsonValueRelease(hJsonValDeviceTests);
530 }
531 else
532 tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"DeviceTests\" value");
533
534 RTJsonValueRelease(hJsonRoot);
535 }
536
537 return rc;
538}
539
540
541DECLHIDDEN(void) tstDevCfgDestroy(PCTSTDEVCFG pDevTstCfg)
542{
543 RT_NOREF(pDevTstCfg);
544}
545
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette