VirtualBox

source: vbox/trunk/src/VBox/HostServices/GuestProperties/testcase/tstGuestPropSvc.cpp@ 100524

Last change on this file since 100524 was 99775, checked in by vboxsync, 20 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 46.5 KB
Line 
1/* $Id: tstGuestPropSvc.cpp 99775 2023-05-12 12:21:58Z vboxsync $ */
2/** @file
3 *
4 * Testcase for the guest property service.
5 */
6
7/*
8 * Copyright (C) 2008-2023 Oracle and/or its affiliates.
9 *
10 * This file is part of VirtualBox base platform packages, as
11 * available from https://www.virtualbox.org.
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation, in version 3 of the
16 * License.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses>.
25 *
26 * SPDX-License-Identifier: GPL-3.0-only
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33#include <VBox/HostServices/GuestPropertySvc.h>
34#include <VBox/err.h>
35#include <VBox/hgcmsvc.h>
36#include <iprt/test.h>
37#include <iprt/time.h>
38
39
40/*********************************************************************************************************************************
41* Global Variables *
42*********************************************************************************************************************************/
43static RTTEST g_hTest = NIL_RTTEST;
44
45
46/*********************************************************************************************************************************
47* Internal Functions *
48*********************************************************************************************************************************/
49extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable);
50
51
52/** Simple call handle structure for the guest call completion callback */
53struct VBOXHGCMCALLHANDLE_TYPEDEF
54{
55 /** Where to store the result code */
56 int32_t rc;
57};
58
59/** Dummy helper callback. */
60static DECLCALLBACK(int) tstHlpInfoDeregister(void *pvInstance, const char *pszName)
61{
62 RT_NOREF(pvInstance, pszName);
63 return VINF_SUCCESS;
64}
65
66/** Dummy helper callback. */
67static DECLCALLBACK(int) tstHlpInfoRegister(void *pvInstance, const char *pszName, const char *pszDesc,
68 PFNDBGFHANDLEREXT pfnHandler, void *pvUser)
69{
70 RT_NOREF(pvInstance, pszName, pszDesc, pfnHandler, pvUser);
71 return VINF_SUCCESS;
72}
73
74/** Call completion callback for guest calls. */
75static DECLCALLBACK(int) callComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc)
76{
77 callHandle->rc = rc;
78 return VINF_SUCCESS;
79}
80
81/**
82 * Initialise the HGCM service table as much as we need to start the
83 * service
84 * @param pTable the table to initialise
85 */
86void initTable(VBOXHGCMSVCFNTABLE *pTable, VBOXHGCMSVCHELPERS *pHelpers)
87{
88 RT_ZERO(*pHelpers);
89 pHelpers->pfnCallComplete = callComplete;
90 pHelpers->pfnInfoRegister = tstHlpInfoRegister;
91 pHelpers->pfnInfoDeregister = tstHlpInfoDeregister;
92
93 RT_ZERO(*pTable);
94 pTable->cbSize = sizeof(VBOXHGCMSVCFNTABLE);
95 pTable->u32Version = VBOX_HGCM_SVC_VERSION;
96 pTable->pHelpers = pHelpers;
97}
98
99/**
100 * A list of valid flag strings for testConvertFlags. The flag conversion
101 * functions should accept these and convert them from string to a flag type
102 * and back without errors.
103 */
104struct flagStrings
105{
106 /** Flag string in a format the functions should recognise */
107 const char *pcszIn;
108 /** How the functions should output the string again */
109 const char *pcszOut;
110}
111g_aValidFlagStrings[] =
112{
113 /* pcszIn, pcszOut */
114 { " ", "" },
115 { "transient, ", "TRANSIENT" },
116 { " rdOnLyHOST, transIENT , READONLY ", "TRANSIENT, READONLY" },
117 { " rdonlyguest", "RDONLYGUEST" },
118 { "rdonlyhost ", "RDONLYHOST" },
119 { "transient, transreset, rdonlyhost", "TRANSIENT, RDONLYHOST, TRANSRESET" },
120 { "transient, transreset, rdonlyguest", "TRANSIENT, RDONLYGUEST, TRANSRESET" }, /* max length */
121 { "rdonlyguest, rdonlyhost", "READONLY" },
122 { "transient, transreset, ", "TRANSIENT, TRANSRESET" }, /* Don't combine them ... */
123 { "transreset, ", "TRANSIENT, TRANSRESET" }, /* ... instead expand transreset for old adds. */
124};
125
126/**
127 * A list of invalid flag strings for testConvertFlags. The flag conversion
128 * functions should reject these.
129 */
130const char *g_apszInvalidFlagStrings[] =
131{
132 "RDONLYHOST,,",
133 " TRANSIENT READONLY"
134};
135
136/**
137 * Test the flag conversion functions.
138 * @returns iprt status value to indicate whether the test went as expected.
139 * @note prints its own diagnostic information to stdout.
140 */
141static void testConvertFlags(void)
142{
143 int rc = VINF_SUCCESS;
144 char *pszFlagBuffer = (char *)RTTestGuardedAllocTail(g_hTest, GUEST_PROP_MAX_FLAGS_LEN);
145
146 RTTestISub("Conversion of valid flags strings");
147 for (unsigned i = 0; i < RT_ELEMENTS(g_aValidFlagStrings) && RT_SUCCESS(rc); ++i)
148 {
149 uint32_t fFlags;
150 rc = GuestPropValidateFlags(g_aValidFlagStrings[i].pcszIn, &fFlags);
151 if (RT_FAILURE(rc))
152 RTTestIFailed("Failed to validate flag string '%s'", g_aValidFlagStrings[i].pcszIn);
153 if (RT_SUCCESS(rc))
154 {
155 rc = GuestPropWriteFlags(fFlags, pszFlagBuffer);
156 if (RT_FAILURE(rc))
157 RTTestIFailed("Failed to convert flag string '%s' back to a string.",
158 g_aValidFlagStrings[i].pcszIn);
159 }
160 if (RT_SUCCESS(rc) && (strlen(pszFlagBuffer) > GUEST_PROP_MAX_FLAGS_LEN - 1))
161 {
162 RTTestIFailed("String '%s' converts back to a flag string which is too long.\n",
163 g_aValidFlagStrings[i].pcszIn);
164 rc = VERR_TOO_MUCH_DATA;
165 }
166 if (RT_SUCCESS(rc) && (strcmp(pszFlagBuffer, g_aValidFlagStrings[i].pcszOut) != 0))
167 {
168 RTTestIFailed("String '%s' converts back to '%s' instead of to '%s'\n",
169 g_aValidFlagStrings[i].pcszIn, pszFlagBuffer,
170 g_aValidFlagStrings[i].pcszOut);
171 rc = VERR_PARSE_ERROR;
172 }
173 }
174 if (RT_SUCCESS(rc))
175 {
176 RTTestISub("Rejection of invalid flags strings");
177 for (unsigned i = 0; i < RT_ELEMENTS(g_apszInvalidFlagStrings) && RT_SUCCESS(rc); ++i)
178 {
179 uint32_t fFlags;
180 /* This is required to fail. */
181 if (RT_SUCCESS(GuestPropValidateFlags(g_apszInvalidFlagStrings[i], &fFlags)))
182 {
183 RTTestIFailed("String '%s' was incorrectly accepted as a valid flag string.\n",
184 g_apszInvalidFlagStrings[i]);
185 rc = VERR_PARSE_ERROR;
186 }
187 }
188 }
189 if (RT_SUCCESS(rc))
190 {
191 uint32_t u32BadFlags = GUEST_PROP_F_ALLFLAGS << 1;
192 RTTestISub("Rejection of an invalid flags field");
193 /* This is required to fail. */
194 if (RT_SUCCESS(GuestPropWriteFlags(u32BadFlags, pszFlagBuffer)))
195 {
196 RTTestIFailed("Flags 0x%x were incorrectly written out as '%.*s'\n",
197 u32BadFlags, GUEST_PROP_MAX_FLAGS_LEN, pszFlagBuffer);
198 rc = VERR_PARSE_ERROR;
199 }
200 }
201
202 RTTestGuardedFree(g_hTest, pszFlagBuffer);
203}
204
205/**
206 * List of property names for testSetPropsHost.
207 */
208const char *g_apcszNameBlock[] =
209{
210 "test/name/",
211 "test name",
212 "TEST NAME",
213 "/test/name",
214 NULL
215};
216
217/**
218 * List of property values for testSetPropsHost.
219 */
220const char *g_apcszValueBlock[] =
221{
222 "test/value/",
223 "test value",
224 "TEST VALUE",
225 "/test/value",
226 NULL
227};
228
229/**
230 * List of property timestamps for testSetPropsHost.
231 */
232uint64_t g_au64TimestampBlock[] =
233{
234 0, 999, 999999, UINT64_C(999999999999), 0
235};
236
237/**
238 * List of property flags for testSetPropsHost.
239 */
240const char *g_apcszFlagsBlock[] =
241{
242 "",
243 "readonly, transient",
244 "RDONLYHOST",
245 "RdOnlyGuest",
246 NULL
247};
248
249/**
250 * Test the SET_PROPS_HOST function.
251 * @returns iprt status value to indicate whether the test went as expected.
252 * @note prints its own diagnostic information to stdout.
253 */
254static void testSetPropsHost(VBOXHGCMSVCFNTABLE *ptable)
255{
256 RTTestISub("SET_PROPS_HOST");
257 RTTESTI_CHECK_RETV(RT_VALID_PTR(ptable->pfnHostCall));
258
259 VBOXHGCMSVCPARM aParms[4];
260 HGCMSvcSetPv(&aParms[0], (void *)g_apcszNameBlock, 0);
261 HGCMSvcSetPv(&aParms[1], (void *)g_apcszValueBlock, 0);
262 HGCMSvcSetPv(&aParms[2], (void *)g_au64TimestampBlock, 0);
263 HGCMSvcSetPv(&aParms[3], (void *)g_apcszFlagsBlock, 0);
264 RTTESTI_CHECK_RC(ptable->pfnHostCall(ptable->pvService, GUEST_PROP_FN_HOST_SET_PROPS, 4, &aParms[0]), VINF_SUCCESS);
265}
266
267#if 0
268/** Result strings for zeroth enumeration test */
269static const char *g_apchEnumResult0[] =
270{
271 "test/name/\0test/value/\0""0\0",
272 "test name\0test value\0""999\0TRANSIENT, READONLY",
273 "TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST",
274 "/test/name\0/test/value\0""999999999999\0RDONLYGUEST",
275 NULL
276};
277
278/** Result string sizes for zeroth enumeration test */
279static const uint32_t g_acbEnumResult0[] =
280{
281 sizeof("test/name/\0test/value/\0""0\0"),
282 sizeof("test name\0test value\0""999\0TRANSIENT, READONLY"),
283 sizeof("TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST"),
284 sizeof("/test/name\0/test/value\0""999999999999\0RDONLYGUEST"),
285 0
286};
287
288/**
289 * The size of the buffer returned by the zeroth enumeration test -
290 * the - 1 at the end is because of the hidden zero terminator
291 */
292static const uint32_t g_cbEnumBuffer0 =
293 sizeof("test/name/\0test/value/\0""0\0\0"
294 "test name\0test value\0""999\0TRANSIENT, READONLY\0"
295 "TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST\0"
296 "/test/name\0/test/value\0""999999999999\0RDONLYGUEST\0\0\0\0\0") - 1;
297#endif
298
299/** Result strings for first and second enumeration test */
300static const char *g_apchEnumResult1[] =
301{
302 "TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST",
303 "/test/name\0/test/value\0""999999999999\0RDONLYGUEST",
304 NULL
305};
306
307/** Result string sizes for first and second enumeration test */
308static const uint32_t g_acbEnumResult1[] =
309{
310 sizeof("TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST"),
311 sizeof("/test/name\0/test/value\0""999999999999\0RDONLYGUEST"),
312 0
313};
314
315/**
316 * The size of the buffer returned by the first enumeration test -
317 * the - 1 at the end is because of the hidden zero terminator
318 */
319static const uint32_t g_cbEnumBuffer1 =
320 sizeof("TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST\0"
321 "/test/name\0/test/value\0""999999999999\0RDONLYGUEST\0\0\0\0\0") - 1;
322
323static const struct enumStringStruct
324{
325 /** The enumeration pattern to test */
326 const char *pszPatterns;
327 /** The size of the pattern string (including terminator) */
328 const uint32_t cbPatterns;
329 /** The expected enumeration output strings */
330 const char **papchResult;
331 /** The size of the output strings */
332 const uint32_t *pacchResult;
333 /** The size of the buffer needed for the enumeration */
334 const uint32_t cbBuffer;
335} g_aEnumStrings[] =
336{
337#if 0 /* unpredictable automatic variables set by the service now */
338 {
339 "", sizeof(""),
340 g_apchEnumResult0,
341 g_acbEnumResult0,
342 g_cbEnumBuffer0
343 },
344#endif
345 {
346 "/t*\0?E*", sizeof("/t*\0?E*"),
347 g_apchEnumResult1,
348 g_acbEnumResult1,
349 g_cbEnumBuffer1
350 },
351 {
352 "/t*|?E*", sizeof("/t*|?E*"),
353 g_apchEnumResult1,
354 g_acbEnumResult1,
355 g_cbEnumBuffer1
356 }
357};
358
359/**
360 * Test the ENUM_PROPS_HOST function.
361 * @returns iprt status value to indicate whether the test went as expected.
362 * @note prints its own diagnostic information to stdout.
363 */
364static void testEnumPropsHost(VBOXHGCMSVCFNTABLE *ptable)
365{
366 RTTestISub("ENUM_PROPS_HOST");
367 RTTESTI_CHECK_RETV(RT_VALID_PTR(ptable->pfnHostCall));
368
369 for (unsigned i = 0; i < RT_ELEMENTS(g_aEnumStrings); ++i)
370 {
371 VBOXHGCMSVCPARM aParms[3];
372 char abBuffer[2048];
373 RTTESTI_CHECK_RETV(g_aEnumStrings[i].cbBuffer < sizeof(abBuffer));
374
375 /* Check that we get buffer overflow with a too small buffer. */
376 HGCMSvcSetPv(&aParms[0], (void *)g_aEnumStrings[i].pszPatterns, g_aEnumStrings[i].cbPatterns);
377 HGCMSvcSetPv(&aParms[1], (void *)abBuffer, g_aEnumStrings[i].cbBuffer - 1);
378 memset(abBuffer, 0x55, sizeof(abBuffer));
379 int rc2 = ptable->pfnHostCall(ptable->pvService, GUEST_PROP_FN_HOST_ENUM_PROPS, 3, aParms);
380 if (rc2 == VERR_BUFFER_OVERFLOW)
381 {
382 uint32_t cbNeeded;
383 RTTESTI_CHECK_RC(rc2 = HGCMSvcGetU32(&aParms[2], &cbNeeded), VINF_SUCCESS);
384 if (RT_SUCCESS(rc2))
385 RTTESTI_CHECK_MSG(cbNeeded == g_aEnumStrings[i].cbBuffer,
386 ("expected %#x, got %#x, pattern %d\n", g_aEnumStrings[i].cbBuffer, cbNeeded, i));
387 }
388 else
389 RTTestIFailed("ENUM_PROPS_HOST returned %Rrc instead of VERR_BUFFER_OVERFLOW on too small buffer, pattern number %d.", rc2, i);
390
391 /* Make a successfull call. */
392 HGCMSvcSetPv(&aParms[0], (void *)g_aEnumStrings[i].pszPatterns, g_aEnumStrings[i].cbPatterns);
393 HGCMSvcSetPv(&aParms[1], (void *)abBuffer, g_aEnumStrings[i].cbBuffer);
394 memset(abBuffer, 0x55, sizeof(abBuffer));
395 rc2 = ptable->pfnHostCall(ptable->pvService, GUEST_PROP_FN_HOST_ENUM_PROPS, 3, aParms);
396 if (rc2 == VINF_SUCCESS)
397 {
398 /* Look for each of the result strings in the buffer which was returned */
399 for (unsigned j = 0; g_aEnumStrings[i].papchResult[j] != NULL; ++j)
400 {
401 bool found = false;
402 for (unsigned k = 0; !found && k < g_aEnumStrings[i].cbBuffer - g_aEnumStrings[i].pacchResult[j]; ++k)
403 if (memcmp(abBuffer + k, g_aEnumStrings[i].papchResult[j], g_aEnumStrings[i].pacchResult[j]) == 0)
404 found = true;
405 if (!found)
406 RTTestIFailed("ENUM_PROPS_HOST did not produce the expected output for pattern %d.", i);
407 }
408 }
409 else
410 RTTestIFailed("ENUM_PROPS_HOST returned %Rrc instead of VINF_SUCCESS, pattern number %d.", rc2, i);
411 }
412}
413
414/**
415 * Set a property by calling the service
416 * @returns the status returned by the call to the service
417 *
418 * @param pTable the service instance handle
419 * @param pcszName the name of the property to set
420 * @param pcszValue the value to set the property to
421 * @param pcszFlags the flag string to set if one of the SET_PROP[_HOST]
422 * commands is used
423 * @param isHost whether the SET_PROP[_VALUE]_HOST commands should be
424 * used, rather than the guest ones
425 * @param useSetProp whether SET_PROP[_HOST] should be used rather than
426 * SET_PROP_VALUE[_HOST]
427 */
428static int doSetProperty(VBOXHGCMSVCFNTABLE *pTable, const char *pcszName,
429 const char *pcszValue, const char *pcszFlags, bool isHost,
430 bool useSetProp)
431{
432 RTThreadSleep(1); /* stupid, stupid timestamp fudge to avoid asserting in getOldNotification() */
433
434 VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
435 int command = GUEST_PROP_FN_SET_PROP_VALUE;
436 if (isHost)
437 {
438 if (useSetProp)
439 command = GUEST_PROP_FN_HOST_SET_PROP;
440 else
441 command = GUEST_PROP_FN_HOST_SET_PROP_VALUE;
442 }
443 else if (useSetProp)
444 command = GUEST_PROP_FN_SET_PROP;
445 VBOXHGCMSVCPARM aParms[3];
446 /* Work around silly constant issues - we ought to allow passing
447 * constant strings in the hgcm parameters. */
448 char szName[GUEST_PROP_MAX_NAME_LEN];
449 char szValue[GUEST_PROP_MAX_VALUE_LEN];
450 char szFlags[GUEST_PROP_MAX_FLAGS_LEN];
451 RTStrPrintf(szName, sizeof(szName), "%s", pcszName);
452 RTStrPrintf(szValue, sizeof(szValue), "%s", pcszValue);
453 RTStrPrintf(szFlags, sizeof(szFlags), "%s", pcszFlags);
454 HGCMSvcSetStr(&aParms[0], szName);
455 HGCMSvcSetStr(&aParms[1], szValue);
456 HGCMSvcSetStr(&aParms[2], szFlags);
457 if (isHost)
458 callHandle.rc = pTable->pfnHostCall(pTable->pvService, command,
459 useSetProp ? 3 : 2, aParms);
460 else
461 pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, command,
462 useSetProp ? 3 : 2, aParms, 0);
463 return callHandle.rc;
464}
465
466/**
467 * Test the SET_PROP, SET_PROP_VALUE, SET_PROP_HOST and SET_PROP_VALUE_HOST
468 * functions.
469 * @returns iprt status value to indicate whether the test went as expected.
470 * @note prints its own diagnostic information to stdout.
471 */
472static void testSetProp(VBOXHGCMSVCFNTABLE *pTable)
473{
474 RTTestISub("SET_PROP, _VALUE, _HOST, _VALUE_HOST");
475
476 /** Array of properties for testing SET_PROP_HOST and _GUEST. */
477 static const struct
478 {
479 /** Property name */
480 const char *pcszName;
481 /** Property value */
482 const char *pcszValue;
483 /** Property flags */
484 const char *pcszFlags;
485 /** Should this be set as the host or the guest? */
486 bool isHost;
487 /** Should we use SET_PROP or SET_PROP_VALUE? */
488 bool useSetProp;
489 /** Should this succeed or be rejected with VERR_PERMISSION_DENIED? */
490 bool isAllowed;
491 }
492 s_aSetProperties[] =
493 {
494 { "Red", "Stop!", "transient", false, true, true },
495 { "Amber", "Caution!", "", false, false, true },
496 { "Green", "Go!", "readonly", true, true, true },
497 { "Blue", "What on earth...?", "", true, false, true },
498 { "/test/name", "test", "", false, true, false },
499 { "TEST NAME", "test", "", true, true, false },
500 { "Green", "gone out...", "", false, false, false },
501 { "Green", "gone out...", "", true, false, false },
502 { "/VirtualBox/GuestAdd/SharedFolders/MountDir", "test", "", false, true, false },
503 { "/VirtualBox/GuestAdd/SomethingElse", "test", "", false, true, true },
504 { "/VirtualBox/HostInfo/VRDP/Client/1/Name", "test", "", false, false, false },
505 { "/VirtualBox/GuestAdd/SharedFolders/MountDir", "test", "", true, true, true },
506 { "/VirtualBox/HostInfo/VRDP/Client/1/Name", "test", "TRANSRESET", true, true, true },
507 };
508
509 for (unsigned i = 0; i < RT_ELEMENTS(s_aSetProperties); ++i)
510 {
511 int rc = doSetProperty(pTable,
512 s_aSetProperties[i].pcszName,
513 s_aSetProperties[i].pcszValue,
514 s_aSetProperties[i].pcszFlags,
515 s_aSetProperties[i].isHost,
516 s_aSetProperties[i].useSetProp);
517 if (s_aSetProperties[i].isAllowed && RT_FAILURE(rc))
518 RTTestIFailed("Setting property '%s' failed with rc=%Rrc.",
519 s_aSetProperties[i].pcszName, rc);
520 else if ( !s_aSetProperties[i].isAllowed
521 && rc != VERR_PERMISSION_DENIED)
522 RTTestIFailed("Setting property '%s' returned %Rrc instead of VERR_PERMISSION_DENIED.",
523 s_aSetProperties[i].pcszName, rc);
524 }
525}
526
527/**
528 * Delete a property by calling the service
529 * @returns the status returned by the call to the service
530 *
531 * @param pTable the service instance handle
532 * @param pcszName the name of the property to delete
533 * @param isHost whether the DEL_PROP_HOST command should be used, rather
534 * than the guest one
535 */
536static int doDelProp(VBOXHGCMSVCFNTABLE *pTable, const char *pcszName, bool isHost)
537{
538 VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
539 int command = GUEST_PROP_FN_DEL_PROP;
540 if (isHost)
541 command = GUEST_PROP_FN_HOST_DEL_PROP;
542 VBOXHGCMSVCPARM aParms[1];
543 HGCMSvcSetStr(&aParms[0], pcszName);
544 if (isHost)
545 callHandle.rc = pTable->pfnHostCall(pTable->pvService, command, 1, aParms);
546 else
547 pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, command, 1, aParms, 0);
548 return callHandle.rc;
549}
550
551/**
552 * Test the DEL_PROP, and DEL_PROP_HOST functions.
553 * @returns iprt status value to indicate whether the test went as expected.
554 * @note prints its own diagnostic information to stdout.
555 */
556static void testDelProp(VBOXHGCMSVCFNTABLE *pTable)
557{
558 RTTestISub("DEL_PROP, DEL_PROP_HOST");
559
560 /** Array of properties for testing DEL_PROP_HOST and _GUEST. */
561 static const struct
562 {
563 /** Property name */
564 const char *pcszName;
565 /** Should this be set as the host or the guest? */
566 bool isHost;
567 /** Should this succeed or be rejected with VERR_PERMISSION_DENIED? */
568 bool isAllowed;
569 } s_aDelProperties[] =
570 {
571 { "Red", false, true },
572 { "Amber", true, true },
573 { "Red2", false, true },
574 { "Amber2", true, true },
575 { "Green", false, false },
576 { "Green", true, false },
577 { "/test/name", false, false },
578 { "TEST NAME", true, false },
579 };
580
581 for (unsigned i = 0; i < RT_ELEMENTS(s_aDelProperties); ++i)
582 {
583 int rc = doDelProp(pTable, s_aDelProperties[i].pcszName, s_aDelProperties[i].isHost);
584 if (s_aDelProperties[i].isAllowed && RT_FAILURE(rc))
585 RTTestIFailed("Deleting property '%s' failed with rc=%Rrc.",
586 s_aDelProperties[i].pcszName, rc);
587 else if ( !s_aDelProperties[i].isAllowed
588 && rc != VERR_PERMISSION_DENIED )
589 RTTestIFailed("Deleting property '%s' returned %Rrc instead of VERR_PERMISSION_DENIED.",
590 s_aDelProperties[i].pcszName, rc);
591 }
592}
593
594/**
595 * Test the GET_PROP_HOST function.
596 * @returns iprt status value to indicate whether the test went as expected.
597 * @note prints its own diagnostic information to stdout.
598 */
599static void testGetProp(VBOXHGCMSVCFNTABLE *pTable)
600{
601 RTTestISub("GET_PROP_HOST");
602
603 /** Array of properties for testing GET_PROP_HOST. */
604 static const struct
605 {
606 /** Property name */
607 const char *pcszName;
608 /** What value/flags pattern do we expect back? */
609 const char *pchValue;
610 /** What size should the value/flags array be? */
611 uint32_t cchValue;
612 /** Should this property exist? */
613 bool exists;
614 /** Do we expect a particular timestamp? */
615 bool hasTimestamp;
616 /** What timestamp if any do ex expect? */
617 uint64_t u64Timestamp;
618 }
619 s_aGetProperties[] =
620 {
621 { "test/name/", "test/value/\0", sizeof("test/value/\0"), true, true, 0 },
622 { "test name", "test value\0TRANSIENT, READONLY",
623 sizeof("test value\0TRANSIENT, READONLY"), true, true, 999 },
624 { "TEST NAME", "TEST VALUE\0RDONLYHOST", sizeof("TEST VALUE\0RDONLYHOST"),
625 true, true, 999999 },
626 { "/test/name", "/test/value\0RDONLYGUEST",
627 sizeof("/test/value\0RDONLYGUEST"), true, true, UINT64_C(999999999999) },
628 { "Green", "Go!\0READONLY", sizeof("Go!\0READONLY"), true, false, 0 },
629 { "Blue", "What on earth...?\0", sizeof("What on earth...?\0"), true,
630 false, 0 },
631 { "Red", "", 0, false, false, 0 },
632 };
633
634 for (unsigned i = 0; i < RT_ELEMENTS(s_aGetProperties); ++i)
635 {
636 VBOXHGCMSVCPARM aParms[4];
637 /* Work around silly constant issues - we ought to allow passing
638 * constant strings in the hgcm parameters. */
639 char szBuffer[GUEST_PROP_MAX_VALUE_LEN + GUEST_PROP_MAX_FLAGS_LEN];
640 RTTESTI_CHECK_RETV(s_aGetProperties[i].cchValue < sizeof(szBuffer));
641
642 HGCMSvcSetStr(&aParms[0], s_aGetProperties[i].pcszName);
643 memset(szBuffer, 0x55, sizeof(szBuffer));
644 HGCMSvcSetPv(&aParms[1], szBuffer, sizeof(szBuffer));
645 int rc2 = pTable->pfnHostCall(pTable->pvService, GUEST_PROP_FN_HOST_GET_PROP, 4, aParms);
646
647 if (s_aGetProperties[i].exists && RT_FAILURE(rc2))
648 {
649 RTTestIFailed("Getting property '%s' failed with rc=%Rrc.",
650 s_aGetProperties[i].pcszName, rc2);
651 continue;
652 }
653
654 if (!s_aGetProperties[i].exists && rc2 != VERR_NOT_FOUND)
655 {
656 RTTestIFailed("Getting property '%s' returned %Rrc instead of VERR_NOT_FOUND.",
657 s_aGetProperties[i].pcszName, rc2);
658 continue;
659 }
660
661 if (s_aGetProperties[i].exists)
662 {
663 AssertRC(rc2);
664
665 uint32_t u32ValueLen = UINT32_MAX;
666 RTTESTI_CHECK_RC(rc2 = HGCMSvcGetU32(&aParms[3], &u32ValueLen), VINF_SUCCESS);
667 if (RT_SUCCESS(rc2))
668 {
669 RTTESTI_CHECK_MSG(u32ValueLen <= sizeof(szBuffer), ("u32ValueLen=%d", u32ValueLen));
670 if (memcmp(szBuffer, s_aGetProperties[i].pchValue, s_aGetProperties[i].cchValue) != 0)
671 RTTestIFailed("Unexpected result '%.*s' for property '%s', expected '%.*s'.",
672 u32ValueLen, szBuffer, s_aGetProperties[i].pcszName,
673 s_aGetProperties[i].cchValue, s_aGetProperties[i].pchValue);
674 }
675
676 if (s_aGetProperties[i].hasTimestamp)
677 {
678 uint64_t u64Timestamp = UINT64_MAX;
679 RTTESTI_CHECK_RC(rc2 = HGCMSvcGetU64(&aParms[2], &u64Timestamp), VINF_SUCCESS);
680 if (u64Timestamp != s_aGetProperties[i].u64Timestamp)
681 RTTestIFailed("Bad timestamp %llu for property '%s', expected %llu.",
682 u64Timestamp, s_aGetProperties[i].pcszName,
683 s_aGetProperties[i].u64Timestamp);
684 }
685 }
686 }
687}
688
689/** Array of properties for testing GET_PROP_HOST. */
690static const struct
691{
692 /** Buffer returned */
693 const char *pchBuffer;
694 /** What size should the buffer be? */
695 uint32_t cbBuffer;
696}
697g_aGetNotifications[] =
698{
699 // Name\0Value\0Flags\0fWasDeleted\0
700#define STR_AND_SIZE(a_sz) { a_sz, sizeof(a_sz) }
701 STR_AND_SIZE("Red\0Stop!\0TRANSIENT\0" "0"), /* first test is used by testAsyncNotification, - testGetNotification skips it. (mess) */
702 STR_AND_SIZE("Red\0Stop!\0TRANSIENT\0" "1"),
703 STR_AND_SIZE("Amber\0Caution!\0\0" "1"),
704 STR_AND_SIZE("Green\0Go!\0READONLY\0" "0"),
705 STR_AND_SIZE("Blue\0What on earth...?\0\0" "0"),
706 STR_AND_SIZE("/VirtualBox/GuestAdd/SomethingElse\0test\0\0" "0"),
707 STR_AND_SIZE("/VirtualBox/GuestAdd/SharedFolders/MountDir\0test\0RDONLYGUEST\0" "0"),
708 STR_AND_SIZE("/VirtualBox/HostInfo/VRDP/Client/1/Name\0test\0TRANSIENT, RDONLYGUEST, TRANSRESET\0" "0"),
709 STR_AND_SIZE("Red\0\0\0" "1"),
710 STR_AND_SIZE("Amber\0\0\0" "1"),
711#undef STR_AND_SIZE
712};
713
714/**
715 * Test the GET_NOTIFICATION function.
716 * @returns iprt status value to indicate whether the test went as expected.
717 * @note prints its own diagnostic information to stdout.
718 */
719static void testGetNotification(VBOXHGCMSVCFNTABLE *pTable)
720{
721 RTTestISub("GET_NOTIFICATION");
722
723 /* Test "buffer too small" */
724 static char s_szPattern[] = "/VirtualBox/GuestAdd/*|/VirtualBox/HostInfo/VRDP/Client*|Red*|Amber*|Green*|Blue*";
725 VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
726 VBOXHGCMSVCPARM aParms[4];
727 uint32_t cbRetNeeded = 0;
728
729 for (uint32_t cbBuf = 1;
730 cbBuf < g_aGetNotifications[1].cbBuffer - 1;
731 cbBuf++)
732 {
733 void *pvBuf = RTTestGuardedAllocTail(g_hTest, cbBuf);
734 RTTESTI_CHECK_BREAK(pvBuf);
735 memset(pvBuf, 0x55, cbBuf);
736
737 HGCMSvcSetStr(&aParms[0], s_szPattern);
738 HGCMSvcSetU64(&aParms[1], 1);
739 HGCMSvcSetPv(&aParms[2], pvBuf, cbBuf);
740 pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, GUEST_PROP_FN_GET_NOTIFICATION, 4, aParms, 0);
741
742 if ( callHandle.rc != VERR_BUFFER_OVERFLOW
743 || RT_FAILURE(HGCMSvcGetU32(&aParms[3], &cbRetNeeded))
744 || cbRetNeeded != g_aGetNotifications[1].cbBuffer
745 )
746 RTTestIFailed("Getting notification for property '%s' with a too small buffer did not fail correctly: rc=%Rrc, cbRetNeeded=%#x (expected %#x)",
747 g_aGetNotifications[1].pchBuffer, callHandle.rc, cbRetNeeded, g_aGetNotifications[1].cbBuffer);
748 RTTestGuardedFree(g_hTest, pvBuf);
749 }
750
751 /* Test successful notification queries. Start with an unknown timestamp
752 * to get the oldest available notification. */
753 uint64_t u64Timestamp = 1;
754 for (unsigned i = 1; i < RT_ELEMENTS(g_aGetNotifications); ++i)
755 {
756 uint32_t cbBuf = g_aGetNotifications[i].cbBuffer + _1K;
757 void *pvBuf = RTTestGuardedAllocTail(g_hTest, cbBuf);
758 RTTESTI_CHECK_BREAK(pvBuf);
759 memset(pvBuf, 0x55, cbBuf);
760
761 HGCMSvcSetStr(&aParms[0], s_szPattern);
762 HGCMSvcSetU64(&aParms[1], u64Timestamp);
763 HGCMSvcSetPv(&aParms[2], pvBuf, cbBuf);
764 pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, GUEST_PROP_FN_GET_NOTIFICATION, 4, aParms, 0);
765 if ( RT_FAILURE(callHandle.rc)
766 || (i == 0 && callHandle.rc != VWRN_NOT_FOUND)
767 || RT_FAILURE(HGCMSvcGetU64(&aParms[1], &u64Timestamp))
768 || RT_FAILURE(HGCMSvcGetU32(&aParms[3], &cbRetNeeded))
769 || cbRetNeeded != g_aGetNotifications[i].cbBuffer
770 || memcmp(pvBuf, g_aGetNotifications[i].pchBuffer, cbRetNeeded) != 0
771 )
772 {
773 RTTestIFailed("Failed to get notification for property '%s' (#%u): rc=%Rrc (expected %Rrc), cbRetNeeded=%#x (expected %#x)\n"
774 "%.*Rhxd\n---expected:---\n%.*Rhxd",
775 g_aGetNotifications[i].pchBuffer, i, callHandle.rc, i == 0 ? VWRN_NOT_FOUND : VINF_SUCCESS,
776 cbRetNeeded, g_aGetNotifications[i].cbBuffer, RT_MIN(cbRetNeeded, cbBuf), pvBuf,
777 g_aGetNotifications[i].cbBuffer, g_aGetNotifications[i].pchBuffer);
778 }
779 RTTestGuardedFree(g_hTest, pvBuf);
780 }
781}
782
783/** Parameters for the asynchronous guest notification call */
784struct asyncNotification_
785{
786 /** Call parameters */
787 VBOXHGCMSVCPARM aParms[4];
788 /** Result buffer */
789 char abBuffer[GUEST_PROP_MAX_NAME_LEN + GUEST_PROP_MAX_VALUE_LEN + GUEST_PROP_MAX_FLAGS_LEN];
790 /** Return value */
791 VBOXHGCMCALLHANDLE_TYPEDEF callHandle;
792} g_AsyncNotification;
793
794/**
795 * Set up the test for the asynchronous GET_NOTIFICATION function.
796 */
797static void setupAsyncNotification(VBOXHGCMSVCFNTABLE *pTable)
798{
799 RTTestISub("Async GET_NOTIFICATION without notifications");
800 static char s_szPattern[] = "";
801
802 HGCMSvcSetStr(&g_AsyncNotification.aParms[0], s_szPattern);
803 HGCMSvcSetU64(&g_AsyncNotification.aParms[1], 0);
804 HGCMSvcSetPv(&g_AsyncNotification.aParms[2], g_AsyncNotification.abBuffer, sizeof(g_AsyncNotification.abBuffer));
805 g_AsyncNotification.callHandle.rc = VINF_HGCM_ASYNC_EXECUTE;
806 pTable->pfnCall(pTable->pvService, &g_AsyncNotification.callHandle, 0, NULL,
807 GUEST_PROP_FN_GET_NOTIFICATION, 4, g_AsyncNotification.aParms, 0);
808 if (RT_FAILURE(g_AsyncNotification.callHandle.rc))
809 RTTestIFailed("GET_NOTIFICATION call failed, rc=%Rrc.", g_AsyncNotification.callHandle.rc);
810 else if (g_AsyncNotification.callHandle.rc != VINF_HGCM_ASYNC_EXECUTE)
811 RTTestIFailed("GET_NOTIFICATION call completed when no new notifications should be available.");
812}
813
814/**
815 * Test the asynchronous GET_NOTIFICATION function.
816 */
817static void testAsyncNotification(VBOXHGCMSVCFNTABLE *pTable)
818{
819 RT_NOREF1(pTable);
820 uint64_t u64Timestamp;
821 uint32_t cb = 0;
822 if ( g_AsyncNotification.callHandle.rc != VINF_SUCCESS
823 || RT_FAILURE(HGCMSvcGetU64(&g_AsyncNotification.aParms[1], &u64Timestamp))
824 || RT_FAILURE(HGCMSvcGetU32(&g_AsyncNotification.aParms[3], &cb))
825 || cb != g_aGetNotifications[0].cbBuffer
826 || memcmp(g_AsyncNotification.abBuffer, g_aGetNotifications[0].pchBuffer, cb) != 0
827 )
828 {
829 RTTestIFailed("Asynchronous GET_NOTIFICATION call did not complete as expected: rc=%Rrc, cb=%#x (expected %#x)\n"
830 "abBuffer=%.*Rhxs\n"
831 "expected=%.*Rhxs",
832 g_AsyncNotification.callHandle.rc, cb, g_aGetNotifications[0].cbBuffer,
833 cb, g_AsyncNotification.abBuffer, g_aGetNotifications[0].cbBuffer, g_aGetNotifications[0].pchBuffer);
834 }
835}
836
837
838static void test2(void)
839{
840 VBOXHGCMSVCFNTABLE svcTable;
841 VBOXHGCMSVCHELPERS svcHelpers;
842 initTable(&svcTable, &svcHelpers);
843
844 /* The function is inside the service, not HGCM. */
845 RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(&svcTable));
846
847 testSetPropsHost(&svcTable);
848 testEnumPropsHost(&svcTable);
849
850 /* Set up the asynchronous notification test */
851 setupAsyncNotification(&svcTable);
852 testSetProp(&svcTable);
853 RTTestISub("Async notification call data");
854 testAsyncNotification(&svcTable); /* Our previous notification call should have completed by now. */
855
856 testDelProp(&svcTable);
857 testGetProp(&svcTable);
858 testGetNotification(&svcTable);
859
860 /* Cleanup */
861 RTTESTI_CHECK_RC_OK(svcTable.pfnUnload(svcTable.pvService));
862}
863
864/**
865 * Set the global flags value by calling the service
866 * @returns the status returned by the call to the service
867 *
868 * @param pTable the service instance handle
869 * @param fFlags the flags to set
870 */
871static int doSetGlobalFlags(VBOXHGCMSVCFNTABLE *pTable, uint32_t fFlags)
872{
873 VBOXHGCMSVCPARM paParm;
874 HGCMSvcSetU32(&paParm, fFlags);
875 int rc = pTable->pfnHostCall(pTable->pvService, GUEST_PROP_FN_HOST_SET_GLOBAL_FLAGS, 1, &paParm);
876 if (RT_FAILURE(rc))
877 {
878 char szFlags[GUEST_PROP_MAX_FLAGS_LEN];
879 if (RT_FAILURE(GuestPropWriteFlags(fFlags, szFlags)))
880 RTTestIFailed("Failed to set the global flags.");
881 else
882 RTTestIFailed("Failed to set the global flags \"%s\".", szFlags);
883 }
884 return rc;
885}
886
887/**
888 * Test the SET_PROP, SET_PROP_VALUE, SET_PROP_HOST and SET_PROP_VALUE_HOST
889 * functions.
890 * @returns iprt status value to indicate whether the test went as expected.
891 * @note prints its own diagnostic information to stdout.
892 */
893static void testSetPropROGuest(VBOXHGCMSVCFNTABLE *pTable)
894{
895 RTTestISub("global READONLYGUEST and SET_PROP*");
896
897 /** Array of properties for testing SET_PROP_HOST and _GUEST with the
898 * READONLYGUEST global flag set. */
899 static const struct
900 {
901 /** Property name */
902 const char *pcszName;
903 /** Property value */
904 const char *pcszValue;
905 /** Property flags */
906 const char *pcszFlags;
907 /** Should this be set as the host or the guest? */
908 bool isHost;
909 /** Should we use SET_PROP or SET_PROP_VALUE? */
910 bool useSetProp;
911 /** Should this succeed or be rejected with VERR_ (NOT VINF_!)
912 * PERMISSION_DENIED? The global check is done after the property one. */
913 bool isAllowed;
914 }
915 s_aSetPropertiesROGuest[] =
916 {
917 { "Red", "Stop!", "transient", false, true, true },
918 { "Amber", "Caution!", "", false, false, true },
919 { "Green", "Go!", "readonly", true, true, true },
920 { "Blue", "What on earth...?", "", true, false, true },
921 { "/test/name", "test", "", false, true, true },
922 { "TEST NAME", "test", "", true, true, true },
923 { "Green", "gone out...", "", false, false, false },
924 { "Green", "gone out....", "", true, false, false },
925 };
926
927 RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(pTable));
928 int rc = doSetGlobalFlags(pTable, GUEST_PROP_F_RDONLYGUEST);
929 if (RT_SUCCESS(rc))
930 {
931 for (unsigned i = 0; i < RT_ELEMENTS(s_aSetPropertiesROGuest); ++i)
932 {
933 rc = doSetProperty(pTable, s_aSetPropertiesROGuest[i].pcszName,
934 s_aSetPropertiesROGuest[i].pcszValue,
935 s_aSetPropertiesROGuest[i].pcszFlags,
936 s_aSetPropertiesROGuest[i].isHost,
937 s_aSetPropertiesROGuest[i].useSetProp);
938 if (s_aSetPropertiesROGuest[i].isAllowed && RT_FAILURE(rc))
939 RTTestIFailed("Setting property '%s' to '%s' failed with rc=%Rrc.",
940 s_aSetPropertiesROGuest[i].pcszName,
941 s_aSetPropertiesROGuest[i].pcszValue, rc);
942 else if ( !s_aSetPropertiesROGuest[i].isAllowed
943 && rc != VERR_PERMISSION_DENIED)
944 RTTestIFailed("Setting property '%s' to '%s' returned %Rrc instead of VERR_PERMISSION_DENIED.\n",
945 s_aSetPropertiesROGuest[i].pcszName,
946 s_aSetPropertiesROGuest[i].pcszValue, rc);
947 else if ( !s_aSetPropertiesROGuest[i].isHost
948 && s_aSetPropertiesROGuest[i].isAllowed
949 && rc != VINF_PERMISSION_DENIED)
950 RTTestIFailed("Setting property '%s' to '%s' returned %Rrc instead of VINF_PERMISSION_DENIED.\n",
951 s_aSetPropertiesROGuest[i].pcszName,
952 s_aSetPropertiesROGuest[i].pcszValue, rc);
953 }
954 }
955 RTTESTI_CHECK_RC_OK(pTable->pfnUnload(pTable->pvService));
956}
957
958/**
959 * Test the DEL_PROP, and DEL_PROP_HOST functions.
960 * @returns iprt status value to indicate whether the test went as expected.
961 * @note prints its own diagnostic information to stdout.
962 */
963static void testDelPropROGuest(VBOXHGCMSVCFNTABLE *pTable)
964{
965 RTTestISub("global READONLYGUEST and DEL_PROP*");
966
967 /** Array of properties for testing DEL_PROP_HOST and _GUEST with
968 * READONLYGUEST set globally. */
969 static const struct
970 {
971 /** Property name */
972 const char *pcszName;
973 /** Should this be deleted as the host (or the guest)? */
974 bool isHost;
975 /** Should this property be created first? (As host, obviously) */
976 bool shouldCreate;
977 /** And with what flags? */
978 const char *pcszFlags;
979 /** Should this succeed or be rejected with VERR_ (NOT VINF_!)
980 * PERMISSION_DENIED? The global check is done after the property one. */
981 bool isAllowed;
982 }
983 s_aDelPropertiesROGuest[] =
984 {
985 { "Red", true, true, "", true },
986 { "Amber", false, true, "", true },
987 { "Red2", true, false, "", true },
988 { "Amber2", false, false, "", true },
989 { "Red3", true, true, "READONLY", false },
990 { "Amber3", false, true, "READONLY", false },
991 { "Red4", true, true, "RDONLYHOST", false },
992 { "Amber4", false, true, "RDONLYHOST", true },
993 };
994
995 RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(pTable));
996 int rc = doSetGlobalFlags(pTable, GUEST_PROP_F_RDONLYGUEST);
997 if (RT_SUCCESS(rc))
998 {
999 for (unsigned i = 0; i < RT_ELEMENTS(s_aDelPropertiesROGuest); ++i)
1000 {
1001 if (s_aDelPropertiesROGuest[i].shouldCreate)
1002 rc = doSetProperty(pTable, s_aDelPropertiesROGuest[i].pcszName,
1003 "none", s_aDelPropertiesROGuest[i].pcszFlags,
1004 true, true);
1005 rc = doDelProp(pTable, s_aDelPropertiesROGuest[i].pcszName,
1006 s_aDelPropertiesROGuest[i].isHost);
1007 if (s_aDelPropertiesROGuest[i].isAllowed && RT_FAILURE(rc))
1008 RTTestIFailed("Deleting property '%s' failed with rc=%Rrc.",
1009 s_aDelPropertiesROGuest[i].pcszName, rc);
1010 else if ( !s_aDelPropertiesROGuest[i].isAllowed
1011 && rc != VERR_PERMISSION_DENIED)
1012 RTTestIFailed("Deleting property '%s' returned %Rrc instead of VERR_PERMISSION_DENIED.",
1013 s_aDelPropertiesROGuest[i].pcszName, rc);
1014 else if ( !s_aDelPropertiesROGuest[i].isHost
1015 && s_aDelPropertiesROGuest[i].shouldCreate
1016 && s_aDelPropertiesROGuest[i].isAllowed
1017 && rc != VINF_PERMISSION_DENIED)
1018 RTTestIFailed("Deleting property '%s' as guest returned %Rrc instead of VINF_PERMISSION_DENIED.",
1019 s_aDelPropertiesROGuest[i].pcszName, rc);
1020 }
1021 }
1022 RTTESTI_CHECK_RC_OK(pTable->pfnUnload(pTable->pvService));
1023}
1024
1025static void test3(void)
1026{
1027 VBOXHGCMSVCFNTABLE svcTable;
1028 VBOXHGCMSVCHELPERS svcHelpers;
1029 initTable(&svcTable, &svcHelpers);
1030 testSetPropROGuest(&svcTable);
1031 testDelPropROGuest(&svcTable);
1032}
1033
1034static void test4(void)
1035{
1036 RTTestISub("GET_PROP_HOST buffer handling");
1037
1038 VBOXHGCMSVCFNTABLE svcTable;
1039 VBOXHGCMSVCHELPERS svcHelpers;
1040 initTable(&svcTable, &svcHelpers);
1041 RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(&svcTable));
1042
1043 /* Insert a property that we can mess around with. */
1044 static char const s_szProp[] = "/MyProperties/Sub/Sub/Sub/Sub/Sub/Sub/Sub/Property";
1045 static char const s_szValue[] = "Property Value";
1046 RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, s_szProp, s_szValue, "", true, true));
1047
1048
1049 /* Get the value with buffer sizes up to 1K. */
1050 for (unsigned iVariation = 0; iVariation < 2; iVariation++)
1051 {
1052 for (uint32_t cbBuf = 0; cbBuf < _1K; cbBuf++)
1053 {
1054 void *pvBuf;
1055 RTTESTI_CHECK_RC_BREAK(RTTestGuardedAlloc(g_hTest, cbBuf, 1, iVariation == 0, &pvBuf), VINF_SUCCESS);
1056
1057 VBOXHGCMSVCPARM aParms[4];
1058 HGCMSvcSetStr(&aParms[0], s_szProp);
1059 HGCMSvcSetPv(&aParms[1], pvBuf, cbBuf);
1060 svcTable.pfnHostCall(svcTable.pvService, GUEST_PROP_FN_HOST_GET_PROP, RT_ELEMENTS(aParms), aParms);
1061
1062 RTTestGuardedFree(g_hTest, pvBuf);
1063 }
1064 }
1065
1066 /* Done. */
1067 RTTESTI_CHECK_RC_OK(svcTable.pfnUnload(svcTable.pvService));
1068}
1069
1070static void test5(void)
1071{
1072 RTTestISub("ENUM_PROPS_HOST buffer handling");
1073
1074 VBOXHGCMSVCFNTABLE svcTable;
1075 VBOXHGCMSVCHELPERS svcHelpers;
1076 initTable(&svcTable, &svcHelpers);
1077 RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(&svcTable));
1078
1079 /* Insert a few property that we can mess around with. */
1080 RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, "/MyProperties/Sub/Sub/Sub/Sub/Sub/Sub/Sub/Property", "Property Value", "", true, true));
1081 RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, "/MyProperties/12357", "83848569", "", true, true));
1082 RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, "/MyProperties/56678", "abcdefghijklm", "", true, true));
1083 RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, "/MyProperties/932769", "n", "", true, true));
1084
1085 /* Get the value with buffer sizes up to 1K. */
1086 for (unsigned iVariation = 0; iVariation < 2; iVariation++)
1087 {
1088 for (uint32_t cbBuf = 0; cbBuf < _1K; cbBuf++)
1089 {
1090 void *pvBuf;
1091 RTTESTI_CHECK_RC_BREAK(RTTestGuardedAlloc(g_hTest, cbBuf, 1, iVariation == 0, &pvBuf), VINF_SUCCESS);
1092
1093 VBOXHGCMSVCPARM aParms[3];
1094 HGCMSvcSetStr(&aParms[0], "*");
1095 HGCMSvcSetPv(&aParms[1], pvBuf, cbBuf);
1096 svcTable.pfnHostCall(svcTable.pvService, GUEST_PROP_FN_HOST_ENUM_PROPS, RT_ELEMENTS(aParms), aParms);
1097
1098 RTTestGuardedFree(g_hTest, pvBuf);
1099 }
1100 }
1101
1102 /* Done. */
1103 RTTESTI_CHECK_RC_OK(svcTable.pfnUnload(svcTable.pvService));
1104}
1105
1106static void test6(void)
1107{
1108 RTTestISub("Max properties");
1109
1110 VBOXHGCMSVCFNTABLE svcTable;
1111 VBOXHGCMSVCHELPERS svcHelpers;
1112 initTable(&svcTable, &svcHelpers);
1113 RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(&svcTable));
1114
1115 /* Insert the max number of properties. */
1116 static char const s_szPropFmt[] = "/MyProperties/Sub/Sub/Sub/Sub/Sub/Sub/Sub/PropertyNo#%u";
1117 char szProp[80];
1118 unsigned cProps = 0;
1119 for (;;)
1120 {
1121 RTStrPrintf(szProp, sizeof(szProp), s_szPropFmt, cProps);
1122 int rc = doSetProperty(&svcTable, szProp, "myvalue", "", true, true);
1123 if (rc == VERR_TOO_MUCH_DATA)
1124 break;
1125 if (RT_FAILURE(rc))
1126 {
1127 RTTestIFailed("Unexpected error %Rrc setting property number %u", rc, cProps);
1128 break;
1129 }
1130 cProps++;
1131 }
1132 RTTestIValue("Max Properties", cProps, RTTESTUNIT_OCCURRENCES);
1133
1134 /* Touch them all again. */
1135 for (unsigned iProp = 0; iProp < cProps; iProp++)
1136 {
1137 RTStrPrintf(szProp, sizeof(szProp), s_szPropFmt, iProp);
1138 int rc;
1139 RTTESTI_CHECK_MSG((rc = doSetProperty(&svcTable, szProp, "myvalue", "", true, true)) == VINF_SUCCESS,
1140 ("%Rrc - #%u\n", rc, iProp));
1141 RTTESTI_CHECK_MSG((rc = doSetProperty(&svcTable, szProp, "myvalue", "", true, false)) == VINF_SUCCESS,
1142 ("%Rrc - #%u\n", rc, iProp));
1143 RTTESTI_CHECK_MSG((rc = doSetProperty(&svcTable, szProp, "myvalue", "", false, true)) == VINF_SUCCESS,
1144 ("%Rrc - #%u\n", rc, iProp));
1145 RTTESTI_CHECK_MSG((rc = doSetProperty(&svcTable, szProp, "myvalue", "", false, false)) == VINF_SUCCESS,
1146 ("%Rrc - #%u\n", rc, iProp));
1147 }
1148
1149 /* Benchmark. */
1150 uint64_t cNsMax = 0;
1151 uint64_t cNsMin = UINT64_MAX;
1152 uint64_t cNsAvg = 0;
1153 for (unsigned iProp = 0; iProp < cProps; iProp++)
1154 {
1155 size_t cchProp = RTStrPrintf(szProp, sizeof(szProp), s_szPropFmt, iProp);
1156
1157 uint64_t cNsElapsed = RTTimeNanoTS();
1158 unsigned iCall;
1159 for (iCall = 0; iCall < 1000; iCall++)
1160 {
1161 VBOXHGCMSVCPARM aParms[4];
1162 char szBuffer[256];
1163 HGCMSvcSetPv(&aParms[0], szProp, (uint32_t)cchProp + 1);
1164 HGCMSvcSetPv(&aParms[1], szBuffer, sizeof(szBuffer));
1165 RTTESTI_CHECK_RC_BREAK(svcTable.pfnHostCall(svcTable.pvService, GUEST_PROP_FN_HOST_GET_PROP, 4, aParms), VINF_SUCCESS);
1166 }
1167 cNsElapsed = RTTimeNanoTS() - cNsElapsed;
1168 if (iCall)
1169 {
1170 uint64_t cNsPerCall = cNsElapsed / iCall;
1171 cNsAvg += cNsPerCall;
1172 if (cNsPerCall < cNsMin)
1173 cNsMin = cNsPerCall;
1174 if (cNsPerCall > cNsMax)
1175 cNsMax = cNsPerCall;
1176 }
1177 }
1178 if (cProps)
1179 cNsAvg /= cProps;
1180 RTTestIValue("GET_PROP_HOST Min", cNsMin, RTTESTUNIT_NS_PER_CALL);
1181 RTTestIValue("GET_PROP_HOST Avg", cNsAvg, RTTESTUNIT_NS_PER_CALL);
1182 RTTestIValue("GET_PROP_HOST Max", cNsMax, RTTESTUNIT_NS_PER_CALL);
1183
1184 /* Done. */
1185 RTTESTI_CHECK_RC_OK(svcTable.pfnUnload(svcTable.pvService));
1186}
1187
1188
1189
1190
1191int main()
1192{
1193 RTEXITCODE rcExit = RTTestInitAndCreate("tstGuestPropSvc", &g_hTest);
1194 if (rcExit != RTEXITCODE_SUCCESS)
1195 return rcExit;
1196 RTTestBanner(g_hTest);
1197
1198 testConvertFlags();
1199 test2();
1200 test3();
1201 test4();
1202 test5();
1203 test6();
1204
1205 return RTTestSummaryAndDestroy(g_hTest);
1206}
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