VirtualBox

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

Last change on this file since 100685 was 100685, checked in by vboxsync, 18 months ago

Shared Clipboard/X11: Enabled a minimal subset of the testcases again, to have at least a tiny bit of testing. bugref:9437

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