VirtualBox

source: vbox/trunk/src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardGH-X11.cpp@ 100204

Last change on this file since 100204 was 100204, checked in by vboxsync, 21 months ago

Shared Clipboard: Unified root list entry code to also use the generic list entry code, a lot of updates for the cross OS transfer handling code, more updates for HTTP server transfer handling.

This also changed the handling of how that transfers are being initiated, as we needed to have this for X11: Before, transfers were initiated as soon as on side announced the URI list format -- now we postpone initiating the transfer until the receiving side requests the data as URI list.

bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.2 KB
Line 
1/* $Id: tstClipboardGH-X11.cpp 100204 2023-06-19 09:11:37Z vboxsync $ */
2/** @file
3 * Shared Clipboard guest/host X11 code test cases.
4 */
5
6/*
7 * Copyright (C) 2011-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.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 <VBox/GuestHost/SharedClipboard.h>
29#include <VBox/GuestHost/SharedClipboard-x11.h>
30#include <VBox/GuestHost/clipboard-helper.h>
31#include <VBox/HostServices/VBoxClipboardSvc.h>
32
33#include <iprt/assert.h>
34#include <iprt/string.h>
35#include <iprt/test.h>
36#include <iprt/utf16.h>
37
38#include <poll.h>
39#include <X11/Xatom.h>
40
41
42/*********************************************************************************************************************************
43* Externals *
44*********************************************************************************************************************************/
45extern SHCLX11FMTTABLE g_aFormats[];
46
47extern void clipUpdateX11Targets(PSHCLX11CTX pCtx, SHCLX11FMTIDX *pTargets, size_t cTargets);
48extern void clipReportEmpty(PSHCLX11CTX pCtx);
49extern void clipConvertDataFromX11Worker(void *pClient, void *pvSrc, unsigned cbSrc);
50extern SHCLX11FMTIDX clipGetTextFormatFromTargets(PSHCLX11CTX pCtx, SHCLX11FMTIDX *pTargets, size_t cTargets);
51extern SHCLX11FMT clipRealFormatForX11Format(SHCLX11FMTIDX uFmtIdx);
52extern Atom clipGetAtom(PSHCLX11CTX pCtx, const char *pcszName);
53extern void clipQueryX11Targets(PSHCLX11CTX pCtx);
54extern size_t clipReportMaxX11Formats(void);
55
56
57/*********************************************************************************************************************************
58* Internal prototypes *
59*********************************************************************************************************************************/
60static SHCLX11FMTIDX tstClipFindX11FormatByAtomText(const char *pcszAtom);
61
62
63/*********************************************************************************************************************************
64* Prototypes, used for testcases by clipboard-x11.cpp *
65*********************************************************************************************************************************/
66void tstRequestTargets(SHCLX11CTX* pCtx);
67void tstClipRequestData(PSHCLX11CTX pCtx, SHCLX11FMTIDX target, void *closure);
68void tstThreadScheduleCall(void (*proc)(void *, void *), void *client_data);
69
70
71/*********************************************************************************************************************************
72* Own callback implementations *
73*********************************************************************************************************************************/
74extern DECLCALLBACK(void) clipQueryX11TargetsCallback(Widget widget, XtPointer pClient,
75 Atom * /* selection */, Atom *atomType,
76 XtPointer pValue, long unsigned int *pcLen,
77 int *piFormat);
78
79
80/*********************************************************************************************************************************
81* Defines *
82*********************************************************************************************************************************/
83#define TESTCASE_WIDGET_ID (Widget)0xffff
84
85
86/* For the purpose of the test case, we just execute the procedure to be
87 * scheduled, as we are running single threaded. */
88void tstThreadScheduleCall(void (*proc)(void *, void *), void *client_data)
89{
90 proc(client_data, NULL);
91}
92
93/* The data in the simulated VBox clipboard. */
94static int g_tst_rcDataVBox = VINF_SUCCESS;
95static void *g_tst_pvDataVBox = NULL;
96static uint32_t g_tst_cbDataVBox = 0;
97
98/* Set empty data in the simulated VBox clipboard. */
99static void tstClipEmptyVBox(PSHCLX11CTX pCtx, int retval)
100{
101 g_tst_rcDataVBox = retval;
102 RTMemFree(g_tst_pvDataVBox);
103 g_tst_pvDataVBox = NULL;
104 g_tst_cbDataVBox = 0;
105 ShClX11ReportFormatsToX11Async(pCtx, 0);
106}
107
108/* Set the data in the simulated VBox clipboard. */
109static int tstClipSetVBoxUtf16(PSHCLX11CTX pCtx, int retval,
110 const char *pcszData, size_t cb)
111{
112 PRTUTF16 pwszData = NULL;
113 size_t cwData = 0;
114 int rc = RTStrToUtf16Ex(pcszData, RTSTR_MAX, &pwszData, 0, &cwData);
115 if (RT_FAILURE(rc))
116 return rc;
117 AssertReturn(cb <= cwData * 2 + 2, VERR_BUFFER_OVERFLOW);
118 void *pv = RTMemDup(pwszData, cb);
119 RTUtf16Free(pwszData);
120 if (pv == NULL)
121 return VERR_NO_MEMORY;
122 if (g_tst_pvDataVBox)
123 RTMemFree(g_tst_pvDataVBox);
124 g_tst_rcDataVBox = retval;
125 g_tst_pvDataVBox = pv;
126 g_tst_cbDataVBox = cb;
127 ShClX11ReportFormatsToX11Async(pCtx, VBOX_SHCL_FMT_UNICODETEXT);
128 return VINF_SUCCESS;
129}
130
131Display *XtDisplay(Widget w) { NOREF(w); return (Display *) 0xffff; }
132
133void XtAppSetExitFlag(XtAppContext app_context) { NOREF(app_context); }
134
135void XtDestroyWidget(Widget w) { NOREF(w); }
136
137XtAppContext XtCreateApplicationContext(void) { return (XtAppContext)0xffff; }
138
139void XtDestroyApplicationContext(XtAppContext app_context) { NOREF(app_context); }
140
141void XtToolkitInitialize(void) {}
142
143Boolean XtToolkitThreadInitialize(void) { return True; }
144
145Display *XtOpenDisplay(XtAppContext app_context,
146 _Xconst _XtString display_string,
147 _Xconst _XtString application_name,
148 _Xconst _XtString application_class,
149 XrmOptionDescRec *options, Cardinal num_options,
150 int *argc, char **argv)
151{
152 RT_NOREF8(app_context, display_string, application_name, application_class, options, num_options, argc, argv);
153 return (Display *)0xffff;
154}
155
156Widget XtVaAppCreateShell(_Xconst _XtString application_name, _Xconst _XtString application_class,
157 WidgetClass widget_class, Display *display, ...)
158{
159 RT_NOREF(application_name, application_class, widget_class, display);
160 return TESTCASE_WIDGET_ID;
161}
162
163void XtSetMappedWhenManaged(Widget widget, _XtBoolean mapped_when_managed) { RT_NOREF(widget, mapped_when_managed); }
164
165void XtRealizeWidget(Widget widget) { NOREF(widget); }
166
167XtInputId XtAppAddInput(XtAppContext app_context, int source, XtPointer condition, XtInputCallbackProc proc, XtPointer closure)
168{
169 RT_NOREF(app_context, source, condition, proc, closure);
170 return 0xffff;
171}
172
173/* Atoms we need other than the formats we support. */
174static const char *g_tst_apszSupAtoms[] =
175{
176 "PRIMARY", "CLIPBOARD", "TARGETS", "MULTIPLE", "TIMESTAMP"
177};
178
179/* This just looks for the atom names in a couple of tables and returns an
180 * index with an offset added. */
181Atom XInternAtom(Display *, const char *pcsz, int)
182{
183 Atom atom = 0;
184 size_t const cFormats = clipReportMaxX11Formats();
185 size_t i;
186 for (i = 0; i < cFormats; ++i)
187 {
188 if (!strcmp(pcsz, g_aFormats[i].pcszAtom))
189 atom = (Atom) (i + 0x1000);
190 }
191 for (i = 0; i < RT_ELEMENTS(g_tst_apszSupAtoms); ++i)
192 if (!strcmp(pcsz, g_tst_apszSupAtoms[i]))
193 atom = (Atom) (i + 0x2000);
194 Assert(atom); /* Have we missed any atoms? */
195 return atom;
196}
197
198/* Take a request for the targets we are currently offering. */
199static SHCLX11FMTIDX g_tst_aSelTargetsIdx[10] = { 0 };
200static size_t g_tst_cTargets = 0;
201
202void tstRequestTargets(SHCLX11CTX* pCtx)
203{
204 clipUpdateX11Targets(pCtx, g_tst_aSelTargetsIdx, g_tst_cTargets);
205}
206
207/* The current values of the X selection, which will be returned to the
208 * XtGetSelectionValue callback. */
209static Atom g_tst_atmSelType = 0;
210static const void *g_tst_pSelData = NULL;
211static unsigned long g_tst_cSelData = 0;
212static int g_tst_selFormat = 0;
213
214void tstClipRequestData(PSHCLX11CTX pCtx, SHCLX11FMTIDX target, void *closure)
215{
216 RT_NOREF(pCtx);
217 unsigned long count = 0;
218 int format = 0;
219 if (target != g_tst_aSelTargetsIdx[0])
220 {
221 clipConvertDataFromX11Worker(closure, NULL, 0); /* Could not convert to target. */
222 return;
223 }
224 void *pValue = NULL;
225 pValue = g_tst_pSelData ? RTMemDup(g_tst_pSelData, g_tst_cSelData) : NULL;
226 count = g_tst_pSelData ? g_tst_cSelData : 0;
227 format = g_tst_selFormat;
228 if (!pValue)
229 {
230 count = 0;
231 format = 0;
232 }
233 clipConvertDataFromX11Worker(closure, pValue, count * format / 8);
234 if (pValue)
235 RTMemFree(pValue);
236}
237
238/* The formats currently on offer from X11 via the shared clipboard. */
239static uint32_t g_tst_uX11Formats = 0;
240
241static uint32_t tstClipQueryFormats(void)
242{
243 return g_tst_uX11Formats;
244}
245
246static void tstClipInvalidateFormats(void)
247{
248 g_tst_uX11Formats = ~0;
249}
250
251/* Does our clipboard code currently own the selection? */
252static bool g_tst_fOwnsSel = false;
253/* The procedure that is called when we should convert the selection to a
254 * given format. */
255static XtConvertSelectionProc g_tst_pfnSelConvert = NULL;
256/* The procedure which is called when we lose the selection. */
257static XtLoseSelectionProc g_tst_pfnSelLose = NULL;
258/* The procedure which is called when the selection transfer has completed. */
259static XtSelectionDoneProc g_tst_pfnSelDone = NULL;
260
261Boolean XtOwnSelection(Widget widget, Atom selection, Time time,
262 XtConvertSelectionProc convert,
263 XtLoseSelectionProc lose,
264 XtSelectionDoneProc done)
265{
266 RT_NOREF(widget, time);
267 if (selection != XInternAtom(NULL, "CLIPBOARD", 0))
268 return True; /* We don't really care about this. */
269 g_tst_fOwnsSel = true; /* Always succeed. */
270 g_tst_pfnSelConvert = convert;
271 g_tst_pfnSelLose = lose;
272 g_tst_pfnSelDone = done;
273 return True;
274}
275
276void XtDisownSelection(Widget widget, Atom selection, Time time)
277{
278 RT_NOREF(widget, time, selection);
279 g_tst_fOwnsSel = false;
280 g_tst_pfnSelConvert = NULL;
281 g_tst_pfnSelLose = NULL;
282 g_tst_pfnSelDone = NULL;
283}
284
285/* Request the shared clipboard to convert its data to a given format. */
286static bool tstClipConvertSelection(const char *pcszTarget, Atom *type,
287 XtPointer *value, unsigned long *length,
288 int *format)
289{
290 Atom target = XInternAtom(NULL, pcszTarget, 0);
291 if (target == 0)
292 return false;
293 /* Initialise all return values in case we make a quick exit. */
294 *type = XA_STRING;
295 *value = NULL;
296 *length = 0;
297 *format = 0;
298 if (!g_tst_fOwnsSel)
299 return false;
300 if (!g_tst_pfnSelConvert)
301 return false;
302 Atom clipAtom = XInternAtom(NULL, "CLIPBOARD", 0);
303 if (!g_tst_pfnSelConvert(TESTCASE_WIDGET_ID, &clipAtom, &target, type,
304 value, length, format))
305 return false;
306 if (g_tst_pfnSelDone)
307 g_tst_pfnSelDone(TESTCASE_WIDGET_ID, &clipAtom, &target);
308 return true;
309}
310
311/* Set the current X selection data */
312static void tstClipSetSelectionValues(const char *pcszTarget, Atom type,
313 const void *data,
314 unsigned long count, int format)
315{
316 Atom clipAtom = XInternAtom(NULL, "CLIPBOARD", 0);
317 g_tst_aSelTargetsIdx[0] = tstClipFindX11FormatByAtomText(pcszTarget);
318 g_tst_cTargets = 1;
319 g_tst_atmSelType = type;
320 g_tst_pSelData = data;
321 g_tst_cSelData = count;
322 g_tst_selFormat = format;
323 if (g_tst_pfnSelLose)
324 g_tst_pfnSelLose(TESTCASE_WIDGET_ID, &clipAtom);
325 g_tst_fOwnsSel = false;
326}
327
328static void tstClipSendTargetUpdate(PSHCLX11CTX pCtx)
329{
330 clipQueryX11Targets(pCtx);
331}
332
333/* Configure if and how the X11 TARGETS clipboard target will fail. */
334static void tstClipSetTargetsFailure(void)
335{
336 g_tst_cTargets = 0;
337}
338
339char *XtMalloc(Cardinal size)
340{
341 return (char *) RTMemAlloc(size);
342}
343
344void XtFree(char *ptr)
345{
346 RTMemFree((void *)ptr);
347}
348
349char *XGetAtomName(Display *display, Atom atom)
350{
351 RT_NOREF(display);
352 const char *pcszName = NULL;
353 if (atom < 0x1000)
354 return NULL;
355 if (0x1000 <= atom && atom < 0x2000)
356 {
357 unsigned index = atom - 0x1000;
358 AssertReturn(index < clipReportMaxX11Formats(), NULL);
359 pcszName = g_aFormats[index].pcszAtom;
360 }
361 else
362 {
363 unsigned index = atom - 0x2000;
364 AssertReturn(index < RT_ELEMENTS(g_tst_apszSupAtoms), NULL);
365 pcszName = g_tst_apszSupAtoms[index];
366 }
367 return (char *)RTMemDup(pcszName, sizeof(pcszName) + 1);
368}
369
370int XFree(void *data)
371{
372 RTMemFree(data);
373 return 0;
374}
375
376void XFreeStringList(char **list)
377{
378 if (list)
379 RTMemFree(*list);
380 RTMemFree(list);
381}
382
383#define TESTCASE_MAX_BUF_SIZE 256
384
385static int g_tst_rcCompleted = VINF_SUCCESS;
386static int g_tst_cbCompleted = 0;
387static SHCLX11REQUEST *g_tst_pCompletedReq = NULL;
388static char g_tst_abCompletedBuf[TESTCASE_MAX_BUF_SIZE];
389
390static DECLCALLBACK(int) tstShClReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
391{
392 RT_NOREF(pCtx, pvUser);
393 g_tst_uX11Formats = fFormats;
394 return VINF_SUCCESS;
395}
396
397static DECLCALLBACK(int) tstShClOnRequestDataFromSourceCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
398{
399 RT_NOREF(pCtx, uFmt, pvUser);
400 *pcb = g_tst_cbDataVBox;
401 if (g_tst_pvDataVBox != NULL)
402 {
403 void *pv = RTMemDup(g_tst_pvDataVBox, g_tst_cbDataVBox);
404 *ppv = pv;
405 return pv != NULL ? g_tst_rcDataVBox : VERR_NO_MEMORY;
406 }
407 *ppv = NULL;
408 return g_tst_rcDataVBox;
409}
410
411static DECLCALLBACK(int) tstShClOnSendDataToDestCallback(PSHCLCONTEXT pCtx, void *pv, uint32_t cb, void *pvUser)
412{
413 RT_NOREF(pCtx);
414
415 PSHCLX11RESPONSE pData = (PSHCLX11RESPONSE)pvUser;
416
417 if (cb <= TESTCASE_MAX_BUF_SIZE)
418 {
419 g_tst_rcCompleted = pData->rc;
420 if (cb != 0)
421 memcpy(g_tst_abCompletedBuf, pv, cb);
422 }
423 else
424 g_tst_rcCompleted = VERR_BUFFER_OVERFLOW;
425 g_tst_cbCompleted = cb;
426 g_tst_pCompletedReq = pData->pReq;
427
428 return VINF_SUCCESS;
429}
430
431/**
432 * Looks up the X11 format matching a given X11 atom text.
433 *
434 * @returns the format on success, NIL_CLIPX11FORMAT on failure
435 * @param pcszAtom Atom text to look up format for.
436 */
437static SHCLX11FMTIDX tstClipFindX11FormatByAtomText(const char *pcszAtom)
438{
439 const size_t j = clipReportMaxX11Formats();
440
441 for (unsigned i = 0; i < j; ++i)
442 {
443 if (!strcmp(g_aFormats[i].pcszAtom, pcszAtom))
444 return i;
445 }
446 return NIL_CLIPX11FORMAT;
447}
448
449static bool tstClipTextFormatConversion(PSHCLX11CTX pCtx)
450{
451 bool fSuccess = true;
452 SHCLX11FMTIDX targets[2];
453 SHCLX11FMTIDX x11Format;
454 targets[0] = tstClipFindX11FormatByAtomText("text/plain");
455 targets[1] = tstClipFindX11FormatByAtomText("image/bmp");
456 x11Format = clipGetTextFormatFromTargets(pCtx, targets, 2);
457 if (clipRealFormatForX11Format(x11Format) != SHCLX11FMT_TEXT)
458 fSuccess = false;
459 targets[0] = tstClipFindX11FormatByAtomText("UTF8_STRING");
460 targets[1] = tstClipFindX11FormatByAtomText("text/plain");
461 x11Format = clipGetTextFormatFromTargets(pCtx, targets, 2);
462 if (clipRealFormatForX11Format(x11Format) != SHCLX11FMT_UTF8)
463 fSuccess = false;
464 return fSuccess;
465}
466
467static void tstClipGetCompletedRequest(int *prc, char ** ppc, uint32_t *pcb, SHCLX11REQREAD **ppReq)
468{
469 *prc = g_tst_rcCompleted;
470 *ppc = g_tst_abCompletedBuf;
471 *pcb = g_tst_cbCompleted;
472 *ppReq = g_tst_pCompletedReq;
473}
474
475static void tstStringFromX11(RTTEST hTest, PSHCLX11CTX pCtx,
476 const char *pcszExp, int rcExp)
477{
478 bool retval = true;
479 tstClipSendTargetUpdate(pCtx);
480 if (tstClipQueryFormats() != VBOX_SHCL_FMT_UNICODETEXT)
481 {
482 RTTestFailed(hTest, "Wrong targets reported: %02X\n", tstClipQueryFormats());
483 }
484 else
485 {
486 char *pc;
487 SHCLX11REQREAD *pReq = (SHCLX11REQREAD *)&pReq, *pReqRet = NULL;
488 ShClX11ReadDataFromX11Async(pCtx, VBOX_SHCL_FMT_UNICODETEXT, pReq);
489 int rc = VINF_SUCCESS;
490 uint32_t cbActual = 0;
491 tstClipGetCompletedRequest(&rc, &pc, &cbActual, &pReqRet);
492 if (rc != rcExp)
493 RTTestFailed(hTest, "Wrong return code, expected %Rrc, got %Rrc\n",
494 rcExp, rc);
495 else if (pReqRet != pReq)
496 RTTestFailed(hTest, "Wrong returned request data, expected %p, got %p\n",
497 pReq, pReqRet);
498 else if (RT_FAILURE(rcExp))
499 retval = true;
500 else
501 {
502 RTUTF16 wcExp[TESTCASE_MAX_BUF_SIZE / 2];
503 RTUTF16 *pwcExp = wcExp;
504 size_t cwc = 0;
505 rc = RTStrToUtf16Ex(pcszExp, RTSTR_MAX, &pwcExp,
506 RT_ELEMENTS(wcExp), &cwc);
507 size_t cbExp = cwc * 2 + 2;
508 AssertRC(rc);
509 if (RT_SUCCESS(rc))
510 {
511 if (cbActual != cbExp)
512 {
513 RTTestFailed(hTest, "Returned string is the wrong size, string \"%.*ls\", size %u, expected \"%s\", size %u\n",
514 RT_MIN(TESTCASE_MAX_BUF_SIZE, cbActual), pc, cbActual,
515 pcszExp, cbExp);
516 }
517 else
518 {
519 if (memcmp(pc, wcExp, cbExp) == 0)
520 retval = true;
521 else
522 RTTestFailed(hTest, "Returned string \"%.*ls\" does not match expected string \"%s\"\n",
523 TESTCASE_MAX_BUF_SIZE, pc, pcszExp);
524 }
525 }
526 }
527 }
528 if (!retval)
529 RTTestFailureDetails(hTest, "Expected: string \"%s\", rc %Rrc\n",
530 pcszExp, rcExp);
531}
532
533static void tstLatin1FromX11(RTTEST hTest, PSHCLX11CTX pCtx,
534 const char *pcszExp, int rcExp)
535{
536 bool retval = false;
537 tstClipSendTargetUpdate(pCtx);
538 if (tstClipQueryFormats() != VBOX_SHCL_FMT_UNICODETEXT)
539 RTTestFailed(hTest, "Wrong targets reported: %02X\n",
540 tstClipQueryFormats());
541 else
542 {
543 char *pc;
544 SHCLX11REQREAD *pReq = (SHCLX11REQREAD *)&pReq, *pReqRet = NULL;
545 ShClX11ReadDataFromX11Async(pCtx, VBOX_SHCL_FMT_UNICODETEXT, pReq);
546 int rc = VINF_SUCCESS;
547 uint32_t cbActual = 0;
548 tstClipGetCompletedRequest(&rc, &pc, &cbActual, &pReqRet);
549 if (rc != rcExp)
550 RTTestFailed(hTest, "Wrong return code, expected %Rrc, got %Rrc\n",
551 rcExp, rc);
552 else if (pReqRet != pReq)
553 RTTestFailed(hTest, "Wrong returned request data, expected %p, got %p\n",
554 pReq, pReqRet);
555 else if (RT_FAILURE(rcExp))
556 retval = true;
557 else
558 {
559 RTUTF16 wcExp[TESTCASE_MAX_BUF_SIZE / 2];
560 //RTUTF16 *pwcExp = wcExp; - unused
561 size_t cwc;
562 for (cwc = 0; cwc == 0 || pcszExp[cwc - 1] != '\0'; ++cwc)
563 wcExp[cwc] = pcszExp[cwc];
564 size_t cbExp = cwc * 2;
565 if (cbActual != cbExp)
566 {
567 RTTestFailed(hTest, "Returned string is the wrong size, string \"%.*ls\", size %u, expected \"%s\", size %u\n",
568 RT_MIN(TESTCASE_MAX_BUF_SIZE, cbActual), pc, cbActual,
569 pcszExp, cbExp);
570 }
571 else
572 {
573 if (memcmp(pc, wcExp, cbExp) == 0)
574 retval = true;
575 else
576 RTTestFailed(hTest, "Returned string \"%.*ls\" does not match expected string \"%s\"\n",
577 TESTCASE_MAX_BUF_SIZE, pc, pcszExp);
578 }
579 }
580 }
581 if (!retval)
582 RTTestFailureDetails(hTest, "Expected: string \"%s\", rc %Rrc\n",
583 pcszExp, rcExp);
584}
585
586static void tstStringFromVBox(RTTEST hTest, PSHCLX11CTX pCtx, const char *pcszTarget, Atom typeExp, const char *valueExp)
587{
588 RT_NOREF(pCtx);
589 bool retval = false;
590 Atom type;
591 XtPointer value = NULL;
592 unsigned long length;
593 int format;
594 size_t lenExp = strlen(valueExp);
595 if (tstClipConvertSelection(pcszTarget, &type, &value, &length, &format))
596 {
597 if ( type != typeExp
598 || length != lenExp
599 || format != 8
600 || memcmp((const void *) value, (const void *)valueExp,
601 lenExp))
602 {
603 RTTestFailed(hTest, "Bad data: type %d, (expected %d), length %u, (expected %u), format %d (expected %d), value \"%.*s\" (expected \"%.*s\")\n",
604 type, typeExp, length, lenExp, format, 8,
605 RT_MIN(length, 20), value, RT_MIN(lenExp, 20), valueExp);
606 }
607 else
608 retval = true;
609 }
610 else
611 RTTestFailed(hTest, "Conversion failed\n");
612 XtFree((char *)value);
613 if (!retval)
614 RTTestFailureDetails(hTest, "Conversion to %s, expected \"%s\"\n",
615 pcszTarget, valueExp);
616}
617
618static void tstNoX11(PSHCLX11CTX pCtx, const char *pcszTestCtx)
619{
620 SHCLX11REQREAD *pReq = (SHCLX11REQREAD *)&pReq;
621 int rc = ShClX11ReadDataFromX11Async(pCtx, VBOX_SHCL_FMT_UNICODETEXT, pReq);
622 RTTESTI_CHECK_MSG(rc == VERR_NO_DATA, ("context: %s\n", pcszTestCtx));
623}
624
625static void tstStringFromVBoxFailed(RTTEST hTest, PSHCLX11CTX pCtx, const char *pcszTarget)
626{
627 RT_NOREF(pCtx);
628 Atom type;
629 XtPointer value = NULL;
630 unsigned long length;
631 int format;
632 RTTEST_CHECK_MSG(hTest, !tstClipConvertSelection(pcszTarget, &type, &value,
633 &length, &format),
634 (hTest, "Conversion to target %s, should have failed but didn't, returned type %d, length %u, format %d, value \"%.*s\"\n",
635 pcszTarget, type, length, format, RT_MIN(length, 20),
636 value));
637 XtFree((char *)value);
638}
639
640static void tstNoSelectionOwnership(PSHCLX11CTX pCtx, const char *pcszTestCtx)
641{
642 RT_NOREF(pCtx);
643 RTTESTI_CHECK_MSG(!g_tst_fOwnsSel, ("context: %s\n", pcszTestCtx));
644}
645
646static void tstBadFormatRequestFromHost(RTTEST hTest, PSHCLX11CTX pCtx)
647{
648 tstClipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
649 sizeof("hello world"), 8);
650 tstClipSendTargetUpdate(pCtx);
651 if (tstClipQueryFormats() != VBOX_SHCL_FMT_UNICODETEXT)
652 RTTestFailed(hTest, "Wrong targets reported: %02X\n",
653 tstClipQueryFormats());
654 else
655 {
656 char *pc;
657 SHCLX11REQREAD *pReq = (SHCLX11REQREAD *)&pReq, *pReqRet = NULL;
658 ShClX11ReadDataFromX11Async(pCtx, 0xF000 /* vboxFormat */, pReq); /* Bad format. */
659 int rc = VINF_SUCCESS;
660 uint32_t cbActual = 0;
661 tstClipGetCompletedRequest(&rc, &pc, &cbActual, &pReqRet);
662 if (rc != VERR_NOT_IMPLEMENTED)
663 RTTestFailed(hTest, "Wrong return code, expected VERR_NOT_IMPLEMENTED, got %Rrc\n",
664 rc);
665 tstClipSetSelectionValues("", XA_STRING, "", sizeof(""), 8);
666 tstClipSendTargetUpdate(pCtx);
667 if (tstClipQueryFormats() == VBOX_SHCL_FMT_UNICODETEXT)
668 RTTestFailed(hTest, "Failed to report targets after bad host request.\n");
669 }
670}
671
672int main()
673{
674 /*
675 * Init the runtime, test and say hello.
676 */
677 RTTEST hTest;
678 int rc = RTTestInitAndCreate("tstClipboardGH-X11", &hTest);
679 if (RT_FAILURE(rc))
680 return RTEXITCODE_FAILURE;
681 RTTestBanner(hTest);
682
683 /*
684 * Run the tests.
685 */
686 SHCLCALLBACKS Callbacks;
687 RT_ZERO(Callbacks);
688 Callbacks.pfnReportFormats = tstShClReportFormatsCallback;
689 Callbacks.pfnOnRequestDataFromSource = tstShClOnRequestDataFromSourceCallback;
690 Callbacks.pfnOnSendDataToDest = tstShClOnSendDataToDestCallback;
691
692 SHCLX11CTX X11Ctx;
693 rc = ShClX11Init(&X11Ctx, &Callbacks, NULL /* pParent */, false /* fHeadless */);
694 AssertRCReturn(rc, RTEXITCODE_FAILURE);
695
696 char *pc;
697 uint32_t cbActual;
698 SHCLX11REQREAD *pReq = (SHCLX11REQREAD *)&pReq, *pReqRet = NULL;
699
700 /* UTF-8 from X11 */
701 RTTestSub(hTest, "reading UTF-8 from X11");
702 /* Simple test */
703 tstClipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
704 sizeof("hello world"), 8);
705 tstStringFromX11(hTest, &X11Ctx, "hello world", VINF_SUCCESS);
706 /* With an embedded carriage return */
707 tstClipSetSelectionValues("text/plain;charset=UTF-8", XA_STRING,
708 "hello\nworld", sizeof("hello\nworld"), 8);
709 tstStringFromX11(hTest, &X11Ctx, "hello\r\nworld", VINF_SUCCESS);
710 /* With an embedded CRLF */
711 tstClipSetSelectionValues("text/plain;charset=UTF-8", XA_STRING,
712 "hello\r\nworld", sizeof("hello\r\nworld"), 8);
713 tstStringFromX11(hTest, &X11Ctx, "hello\r\r\nworld", VINF_SUCCESS);
714 /* With an embedded LFCR */
715 tstClipSetSelectionValues("text/plain;charset=UTF-8", XA_STRING,
716 "hello\n\rworld", sizeof("hello\n\rworld"), 8);
717 tstStringFromX11(hTest, &X11Ctx, "hello\r\n\rworld", VINF_SUCCESS);
718 /* An empty string */
719 tstClipSetSelectionValues("text/plain;charset=utf-8", XA_STRING, "",
720 sizeof(""), 8);
721 tstStringFromX11(hTest, &X11Ctx, "", VINF_SUCCESS);
722 /* With an embedded UTF-8 character. */
723 tstClipSetSelectionValues("STRING", XA_STRING,
724 "100\xE2\x82\xAC" /* 100 Euro */,
725 sizeof("100\xE2\x82\xAC"), 8);
726 tstStringFromX11(hTest, &X11Ctx, "100\xE2\x82\xAC", VINF_SUCCESS);
727 /* A non-zero-terminated string */
728 tstClipSetSelectionValues("TEXT", XA_STRING,
729 "hello world", sizeof("hello world") - 1, 8);
730 tstStringFromX11(hTest, &X11Ctx, "hello world", VINF_SUCCESS);
731
732 /* Latin1 from X11 */
733 RTTestSub(hTest, "reading Latin1 from X11");
734 /* Simple test */
735 tstClipSetSelectionValues("STRING", XA_STRING, "Georges Dupr\xEA",
736 sizeof("Georges Dupr\xEA"), 8);
737 tstLatin1FromX11(hTest, &X11Ctx, "Georges Dupr\xEA", VINF_SUCCESS);
738 /* With an embedded carriage return */
739 tstClipSetSelectionValues("TEXT", XA_STRING, "Georges\nDupr\xEA",
740 sizeof("Georges\nDupr\xEA"), 8);
741 tstLatin1FromX11(hTest, &X11Ctx, "Georges\r\nDupr\xEA", VINF_SUCCESS);
742 /* With an embedded CRLF */
743 tstClipSetSelectionValues("TEXT", XA_STRING, "Georges\r\nDupr\xEA",
744 sizeof("Georges\r\nDupr\xEA"), 8);
745 tstLatin1FromX11(hTest, &X11Ctx, "Georges\r\r\nDupr\xEA", VINF_SUCCESS);
746 /* With an embedded LFCR */
747 tstClipSetSelectionValues("TEXT", XA_STRING, "Georges\n\rDupr\xEA",
748 sizeof("Georges\n\rDupr\xEA"), 8);
749 tstLatin1FromX11(hTest, &X11Ctx, "Georges\r\n\rDupr\xEA", VINF_SUCCESS);
750 /* A non-zero-terminated string */
751 tstClipSetSelectionValues("text/plain", XA_STRING,
752 "Georges Dupr\xEA!",
753 sizeof("Georges Dupr\xEA!") - 1, 8);
754 tstLatin1FromX11(hTest, &X11Ctx, "Georges Dupr\xEA!", VINF_SUCCESS);
755
756 /*
757 * Unknown X11 format
758 */
759 RTTestSub(hTest, "handling of an unknown X11 format");
760 tstClipInvalidateFormats();
761 tstClipSetSelectionValues("CLIPBOARD", XA_STRING, "Test",
762 sizeof("Test"), 8);
763 tstClipSendTargetUpdate(&X11Ctx);
764 RTTEST_CHECK_MSG(hTest, tstClipQueryFormats() == 0,
765 (hTest, "Failed to send a format update notification\n"));
766
767 /*
768 * Timeout from X11
769 */
770 RTTestSub(hTest, "X11 timeout");
771 tstClipSetSelectionValues("UTF8_STRING", XT_CONVERT_FAIL, NULL,0, 8);
772 tstStringFromX11(hTest, &X11Ctx, "", VERR_NO_DATA);
773
774 /*
775 * No data in X11 clipboard
776 */
777 RTTestSub(hTest, "a data request from an empty X11 clipboard");
778 tstClipSetSelectionValues("UTF8_STRING", XA_STRING, NULL,
779 0, 8);
780 ShClX11ReadDataFromX11Async(&X11Ctx, VBOX_SHCL_FMT_UNICODETEXT, pReq);
781 tstClipGetCompletedRequest(&rc, &pc, &cbActual, &pReqRet);
782 RTTEST_CHECK_MSG(hTest, rc == VERR_NO_DATA,
783 (hTest, "Returned %Rrc instead of VERR_NO_DATA\n",
784 rc));
785 RTTEST_CHECK_MSG(hTest, pReqRet == pReq,
786 (hTest, "Wrong returned request data, expected %p, got %p\n",
787 pReq, pReqRet));
788
789 /*
790 * Ensure that VBox is notified when we return the CB to X11
791 */
792 RTTestSub(hTest, "notification of switch to X11 clipboard");
793 tstClipInvalidateFormats();
794 clipReportEmpty(&X11Ctx);
795 RTTEST_CHECK_MSG(hTest, tstClipQueryFormats() == 0,
796 (hTest, "Failed to send a format update (release) notification\n"));
797
798 /*
799 * Request for an invalid VBox format from X11
800 */
801 RTTestSub(hTest, "a request for an invalid VBox format from X11");
802 /* Testing for 0xffff will go into handling VBOX_SHCL_FMT_UNICODETEXT, where we don't have
803 * have any data at the moment so far, so this will return VERR_NO_DATA. */
804 ShClX11ReadDataFromX11Async(&X11Ctx, 0xffff /* vboxFormat */, pReq);
805 tstClipGetCompletedRequest(&rc, &pc, &cbActual, &pReqRet);
806 RTTEST_CHECK_MSG(hTest, rc == VERR_NO_DATA,
807 (hTest, "Returned %Rrc instead of VERR_NO_DATA\n",
808 rc));
809 RTTEST_CHECK_MSG(hTest, pReqRet == pReq,
810 (hTest, "Wrong returned request data, expected %p, got %p\n",
811 pReq, pReqRet));
812
813 /*
814 * Targets failure from X11
815 */
816 RTTestSub(hTest, "X11 targets conversion failure");
817 tstClipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
818 sizeof("hello world"), 8);
819 tstClipSetTargetsFailure();
820 Atom atom = XA_STRING;
821 long unsigned int cLen = 0;
822 int format = 8;
823 clipQueryX11TargetsCallback(NULL, (XtPointer) &X11Ctx, NULL, &atom, NULL, &cLen,
824 &format);
825 RTTEST_CHECK_MSG(hTest, tstClipQueryFormats() == 0,
826 (hTest, "Wrong targets reported: %02X\n",
827 tstClipQueryFormats()));
828
829 /*
830 * X11 text format conversion
831 */
832 RTTestSub(hTest, "handling of X11 selection targets");
833 RTTEST_CHECK_MSG(hTest, tstClipTextFormatConversion(&X11Ctx),
834 (hTest, "failed to select the right X11 text formats\n"));
835 /*
836 * UTF-8 from VBox
837 */
838 RTTestSub(hTest, "reading UTF-8 from VBox");
839 /* Simple test */
840 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello world",
841 sizeof("hello world") * 2);
842 tstStringFromVBox(hTest, &X11Ctx, "UTF8_STRING",
843 clipGetAtom(&X11Ctx, "UTF8_STRING"), "hello world");
844 /* With an embedded carriage return */
845 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello\r\nworld",
846 sizeof("hello\r\nworld") * 2);
847 tstStringFromVBox(hTest, &X11Ctx, "text/plain;charset=UTF-8",
848 clipGetAtom(&X11Ctx, "text/plain;charset=UTF-8"),
849 "hello\nworld");
850 /* With an embedded CRCRLF */
851 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello\r\r\nworld",
852 sizeof("hello\r\r\nworld") * 2);
853 tstStringFromVBox(hTest, &X11Ctx, "text/plain;charset=UTF-8",
854 clipGetAtom(&X11Ctx, "text/plain;charset=UTF-8"),
855 "hello\r\nworld");
856 /* With an embedded CRLFCR */
857 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello\r\n\rworld",
858 sizeof("hello\r\n\rworld") * 2);
859 tstStringFromVBox(hTest, &X11Ctx, "text/plain;charset=UTF-8",
860 clipGetAtom(&X11Ctx, "text/plain;charset=UTF-8"),
861 "hello\n\rworld");
862 /* An empty string */
863 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "", 2);
864 tstStringFromVBox(hTest, &X11Ctx, "text/plain;charset=utf-8",
865 clipGetAtom(&X11Ctx, "text/plain;charset=utf-8"), "");
866 /* With an embedded UTF-8 character. */
867 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "100\xE2\x82\xAC" /* 100 Euro */,
868 10);
869 tstStringFromVBox(hTest, &X11Ctx, "STRING",
870 clipGetAtom(&X11Ctx, "STRING"), "100\xE2\x82\xAC");
871 /* A non-zero-terminated string */
872 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello world",
873 sizeof("hello world") * 2 - 2);
874 tstStringFromVBox(hTest, &X11Ctx, "TEXT", clipGetAtom(&X11Ctx, "TEXT"),
875 "hello world");
876
877 /*
878 * Timeout from VBox
879 */
880 RTTestSub(hTest, "reading from VBox with timeout");
881 tstClipEmptyVBox(&X11Ctx, VERR_TIMEOUT);
882 tstStringFromVBoxFailed(hTest, &X11Ctx, "UTF8_STRING");
883
884 /*
885 * No data in VBox clipboard
886 */
887 RTTestSub(hTest, "an empty VBox clipboard");
888 tstClipSetSelectionValues("TEXT", XA_STRING, "", sizeof(""), 8);
889 tstClipEmptyVBox(&X11Ctx, VINF_SUCCESS);
890 RTTEST_CHECK_MSG(hTest, g_tst_fOwnsSel,
891 (hTest, "VBox grabbed the clipboard with no data and we ignored it\n"));
892 tstStringFromVBoxFailed(hTest, &X11Ctx, "UTF8_STRING");
893
894 /*
895 * An unknown VBox format
896 */
897 RTTestSub(hTest, "reading an unknown VBox format");
898 tstClipSetSelectionValues("TEXT", XA_STRING, "", sizeof(""), 8);
899 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "", 2);
900 ShClX11ReportFormatsToX11Async(&X11Ctx, 0xa0000);
901 RTTEST_CHECK_MSG(hTest, g_tst_fOwnsSel,
902 (hTest, "VBox grabbed the clipboard with unknown data and we ignored it\n"));
903 tstStringFromVBoxFailed(hTest, &X11Ctx, "UTF8_STRING");
904
905 /*
906 * VBox requests a bad format
907 */
908 RTTestSub(hTest, "recovery from a bad format request");
909 tstBadFormatRequestFromHost(hTest, &X11Ctx);
910
911 ShClX11Destroy(&X11Ctx);
912
913 /*
914 * Headless clipboard tests
915 */
916 rc = ShClX11Init(&X11Ctx, &Callbacks, NULL /* pParent */, true /* fHeadless */);
917 AssertRCReturn(rc, RTEXITCODE_FAILURE);
918
919 /* Read from X11 */
920 RTTestSub(hTest, "reading from X11, headless clipboard");
921 /* Simple test */
922 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "",
923 sizeof("") * 2);
924 tstClipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
925 sizeof("hello world"), 8);
926 tstNoX11(&X11Ctx, "reading from X11, headless clipboard");
927
928 /* Read from VBox */
929 RTTestSub(hTest, "reading from VBox, headless clipboard");
930 /* Simple test */
931 tstClipEmptyVBox(&X11Ctx, VERR_WRONG_ORDER);
932 tstClipSetSelectionValues("TEXT", XA_STRING, "", sizeof(""), 8);
933 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello world",
934 sizeof("hello world") * 2);
935 tstNoSelectionOwnership(&X11Ctx, "reading from VBox, headless clipboard");
936
937 ShClX11Destroy(&X11Ctx);
938
939 return RTTestSummaryAndDestroy(hTest);
940}
941
Note: See TracBrowser for help on using the repository browser.

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