VirtualBox

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

Last change on this file was 99739, checked in by vboxsync, 12 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use