VirtualBox

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

Last change on this file since 102843 was 102466, checked in by vboxsync, 14 months ago

Shared Clipboard/testcases: Re-enabled (+ adapted) formerly disabled testcases of tstClipboardGH-X11. bugref:10384

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