VirtualBox

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

Last change on this file since 106199 was 106061, checked in by vboxsync, 5 months ago

Copyright year updates by scm.

  • 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 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * Shared Clipboard guest/host X11 code test cases.
4 */
5
6/*
7 * Copyright (C) 2011-2024 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 if (!atom)
355 return NULL;
356 const char *pcszName = 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 if (pData->enmType == SHCLX11EVENTTYPE_READ)
429 RTMemFree(pData->Read.pvData);
430 RTMemFree(pData);
431
432 return VINF_SUCCESS;
433}
434
435/**
436 * Looks up the X11 format matching a given X11 atom text.
437 *
438 * @returns the format on success, NIL_CLIPX11FORMAT on failure
439 * @param pcszAtom Atom text to look up format for.
440 */
441static SHCLX11FMTIDX tstClipFindX11FormatByAtomText(const char *pcszAtom)
442{
443 const size_t j = clipReportMaxX11Formats();
444
445 for (unsigned i = 0; i < j; ++i)
446 {
447 if (!strcmp(g_aFormats[i].pcszAtom, pcszAtom))
448 return i;
449 }
450 return NIL_CLIPX11FORMAT;
451}
452
453static bool tstClipTextFormatConversion(PSHCLX11CTX pCtx)
454{
455 bool fSuccess = true;
456 SHCLX11FMTIDX targets[2];
457 SHCLX11FMTIDX x11Format;
458 targets[0] = tstClipFindX11FormatByAtomText("text/plain");
459 targets[1] = tstClipFindX11FormatByAtomText("image/bmp");
460 x11Format = clipGetTextFormatFromTargets(pCtx, targets, 2);
461 if (clipRealFormatForX11Format(x11Format) != SHCLX11FMT_TEXT)
462 fSuccess = false;
463 targets[0] = tstClipFindX11FormatByAtomText("UTF8_STRING");
464 targets[1] = tstClipFindX11FormatByAtomText("text/plain");
465 x11Format = clipGetTextFormatFromTargets(pCtx, targets, 2);
466 if (clipRealFormatForX11Format(x11Format) != SHCLX11FMT_UTF8)
467 fSuccess = false;
468 return fSuccess;
469}
470
471static void tstStringFromX11(RTTEST hTest, PSHCLX11CTX pCtx,
472 const char *pcszExp, int rcExp)
473{
474 bool fRc = true;
475 tstClipSendTargetUpdate(pCtx);
476 if (tstClipQueryFormats() != VBOX_SHCL_FMT_UNICODETEXT)
477 {
478 RTTestFailed(hTest, "Wrong targets reported: %02X\n", tstClipQueryFormats());
479 }
480 else
481 {
482 uint32_t cbActual = 0;
483 uint8_t abBuf[TESTCASE_MAX_BUF_SIZE];
484 int rc = ShClX11ReadDataFromX11(pCtx, &g_EventSource, g_msTimeout, VBOX_SHCL_FMT_UNICODETEXT, abBuf, sizeof(abBuf), &cbActual);
485 if (rc != rcExp)
486 RTTestFailed(hTest, "Wrong return code, expected %Rrc, got %Rrc\n", rcExp, rc);
487 else if (RT_FAILURE(rcExp))
488 fRc = true;
489 else
490 {
491 RTUTF16 wcExp[TESTCASE_MAX_BUF_SIZE / 2];
492 RTUTF16 *pwcExp = wcExp;
493 size_t cwc = 0;
494 rc = RTStrToUtf16Ex(pcszExp, RTSTR_MAX, &pwcExp, RT_ELEMENTS(wcExp), &cwc);
495 AssertRC(rc);
496 size_t cbExp = cwc * 2 + 2;
497 if (RT_SUCCESS(rc))
498 {
499 if (cbActual != cbExp)
500 {
501 RTTestFailed(hTest, "Returned string is the wrong size: got size %u, expected %u\n", cbActual, cbExp);
502 }
503 else
504 {
505 if (memcmp(abBuf, wcExp, cbExp) == 0)
506 fRc = true;
507 else
508 RTTestFailed(hTest, "Returned string \"%.*ls\" does not match expected string \"%s\"\n",
509 TESTCASE_MAX_BUF_SIZE, abBuf, pcszExp);
510 }
511 }
512 }
513 }
514 if (!fRc)
515 RTTestFailureDetails(hTest, "Expected: string \"%s\", rc=%Rrc\n", pcszExp, rcExp);
516}
517
518static void tstLatin1FromX11(RTTEST hTest, PSHCLX11CTX pCtx,
519 const char *pcszExp, int rcExp)
520{
521 bool retval = false;
522 tstClipSendTargetUpdate(pCtx);
523 if (tstClipQueryFormats() != VBOX_SHCL_FMT_UNICODETEXT)
524 RTTestFailed(hTest, "Wrong targets reported: %02X\n",
525 tstClipQueryFormats());
526 else
527 {
528 uint32_t cbActual = 0;
529 uint8_t abBuf[TESTCASE_MAX_BUF_SIZE];
530 int rc = ShClX11ReadDataFromX11(pCtx, &g_EventSource, g_msTimeout, VBOX_SHCL_FMT_UNICODETEXT, abBuf, sizeof(abBuf), &cbActual);
531 if (rc != rcExp)
532 RTTestFailed(hTest, "Wrong return code, expected %Rrc, got %Rrc\n", rcExp, rc);
533 else if (RT_FAILURE(rcExp))
534 retval = true;
535 else
536 {
537 RTUTF16 wcExp[TESTCASE_MAX_BUF_SIZE / 2];
538 //RTUTF16 *pwcExp = wcExp; - unused
539 size_t cwc;
540 for (cwc = 0; cwc == 0 || pcszExp[cwc - 1] != '\0'; ++cwc)
541 wcExp[cwc] = pcszExp[cwc];
542 size_t cbExp = cwc * 2;
543 if (cbActual != cbExp)
544 {
545 RTTestFailed(hTest, "Returned string is the wrong size, string \"%.*ls\", size %u, expected \"%s\", size %u\n",
546 RT_MIN(TESTCASE_MAX_BUF_SIZE, cbActual), abBuf, cbActual,
547 pcszExp, cbExp);
548 }
549 else
550 {
551 if (memcmp(abBuf, wcExp, cbExp) == 0)
552 retval = true;
553 else
554 RTTestFailed(hTest, "Returned string \"%.*ls\" does not match expected string \"%s\"\n",
555 TESTCASE_MAX_BUF_SIZE, abBuf, pcszExp);
556 }
557 }
558 }
559 if (!retval)
560 RTTestFailureDetails(hTest, "Expected: string \"%s\", rc %Rrc\n",
561 pcszExp, rcExp);
562}
563
564static void tstStringFromVBox(RTTEST hTest, PSHCLX11CTX pCtx, const char *pcszTarget, Atom typeExp, const char *valueExp)
565{
566 RT_NOREF(pCtx);
567 bool retval = false;
568 Atom type;
569 XtPointer value = NULL;
570 unsigned long length;
571 int format;
572 size_t lenExp = strlen(valueExp);
573 if (tstClipConvertSelection(pcszTarget, &type, &value, &length, &format))
574 {
575 if ( type != typeExp
576 || length != lenExp
577 || format != 8
578 || memcmp((const void *) value, (const void *)valueExp,
579 lenExp))
580 {
581 RTTestFailed(hTest, "Bad data: type %d, (expected %d), length %u, (expected %u), format %d (expected %d), value \"%.*s\" (expected \"%.*s\")\n",
582 type, typeExp, length, lenExp, format, 8,
583 RT_MIN(length, 20), value, RT_MIN(lenExp, 20), valueExp);
584 }
585 else
586 retval = true;
587 }
588 else
589 RTTestFailed(hTest, "Conversion failed\n");
590 XtFree((char *)value);
591 if (!retval)
592 RTTestFailureDetails(hTest, "Conversion to %s, expected \"%s\"\n",
593 pcszTarget, valueExp);
594}
595
596static void tstStringFromVBoxFailed(RTTEST hTest, PSHCLX11CTX pCtx, const char *pcszTarget)
597{
598 RT_NOREF(pCtx);
599 Atom type;
600 XtPointer value = NULL;
601 unsigned long length;
602 int format;
603 RTTEST_CHECK_MSG(hTest, !tstClipConvertSelection(pcszTarget, &type, &value,
604 &length, &format),
605 (hTest, "Conversion to target %s, should have failed but didn't, returned type %d, length %u, format %d, value \"%.*s\"\n",
606 pcszTarget, type, length, format, RT_MIN(length, 20),
607 value));
608 XtFree((char *)value);
609}
610
611static void tstNoSelectionOwnership(PSHCLX11CTX pCtx, const char *pcszTestCtx)
612{
613 RT_NOREF(pCtx);
614 RTTESTI_CHECK_MSG(!g_tst_fOwnsSel, ("context: %s\n", pcszTestCtx));
615}
616
617static void tstBadFormatRequestFromHost(RTTEST hTest, PSHCLX11CTX pCtx)
618{
619 tstClipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
620 sizeof("hello world"), 8);
621 tstClipSendTargetUpdate(pCtx);
622 if (tstClipQueryFormats() != VBOX_SHCL_FMT_UNICODETEXT)
623 RTTestFailed(hTest, "Wrong targets reported: %02X\n",
624 tstClipQueryFormats());
625 else
626 {
627 uint32_t cbActual = 0;
628 uint8_t abBuf[TESTCASE_MAX_BUF_SIZE];
629 int rc = ShClX11ReadDataFromX11(pCtx, &g_EventSource, g_msTimeout, 0xF000 /* vboxFormat */, abBuf, sizeof(abBuf), &cbActual);
630 if (rc != VERR_NOT_IMPLEMENTED)
631 RTTestFailed(hTest, "Wrong return code, expected VERR_NOT_IMPLEMENTED, got %Rrc\n",
632 rc);
633 tstClipSetSelectionValues("", XA_STRING, "", sizeof(""), 8);
634 tstClipSendTargetUpdate(pCtx);
635 if (tstClipQueryFormats() == VBOX_SHCL_FMT_UNICODETEXT)
636 RTTestFailed(hTest, "Failed to report targets after bad host request.\n");
637 }
638}
639
640int main()
641{
642 /*
643 * Init the runtime, test and say hello.
644 */
645 RTTEST hTest;
646 int rc = RTTestInitAndCreate("tstClipboardGH-X11", &hTest);
647 if (RT_FAILURE(rc))
648 return RTEXITCODE_FAILURE;
649 RTTestBanner(hTest);
650
651 /*
652 * Run the tests.
653 */
654 SHCLCALLBACKS Callbacks;
655 RT_ZERO(Callbacks);
656 Callbacks.pfnReportFormats = tstShClReportFormatsCallback;
657 Callbacks.pfnOnRequestDataFromSource = tstShClOnRequestDataFromSourceCallback;
658 Callbacks.pfnOnSendDataToDest = tstShClOnSendDataToDestCallback;
659
660 SHCLX11CTX X11Ctx;
661 rc = ShClX11Init(&X11Ctx, &Callbacks, NULL /* pParent */, false /* fHeadless */);
662 AssertRCReturn(rc, RTEXITCODE_FAILURE);
663
664 uint32_t cbActual = 0;
665 uint8_t abBuf[TESTCASE_MAX_BUF_SIZE];
666
667 RTTESTI_CHECK_RC_OK(ShClEventSourceCreate(&g_EventSource, 0 /* ID */));
668
669 /* UTF-8 from X11 */
670 RTTestSub(hTest, "reading UTF-8 from X11");
671 /* Simple test */
672 tstClipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world", sizeof("hello world"), 8);
673 tstStringFromX11(hTest, &X11Ctx, "hello world", VINF_SUCCESS);
674
675 /* With an embedded carriage return */
676 tstClipSetSelectionValues("text/plain;charset=UTF-8", XA_STRING,
677 "hello\nworld", sizeof("hello\nworld"), 8);
678 tstStringFromX11(hTest, &X11Ctx, "hello\r\nworld", VINF_SUCCESS);
679 /* With an embedded CRLF */
680 tstClipSetSelectionValues("text/plain;charset=UTF-8", XA_STRING,
681 "hello\r\nworld", sizeof("hello\r\nworld"), 8);
682 tstStringFromX11(hTest, &X11Ctx, "hello\r\nworld", VINF_SUCCESS);
683 /* With an embedded LFCR */
684 tstClipSetSelectionValues("text/plain;charset=UTF-8", XA_STRING,
685 "hello\n\rworld", sizeof("hello\n\rworld"), 8);
686 tstStringFromX11(hTest, &X11Ctx, "hello\r\n\rworld", VINF_SUCCESS);
687 /* An empty string */
688 tstClipSetSelectionValues("text/plain;charset=utf-8", XA_STRING, "",
689 sizeof(""), 8);
690 tstStringFromX11(hTest, &X11Ctx, "", VINF_SUCCESS);
691 /* With an embedded UTF-8 character. */
692 tstClipSetSelectionValues("STRING", XA_STRING,
693 "100\xE2\x82\xAC" /* 100 Euro */,
694 sizeof("100\xE2\x82\xAC"), 8);
695 tstStringFromX11(hTest, &X11Ctx, "100\xE2\x82\xAC", VINF_SUCCESS);
696 /* A non-zero-terminated string */
697 tstClipSetSelectionValues("TEXT", XA_STRING,
698 "hello world", sizeof("hello world") - 1, 8);
699 tstStringFromX11(hTest, &X11Ctx, "hello world", VINF_SUCCESS);
700
701 /* Latin1 from X11 */
702 RTTestSub(hTest, "reading Latin1 from X11");
703 /* Simple test */
704 tstClipSetSelectionValues("STRING", XA_STRING, "Georges Dupr\xEA",
705 sizeof("Georges Dupr\xEA"), 8);
706 tstLatin1FromX11(hTest, &X11Ctx, "Georges Dupr\xEA", VINF_SUCCESS);
707 /* With an embedded carriage return */
708 tstClipSetSelectionValues("TEXT", XA_STRING, "Georges\nDupr\xEA",
709 sizeof("Georges\nDupr\xEA"), 8);
710 tstLatin1FromX11(hTest, &X11Ctx, "Georges\r\nDupr\xEA", VINF_SUCCESS);
711 /* With an embedded CRLF */
712 tstClipSetSelectionValues("TEXT", XA_STRING, "Georges\r\nDupr\xEA",
713 sizeof("Georges\r\nDupr\xEA"), 8);
714 tstLatin1FromX11(hTest, &X11Ctx, "Georges\r\r\nDupr\xEA", VINF_SUCCESS);
715 /* With an embedded LFCR */
716 tstClipSetSelectionValues("TEXT", XA_STRING, "Georges\n\rDupr\xEA",
717 sizeof("Georges\n\rDupr\xEA"), 8);
718 tstLatin1FromX11(hTest, &X11Ctx, "Georges\r\n\rDupr\xEA", VINF_SUCCESS);
719 /* A non-zero-terminated string */
720 tstClipSetSelectionValues("text/plain", XA_STRING,
721 "Georges Dupr\xEA!",
722 sizeof("Georges Dupr\xEA!") - 1, 8);
723 tstLatin1FromX11(hTest, &X11Ctx, "Georges Dupr\xEA!", VINF_SUCCESS);
724
725 /*
726 * Unknown X11 format
727 */
728 RTTestSub(hTest, "handling of an unknown X11 format");
729 tstClipInvalidateFormats();
730 tstClipSetSelectionValues("CLIPBOARD", XA_STRING, "Test",
731 sizeof("Test"), 8);
732 tstClipSendTargetUpdate(&X11Ctx);
733 RTTEST_CHECK_MSG(hTest, tstClipQueryFormats() == 0,
734 (hTest, "Failed to send a format update notification\n"));
735
736 /*
737 * Timeout from X11
738 */
739 RTTestSub(hTest, "X11 conversion failure (timeout)");
740 tstClipSetSelectionValues("UTF8_STRING", XT_CONVERT_FAIL, NULL,0, 8);
741 tstStringFromX11(hTest, &X11Ctx, "", VERR_SHCLPB_NO_DATA);
742
743 /*
744 * No data in X11 clipboard
745 */
746 RTTestSub(hTest, "a data request from an empty X11 clipboard");
747 tstClipSetSelectionValues("UTF8_STRING", XA_STRING, NULL, 0, 8);
748 rc = ShClX11ReadDataFromX11(&X11Ctx, &g_EventSource, g_msTimeout, VBOX_SHCL_FMT_UNICODETEXT, abBuf, sizeof(abBuf), &cbActual);
749 RTTEST_CHECK_MSG(hTest, rc == VERR_SHCLPB_NO_DATA,
750 (hTest, "Returned %Rrc instead of VERR_SHCLPB_NO_DATA\n",
751 rc));
752
753 /*
754 * Ensure that VBox is notified when we return the CB to X11
755 */
756 RTTestSub(hTest, "notification of switch to X11 clipboard");
757 tstClipInvalidateFormats();
758 clipReportEmpty(&X11Ctx);
759 RTTEST_CHECK_MSG(hTest, tstClipQueryFormats() == 0,
760 (hTest, "Failed to send a format update (release) notification\n"));
761
762 /*
763 * Request for an invalid VBox format from X11
764 */
765 RTTestSub(hTest, "a request for an invalid VBox format from X11");
766 /* Testing for 0xffff will go into handling VBOX_SHCL_FMT_UNICODETEXT, where we don't have
767 * have any data at the moment so far, so this will return VERR_SHCLPB_NO_DATA. */
768 rc = ShClX11ReadDataFromX11(&X11Ctx, &g_EventSource, g_msTimeout, 0xffff /* vboxFormat */, abBuf, sizeof(abBuf), &cbActual);
769 RTTEST_CHECK_MSG(hTest, rc == VERR_SHCLPB_NO_DATA,
770 (hTest, "Returned %Rrc instead of VERR_SHCLPB_NO_DATA\n",
771 rc));
772 /*
773 * Targets failure from X11
774 */
775 RTTestSub(hTest, "X11 targets conversion failure");
776 tstClipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
777 sizeof("hello world"), 8);
778 tstClipSetTargetsFailure();
779 Atom atom = XA_STRING;
780 long unsigned int cLen = 0;
781 int format = 8;
782 clipQueryX11TargetsCallback(NULL, (XtPointer) &X11Ctx, NULL, &atom, NULL, &cLen,
783 &format);
784 RTTEST_CHECK_MSG(hTest, tstClipQueryFormats() == 0,
785 (hTest, "Wrong targets reported: %02X\n",
786 tstClipQueryFormats()));
787
788 /*
789 * X11 text format conversion
790 */
791 RTTestSub(hTest, "handling of X11 selection targets");
792 RTTEST_CHECK_MSG(hTest, tstClipTextFormatConversion(&X11Ctx),
793 (hTest, "failed to select the right X11 text formats\n"));
794 /*
795 * UTF-8 from VBox
796 */
797 RTTestSub(hTest, "reading UTF-8 from VBox");
798 /* Simple test */
799 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello world",
800 sizeof("hello world") * 2);
801 tstStringFromVBox(hTest, &X11Ctx, "UTF8_STRING",
802 clipGetAtom(&X11Ctx, "UTF8_STRING"), "hello world");
803 /* With an embedded carriage return */
804 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello\r\nworld",
805 sizeof("hello\r\nworld") * 2);
806 tstStringFromVBox(hTest, &X11Ctx, "text/plain;charset=UTF-8",
807 clipGetAtom(&X11Ctx, "text/plain;charset=UTF-8"),
808 "hello\nworld");
809 /* With an embedded CRCRLF */
810 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello\r\r\nworld",
811 sizeof("hello\r\r\nworld") * 2);
812 tstStringFromVBox(hTest, &X11Ctx, "text/plain;charset=UTF-8",
813 clipGetAtom(&X11Ctx, "text/plain;charset=UTF-8"),
814 "hello\r\nworld");
815 /* With an embedded CRLFCR */
816 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello\r\n\rworld",
817 sizeof("hello\r\n\rworld") * 2);
818 tstStringFromVBox(hTest, &X11Ctx, "text/plain;charset=UTF-8",
819 clipGetAtom(&X11Ctx, "text/plain;charset=UTF-8"),
820 "hello\n\rworld");
821 /* An empty string */
822 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "", 2);
823 tstStringFromVBox(hTest, &X11Ctx, "text/plain;charset=utf-8",
824 clipGetAtom(&X11Ctx, "text/plain;charset=utf-8"), "");
825 /* With an embedded UTF-8 character. */
826 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "100\xE2\x82\xAC" /* 100 Euro */,
827 10);
828 tstStringFromVBox(hTest, &X11Ctx, "STRING",
829 clipGetAtom(&X11Ctx, "STRING"), "100\xE2\x82\xAC");
830 /* A non-zero-terminated string */
831 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello world",
832 sizeof("hello world") * 2 - 2);
833 tstStringFromVBox(hTest, &X11Ctx, "TEXT", clipGetAtom(&X11Ctx, "TEXT"),
834 "hello world");
835
836 /*
837 * Timeout from VBox
838 */
839 RTTestSub(hTest, "reading from VBox with timeout");
840 tstClipEmptyVBox(&X11Ctx, VERR_TIMEOUT);
841 tstStringFromVBoxFailed(hTest, &X11Ctx, "UTF8_STRING");
842
843 /*
844 * No data in VBox clipboard
845 */
846 RTTestSub(hTest, "an empty VBox clipboard");
847 tstClipSetSelectionValues("TEXT", XA_STRING, "", sizeof(""), 8);
848 tstClipEmptyVBox(&X11Ctx, VINF_SUCCESS);
849 RTTEST_CHECK_MSG(hTest, g_tst_fOwnsSel,
850 (hTest, "VBox grabbed the clipboard with no data and we ignored it\n"));
851 tstStringFromVBoxFailed(hTest, &X11Ctx, "UTF8_STRING");
852
853 /*
854 * An unknown VBox format
855 */
856 RTTestSub(hTest, "reading an unknown VBox format");
857 tstClipSetSelectionValues("TEXT", XA_STRING, "", sizeof(""), 8);
858 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "", 2);
859 ShClX11ReportFormatsToX11Async(&X11Ctx, 0xa0000);
860 RTTEST_CHECK_MSG(hTest, g_tst_fOwnsSel,
861 (hTest, "VBox grabbed the clipboard with unknown data and we ignored it\n"));
862 tstStringFromVBoxFailed(hTest, &X11Ctx, "UTF8_STRING");
863
864 /*
865 * VBox requests a bad format
866 */
867 RTTestSub(hTest, "recovery from a bad format request");
868 tstBadFormatRequestFromHost(hTest, &X11Ctx);
869
870 ShClX11Destroy(&X11Ctx);
871
872 /*
873 * Headless clipboard tests
874 */
875 RTTEST_CHECK_RC_OK(hTest, ShClX11Init(&X11Ctx, &Callbacks, NULL /* pParent */, true /* fHeadless */));
876
877 /* Read from X11 */
878 RTTestSub(hTest, "reading from X11, headless clipboard");
879
880 /* Simple test */
881 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "", sizeof("") * 2);
882 tstClipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world", sizeof("hello world"), 8);
883 rc = ShClX11ReadDataFromX11(&X11Ctx, &g_EventSource, g_msTimeout, VBOX_SHCL_FMT_UNICODETEXT, abBuf, sizeof(abBuf), &cbActual);
884 RTTEST_CHECK_MSG(hTest, cbActual == 0, (hTest, "expected 0 but got %RU32\n", cbActual));
885 RTTEST_CHECK_MSG(hTest, rc == VINF_SUCCESS, (hTest, "expected VINF_SUCCESS but got %Rrc\n", rc));
886
887 /* Read from VBox */
888 RTTestSub(hTest, "reading from VBox, headless clipboard");
889
890 /* Simple test */
891 tstClipEmptyVBox(&X11Ctx, VERR_WRONG_ORDER);
892 tstClipSetSelectionValues("TEXT", XA_STRING, "", sizeof(""), 8);
893 tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello world",
894 sizeof("hello world") * 2);
895 tstNoSelectionOwnership(&X11Ctx, "reading from VBox, headless clipboard");
896
897 RTTEST_CHECK_RC_OK(hTest, ShClX11Destroy(&X11Ctx));
898 ShClEventSourceDestroy(&g_EventSource);
899
900 return RTTestSummaryAndDestroy(hTest);
901}
902
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