VirtualBox

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

Last change on this file since 94264 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.4 KB
Line 
1/* $Id: tstDeviceCfg.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * tstDevice - Configuration loader.
4 */
5
6/*
7 * Copyright (C) 2020-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.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 else
313 {
314 *ppszTestcaseId = pszTestcaseId;
315 *pcTestcaseCfgItems = 0;
316 *ppTestcaseCfg = NULL;
317 }
318 }
319 else
320 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"Config\" is not an array");
321
322 RTJsonValueRelease(hJsonValCfg);
323 }
324 else
325 tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"Config\" value");
326
327 if (RT_FAILURE(rc))
328 RTStrFree(pszTestcaseId);
329 }
330
331 return rc;
332}
333
334
335/**
336 * Loads the testcase descriptions from the config.
337 *
338 * @returns VBox status code.
339 * @param pDevTest Where to store the testcases config on success.
340 * @param hJsonValTest Where to load the testcases config from.
341 * @param pErrInfo Pointer to error info.
342 */
343static int tstDevCfgLoadTestcases(PTSTDEVTEST pDevTest, RTJSONVAL hJsonValTest, PRTERRINFO pErrInfo)
344{
345 RTJSONVAL hJsonValTestcases;
346 int rc = RTJsonValueQueryByName(hJsonValTest, "Testcases", &hJsonValTestcases);
347 if (RT_SUCCESS(rc))
348 {
349 unsigned cTestcases = 0;
350 rc = RTJsonValueQueryArraySize(hJsonValTestcases, &cTestcases);
351 if (RT_SUCCESS(rc))
352 {
353 pDevTest->cTestcases = cTestcases;
354 if (cTestcases > 0)
355 {
356 size_t cbArray = sizeof(void *) * 2 * cTestcases + cTestcases * sizeof(uint32_t); /* One for the testcase ID and one for the associated configuration. */
357 uint8_t *pbTmp = (uint8_t *)RTMemAllocZ(cbArray);
358 if (pbTmp)
359 {
360 pDevTest->papszTestcaseIds = (const char **)pbTmp;
361 pDevTest->pacTestcaseCfgItems = (uint32_t *)&pDevTest->papszTestcaseIds[cTestcases];
362 pDevTest->papTestcaseCfg = (PCTSTDEVCFGITEM *)&pDevTest->pacTestcaseCfgItems[cTestcases];
363
364 for (uint32_t i = 0; i < cTestcases; i++)
365 {
366 RTJSONVAL hJsonTestcase;
367
368 rc = RTJsonValueQueryByIndex(hJsonValTestcases, i, &hJsonTestcase);
369 if (RT_SUCCESS(rc))
370 {
371 rc = tstDevCfgLoadTestcase(hJsonTestcase, &pDevTest->papszTestcaseIds[i],
372 &pDevTest->pacTestcaseCfgItems[i], &pDevTest->papTestcaseCfg[i], pErrInfo);
373 RTJsonValueRelease(hJsonTestcase);
374 }
375 else
376 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query testcase item %u", i);
377 }
378 }
379 else
380 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to allocate %zu bytes for the testcases", cbArray);
381 }
382 else
383 rc = tstDevCfgErrorRc(pErrInfo, VERR_INVALID_PARAMETER, "tstDevCfg/JSON: \"Testcases\" doesn't contain anything");
384 }
385 else
386 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"Testcases\" is not an array");
387
388 RTJsonValueRelease(hJsonValTestcases);
389 }
390 else
391 tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"Testcases\" value");
392
393 return rc;
394}
395
396
397/**
398 * Loads a test config from the given JSON object.
399 *
400 * @returns VBox status code.
401 * @param pDevTest Where to store the test config on success.
402 * @param hJsonValTest Where to load the test config from.
403 * @param pErrInfo Pointer to error info.
404 */
405static int tstDevCfgLoadTest(PTSTDEVTEST pDevTest, RTJSONVAL hJsonValTest, PRTERRINFO pErrInfo)
406{
407 int rc = tstDevCfgLoadBool(hJsonValTest, "R0Enabled", &pDevTest->fR0Enabled, pErrInfo);
408 if (RT_SUCCESS(rc))
409 rc = tstDevCfgLoadBool(hJsonValTest, "RCEnabled", &pDevTest->fRCEnabled, pErrInfo);
410
411 if (RT_SUCCESS(rc))
412 {
413 RTJSONVAL hJsonValCfg;
414 rc = RTJsonValueQueryByName(hJsonValTest, "Config", &hJsonValCfg);
415 if (RT_SUCCESS(rc))
416 {
417 unsigned cCfgItems = 0;
418 rc = RTJsonValueQueryArraySize(hJsonValCfg, &cCfgItems);
419 if (RT_SUCCESS(rc))
420 {
421 pDevTest->cCfgItems = cCfgItems;
422 if (cCfgItems > 0)
423 {
424 size_t cbCfg = sizeof(TSTDEVCFGITEM) * cCfgItems;
425 PTSTDEVCFGITEM paCfg = (PTSTDEVCFGITEM)RTMemAllocZ(cbCfg);
426 if (paCfg)
427 {
428 rc = tstDevCfgLoadTestCfgWorker(paCfg, cCfgItems, hJsonValCfg, pErrInfo);
429 if (RT_SUCCESS(rc))
430 pDevTest->paCfgItems = paCfg;
431 else /* Error already set, free test config structure. */
432 tstDevCfgItemsDestroy(paCfg, cCfgItems);
433 }
434 else
435 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to allocate %zu bytes for the test config structure", cbCfg);
436 }
437 }
438 else
439 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"Config\" is not an array");
440
441 RTJsonValueRelease(hJsonValCfg);
442 }
443 else
444 tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"Config\" value");
445 }
446
447 /* Load the test configs. */
448 if (RT_SUCCESS(rc))
449 rc = tstDevCfgLoadTestcases(pDevTest, hJsonValTest, pErrInfo);
450
451 return rc;
452}
453
454
455/**
456 * Configuration loader worker.
457 *
458 * @returns VBox status code.
459 * @param pDevTstCfg The test config structure to fill.
460 * @param hJsonRoot Handle of the root JSON value.
461 * @param hJsonValDeviceTests Handle to the test JSON array.
462 * @param pErrInfo Pointer to the error info.
463 */
464static int tstDevCfgLoadWorker(PTSTDEVCFG pDevTstCfg, RTJSONVAL hJsonRoot, RTJSONVAL hJsonValDeviceTests, PRTERRINFO pErrInfo)
465{
466 int rc = tstDevCfgLoadString(hJsonRoot, "PdmR3Module", (char **)&pDevTstCfg->pszPdmR3Mod, false /*fMissingOk*/, pErrInfo);
467 if (RT_SUCCESS(rc))
468 rc = tstDevCfgLoadString(hJsonRoot, "PdmR0Module", (char **)&pDevTstCfg->pszPdmR0Mod, true /*fMissingOk*/, pErrInfo);
469 if (RT_SUCCESS(rc))
470 rc = tstDevCfgLoadString(hJsonRoot, "PdmRCModule", (char **)&pDevTstCfg->pszPdmRCMod, true /*fMissingOk*/, pErrInfo);
471 if (RT_SUCCESS(rc))
472 rc = tstDevCfgLoadString(hJsonRoot, "TestcaseModule", (char **)&pDevTstCfg->pszTstDevMod, true /*fMissingOk*/, pErrInfo);
473 if (RT_SUCCESS(rc))
474 rc = tstDevCfgLoadString(hJsonRoot, "Device", (char **)&pDevTstCfg->pszDevName, false /*fMissingOk*/, pErrInfo);
475
476 if (RT_SUCCESS(rc))
477 {
478 /* Load the individual test configs. */
479 for (uint32_t idx = 0; idx < pDevTstCfg->cTests && RT_SUCCESS(rc); idx++)
480 {
481 RTJSONVAL hJsonValTest;
482
483 rc = RTJsonValueQueryByIndex(hJsonValDeviceTests, idx, &hJsonValTest);
484 if (RT_SUCCESS(rc))
485 {
486 rc = tstDevCfgLoadTest(&pDevTstCfg->aTests[idx], hJsonValTest, pErrInfo);
487 RTJsonValueRelease(hJsonValTest);
488 }
489 else
490 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query test %u from \"DeviceTests\"", idx);
491 }
492 }
493
494 return rc;
495}
496
497
498DECLHIDDEN(int) tstDevCfgLoad(const char *pszCfgFilename, PRTERRINFO pErrInfo, PCTSTDEVCFG *ppDevTstCfg)
499{
500 RTJSONVAL hJsonRoot;
501 int rc = RTJsonParseFromFile(&hJsonRoot, pszCfgFilename, pErrInfo);
502 if (RT_SUCCESS(rc))
503 {
504 RTJSONVAL hJsonValDeviceTests;
505
506 rc = RTJsonValueQueryByName(hJsonRoot, "DeviceTests", &hJsonValDeviceTests);
507 if (RT_SUCCESS(rc))
508 {
509 unsigned cTests = 0;
510 rc = RTJsonValueQueryArraySize(hJsonValDeviceTests, &cTests);
511 if (RT_SUCCESS(rc))
512 {
513 if (cTests > 0)
514 {
515 size_t cbTestCfg = RT_UOFFSETOF_DYN(TSTDEVCFG, aTests[cTests]);
516 PTSTDEVCFG pDevTstCfg = (PTSTDEVCFG)RTMemAllocZ(cbTestCfg);
517 if (pDevTstCfg)
518 {
519 pDevTstCfg->cTests = cTests;
520 rc = tstDevCfgLoadWorker(pDevTstCfg, hJsonRoot, hJsonValDeviceTests, pErrInfo);
521 if (RT_SUCCESS(rc))
522 *ppDevTstCfg = pDevTstCfg;
523 else /* Error already set, free test config structure. */
524 tstDevCfgDestroy(pDevTstCfg);
525 }
526 else
527 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to allocate %zu bytes for the test config structure", cbTestCfg);
528 }
529 else
530 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"DeviceTests\" is empty");
531 }
532 else
533 rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"DeviceTests\" is not an array");
534
535 RTJsonValueRelease(hJsonValDeviceTests);
536 }
537 else
538 tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"DeviceTests\" value");
539
540 RTJsonValueRelease(hJsonRoot);
541 }
542
543 return rc;
544}
545
546
547DECLHIDDEN(void) tstDevCfgDestroy(PCTSTDEVCFG pDevTstCfg)
548{
549 RT_NOREF(pDevTstCfg);
550}
551
Note: See TracBrowser for help on using the repository browser.

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