VirtualBox

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

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

HGCM: build fixes.

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