summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/testcase/tstDeviceCfg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/testcase/tstDeviceCfg.cpp')
-rw-r--r--src/VBox/Devices/testcase/tstDeviceCfg.cpp560
1 files changed, 560 insertions, 0 deletions
diff --git a/src/VBox/Devices/testcase/tstDeviceCfg.cpp b/src/VBox/Devices/testcase/tstDeviceCfg.cpp
new file mode 100644
index 00000000..cb741b02
--- /dev/null
+++ b/src/VBox/Devices/testcase/tstDeviceCfg.cpp
@@ -0,0 +1,560 @@
+/* $Id: tstDeviceCfg.cpp $ */
+/** @file
+ * tstDevice - Configuration loader.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEFAULT /** @todo */
+#include <VBox/types.h>
+#include <iprt/errcore.h>
+#include <iprt/json.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/string.h>
+
+#include "tstDeviceCfg.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+
+/**
+ * Wrapper around RTErrInfoSetV / RTMsgErrorV.
+ *
+ * @returns @a rc
+ * @param pErrInfo Extended error info.
+ * @param rc The return code.
+ * @param pszFormat The message format.
+ * @param ... The message format arguments.
+ */
+static int tstDevCfgErrorRc(PRTERRINFO pErrInfo, int rc, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ if (pErrInfo)
+ RTErrInfoSetV(pErrInfo, rc, pszFormat, va);
+ else
+ RTMsgErrorV(pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+/**
+ * Destroys the given configuration item array freeing all allocated resources.
+ *
+ * @param paCfg The configuration item array to destroy.
+ * @param cCfgItems Number of items in the array.
+ */
+static void tstDevCfgItemsDestroy(PTSTDEVCFGITEM paCfg, uint32_t cCfgItems)
+{
+ RT_NOREF(paCfg, cCfgItems);
+}
+
+
+/**
+ * Loads the given string from the config, creating a duplicate.
+ *
+ * @returns VBox status code.
+ * @param hJsonTop The JSON top value handle containing the value to load.
+ * @param pszValName The value name.
+ * @param ppszValCopy Where to store the pointer to the value on success, must be freed with RTStrFree().
+ * @param fMissingOk Flag whether it is considered success if the value does not exist.
+ * @param pErrInfo Pointer to the error info to fill on error.
+ */
+static int tstDevCfgLoadString(RTJSONVAL hJsonTop, const char *pszValName, char **ppszValCopy, bool fMissingOk, PRTERRINFO pErrInfo)
+{
+ RTJSONVAL hJsonVal;
+ int rc = RTJsonValueQueryByName(hJsonTop, pszValName, &hJsonVal);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszVal = RTJsonValueGetString(hJsonVal);
+ if (RT_LIKELY(pszVal))
+ {
+ *ppszValCopy = RTStrDup(pszVal);
+ if (RT_UNLIKELY(!*ppszValCopy))
+ rc = tstDevCfgErrorRc(pErrInfo, VERR_NO_STR_MEMORY, "tstDevCfg/JSON: Out of memory allocating memory for value of \"%s\" ", pszValName);
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "tstDevCfg/JSON: \"%s\" is not a string", pszValName);
+
+ RTJsonValueRelease(hJsonVal);
+ }
+ else if ( rc == VERR_NOT_FOUND
+ && fMissingOk)
+ {
+ *ppszValCopy = NULL;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"%s\"", pszValName);
+
+ return rc;
+}
+
+
+/**
+ * Loads a bool value using the given value name from the config.
+ *
+ * @returns VBox status code.
+ * @param hJsonTop The JSON top value handle containing the value to load.
+ * @param pszValName The value name.
+ * @param pf Where to store the value on success.
+ * @param pErrInfo Pointer to the error info to fill on error.
+ */
+static int tstDevCfgLoadBool(RTJSONVAL hJsonTop, const char *pszValName, bool *pf, PRTERRINFO pErrInfo)
+{
+ int rc = RTJsonValueQueryBooleanByName(hJsonTop, pszValName, pf);
+ if (RT_FAILURE(rc))
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query boolean value of \"%s\"", pszValName);
+
+ return rc;
+}
+
+
+/**
+ * Determines the config item type from the given.value.
+ *
+ * @returns VBox status code.
+ * @param hJsonTop The JSON top value handle containing the value to load.
+ * @param pszValName The value name.
+ * @param penmCfgItemType Where to store the determined config item type on success.
+ * @param pErrInfo Pointer to the error info to fill on error.
+ */
+static int tstDevCfgLoadCfgItemType(RTJSONVAL hJsonTop, const char *pszValName, PTSTDEVCFGITEMTYPE penmCfgItemType, PRTERRINFO pErrInfo)
+{
+ RTJSONVAL hJsonVal;
+ int rc = RTJsonValueQueryByName(hJsonTop, pszValName, &hJsonVal);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszVal = RTJsonValueGetString(hJsonVal);
+ if (!RTStrCmp(pszVal, "Integer"))
+ *penmCfgItemType = TSTDEVCFGITEMTYPE_INTEGER;
+ else if (!RTStrCmp(pszVal, "String"))
+ *penmCfgItemType = TSTDEVCFGITEMTYPE_STRING;
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "tstDevCfg/JSON: \"%s\" is not a valid config item type", pszVal);
+
+ RTJsonValueRelease(hJsonVal);
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"%s\"", pszValName);
+
+ return rc;
+}
+
+
+/**
+ * Loads the config item value from the given config based on the earlier determined type.
+ *
+ * @returns VBox status code.
+ * @param hJsonTop The JSON top value handle containing the value to load.
+ * @param pszValName The value name.
+ * @param pCfg Where to store the retrieved config value.
+ * @param enmCfgItemType The earlier determined config item type.
+ * @param pErrInfo Pointer to the error info to fill on error.
+ */
+static int tstDevCfgLoadCfgItemValue(RTJSONVAL hJsonTop, const char *pszValName, PTSTDEVCFGITEM pCfg, TSTDEVCFGITEMTYPE enmCfgItemType, PRTERRINFO pErrInfo)
+{
+ RTJSONVAL hJsonVal;
+
+ int rc = RTJsonValueQueryByName(hJsonTop, pszValName, &hJsonVal);
+ if (RT_SUCCESS(rc))
+ {
+ RTJSONVALTYPE enmJsonType = RTJsonValueGetType(hJsonVal);
+
+ if ( ( enmJsonType == RTJSONVALTYPE_INTEGER
+ && enmCfgItemType == TSTDEVCFGITEMTYPE_INTEGER)
+ || ( enmJsonType == RTJSONVALTYPE_STRING
+ && enmCfgItemType == TSTDEVCFGITEMTYPE_STRING))
+ {
+ switch (enmCfgItemType)
+ {
+ case TSTDEVCFGITEMTYPE_INTEGER:
+ {
+ rc = RTJsonValueQueryInteger(hJsonVal, &pCfg->u.i64);
+ break;
+ }
+ case TSTDEVCFGITEMTYPE_STRING:
+ {
+ const char *psz = RTJsonValueGetString(hJsonVal);
+ AssertPtr(psz);
+
+ pCfg->u.psz = RTStrDup(psz);
+ if (RT_UNLIKELY(!pCfg->u.psz))
+ rc = VERR_NO_STR_MEMORY;
+ break;
+ }
+ default:
+ AssertFailed(); /* Should never ever get here. */
+ rc = tstDevCfgErrorRc(pErrInfo, VERR_INTERNAL_ERROR, "tstDevCfg/JSON: Invalid config item type %u", enmCfgItemType);
+ }
+
+ if (RT_SUCCESS(rc))
+ pCfg->enmType = enmCfgItemType;
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query config item value");
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: JSON value type doesn't match config item type (got %u, expected %u)", enmJsonType, enmCfgItemType);
+
+ RTJsonValueRelease(hJsonVal);
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"%s\"", pszValName);
+
+ return rc;
+}
+
+
+/**
+ * Loads the test configuration from the given JSON value.
+ *
+ * @returns VBox status code.
+ * @param paCfg The configuration array to fill.
+ * @param cCfgItems Number of configuration items.
+ * @param hJsonValCfg The JSON value to gather the config items from.
+ * @param pErrInfo Pointer to error info.
+ */
+static int tstDevCfgLoadTestCfgWorker(PTSTDEVCFGITEM paCfg, uint32_t cCfgItems, RTJSONVAL hJsonValCfg, PRTERRINFO pErrInfo)
+{
+ int rc = VINF_SUCCESS;
+
+ for (uint32_t i = 0; i < cCfgItems && RT_SUCCESS(rc); i++)
+ {
+ PTSTDEVCFGITEM pCfg = &paCfg[i];
+ RTJSONVAL hJsonCfg;
+
+ rc = RTJsonValueQueryByIndex(hJsonValCfg, i, &hJsonCfg);
+ if (RT_SUCCESS(rc))
+ {
+ TSTDEVCFGITEMTYPE enmCfgItemType;
+
+ rc = tstDevCfgLoadString(hJsonCfg, "Key", (char **)&pCfg->pszKey, false /*fMissingOk*/, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = tstDevCfgLoadCfgItemType(hJsonCfg, "Type", &enmCfgItemType, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = tstDevCfgLoadCfgItemValue(hJsonCfg, "Value", pCfg, enmCfgItemType, pErrInfo);
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query config item %u", i);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Loads a single testcase from the given JSON config value.
+ *
+ * @returns VBox status code.
+ * @param ppszTestcaseId Where to store the testcase ID on success.
+ * @param pcTestcaseCfgItems Where to store the number of testcase config items on success.
+ * @param ppTestcaseCfg Where to store the testcase config on success.
+ * @param pErrInfo Pointer to error info.
+ */
+static int tstDevCfgLoadTestcase(RTJSONVAL hJsonTestcase, const char **ppszTestcaseId, uint32_t *pcTestcaseCfgItems, PCTSTDEVCFGITEM *ppTestcaseCfg, PRTERRINFO pErrInfo)
+{
+ char *pszTestcaseId = NULL;
+ int rc = tstDevCfgLoadString(hJsonTestcase, "Testcase", &pszTestcaseId, false /*fMissingOk*/, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ RTJSONVAL hJsonValCfg;
+ rc = RTJsonValueQueryByName(hJsonTestcase, "Config", &hJsonValCfg);
+ if (RT_SUCCESS(rc))
+ {
+ unsigned cCfgItems = 0;
+ rc = RTJsonValueQueryArraySize(hJsonValCfg, &cCfgItems);
+ if (RT_SUCCESS(rc))
+ {
+ if (cCfgItems > 0)
+ {
+ size_t cbCfg = sizeof(TSTDEVCFGITEM) * cCfgItems;
+ PTSTDEVCFGITEM paCfg = (PTSTDEVCFGITEM)RTMemAllocZ(cbCfg);
+ if (paCfg)
+ {
+ rc = tstDevCfgLoadTestCfgWorker(paCfg, cCfgItems, hJsonValCfg, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ *ppszTestcaseId = pszTestcaseId;
+ *pcTestcaseCfgItems = cCfgItems;
+ *ppTestcaseCfg = paCfg;
+ }
+ else /* Error already set, free test config structure. */
+ tstDevCfgItemsDestroy(paCfg, cCfgItems);
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to allocate %zu bytes for the test config structure", cbCfg);
+ }
+ else
+ {
+ *ppszTestcaseId = pszTestcaseId;
+ *pcTestcaseCfgItems = 0;
+ *ppTestcaseCfg = NULL;
+ }
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"Config\" is not an array");
+
+ RTJsonValueRelease(hJsonValCfg);
+ }
+ else
+ tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"Config\" value");
+
+ if (RT_FAILURE(rc))
+ RTStrFree(pszTestcaseId);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Loads the testcase descriptions from the config.
+ *
+ * @returns VBox status code.
+ * @param pDevTest Where to store the testcases config on success.
+ * @param hJsonValTest Where to load the testcases config from.
+ * @param pErrInfo Pointer to error info.
+ */
+static int tstDevCfgLoadTestcases(PTSTDEVTEST pDevTest, RTJSONVAL hJsonValTest, PRTERRINFO pErrInfo)
+{
+ RTJSONVAL hJsonValTestcases;
+ int rc = RTJsonValueQueryByName(hJsonValTest, "Testcases", &hJsonValTestcases);
+ if (RT_SUCCESS(rc))
+ {
+ unsigned cTestcases = 0;
+ rc = RTJsonValueQueryArraySize(hJsonValTestcases, &cTestcases);
+ if (RT_SUCCESS(rc))
+ {
+ pDevTest->cTestcases = cTestcases;
+ if (cTestcases > 0)
+ {
+ size_t cbArray = sizeof(void *) * 2 * cTestcases + cTestcases * sizeof(uint32_t); /* One for the testcase ID and one for the associated configuration. */
+ uint8_t *pbTmp = (uint8_t *)RTMemAllocZ(cbArray);
+ if (pbTmp)
+ {
+ pDevTest->papszTestcaseIds = (const char **)pbTmp;
+ pDevTest->pacTestcaseCfgItems = (uint32_t *)&pDevTest->papszTestcaseIds[cTestcases];
+ pDevTest->papTestcaseCfg = (PCTSTDEVCFGITEM *)&pDevTest->pacTestcaseCfgItems[cTestcases];
+
+ for (uint32_t i = 0; i < cTestcases; i++)
+ {
+ RTJSONVAL hJsonTestcase;
+
+ rc = RTJsonValueQueryByIndex(hJsonValTestcases, i, &hJsonTestcase);
+ if (RT_SUCCESS(rc))
+ {
+ rc = tstDevCfgLoadTestcase(hJsonTestcase, &pDevTest->papszTestcaseIds[i],
+ &pDevTest->pacTestcaseCfgItems[i], &pDevTest->papTestcaseCfg[i], pErrInfo);
+ RTJsonValueRelease(hJsonTestcase);
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query testcase item %u", i);
+ }
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to allocate %zu bytes for the testcases", cbArray);
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, VERR_INVALID_PARAMETER, "tstDevCfg/JSON: \"Testcases\" doesn't contain anything");
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"Testcases\" is not an array");
+
+ RTJsonValueRelease(hJsonValTestcases);
+ }
+ else
+ tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"Testcases\" value");
+
+ return rc;
+}
+
+
+/**
+ * Loads a test config from the given JSON object.
+ *
+ * @returns VBox status code.
+ * @param pDevTest Where to store the test config on success.
+ * @param hJsonValTest Where to load the test config from.
+ * @param pErrInfo Pointer to error info.
+ */
+static int tstDevCfgLoadTest(PTSTDEVTEST pDevTest, RTJSONVAL hJsonValTest, PRTERRINFO pErrInfo)
+{
+ int rc = tstDevCfgLoadBool(hJsonValTest, "R0Enabled", &pDevTest->fR0Enabled, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = tstDevCfgLoadBool(hJsonValTest, "RCEnabled", &pDevTest->fRCEnabled, pErrInfo);
+
+ if (RT_SUCCESS(rc))
+ {
+ RTJSONVAL hJsonValCfg;
+ rc = RTJsonValueQueryByName(hJsonValTest, "Config", &hJsonValCfg);
+ if (RT_SUCCESS(rc))
+ {
+ unsigned cCfgItems = 0;
+ rc = RTJsonValueQueryArraySize(hJsonValCfg, &cCfgItems);
+ if (RT_SUCCESS(rc))
+ {
+ pDevTest->cCfgItems = cCfgItems;
+ if (cCfgItems > 0)
+ {
+ size_t cbCfg = sizeof(TSTDEVCFGITEM) * cCfgItems;
+ PTSTDEVCFGITEM paCfg = (PTSTDEVCFGITEM)RTMemAllocZ(cbCfg);
+ if (paCfg)
+ {
+ rc = tstDevCfgLoadTestCfgWorker(paCfg, cCfgItems, hJsonValCfg, pErrInfo);
+ if (RT_SUCCESS(rc))
+ pDevTest->paCfgItems = paCfg;
+ else /* Error already set, free test config structure. */
+ tstDevCfgItemsDestroy(paCfg, cCfgItems);
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to allocate %zu bytes for the test config structure", cbCfg);
+ }
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"Config\" is not an array");
+
+ RTJsonValueRelease(hJsonValCfg);
+ }
+ else
+ tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"Config\" value");
+ }
+
+ /* Load the test configs. */
+ if (RT_SUCCESS(rc))
+ rc = tstDevCfgLoadTestcases(pDevTest, hJsonValTest, pErrInfo);
+
+ return rc;
+}
+
+
+/**
+ * Configuration loader worker.
+ *
+ * @returns VBox status code.
+ * @param pDevTstCfg The test config structure to fill.
+ * @param hJsonRoot Handle of the root JSON value.
+ * @param hJsonValDeviceTests Handle to the test JSON array.
+ * @param pErrInfo Pointer to the error info.
+ */
+static int tstDevCfgLoadWorker(PTSTDEVCFG pDevTstCfg, RTJSONVAL hJsonRoot, RTJSONVAL hJsonValDeviceTests, PRTERRINFO pErrInfo)
+{
+ int rc = tstDevCfgLoadString(hJsonRoot, "PdmR3Module", (char **)&pDevTstCfg->pszPdmR3Mod, false /*fMissingOk*/, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = tstDevCfgLoadString(hJsonRoot, "PdmR0Module", (char **)&pDevTstCfg->pszPdmR0Mod, true /*fMissingOk*/, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = tstDevCfgLoadString(hJsonRoot, "PdmRCModule", (char **)&pDevTstCfg->pszPdmRCMod, true /*fMissingOk*/, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = tstDevCfgLoadString(hJsonRoot, "TestcaseModule", (char **)&pDevTstCfg->pszTstDevMod, true /*fMissingOk*/, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = tstDevCfgLoadString(hJsonRoot, "Device", (char **)&pDevTstCfg->pszDevName, false /*fMissingOk*/, pErrInfo);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Load the individual test configs. */
+ for (uint32_t idx = 0; idx < pDevTstCfg->cTests && RT_SUCCESS(rc); idx++)
+ {
+ RTJSONVAL hJsonValTest;
+
+ rc = RTJsonValueQueryByIndex(hJsonValDeviceTests, idx, &hJsonValTest);
+ if (RT_SUCCESS(rc))
+ {
+ rc = tstDevCfgLoadTest(&pDevTstCfg->aTests[idx], hJsonValTest, pErrInfo);
+ RTJsonValueRelease(hJsonValTest);
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query test %u from \"DeviceTests\"", idx);
+ }
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) tstDevCfgLoad(const char *pszCfgFilename, PRTERRINFO pErrInfo, PCTSTDEVCFG *ppDevTstCfg)
+{
+ RTJSONVAL hJsonRoot;
+ int rc = RTJsonParseFromFile(&hJsonRoot, pszCfgFilename, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ RTJSONVAL hJsonValDeviceTests;
+
+ rc = RTJsonValueQueryByName(hJsonRoot, "DeviceTests", &hJsonValDeviceTests);
+ if (RT_SUCCESS(rc))
+ {
+ unsigned cTests = 0;
+ rc = RTJsonValueQueryArraySize(hJsonValDeviceTests, &cTests);
+ if (RT_SUCCESS(rc))
+ {
+ if (cTests > 0)
+ {
+ size_t cbTestCfg = RT_UOFFSETOF_DYN(TSTDEVCFG, aTests[cTests]);
+ PTSTDEVCFG pDevTstCfg = (PTSTDEVCFG)RTMemAllocZ(cbTestCfg);
+ if (pDevTstCfg)
+ {
+ pDevTstCfg->cTests = cTests;
+ rc = tstDevCfgLoadWorker(pDevTstCfg, hJsonRoot, hJsonValDeviceTests, pErrInfo);
+ if (RT_SUCCESS(rc))
+ *ppDevTstCfg = pDevTstCfg;
+ else /* Error already set, free test config structure. */
+ tstDevCfgDestroy(pDevTstCfg);
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to allocate %zu bytes for the test config structure", cbTestCfg);
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"DeviceTests\" is empty");
+ }
+ else
+ rc = tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: \"DeviceTests\" is not an array");
+
+ RTJsonValueRelease(hJsonValDeviceTests);
+ }
+ else
+ tstDevCfgErrorRc(pErrInfo, rc, "tstDevCfg/JSON: Failed to query \"DeviceTests\" value");
+
+ RTJsonValueRelease(hJsonRoot);
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(void) tstDevCfgDestroy(PCTSTDEVCFG pDevTstCfg)
+{
+ RT_NOREF(pDevTstCfg);
+}
+