1 | /* $Id: tstVBoxWinDrvInstInf.cpp 107076 2024-11-21 09:59:02Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * VirtualBox Windows driver installation tests.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2024 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 | #include <iprt/assert.h>
|
---|
29 | #include <iprt/dir.h>
|
---|
30 | #include <iprt/err.h>
|
---|
31 | #include <iprt/path.h>
|
---|
32 | #include <iprt/process.h>
|
---|
33 | #include <iprt/test.h>
|
---|
34 |
|
---|
35 | #include <VBox/err.h>
|
---|
36 | #include <VBox/log.h>
|
---|
37 |
|
---|
38 | #include <VBox/GuestHost/VBoxWinDrvInst.h>
|
---|
39 | #include <VBox/GuestHost/VBoxWinDrvStore.h>
|
---|
40 |
|
---|
41 | #include "VBoxWinDrvInstInternal.h"
|
---|
42 |
|
---|
43 |
|
---|
44 | /**
|
---|
45 | * Test context to use.
|
---|
46 | */
|
---|
47 | typedef struct VBOXWINDRVINSTTESTCTX
|
---|
48 | {
|
---|
49 | /** Test handle. */
|
---|
50 | RTTEST hTest;
|
---|
51 | /** Installer handle. */
|
---|
52 | VBOXWINDRVINST hInst;
|
---|
53 | /** Index of current test running.
|
---|
54 | * Set to UINT32_MAX if no test running (yet). */
|
---|
55 | size_t idxTest;
|
---|
56 | } VBOXWINDRVINSTTESTCTX;
|
---|
57 | typedef VBOXWINDRVINSTTESTCTX *PVBOXWINDRVINSTTESTCTX;
|
---|
58 |
|
---|
59 | /**
|
---|
60 | * Expected results for a single test.
|
---|
61 | */
|
---|
62 | typedef struct VBOXWINDRVINSTTESTRES
|
---|
63 | {
|
---|
64 | /** Number of errors occurred for this test. */
|
---|
65 | unsigned cErrors;
|
---|
66 | } VBOXWINDRVINSTTESTRES;
|
---|
67 |
|
---|
68 | /**
|
---|
69 | * Parameters for a single test.
|
---|
70 | */
|
---|
71 | typedef struct VBOXWINDRVINSTTESTPARMS
|
---|
72 | {
|
---|
73 | /** Expected section to (un)install.
|
---|
74 | * NULL if not being used. */
|
---|
75 | const char *pszSection;
|
---|
76 | /** Expected model name.
|
---|
77 | * NULL if not being used. */
|
---|
78 | const char *pszModel;
|
---|
79 | /** Expected PnP ID.
|
---|
80 | * NULL if not being used. */
|
---|
81 | const char *pszPnpId;
|
---|
82 | } VBOXWINDRVINSTTESTPARMS;
|
---|
83 |
|
---|
84 | /**
|
---|
85 | * A single test.
|
---|
86 | */
|
---|
87 | typedef struct VBOXWINDRVINSTTEST
|
---|
88 | {
|
---|
89 | /** INF file to use for testing. */
|
---|
90 | const char *pszFile;
|
---|
91 | /** (Un)installation flags. */
|
---|
92 | uint32_t fFlags;
|
---|
93 | /** Expected overall result of the test. */
|
---|
94 | int rc;
|
---|
95 | /** Test parameters. */
|
---|
96 | VBOXWINDRVINSTTESTPARMS Parms;
|
---|
97 | /** Test result(s). Must come last due to array initialization. */
|
---|
98 | VBOXWINDRVINSTTESTRES Result;
|
---|
99 | } VBOXWINDRVINSTTEST;
|
---|
100 | typedef VBOXWINDRVINSTTEST *PVBOXWINDRVINSTTEST;
|
---|
101 |
|
---|
102 | /**
|
---|
103 | * Test definitions.
|
---|
104 | */
|
---|
105 | VBOXWINDRVINSTTEST g_aTests[] =
|
---|
106 | {
|
---|
107 | /**
|
---|
108 | * Failing tests.
|
---|
109 | */
|
---|
110 | /** File does not exist. */
|
---|
111 | { "testInstallFileDoesNotExist.inf", VBOX_WIN_DRIVERINSTALL_F_NONE, VERR_FILE_NOT_FOUND },
|
---|
112 | /** File exists but empty. */
|
---|
113 | { "testInstallEmpty.inf", VBOX_WIN_DRIVERINSTALL_F_NONE, VERR_INSTALLATION_FAILED /* ERROR_GENERAL_SYNTAX */ },
|
---|
114 | /** Does not have an Version section and/or signature. */
|
---|
115 | { "testInstallWrongInfStyle.inf", VBOX_WIN_DRIVERINSTALL_F_NONE, VERR_INSTALLATION_FAILED /* ERROR_WRONG_INF_STYLE */ },
|
---|
116 | /** Does neither have a DefaultInstall nor a Manufacturer section. */
|
---|
117 | { "testInstallNoManufacturerOrDefaultInstall.inf", VBOX_WIN_DRIVERINSTALL_F_NONE, VERR_INVALID_PARAMETER /* ERROR_WRONG_INF_STYLE */ },
|
---|
118 | /** Both, a Manufacturer section *and* a DefaultInstall section are present, which is invalid. */
|
---|
119 | { "testInstallManufacturerAndDefaultInstall.inf", VBOX_WIN_DRIVERINSTALL_F_NONE, VERR_INVALID_PARAMETER /* ERROR_WRONG_INF_STYLE */ },
|
---|
120 | /** Manufacturer section is present, but no model section. */
|
---|
121 | { "testInstallManufacturerButNoModelSection.inf", VBOX_WIN_DRIVERINSTALL_F_NONE, VERR_NOT_FOUND },
|
---|
122 | /** Manufacturer section + model present, but bogus / invalid architecture. */
|
---|
123 | { "testInstallManufacturerInvalidArch.inf", VBOX_WIN_DRIVERINSTALL_F_NONE, VERR_PLATFORM_ARCH_NOT_SUPPORTED },
|
---|
124 | /**
|
---|
125 | * Succeeding tests.
|
---|
126 | */
|
---|
127 | /** Default section is present, but no model section.
|
---|
128 | * This actually is valid and has to succeed. */
|
---|
129 | { "testInstallDefaultInstallButNoModelSection.inf", VBOX_WIN_DRIVERINSTALL_F_NONE, VINF_SUCCESS },
|
---|
130 | /** Manufacturer, model and section given. */
|
---|
131 | { "testInstallManufacturerWithModelSection.inf", VBOX_WIN_DRIVERINSTALL_F_NONE,
|
---|
132 | VINF_SUCCESS, { "VBoxTest" /* Section */, "VBoxTest.NTAMD64" /* Model */, "PCI\\VEN_80ee&DEV_cafe" /* PnP ID */, } }
|
---|
133 | };
|
---|
134 | typedef VBOXWINDRVINSTTEST *PVBOXWINDRVINSTTEST;
|
---|
135 |
|
---|
136 | /**
|
---|
137 | * Logging callback for the Windows driver (un)installation code.
|
---|
138 | */
|
---|
139 | static DECLCALLBACK(void) tstVBoxDrvInstLogCallback(VBOXWINDRIVERLOGTYPE enmType, const char *pszMsg, void *pvUser)
|
---|
140 | {
|
---|
141 | PVBOXWINDRVINSTTESTCTX pCtx = (PVBOXWINDRVINSTTESTCTX)pvUser;
|
---|
142 |
|
---|
143 | RTTestPrintf(pCtx->hTest, RTTESTLVL_ALWAYS, "%s\n", pszMsg);
|
---|
144 |
|
---|
145 | switch (enmType)
|
---|
146 | {
|
---|
147 | case VBOXWINDRIVERLOGTYPE_ERROR:
|
---|
148 | {
|
---|
149 | if (pCtx->idxTest == UINT32_MAX) /* No test defined? Must be an error in the actual framework. */
|
---|
150 | RTTestFailed(pCtx->hTest, "%s", pszMsg);
|
---|
151 | else /* Just count the error here. If that actually is a not expected error is up to the test later then. */
|
---|
152 | g_aTests[pCtx->idxTest].Result.cErrors++;
|
---|
153 | break;
|
---|
154 | }
|
---|
155 |
|
---|
156 | default:
|
---|
157 | break;
|
---|
158 | }
|
---|
159 | }
|
---|
160 |
|
---|
161 | static int tstVBoxDrvInstTestOne(PVBOXWINDRVINSTTESTCTX pCtx, const char *pszInfFile, PVBOXWINDRVINSTTEST pTest)
|
---|
162 | {
|
---|
163 | pTest->fFlags |= /* Only run in dry-mode. */
|
---|
164 | VBOX_WIN_DRIVERINSTALL_F_DRYRUN
|
---|
165 | /* Don't destroy the (un)installation parameters for later inspection (see below). */
|
---|
166 | | VBOX_WIN_DRIVERINSTALL_F_NO_DESTROY;
|
---|
167 |
|
---|
168 | int rc = VBoxWinDrvInstInstall(pCtx->hInst, pszInfFile, pTest->fFlags);
|
---|
169 | RTTEST_CHECK_MSG_RET(pCtx->hTest, rc == pTest->rc, (pCtx->hTest, "Error: Got %Rrc, expected %Rrc\n", rc, pTest->rc), rc);
|
---|
170 | if (RT_FAILURE(rc)) /* Nothing to do here anymore. */
|
---|
171 | return VINF_SUCCESS;
|
---|
172 |
|
---|
173 | PVBOXWINDRVINSTPARMS const pParms = VBoxWinDrvInstTestGetParms(pCtx->hInst);
|
---|
174 |
|
---|
175 | /* Check section. */
|
---|
176 | char *psz;
|
---|
177 | if (pTest->Parms.pszSection)
|
---|
178 | {
|
---|
179 | RTUtf16ToUtf8(pParms->u.UnInstall.pwszSection, &psz);
|
---|
180 | if (RTStrCmp(pTest->Parms.pszSection, psz))
|
---|
181 | RTTestFailed(pCtx->hTest, "Error: Got section %s, expected %s\n", psz, pTest->Parms.pszSection);
|
---|
182 | RTStrFree(psz);
|
---|
183 | }
|
---|
184 |
|
---|
185 | /* Check model. */
|
---|
186 | if (pTest->Parms.pszModel)
|
---|
187 | {
|
---|
188 | RTUtf16ToUtf8(pParms->u.UnInstall.pwszModel, &psz);
|
---|
189 | if (RTStrCmp(pTest->Parms.pszModel, psz))
|
---|
190 | RTTestFailed(pCtx->hTest, "Error: Got model %s, expected %s\n", psz, pTest->Parms.pszModel);
|
---|
191 | RTStrFree(psz);
|
---|
192 | }
|
---|
193 |
|
---|
194 | /* Check PnP ID. */
|
---|
195 | if (pTest->Parms.pszPnpId)
|
---|
196 | {
|
---|
197 | RTUtf16ToUtf8(pParms->u.UnInstall.pwszPnpId, &psz);
|
---|
198 | if (RTStrCmp(pTest->Parms.pszPnpId, psz))
|
---|
199 | RTTestFailed(pCtx->hTest, "Error: Got PnP ID %s, expected %s\n", psz, pTest->Parms.pszPnpId);
|
---|
200 | RTStrFree(psz);
|
---|
201 | }
|
---|
202 |
|
---|
203 | VBoxWinDrvInstTestParmsDestroy(pParms); /* For VBOX_WIN_DRIVERINSTALL_F_NO_DESTROY. */
|
---|
204 |
|
---|
205 | return VINF_SUCCESS;
|
---|
206 | }
|
---|
207 |
|
---|
208 | static int tstVBoxDrvInstTestPath(PVBOXWINDRVINSTTESTCTX pCtx, const char *pszPath)
|
---|
209 | {
|
---|
210 | for (size_t i = 0; i < RT_ELEMENTS(g_aTests); i++)
|
---|
211 | {
|
---|
212 | pCtx->idxTest = i; /* Set current test index for callback. */
|
---|
213 |
|
---|
214 | char szInfPath[RTPATH_MAX];
|
---|
215 | RTTEST_CHECK(pCtx->hTest, RTStrPrintf(szInfPath, sizeof(szInfPath), "%s", pszPath) > 0);
|
---|
216 | RTTEST_CHECK_RC_OK(pCtx->hTest, RTPathAppend(szInfPath, sizeof(szInfPath), g_aTests[i].pszFile));
|
---|
217 | RTTEST_CHECK_RC_OK(pCtx->hTest, tstVBoxDrvInstTestOne(pCtx, szInfPath, &g_aTests[i]));
|
---|
218 | }
|
---|
219 |
|
---|
220 | return VINF_SUCCESS;
|
---|
221 | }
|
---|
222 |
|
---|
223 | int main(int argc, char **argv)
|
---|
224 | {
|
---|
225 | /*
|
---|
226 | * Init the runtime, test and say hello.
|
---|
227 | */
|
---|
228 | RTTEST hTest;
|
---|
229 | int rc = RTTestInitAndCreate("tstVBoxWinDrvInstInf", &hTest);
|
---|
230 | if (rc)
|
---|
231 | return rc;
|
---|
232 | RTTestBanner(hTest);
|
---|
233 |
|
---|
234 | /* Simply create + destroy. */
|
---|
235 | VBOXWINDRVINST hWinDrvInst;
|
---|
236 | RTTEST_CHECK_RC_OK_BREAK(hTest, VBoxWinDrvInstCreateEx(&hWinDrvInst, 4 /* Verbosity */,
|
---|
237 | NULL, NULL /* pvUser */));
|
---|
238 | VBoxWinDrvInstDestroy(hWinDrvInst);
|
---|
239 |
|
---|
240 | /* Run all tests. */
|
---|
241 | do
|
---|
242 | {
|
---|
243 | char szTestPath[RTPATH_MAX];
|
---|
244 |
|
---|
245 | if (argc < 2)
|
---|
246 | {
|
---|
247 | RTTEST_CHECK_BREAK(hTest, RTProcGetExecutablePath(szTestPath, sizeof(szTestPath)) != NULL);
|
---|
248 | RTPathStripFilename(szTestPath);
|
---|
249 | RTTEST_CHECK_RC_OK_BREAK(hTest, RTPathAppend(szTestPath, sizeof(szTestPath), "inf"));
|
---|
250 | }
|
---|
251 | else
|
---|
252 | {
|
---|
253 | RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Warning: Custom directory specified\n");
|
---|
254 | RTTEST_CHECK_BREAK(hTest, RTStrPrintf(szTestPath, sizeof(szTestPath), "%s", argv[1]) > 0);
|
---|
255 | }
|
---|
256 |
|
---|
257 | RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Test directory: %s\n", szTestPath);
|
---|
258 |
|
---|
259 | VBOXWINDRVINSTTESTCTX Ctx = { 0 };
|
---|
260 | Ctx.idxTest = UINT32_MAX;
|
---|
261 |
|
---|
262 | RTTEST_CHECK_RC_OK_BREAK(hTest, VBoxWinDrvInstCreateEx(&hWinDrvInst, 4 /* Verbosity */,
|
---|
263 | &tstVBoxDrvInstLogCallback, &Ctx /* pvUser */));
|
---|
264 | Ctx.hInst = hWinDrvInst;
|
---|
265 |
|
---|
266 | rc = tstVBoxDrvInstTestPath(&Ctx, szTestPath);
|
---|
267 |
|
---|
268 | VBoxWinDrvInstDestroy(hWinDrvInst);
|
---|
269 |
|
---|
270 | } while (0);
|
---|
271 |
|
---|
272 | /*
|
---|
273 | * Run the test.
|
---|
274 | */
|
---|
275 | return RTTestSummaryAndDestroy(hTest);
|
---|
276 | }
|
---|
277 |
|
---|