VirtualBox

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

Last change on this file since 78225 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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