VirtualBox

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

Last change on this file since 38245 was 36412, checked in by vboxsync, 14 years ago

GuestProps: TRANSRESET implies TRANSIENT, return it for old additions and API users.

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