VirtualBox

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

Last change on this file since 99286 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • 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 98103 2023-01-17 14:15:46Z 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 ShClX11ReportFormatsToX11(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 ShClX11ReportFormatsToX11(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 CLIPREADCBREQ *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 PSHCLX11READDATAREQ pData = (PSHCLX11READDATAREQ)pvUser;
416
417 if (cb <= TESTCASE_MAX_BUF_SIZE)
418 {
419 g_tst_rcCompleted = pData->rcCompletion;
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, CLIPREADCBREQ **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 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)&pReq, *pReqRet = NULL;
488 ShClX11ReadDataFromX11(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 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)&pReq, *pReqRet = NULL;
545 ShClX11ReadDataFromX11(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 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)&pReq;
621 int rc = ShClX11ReadDataFromX11(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 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)&pReq, *pReqRet = NULL;
658 ShClX11ReadDataFromX11(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 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)&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 ShClX11ReadDataFromX11(&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 ShClX11ReadDataFromX11(&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 ShClX11ReportFormatsToX11(&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.

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