VirtualBox

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

最後變更 在這個檔案從95331是 95331,由 vboxsync 提交於 3 年 前

tstGuestPropSvc: Unbroke testcase after notification changes, service set properties and whatnot. bugref:10185

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

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