VirtualBox

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

Last change on this file since 30084 was 29394, checked in by vboxsync, 15 years ago

GuestProperties: Try make sure the timestamp is somewhat unique because the code is making assumptions about this (RTTimeNow granularity is very coarse on windows). Fixed several derefernces of rend in getOldNotificationInternal that VC++'s strictness checks pointed out. Also cleaned up the method, dropping the unncessary and somewhat historical/puzzling RT_SUCCESS(rc) bits.

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