VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardMockHGCM.cpp@ 94228

Last change on this file since 94228 was 94228, checked in by vboxsync, 3 years ago

Validation Kit/HGCM: Split out the HGCM testing (mocking) framework into an own header, added documentation, more code for generalizing this [build fix].

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.9 KB
Line 
1/* $Id: tstClipboardMockHGCM.cpp 94228 2022-03-14 19:19:18Z vboxsync $ */
2/** @file
3 * Shared Clipboard host service test case.
4 */
5
6/*
7 * Copyright (C) 2011-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "../VBoxSharedClipboardSvc-internal.h"
19
20#include <VBox/HostServices/VBoxClipboardSvc.h>
21#include <VBox/VBoxGuestLib.h>
22#ifdef RT_OS_LINUX
23# include <VBox/GuestHost/SharedClipboard-x11.h>
24#endif
25
26#include <VBox/GuestHost/HGCMMock.h>
27
28#include <iprt/assert.h>
29#include <iprt/initterm.h>
30#include <iprt/mem.h>
31#include <iprt/rand.h>
32#include <iprt/stream.h>
33#include <iprt/string.h>
34#include <iprt/test.h>
35#include <iprt/utf16.h>
36
37
38/*********************************************************************************************************************************
39* Static globals *
40*********************************************************************************************************************************/
41static RTTEST g_hTest;
42static SHCLCLIENT g_Client;
43
44
45/*********************************************************************************************************************************
46* Shared Clipboard testing *
47*********************************************************************************************************************************/
48struct TESTDESC;
49/** Pointer to a test description. */
50typedef TESTDESC *PTESTDESC;
51
52struct TESTPARMS;
53/** Pointer to a test parameter structure. */
54typedef TESTPARMS *PTESTPARMS;
55
56struct TESTCTX;
57/** Pointer to a test context. */
58typedef TESTCTX *PTESTCTX;
59
60/** Pointer a test descriptor. */
61typedef TESTDESC *PTESTDESC;
62
63typedef DECLCALLBACKTYPE(int, FNTESTSETUP,(PTESTPARMS pTstParms, void **ppvCtx));
64/** Pointer to an test setup callback. */
65typedef FNTESTSETUP *PFNTESTSETUP;
66
67typedef DECLCALLBACKTYPE(int, FNTESTEXEC,(PTESTPARMS pTstParms, void *pvCtx));
68/** Pointer to an test exec callback. */
69typedef FNTESTEXEC *PFNTESTEXEC;
70
71typedef DECLCALLBACKTYPE(int, FNTESTGSTTHREAD,(PTESTCTX pCtx, void *pvCtx));
72/** Pointer to an test guest thread callback. */
73typedef FNTESTGSTTHREAD *PFNTESTGSTTHREAD;
74
75typedef DECLCALLBACKTYPE(int, FNTESTDESTROY,(PTESTPARMS pTstParms, void *pvCtx));
76/** Pointer to an test destroy callback. */
77typedef FNTESTDESTROY *PFNTESTDESTROY;
78
79typedef struct TESTTASK
80{
81 RTSEMEVENT hEvent;
82 int rcCompleted;
83 int rcExpected;
84 SHCLFORMATS enmFmtHst;
85 SHCLFORMATS enmFmtGst;
86 /** For chunked reads / writes. */
87 size_t cbChunk;
88 size_t cbData;
89 void *pvData;
90} TESTTASK;
91typedef TESTTASK *PTESTTASK;
92
93/**
94 * Structure for keeping a test context.
95 */
96typedef struct TESTCTX
97{
98 PTSTHGCMMOCKSVC pSvc;
99 /** Currently we only support one task at a time. */
100 TESTTASK Task;
101 struct
102 {
103 RTTHREAD hThread;
104 VBGLR3SHCLCMDCTX CmdCtx;
105 volatile bool fShutdown;
106 PFNTESTGSTTHREAD pfnThread;
107 } Guest;
108 struct
109 {
110 RTTHREAD hThread;
111 volatile bool fShutdown;
112 } Host;
113} TESTCTX;
114
115/** The one and only test context. */
116TESTCTX g_TstCtx;
117
118/**
119 * Test parameters.
120 */
121typedef struct TESTPARMS
122{
123 /** Pointer to test context to use. */
124 PTESTCTX pTstCtx;
125} TESTPARMS;
126
127typedef struct TESTDESC
128{
129 /** The setup callback. */
130 PFNTESTSETUP pfnSetup;
131 /** The exec callback. */
132 PFNTESTEXEC pfnExec;
133 /** The destruction callback. */
134 PFNTESTDESTROY pfnDestroy;
135} TESTDESC;
136
137typedef struct SHCLCONTEXT
138{
139} SHCLCONTEXT;
140
141
142#if 0
143static void tstBackendWriteData(HGCMCLIENTID idClient, SHCLFORMAT uFormat, void *pvData, size_t cbData)
144{
145 ShClBackendSetClipboardData(&s_tstHgcmClient[idClient].Client, uFormat, pvData, cbData);
146}
147
148/** Adds a host data read request message to the client's message queue. */
149static int tstSvcMockRequestDataFromGuest(uint32_t idClient, SHCLFORMATS fFormats, PSHCLEVENT *ppEvent)
150{
151 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
152
153 int rc = ShClSvcGuestDataRequest(&s_tstHgcmClient[idClient].Client, fFormats, ppEvent);
154 RTTESTI_CHECK_RC_OK_RET(rc, rc);
155
156 return rc;
157}
158#endif
159
160static int tstSetModeRc(PTSTHGCMMOCKSVC pSvc, uint32_t uMode, int rc)
161{
162 VBOXHGCMSVCPARM aParms[2];
163 HGCMSvcSetU32(&aParms[0], uMode);
164 int rc2 = TstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_MODE, 1, aParms);
165 RTTESTI_CHECK_MSG_RET(rc == rc2, ("Expected %Rrc, got %Rrc\n", rc, rc2), rc2);
166 uint32_t const uModeRet = ShClSvcGetMode();
167 RTTESTI_CHECK_MSG_RET(uMode == uModeRet, ("Expected mode %RU32, got %RU32\n", uMode, uModeRet), VERR_WRONG_TYPE);
168 return rc2;
169}
170
171static int tstSetMode(PTSTHGCMMOCKSVC pSvc, uint32_t uMode)
172{
173 return tstSetModeRc(pSvc, uMode, VINF_SUCCESS);
174}
175
176static bool tstGetMode(PTSTHGCMMOCKSVC pSvc, uint32_t uModeExpected)
177{
178 RT_NOREF(pSvc);
179 RTTESTI_CHECK_RET(ShClSvcGetMode() == uModeExpected, false);
180 return true;
181}
182
183static void tstOperationModes(void)
184{
185 struct VBOXHGCMSVCPARM parms[2];
186 uint32_t u32Mode;
187 int rc;
188
189 RTTestISub("Testing VBOX_SHCL_HOST_FN_SET_MODE");
190
191 PTSTHGCMMOCKSVC pSvc = TstHgcmMockSvcInst();
192
193 /* Reset global variable which doesn't reset itself. */
194 HGCMSvcSetU32(&parms[0], VBOX_SHCL_MODE_OFF);
195 rc = TstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_MODE, 1, parms);
196 RTTESTI_CHECK_RC_OK(rc);
197 u32Mode = ShClSvcGetMode();
198 RTTESTI_CHECK_MSG(u32Mode == VBOX_SHCL_MODE_OFF, ("u32Mode=%u\n", (unsigned) u32Mode));
199
200 rc = TstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_MODE, 0, parms);
201 RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
202
203 rc = TstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_MODE, 2, parms);
204 RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
205
206 HGCMSvcSetU64(&parms[0], 99);
207 rc = TstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_MODE, 1, parms);
208 RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
209
210 tstSetMode(pSvc, VBOX_SHCL_MODE_HOST_TO_GUEST);
211 tstSetModeRc(pSvc, 99, VERR_NOT_SUPPORTED);
212 tstGetMode(pSvc, VBOX_SHCL_MODE_OFF);
213}
214
215#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
216static void testSetTransferMode(void)
217{
218 RTTestISub("Testing VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE");
219
220 PTSTHGCMMOCKSVC pSvc = TstHgcmMockSvcInst();
221
222 /* Invalid parameter. */
223 VBOXHGCMSVCPARM parms[2];
224 HGCMSvcSetU64(&parms[0], 99);
225 int rc = TstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE, 1, parms);
226 RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
227
228 /* Invalid mode. */
229 HGCMSvcSetU32(&parms[0], 99);
230 rc = TstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE, 1, parms);
231 RTTESTI_CHECK_RC(rc, VERR_INVALID_FLAGS);
232
233 /* Enable transfers. */
234 HGCMSvcSetU32(&parms[0], VBOX_SHCL_TRANSFER_MODE_ENABLED);
235 rc = TstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE, 1, parms);
236 RTTESTI_CHECK_RC(rc, VINF_SUCCESS);
237
238 /* Disable transfers again. */
239 HGCMSvcSetU32(&parms[0], VBOX_SHCL_TRANSFER_MODE_DISABLED);
240 rc = TstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE, 1, parms);
241 RTTESTI_CHECK_RC(rc, VINF_SUCCESS);
242}
243#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
244
245/* Does testing of VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, needed for providing compatibility to older Guest Additions clients. */
246static void testHostGetMsgOld(void)
247{
248 RTTestISub("Setting up VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT test");
249
250 PTSTHGCMMOCKSVC pSvc = TstHgcmMockSvcInst();
251
252 VBOXHGCMSVCPARM parms[2];
253 RT_ZERO(parms);
254
255 /* Unless we are bidirectional the host message requests will be dropped. */
256 HGCMSvcSetU32(&parms[0], VBOX_SHCL_MODE_BIDIRECTIONAL);
257 int rc = pSvc->fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_MODE, 1, parms);
258 RTTESTI_CHECK_RC_OK(rc);
259
260 RTTestISub("Testing one format, waiting guest u.Call.");
261 RT_ZERO(g_Client);
262 VBOXHGCMCALLHANDLE_TYPEDEF call;
263 rc = VERR_IPE_UNINITIALIZED_STATUS;
264 pSvc->fnTable.pfnConnect(NULL, 1 /* clientId */, &g_Client, 0, 0);
265
266 HGCMSvcSetU32(&parms[0], 0);
267 HGCMSvcSetU32(&parms[1], 0);
268 pSvc->fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
269 RTTESTI_CHECK_RC_OK(rc);
270
271 //testMsgAddReadData(&g_Client, VBOX_SHCL_FMT_UNICODETEXT);
272 RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHCL_HOST_MSG_READ_DATA);
273 RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHCL_FMT_UNICODETEXT);
274#if 0
275 RTTESTI_CHECK_RC_OK(u.Call.rc);
276 u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
277 s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
278 RTTESTI_CHECK_RC(u.Call.rc, VINF_SUCCESS);
279 s_tstHgcmSrv.fnTable.pfnDisconnect(NULL, 1 /* clientId */, &g_Client);
280
281 RTTestISub("Testing one format, no waiting guest calls.");
282 RT_ZERO(g_Client);
283 s_tstHgcmSrv.fnTable.pfnConnect(NULL, 1 /* clientId */, &g_Client, 0, 0);
284 testMsgAddReadData(&g_Client, VBOX_SHCL_FMT_HTML);
285 HGCMSvcSetU32(&parms[0], 0);
286 HGCMSvcSetU32(&parms[1], 0);
287 u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
288 s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
289 RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHCL_HOST_MSG_READ_DATA);
290 RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHCL_FMT_HTML);
291 RTTESTI_CHECK_RC_OK(u.Call.rc);
292 u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
293 s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
294 RTTESTI_CHECK_RC(u.Call.rc, VINF_SUCCESS);
295 s_tstHgcmSrv.fnTable.pfnDisconnect(NULL, 1 /* clientId */, &g_Client);
296
297 RTTestISub("Testing two formats, waiting guest u.Call.");
298 RT_ZERO(g_Client);
299 s_tstHgcmSrv.fnTable.pfnConnect(NULL, 1 /* clientId */, &g_Client, 0, 0);
300 HGCMSvcSetU32(&parms[0], 0);
301 HGCMSvcSetU32(&parms[1], 0);
302 u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
303 s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
304 RTTESTI_CHECK_RC(u.Call.rc, VERR_IPE_UNINITIALIZED_STATUS); /* This should get updated only when the guest call completes. */
305 testMsgAddReadData(&g_Client, VBOX_SHCL_FMT_UNICODETEXT | VBOX_SHCL_FMT_HTML);
306 RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHCL_HOST_MSG_READ_DATA);
307 RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHCL_FMT_UNICODETEXT);
308 RTTESTI_CHECK_RC_OK(u.Call.rc);
309 u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
310 s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
311 RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHCL_HOST_MSG_READ_DATA);
312 RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHCL_FMT_HTML);
313 RTTESTI_CHECK_RC_OK(u.Call.rc);
314 u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
315 s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
316 RTTESTI_CHECK_RC(u.Call.rc, VERR_IPE_UNINITIALIZED_STATUS); /* This call should not complete yet. */
317 s_tstHgcmSrv.fnTable.pfnDisconnect(NULL, 1 /* clientId */, &g_Client);
318
319 RTTestISub("Testing two formats, no waiting guest calls.");
320 RT_ZERO(g_Client);
321 s_tstHgcmSrv.fnTable.pfnConnect(NULL, 1 /* clientId */, &g_Client, 0, 0);
322 testMsgAddReadData(&g_Client, VBOX_SHCL_FMT_UNICODETEXT | VBOX_SHCL_FMT_HTML);
323 HGCMSvcSetU32(&parms[0], 0);
324 HGCMSvcSetU32(&parms[1], 0);
325 u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
326 s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
327 RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHCL_HOST_MSG_READ_DATA);
328 RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHCL_FMT_UNICODETEXT);
329 RTTESTI_CHECK_RC_OK(u.Call.rc);
330 u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
331 s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
332 RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHCL_HOST_MSG_READ_DATA);
333 RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHCL_FMT_HTML);
334 RTTESTI_CHECK_RC_OK(u.Call.rc);
335 u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
336 s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
337 RTTESTI_CHECK_RC(u.Call.rc, VERR_IPE_UNINITIALIZED_STATUS); /* This call should not complete yet. */
338#endif
339 pSvc->fnTable.pfnDisconnect(NULL, 1 /* clientId */, &g_Client);
340}
341
342static void testGuestSimple(void)
343{
344 RTTestISub("Testing client (guest) API - Simple");
345
346 PTSTHGCMMOCKSVC pSvc = TstHgcmMockSvcInst();
347
348 /* Preparations. */
349 VBGLR3SHCLCMDCTX Ctx;
350 RT_ZERO(Ctx);
351
352 /*
353 * Multiple connects / disconnects.
354 */
355 RTTESTI_CHECK_RC_OK(VbglR3ClipboardConnectEx(&Ctx, VBOX_SHCL_GF_0_CONTEXT_ID));
356 RTTESTI_CHECK_RC_OK(VbglR3ClipboardDisconnectEx(&Ctx));
357 /* Report bogus guest features while connecting. */
358 RTTESTI_CHECK_RC_OK(VbglR3ClipboardConnectEx(&Ctx, 0xdeadbeef));
359 RTTESTI_CHECK_RC_OK(VbglR3ClipboardDisconnectEx(&Ctx));
360
361 RTTESTI_CHECK_RC_OK(VbglR3ClipboardConnectEx(&Ctx, VBOX_SHCL_GF_0_CONTEXT_ID));
362
363 /*
364 * Feature tests.
365 */
366
367 RTTESTI_CHECK_RC_OK(VbglR3ClipboardReportFeatures(Ctx.idClient, 0x0, NULL /* pfHostFeatures */));
368 /* Report bogus features to the host. */
369 RTTESTI_CHECK_RC_OK(VbglR3ClipboardReportFeatures(Ctx.idClient, 0xdeadb33f, NULL /* pfHostFeatures */));
370
371 /*
372 * Access denied tests.
373 */
374
375 /* Try reading data from host. */
376 uint8_t abData[32]; uint32_t cbIgnored;
377 RTTESTI_CHECK_RC(VbglR3ClipboardReadData(Ctx.idClient, VBOX_SHCL_FMT_UNICODETEXT,
378 abData, sizeof(abData), &cbIgnored), VERR_ACCESS_DENIED);
379 /* Try writing data without reporting formats before (legacy). */
380 RTTESTI_CHECK_RC(VbglR3ClipboardWriteData(Ctx.idClient, 0xdeadb33f, abData, sizeof(abData)), VERR_ACCESS_DENIED);
381 /* Try writing data without reporting formats before. */
382 RTTESTI_CHECK_RC(VbglR3ClipboardWriteDataEx(&Ctx, 0xdeadb33f, abData, sizeof(abData)), VERR_ACCESS_DENIED);
383 /* Report bogus formats to the host. */
384 RTTESTI_CHECK_RC(VbglR3ClipboardReportFormats(Ctx.idClient, 0xdeadb33f), VERR_ACCESS_DENIED);
385 /* Report supported formats to host. */
386 RTTESTI_CHECK_RC(VbglR3ClipboardReportFormats(Ctx.idClient,
387 VBOX_SHCL_FMT_UNICODETEXT | VBOX_SHCL_FMT_BITMAP | VBOX_SHCL_FMT_HTML),
388 VERR_ACCESS_DENIED);
389 /*
390 * Access allowed tests.
391 */
392 tstSetMode(pSvc, VBOX_SHCL_MODE_BIDIRECTIONAL);
393
394 /* Try writing data without reporting formats before. */
395 RTTESTI_CHECK_RC_OK(VbglR3ClipboardWriteDataEx(&Ctx, 0xdeadb33f, abData, sizeof(abData)));
396 /* Try reading data from host. */
397 RTTESTI_CHECK_RC_OK(VbglR3ClipboardReadData(Ctx.idClient, VBOX_SHCL_FMT_UNICODETEXT,
398 abData, sizeof(abData), &cbIgnored));
399 /* Report bogus formats to the host. */
400 RTTESTI_CHECK_RC_OK(VbglR3ClipboardReportFormats(Ctx.idClient, 0xdeadb33f));
401 /* Report supported formats to host. */
402 RTTESTI_CHECK_RC_OK(VbglR3ClipboardReportFormats(Ctx.idClient,
403 VBOX_SHCL_FMT_UNICODETEXT | VBOX_SHCL_FMT_BITMAP | VBOX_SHCL_FMT_HTML));
404 /* Tear down. */
405 RTTESTI_CHECK_RC_OK(VbglR3ClipboardDisconnectEx(&Ctx));
406}
407
408static void testGuestWrite(void)
409{
410 RTTestISub("Testing client (guest) API - Writing");
411}
412
413#if 0
414/**
415 * Generate a random codepoint for simple UTF-16 encoding.
416 */
417static RTUTF16 tstGetRandUtf16(void)
418{
419 RTUTF16 wc;
420 do
421 {
422 wc = (RTUTF16)RTRandU32Ex(1, 0xfffd);
423 } while (wc >= 0xd800 && wc <= 0xdfff);
424 return wc;
425}
426
427static PRTUTF16 tstGenerateUtf16StringA(uint32_t uCch)
428{
429 PRTUTF16 pwszRand = (PRTUTF16)RTMemAlloc((uCch + 1) * sizeof(RTUTF16));
430 for (uint32_t i = 0; i < uCch; i++)
431 pwszRand[i] = tstGetRandUtf16();
432 pwszRand[uCch] = 0;
433 return pwszRand;
434}
435#endif
436
437#if 0
438static void testGuestRead(void)
439{
440 RTTestISub("Testing client (guest) API - Reading");
441
442 /* Preparations. */
443 tstSetMode(VBOX_SHCL_MODE_BIDIRECTIONAL);
444
445 VBGLR3SHCLCMDCTX Ctx;
446 RTTESTI_CHECK_RC_OK(VbglR3ClipboardConnectEx(&Ctx, VBOX_SHCL_GF_0_CONTEXT_ID));
447 RTThreadSleep(500); /** @todo BUGBUG -- Seems to be a startup race when querying the initial clipboard formats. */
448
449 uint8_t abData[_4K]; uint32_t cbData; uint32_t cbRead;
450
451 /* Issue a host request that we want to read clipboard data from the guest. */
452 PSHCLEVENT pEvent;
453 tstSvcMockRequestDataFromGuest(Ctx.idClient, VBOX_SHCL_FMT_UNICODETEXT, &pEvent);
454
455 /* Write guest clipboard data to the host side. */
456 RTTESTI_CHECK_RC_OK(VbglR3ClipboardReportFormats(Ctx.idClient, VBOX_SHCL_FMT_UNICODETEXT));
457 cbData = RTRandU32Ex(1, sizeof(abData));
458 PRTUTF16 pwszStr = tstGenerateUtf16String(cbData);
459 RTTESTI_CHECK_RC_OK(VbglR3ClipboardWriteDataEx(&Ctx, VBOX_SHCL_FMT_UNICODETEXT, pwszStr, cbData));
460 RTMemFree(pwszStr);
461
462 PSHCLEVENTPAYLOAD pPayload;
463 int rc = ShClEventWait(pEvent, RT_MS_30SEC, &pPayload);
464 if (RT_SUCCESS(rc))
465 {
466
467 }
468 ShClEventRelease(pEvent);
469 pEvent = NULL;
470
471
472 /* Read clipboard data from the host back to the guest side. */
473 /* Note: Also could return VINF_BUFFER_OVERFLOW, so check for VINF_SUCCESS explicitly here. */
474 RTTESTI_CHECK_RC(VbglR3ClipboardReadDataEx(&Ctx, VBOX_SHCL_FMT_UNICODETEXT,
475 abData, sizeof(abData), &cbRead), VINF_SUCCESS);
476 RTTESTI_CHECK(cbRead == cbData);
477
478 RTPrintf("Data (%RU32): %ls\n", cbRead, (PCRTUTF16)abData);
479
480 /* Tear down. */
481 RTTESTI_CHECK_RC_OK(VbglR3ClipboardDisconnectEx(&Ctx));
482}
483#endif
484
485static DECLCALLBACK(int) tstGuestThread(RTTHREAD hThread, void *pvUser)
486{
487 RT_NOREF(hThread);
488 PTESTCTX pCtx = (PTESTCTX)pvUser;
489 AssertPtr(pCtx);
490
491 RTThreadUserSignal(hThread);
492
493 if (pCtx->Guest.pfnThread)
494 return pCtx->Guest.pfnThread(pCtx, NULL);
495
496 return VINF_SUCCESS;
497}
498
499static DECLCALLBACK(int) tstHostThread(RTTHREAD hThread, void *pvUser)
500{
501 RT_NOREF(hThread);
502 PTESTCTX pCtx = (PTESTCTX)pvUser;
503 AssertPtr(pCtx);
504
505 int rc = VINF_SUCCESS;
506
507 RTThreadUserSignal(hThread);
508
509 for (;;)
510 {
511 RTThreadSleep(100);
512
513 if (ASMAtomicReadBool(&pCtx->Host.fShutdown))
514 break;
515 }
516
517 return rc;
518}
519
520static void testSetHeadless(void)
521{
522 RTTestISub("Testing HOST_FN_SET_HEADLESS");
523
524 PTSTHGCMMOCKSVC pSvc = TstHgcmMockSvcInst();
525
526 VBOXHGCMSVCPARM parms[2];
527 HGCMSvcSetU32(&parms[0], false);
528 int rc = pSvc->fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_HEADLESS, 1, parms);
529 RTTESTI_CHECK_RC_OK(rc);
530 bool fHeadless = ShClSvcGetHeadless();
531 RTTESTI_CHECK_MSG(fHeadless == false, ("fHeadless=%RTbool\n", fHeadless));
532 rc = pSvc->fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_HEADLESS, 0, parms);
533 RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
534 rc = pSvc->fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_HEADLESS, 2, parms);
535 RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
536 HGCMSvcSetU64(&parms[0], 99);
537 rc = pSvc->fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_HEADLESS, 1, parms);
538 RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
539 HGCMSvcSetU32(&parms[0], true);
540 rc = pSvc->fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_HEADLESS, 1, parms);
541 RTTESTI_CHECK_RC_OK(rc);
542 fHeadless = ShClSvcGetHeadless();
543 RTTESTI_CHECK_MSG(fHeadless == true, ("fHeadless=%RTbool\n", fHeadless));
544 HGCMSvcSetU32(&parms[0], 99);
545 rc = pSvc->fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_HEADLESS, 1, parms);
546 RTTESTI_CHECK_RC_OK(rc);
547 fHeadless = ShClSvcGetHeadless();
548 RTTESTI_CHECK_MSG(fHeadless == true, ("fHeadless=%RTbool\n", fHeadless));
549}
550
551static void testHostCall(void)
552{
553 tstOperationModes();
554#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
555 testSetTransferMode();
556#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
557 testSetHeadless();
558}
559
560static int tstGuestStart(PTESTCTX pTstCtx, PFNTESTGSTTHREAD pFnThread)
561{
562 pTstCtx->Guest.pfnThread = pFnThread;
563
564 int rc = RTThreadCreate(&pTstCtx->Guest.hThread, tstGuestThread, pTstCtx, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
565 "tstShClGst");
566 if (RT_SUCCESS(rc))
567 rc = RTThreadUserWait(pTstCtx->Guest.hThread, RT_MS_30SEC);
568
569 return rc;
570}
571
572static int tstGuestStop(PTESTCTX pTstCtx)
573{
574 ASMAtomicWriteBool(&pTstCtx->Guest.fShutdown, true);
575
576 int rcThread;
577 int rc = RTThreadWait(pTstCtx->Guest.hThread, RT_MS_30SEC, &rcThread);
578 if (RT_SUCCESS(rc))
579 rc = rcThread;
580 if (RT_FAILURE(rc))
581 RTTestFailed(g_hTest, "Shutting down guest thread failed with %Rrc\n", rc);
582
583 pTstCtx->Guest.hThread = NIL_RTTHREAD;
584
585 return rc;
586}
587
588static int tstHostStart(PTESTCTX pTstCtx)
589{
590 int rc = RTThreadCreate(&pTstCtx->Host.hThread, tstHostThread, pTstCtx, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
591 "tstShClHst");
592 if (RT_SUCCESS(rc))
593 rc = RTThreadUserWait(pTstCtx->Host.hThread, RT_MS_30SEC);
594
595 return rc;
596}
597
598static int tstHostStop(PTESTCTX pTstCtx)
599{
600 ASMAtomicWriteBool(&pTstCtx->Host.fShutdown, true);
601
602 int rcThread;
603 int rc = RTThreadWait(pTstCtx->Host.hThread, RT_MS_30SEC, &rcThread);
604 if (RT_SUCCESS(rc))
605 rc = rcThread;
606 if (RT_FAILURE(rc))
607 RTTestFailed(g_hTest, "Shutting down host thread failed with %Rrc\n", rc);
608
609 pTstCtx->Host.hThread = NIL_RTTHREAD;
610
611 return rc;
612}
613
614#if defined(RT_OS_LINUX)
615static DECLCALLBACK(int) tstShClUserMockReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
616{
617 RT_NOREF(pCtx, fFormats, pvUser);
618 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "tstShClUserMockReportFormatsCallback: fFormats=%#x\n", fFormats);
619 return VINF_SUCCESS;
620}
621
622/*
623static DECLCALLBACK(int) tstTestReadFromHost_RequestDataFromSourceCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
624{
625 RT_NOREF(pCtx, uFmt, ppv, pvUser);
626
627 PTESTTASK pTask = &TaskRead;
628
629 uint8_t *pvData = (uint8_t *)RTMemDup(pTask->pvData, pTask->cbData);
630
631 *ppv = pvData;
632 *pcb = pTask->cbData;
633
634 return VINF_SUCCESS;
635}
636*/
637
638#if 0
639static DECLCALLBACK(int) tstShClUserMockSendDataCallback(PSHCLCONTEXT pCtx, void *pv, uint32_t cb, void *pvUser)
640{
641 RT_NOREF(pCtx, pv, cb, pvUser);
642 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "tstShClUserMockSendDataCallback\n");
643
644 PTESTTASK pTask = &TaskRead;
645
646 memcpy(pv, pTask->pvData, RT_MIN(pTask->cbData, cb));
647
648 return VINF_SUCCESS;
649}
650#endif
651
652static DECLCALLBACK(int) tstShClUserMockOnGetDataCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, size_t *pcb, void *pvUser)
653{
654 RT_NOREF(pCtx, uFmt, pvUser);
655
656 PTESTTASK pTask = &g_TstCtx.Task;
657
658 uint8_t *pvData = pTask->cbData ? (uint8_t *)RTMemDup(pTask->pvData, pTask->cbData) : NULL;
659 size_t cbData = pTask->cbData;
660
661 *ppv = pvData;
662 *pcb = cbData;
663
664 return VINF_SUCCESS;
665}
666#endif /* RT_OS_LINUX */
667
668typedef struct TSTUSERMOCK
669{
670#if defined(RT_OS_LINUX)
671 SHCLX11CTX X11Ctx;
672#endif
673 PSHCLCONTEXT pCtx;
674} TSTUSERMOCK;
675typedef TSTUSERMOCK *PTSTUSERMOCK;
676
677static void tstShClUserMockInit(PTSTUSERMOCK pUsrMock, const char *pszName)
678{
679#if defined(RT_OS_LINUX)
680 SHCLCALLBACKS Callbacks;
681 RT_ZERO(Callbacks);
682 Callbacks.pfnReportFormats = tstShClUserMockReportFormatsCallback;
683 Callbacks.pfnOnClipboardRead = tstShClUserMockOnGetDataCallback;
684
685 pUsrMock->pCtx = (PSHCLCONTEXT)RTMemAllocZ(sizeof(SHCLCONTEXT));
686 AssertPtrReturnVoid(pUsrMock->pCtx);
687
688 ShClX11Init(&pUsrMock->X11Ctx, &Callbacks, pUsrMock->pCtx, false);
689 ShClX11ThreadStartEx(&pUsrMock->X11Ctx, pszName, false /* fGrab */);
690 /* Give the clipboard time to synchronise. */
691 RTThreadSleep(500);
692#else
693 RT_NOREF(pUsrMock);
694#endif /* RT_OS_LINUX */
695}
696
697static void tstShClUserMockDestroy(PTSTUSERMOCK pUsrMock)
698{
699#if defined(RT_OS_LINUX)
700 ShClX11ThreadStop(&pUsrMock->X11Ctx);
701 ShClX11Destroy(&pUsrMock->X11Ctx);
702 RTMemFree(pUsrMock->pCtx);
703#else
704 RT_NOREF(pUsrMock);
705#endif
706}
707
708static int tstTaskGuestRead(PTESTCTX pCtx, PTESTTASK pTask)
709{
710 size_t cbReadTotal = 0;
711 size_t cbToRead = pTask->cbData;
712
713 switch (pTask->enmFmtGst)
714 {
715 case VBOX_SHCL_FMT_UNICODETEXT:
716 cbToRead *= sizeof(RTUTF16);
717 break;
718
719 default:
720 break;
721 }
722
723 size_t cbDst = _64K;
724 uint8_t *pabDst = (uint8_t *)RTMemAllocZ(cbDst);
725 AssertPtrReturn(pabDst, VERR_NO_MEMORY);
726
727 Assert(pTask->cbChunk); /* Buggy test? */
728 Assert(pTask->cbChunk <= pTask->cbData); /* Ditto. */
729
730 uint8_t *pabSrc = (uint8_t *)pTask->pvData;
731
732 do
733 {
734 /* Note! VbglR3ClipboardReadData() currently does not support chunked reads!
735 * It in turn returns VINF_BUFFER_OVERFLOW when the supplied buffer was too small. */
736 uint32_t const cbChunk = cbDst;
737 uint32_t const cbExpected = cbToRead;
738
739 uint32_t cbRead = 0;
740 RTTEST_CHECK_RC(g_hTest, VbglR3ClipboardReadData(pCtx->Guest.CmdCtx.idClient,
741 pTask->enmFmtGst, pabDst, cbChunk, &cbRead), pTask->rcExpected);
742 RTTEST_CHECK_MSG(g_hTest, cbRead == cbExpected, (g_hTest, "Read %RU32 bytes, expected %RU32\n", cbRead, cbExpected));
743 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Guest side received %RU32 bytes\n", cbRead);
744 cbReadTotal += cbRead;
745 Assert(cbReadTotal <= cbToRead);
746
747 } while (cbReadTotal < cbToRead);
748
749 if (pTask->enmFmtGst == VBOX_SHCL_FMT_UNICODETEXT)
750 {
751 RTTEST_CHECK_RC_OK(g_hTest, RTUtf16ValidateEncoding((PRTUTF16)pabDst));
752 }
753 else
754 RTTEST_CHECK(g_hTest, memcmp(pabSrc, pabDst, RT_MIN(pTask->cbData, cbDst) == 0));
755
756 RTMemFree(pabDst);
757
758 return VINF_SUCCESS;
759}
760
761static void tstTaskInit(PTESTTASK pTask)
762{
763 RTSemEventCreate(&pTask->hEvent);
764}
765
766static void tstTaskDestroy(PTESTTASK pTask)
767{
768 RTSemEventDestroy(pTask->hEvent);
769}
770
771static void tstTaskWait(PTESTTASK pTask, RTMSINTERVAL msTimeout)
772{
773 RTTEST_CHECK_RC_OK(g_hTest, RTSemEventWait(pTask->hEvent, msTimeout));
774 RTTEST_CHECK_RC(g_hTest, pTask->rcCompleted, pTask->rcExpected);
775}
776
777static void tstTaskSignal(PTESTTASK pTask, int rc)
778{
779 pTask->rcCompleted = rc;
780 RTTEST_CHECK_RC_OK(g_hTest, RTSemEventSignal(pTask->hEvent));
781}
782
783static DECLCALLBACK(int) tstTestReadFromHostThreadGuest(PTESTCTX pCtx, void *pvCtx)
784{
785 RT_NOREF(pvCtx);
786
787 RTThreadSleep(5000);
788RT_BREAKPOINT();
789
790 RT_ZERO(pCtx->Guest.CmdCtx);
791 RTTEST_CHECK_RC_OK(g_hTest, VbglR3ClipboardConnectEx(&pCtx->Guest.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID));
792
793#if 1
794 PTESTTASK pTask = &pCtx->Task;
795 tstTaskGuestRead(pCtx, pTask);
796 tstTaskSignal(pTask, VINF_SUCCESS);
797#endif
798
799#if 0
800 for (;;)
801 {
802 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
803 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
804
805 uint32_t idMsg = 0;
806 uint32_t cParms = 0;
807 RTTEST_CHECK_RC_OK(g_hTest, VbglR3ClipboardMsgPeekWait(&pCtx->Guest.CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */));
808 RTTEST_CHECK_RC_OK(g_hTest, VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->Guest.CmdCtx, pEvent));
809
810 if (pEvent)
811 {
812 VbglR3ClipboardEventFree(pEvent);
813 pEvent = NULL;
814 }
815
816 if (ASMAtomicReadBool(&pCtx->Guest.fShutdown))
817 break;
818
819 RTThreadSleep(100);
820 }
821#endif
822
823 RTTEST_CHECK_RC_OK(g_hTest, VbglR3ClipboardDisconnectEx(&pCtx->Guest.CmdCtx));
824
825 return VINF_SUCCESS;
826}
827
828static DECLCALLBACK(int) tstTestReadFromHostExec(PTESTPARMS pTstParms, void *pvCtx)
829{
830 RT_NOREF(pvCtx, pTstParms);
831
832 PTESTTASK pTask = &pTstParms->pTstCtx->Task;
833
834 pTask->enmFmtGst = VBOX_SHCL_FMT_UNICODETEXT;
835 pTask->enmFmtHst = pTask->enmFmtGst;
836 pTask->pvData = RTStrAPrintf2("foo!");
837 pTask->cbData = strlen((char *)pTask->pvData) + 1;
838 pTask->cbChunk = pTask->cbData;
839
840 PTSTHGCMMOCKSVC pSvc = TstHgcmMockSvcInst();
841 PTSTHGCMMOCKCLIENT pMockClient = TstHgcmMockSvcWaitForConnect(pSvc);
842
843 AssertPtrReturn(pMockClient, VERR_INVALID_POINTER);
844
845 bool fUseMock = false;
846 TSTUSERMOCK UsrMock;
847 if (fUseMock)
848 tstShClUserMockInit(&UsrMock, "tstX11Hst");
849
850 RTThreadSleep(RT_MS_1SEC * 4);
851
852#if 1
853 PSHCLBACKEND pBackend = ShClSvcGetBackend();
854
855 ShClBackendReportFormats(pBackend, (PSHCLCLIENT)pMockClient->pvClient, pTask->enmFmtHst);
856 tstTaskWait(pTask, RT_MS_30SEC);
857#endif
858
859RTThreadSleep(RT_MS_30SEC);
860
861 //PSHCLCLIENT pClient = &pMockClient->Client;
862
863#if 1
864 if (1)
865 {
866 //RTTEST_CHECK_RC_OK(g_hTest, ShClBackendMockSetData(pBackend, pTask->enmFmt, pwszStr, cbData));
867 //RTMemFree(pwszStr);
868 }
869#endif
870
871 if (fUseMock)
872 tstShClUserMockDestroy(&UsrMock);
873
874 return VINF_SUCCESS;
875}
876
877static DECLCALLBACK(int) tstTestReadFromHostSetup(PTESTPARMS pTstParms, void **ppvCtx)
878{
879 RT_NOREF(ppvCtx);
880
881 PTESTCTX pCtx = pTstParms->pTstCtx;
882
883 tstHostStart(pCtx);
884
885 PSHCLBACKEND pBackend = ShClSvcGetBackend();
886
887 SHCLCALLBACKS Callbacks;
888 RT_ZERO(Callbacks);
889 Callbacks.pfnReportFormats = tstShClUserMockReportFormatsCallback;
890 //Callbacks.pfnOnRequestDataFromSource = tstTestReadFromHost_RequestDataFromSourceCallback;
891 Callbacks.pfnOnClipboardRead = tstShClUserMockOnGetDataCallback;
892 ShClBackendSetCallbacks(pBackend, &Callbacks);
893
894 tstGuestStart(pCtx, tstTestReadFromHostThreadGuest);
895
896 RTThreadSleep(1000);
897
898 tstSetMode(pCtx->pSvc, VBOX_SHCL_MODE_BIDIRECTIONAL);
899
900 return VINF_SUCCESS;
901}
902
903static DECLCALLBACK(int) tstTestReadFromHostDestroy(PTESTPARMS pTstParms, void *pvCtx)
904{
905 RT_NOREF(pvCtx);
906
907 int rc = VINF_SUCCESS;
908
909 tstGuestStop(pTstParms->pTstCtx);
910 tstHostStop(pTstParms->pTstCtx);
911
912 return rc;
913}
914
915/** Test definition table. */
916TESTDESC g_aTests[] =
917{
918 { tstTestReadFromHostSetup, tstTestReadFromHostExec, tstTestReadFromHostDestroy }
919};
920/** Number of tests defined. */
921unsigned g_cTests = RT_ELEMENTS(g_aTests);
922
923static int tstOne(PTSTHGCMMOCKSVC pSvc, PTESTDESC pTstDesc)
924{
925 PTESTCTX pTstCtx = &g_TstCtx;
926
927 TESTPARMS TstParms;
928 RT_ZERO(TstParms);
929
930 pTstCtx->pSvc = pSvc;
931 TstParms.pTstCtx = pTstCtx;
932
933 void *pvCtx;
934 int rc = pTstDesc->pfnSetup(&TstParms, &pvCtx);
935 if (RT_SUCCESS(rc))
936 {
937 rc = pTstDesc->pfnExec(&TstParms, pvCtx);
938
939 int rc2 = pTstDesc->pfnDestroy(&TstParms, pvCtx);
940 if (RT_SUCCESS(rc))
941 rc = rc2;
942 }
943
944 return rc;
945}
946
947int main(int argc, char *argv[])
948{
949 /*
950 * Init the runtime, test and say hello.
951 */
952 const char *pcszExecName;
953 NOREF(argc);
954 pcszExecName = strrchr(argv[0], '/');
955 pcszExecName = pcszExecName ? pcszExecName + 1 : argv[0];
956 RTEXITCODE rcExit = RTTestInitAndCreate(pcszExecName, &g_hTest);
957 if (rcExit != RTEXITCODE_SUCCESS)
958 return rcExit;
959 RTTestBanner(g_hTest);
960
961 /* Don't let assertions in the host service panic (core dump) the test cases. */
962 RTAssertSetMayPanic(false);
963
964 PTSTHGCMMOCKSVC pSvc = TstHgcmMockSvcInst();
965
966 TstHgcmMockSvcCreate(pSvc, sizeof(SHCLCLIENT));
967 TstHgcmMockSvcStart(pSvc);
968
969 /*
970 * Run the tests.
971 */
972 if (1)
973 {
974 testGuestSimple();
975 testGuestWrite();
976 testHostCall();
977 testHostGetMsgOld();
978 }
979
980 RT_ZERO(g_TstCtx);
981 tstTaskInit(&g_TstCtx.Task);
982 for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
983 tstOne(pSvc, &g_aTests[i]);
984 tstTaskDestroy(&g_TstCtx.Task);
985
986 TstHgcmMockSvcStop(pSvc);
987 TstHgcmMockSvcDestroy(pSvc);
988
989 VBOXHGCMSVCFNTABLE fn;
990 VBoxHGCMSvcLoad(&fn);
991
992 /*
993 * Summary
994 */
995 return RTTestSummaryAndDestroy(g_hTest);
996}
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