VirtualBox

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

最後變更 在這個檔案從96407是 96407,由 vboxsync 提交於 3 年 前

scm copyright and license note update

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette