summaryrefslogtreecommitdiffstats
path: root/src/VBox/GuestHost/SharedClipboard/testcase
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/GuestHost/SharedClipboard/testcase
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/GuestHost/SharedClipboard/testcase')
-rw-r--r--src/VBox/GuestHost/SharedClipboard/testcase/Makefile.kmk76
-rw-r--r--src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardGH-X11.cpp941
-rw-r--r--src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardGH-X11Smoke.cpp103
-rw-r--r--src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardQt/Makefile.kmk52
-rw-r--r--src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardQt/tstClipboardQt.cpp47
5 files changed, 1219 insertions, 0 deletions
diff --git a/src/VBox/GuestHost/SharedClipboard/testcase/Makefile.kmk b/src/VBox/GuestHost/SharedClipboard/testcase/Makefile.kmk
new file mode 100644
index 00000000..5b073ea1
--- /dev/null
+++ b/src/VBox/GuestHost/SharedClipboard/testcase/Makefile.kmk
@@ -0,0 +1,76 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Shared Clipboard Guest/Host testcases.
+#
+
+#
+# Copyright (C) 2011-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK)
+ if1of ($(KBUILD_TARGET), freebsd linux netbsd openbsd solaris)
+
+ PROGRAMS += tstClipboardGH-X11 tstClipboardGH-X11Smoke
+ TESTING += \
+ $(tstClipboardGH-X11_0_OUTDIR)/tstClipboardGH-X11.run \
+ $(tstClipboardGH-X11_0_OUTDIR)/tstClipboardGH-X11Smoke.run
+
+ tstClipboardGH-X11_TEMPLATE = VBoxR3TstExe
+ tstClipboardGH-X11_DEFS = VBOX_WITH_HGCM UNIT_TEST TESTCASE
+ tstClipboardGH-X11_SOURCES = \
+ tstClipboardGH-X11.cpp \
+ ../clipboard-x11.cpp \
+ ../clipboard-common.cpp
+ tstClipboardGH-X11_CXXFLAGS += -Wno-array-bounds
+ tstClipboardGH-X11_LIBS = X11 Xt
+ tstClipboardGH-X11_CLEAN = $(tstClipboardGH-X11_0_OUTDIR)/tstClipboardGH-X11.run
+
+ tstClipboardGH-X11Smoke_TEMPLATE = VBoxR3TstExe
+ tstClipboardGH-X11Smoke_DEFS = VBOX_WITH_HGCM SMOKETEST
+ tstClipboardGH-X11Smoke_SOURCES = \
+ tstClipboardGH-X11Smoke.cpp \
+ ../clipboard-x11.cpp \
+ ../clipboard-common.cpp
+ tstClipboardGH-X11Smoke_LIBPATH = $(VBOX_LIBPATH_X11)
+ tstClipboardGH-X11Smoke_LIBS = X11 Xt
+ tstClipboardGH-X11Smoke_CLEAN = $(tstClipboardGH-X11Smoke_0_OUTDIR)/tstClipboardGH-X11Smoke.run
+
+ $$(tstClipboardGH-X11_0_OUTDIR)/tstClipboardGH-X11.run: $$(tstClipboardGH-X11_1_STAGE_TARGET)
+ export VBOX_LOG_DEST=nofile; $(tstClipboardGH-X11_1_STAGE_TARGET) quiet
+ $(QUIET)$(APPEND) -t "$@" "done"
+
+ $$(tstClipboardGH-X11Smoke_0_OUTDIR)/tstClipboardGH-X11Smoke.run: $$(tstClipboardGH-X11Smoke_1_STAGE_TARGET)
+ export VBOX_LOG_DEST=nofile; $(tstClipboardGH-X11Smoke_1_STAGE_TARGET) quiet
+ $(QUIET)$(APPEND) -t "$@" "done"
+
+ if defined(VBOX_WITH_QTGUI)
+ include $(PATH_SUB_CURRENT)/tstClipboardQt/Makefile.kmk
+ endif
+
+
+ endif
+endif
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardGH-X11.cpp b/src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardGH-X11.cpp
new file mode 100644
index 00000000..f9bc19c1
--- /dev/null
+++ b/src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardGH-X11.cpp
@@ -0,0 +1,941 @@
+/* $Id: tstClipboardGH-X11.cpp $ */
+/** @file
+ * Shared Clipboard guest/host X11 code test cases.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include <VBox/GuestHost/SharedClipboard.h>
+#include <VBox/GuestHost/SharedClipboard-x11.h>
+#include <VBox/GuestHost/clipboard-helper.h>
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+#include <iprt/utf16.h>
+
+#include <poll.h>
+#include <X11/Xatom.h>
+
+
+/*********************************************************************************************************************************
+* Externals *
+*********************************************************************************************************************************/
+extern SHCLX11FMTTABLE g_aFormats[];
+
+extern void clipUpdateX11Targets(PSHCLX11CTX pCtx, SHCLX11FMTIDX *pTargets, size_t cTargets);
+extern void clipReportEmpty(PSHCLX11CTX pCtx);
+extern void clipConvertDataFromX11Worker(void *pClient, void *pvSrc, unsigned cbSrc);
+extern SHCLX11FMTIDX clipGetTextFormatFromTargets(PSHCLX11CTX pCtx, SHCLX11FMTIDX *pTargets, size_t cTargets);
+extern SHCLX11FMT clipRealFormatForX11Format(SHCLX11FMTIDX uFmtIdx);
+extern Atom clipGetAtom(PSHCLX11CTX pCtx, const char *pcszName);
+extern void clipQueryX11Targets(PSHCLX11CTX pCtx);
+extern size_t clipReportMaxX11Formats(void);
+
+
+/*********************************************************************************************************************************
+* Internal prototypes *
+*********************************************************************************************************************************/
+static SHCLX11FMTIDX tstClipFindX11FormatByAtomText(const char *pcszAtom);
+
+
+/*********************************************************************************************************************************
+* Prototypes, used for testcases by clipboard-x11.cpp *
+*********************************************************************************************************************************/
+void tstRequestTargets(SHCLX11CTX* pCtx);
+void tstClipRequestData(PSHCLX11CTX pCtx, SHCLX11FMTIDX target, void *closure);
+void tstThreadScheduleCall(void (*proc)(void *, void *), void *client_data);
+
+
+/*********************************************************************************************************************************
+* Own callback implementations *
+*********************************************************************************************************************************/
+extern DECLCALLBACK(void) clipQueryX11TargetsCallback(Widget widget, XtPointer pClient,
+ Atom * /* selection */, Atom *atomType,
+ XtPointer pValue, long unsigned int *pcLen,
+ int *piFormat);
+
+
+/*********************************************************************************************************************************
+* Defines *
+*********************************************************************************************************************************/
+#define TESTCASE_WIDGET_ID (Widget)0xffff
+
+
+/* For the purpose of the test case, we just execute the procedure to be
+ * scheduled, as we are running single threaded. */
+void tstThreadScheduleCall(void (*proc)(void *, void *), void *client_data)
+{
+ proc(client_data, NULL);
+}
+
+/* The data in the simulated VBox clipboard. */
+static int g_tst_rcDataVBox = VINF_SUCCESS;
+static void *g_tst_pvDataVBox = NULL;
+static uint32_t g_tst_cbDataVBox = 0;
+
+/* Set empty data in the simulated VBox clipboard. */
+static void tstClipEmptyVBox(PSHCLX11CTX pCtx, int retval)
+{
+ g_tst_rcDataVBox = retval;
+ RTMemFree(g_tst_pvDataVBox);
+ g_tst_pvDataVBox = NULL;
+ g_tst_cbDataVBox = 0;
+ ShClX11ReportFormatsToX11(pCtx, 0);
+}
+
+/* Set the data in the simulated VBox clipboard. */
+static int tstClipSetVBoxUtf16(PSHCLX11CTX pCtx, int retval,
+ const char *pcszData, size_t cb)
+{
+ PRTUTF16 pwszData = NULL;
+ size_t cwData = 0;
+ int rc = RTStrToUtf16Ex(pcszData, RTSTR_MAX, &pwszData, 0, &cwData);
+ if (RT_FAILURE(rc))
+ return rc;
+ AssertReturn(cb <= cwData * 2 + 2, VERR_BUFFER_OVERFLOW);
+ void *pv = RTMemDup(pwszData, cb);
+ RTUtf16Free(pwszData);
+ if (pv == NULL)
+ return VERR_NO_MEMORY;
+ if (g_tst_pvDataVBox)
+ RTMemFree(g_tst_pvDataVBox);
+ g_tst_rcDataVBox = retval;
+ g_tst_pvDataVBox = pv;
+ g_tst_cbDataVBox = cb;
+ ShClX11ReportFormatsToX11(pCtx, VBOX_SHCL_FMT_UNICODETEXT);
+ return VINF_SUCCESS;
+}
+
+Display *XtDisplay(Widget w) { NOREF(w); return (Display *) 0xffff; }
+
+void XtAppSetExitFlag(XtAppContext app_context) { NOREF(app_context); }
+
+void XtDestroyWidget(Widget w) { NOREF(w); }
+
+XtAppContext XtCreateApplicationContext(void) { return (XtAppContext)0xffff; }
+
+void XtDestroyApplicationContext(XtAppContext app_context) { NOREF(app_context); }
+
+void XtToolkitInitialize(void) {}
+
+Boolean XtToolkitThreadInitialize(void) { return True; }
+
+Display *XtOpenDisplay(XtAppContext app_context,
+ _Xconst _XtString display_string,
+ _Xconst _XtString application_name,
+ _Xconst _XtString application_class,
+ XrmOptionDescRec *options, Cardinal num_options,
+ int *argc, char **argv)
+{
+ RT_NOREF8(app_context, display_string, application_name, application_class, options, num_options, argc, argv);
+ return (Display *)0xffff;
+}
+
+Widget XtVaAppCreateShell(_Xconst _XtString application_name, _Xconst _XtString application_class,
+ WidgetClass widget_class, Display *display, ...)
+{
+ RT_NOREF(application_name, application_class, widget_class, display);
+ return TESTCASE_WIDGET_ID;
+}
+
+void XtSetMappedWhenManaged(Widget widget, _XtBoolean mapped_when_managed) { RT_NOREF(widget, mapped_when_managed); }
+
+void XtRealizeWidget(Widget widget) { NOREF(widget); }
+
+XtInputId XtAppAddInput(XtAppContext app_context, int source, XtPointer condition, XtInputCallbackProc proc, XtPointer closure)
+{
+ RT_NOREF(app_context, source, condition, proc, closure);
+ return 0xffff;
+}
+
+/* Atoms we need other than the formats we support. */
+static const char *g_tst_apszSupAtoms[] =
+{
+ "PRIMARY", "CLIPBOARD", "TARGETS", "MULTIPLE", "TIMESTAMP"
+};
+
+/* This just looks for the atom names in a couple of tables and returns an
+ * index with an offset added. */
+Atom XInternAtom(Display *, const char *pcsz, int)
+{
+ Atom atom = 0;
+ size_t const cFormats = clipReportMaxX11Formats();
+ size_t i;
+ for (i = 0; i < cFormats; ++i)
+ {
+ if (!strcmp(pcsz, g_aFormats[i].pcszAtom))
+ atom = (Atom) (i + 0x1000);
+ }
+ for (i = 0; i < RT_ELEMENTS(g_tst_apszSupAtoms); ++i)
+ if (!strcmp(pcsz, g_tst_apszSupAtoms[i]))
+ atom = (Atom) (i + 0x2000);
+ Assert(atom); /* Have we missed any atoms? */
+ return atom;
+}
+
+/* Take a request for the targets we are currently offering. */
+static SHCLX11FMTIDX g_tst_aSelTargetsIdx[10] = { 0 };
+static size_t g_tst_cTargets = 0;
+
+void tstRequestTargets(SHCLX11CTX* pCtx)
+{
+ clipUpdateX11Targets(pCtx, g_tst_aSelTargetsIdx, g_tst_cTargets);
+}
+
+/* The current values of the X selection, which will be returned to the
+ * XtGetSelectionValue callback. */
+static Atom g_tst_atmSelType = 0;
+static const void *g_tst_pSelData = NULL;
+static unsigned long g_tst_cSelData = 0;
+static int g_tst_selFormat = 0;
+
+void tstClipRequestData(PSHCLX11CTX pCtx, SHCLX11FMTIDX target, void *closure)
+{
+ RT_NOREF(pCtx);
+ unsigned long count = 0;
+ int format = 0;
+ if (target != g_tst_aSelTargetsIdx[0])
+ {
+ clipConvertDataFromX11Worker(closure, NULL, 0); /* Could not convert to target. */
+ return;
+ }
+ void *pValue = NULL;
+ pValue = g_tst_pSelData ? RTMemDup(g_tst_pSelData, g_tst_cSelData) : NULL;
+ count = g_tst_pSelData ? g_tst_cSelData : 0;
+ format = g_tst_selFormat;
+ if (!pValue)
+ {
+ count = 0;
+ format = 0;
+ }
+ clipConvertDataFromX11Worker(closure, pValue, count * format / 8);
+ if (pValue)
+ RTMemFree(pValue);
+}
+
+/* The formats currently on offer from X11 via the shared clipboard. */
+static uint32_t g_tst_uX11Formats = 0;
+
+static uint32_t tstClipQueryFormats(void)
+{
+ return g_tst_uX11Formats;
+}
+
+static void tstClipInvalidateFormats(void)
+{
+ g_tst_uX11Formats = ~0;
+}
+
+/* Does our clipboard code currently own the selection? */
+static bool g_tst_fOwnsSel = false;
+/* The procedure that is called when we should convert the selection to a
+ * given format. */
+static XtConvertSelectionProc g_tst_pfnSelConvert = NULL;
+/* The procedure which is called when we lose the selection. */
+static XtLoseSelectionProc g_tst_pfnSelLose = NULL;
+/* The procedure which is called when the selection transfer has completed. */
+static XtSelectionDoneProc g_tst_pfnSelDone = NULL;
+
+Boolean XtOwnSelection(Widget widget, Atom selection, Time time,
+ XtConvertSelectionProc convert,
+ XtLoseSelectionProc lose,
+ XtSelectionDoneProc done)
+{
+ RT_NOREF(widget, time);
+ if (selection != XInternAtom(NULL, "CLIPBOARD", 0))
+ return True; /* We don't really care about this. */
+ g_tst_fOwnsSel = true; /* Always succeed. */
+ g_tst_pfnSelConvert = convert;
+ g_tst_pfnSelLose = lose;
+ g_tst_pfnSelDone = done;
+ return True;
+}
+
+void XtDisownSelection(Widget widget, Atom selection, Time time)
+{
+ RT_NOREF(widget, time, selection);
+ g_tst_fOwnsSel = false;
+ g_tst_pfnSelConvert = NULL;
+ g_tst_pfnSelLose = NULL;
+ g_tst_pfnSelDone = NULL;
+}
+
+/* Request the shared clipboard to convert its data to a given format. */
+static bool tstClipConvertSelection(const char *pcszTarget, Atom *type,
+ XtPointer *value, unsigned long *length,
+ int *format)
+{
+ Atom target = XInternAtom(NULL, pcszTarget, 0);
+ if (target == 0)
+ return false;
+ /* Initialise all return values in case we make a quick exit. */
+ *type = XA_STRING;
+ *value = NULL;
+ *length = 0;
+ *format = 0;
+ if (!g_tst_fOwnsSel)
+ return false;
+ if (!g_tst_pfnSelConvert)
+ return false;
+ Atom clipAtom = XInternAtom(NULL, "CLIPBOARD", 0);
+ if (!g_tst_pfnSelConvert(TESTCASE_WIDGET_ID, &clipAtom, &target, type,
+ value, length, format))
+ return false;
+ if (g_tst_pfnSelDone)
+ g_tst_pfnSelDone(TESTCASE_WIDGET_ID, &clipAtom, &target);
+ return true;
+}
+
+/* Set the current X selection data */
+static void tstClipSetSelectionValues(const char *pcszTarget, Atom type,
+ const void *data,
+ unsigned long count, int format)
+{
+ Atom clipAtom = XInternAtom(NULL, "CLIPBOARD", 0);
+ g_tst_aSelTargetsIdx[0] = tstClipFindX11FormatByAtomText(pcszTarget);
+ g_tst_cTargets = 1;
+ g_tst_atmSelType = type;
+ g_tst_pSelData = data;
+ g_tst_cSelData = count;
+ g_tst_selFormat = format;
+ if (g_tst_pfnSelLose)
+ g_tst_pfnSelLose(TESTCASE_WIDGET_ID, &clipAtom);
+ g_tst_fOwnsSel = false;
+}
+
+static void tstClipSendTargetUpdate(PSHCLX11CTX pCtx)
+{
+ clipQueryX11Targets(pCtx);
+}
+
+/* Configure if and how the X11 TARGETS clipboard target will fail. */
+static void tstClipSetTargetsFailure(void)
+{
+ g_tst_cTargets = 0;
+}
+
+char *XtMalloc(Cardinal size)
+{
+ return (char *) RTMemAlloc(size);
+}
+
+void XtFree(char *ptr)
+{
+ RTMemFree((void *)ptr);
+}
+
+char *XGetAtomName(Display *display, Atom atom)
+{
+ RT_NOREF(display);
+ const char *pcszName = NULL;
+ if (atom < 0x1000)
+ return NULL;
+ if (0x1000 <= atom && atom < 0x2000)
+ {
+ unsigned index = atom - 0x1000;
+ AssertReturn(index < clipReportMaxX11Formats(), NULL);
+ pcszName = g_aFormats[index].pcszAtom;
+ }
+ else
+ {
+ unsigned index = atom - 0x2000;
+ AssertReturn(index < RT_ELEMENTS(g_tst_apszSupAtoms), NULL);
+ pcszName = g_tst_apszSupAtoms[index];
+ }
+ return (char *)RTMemDup(pcszName, sizeof(pcszName) + 1);
+}
+
+int XFree(void *data)
+{
+ RTMemFree(data);
+ return 0;
+}
+
+void XFreeStringList(char **list)
+{
+ if (list)
+ RTMemFree(*list);
+ RTMemFree(list);
+}
+
+#define TESTCASE_MAX_BUF_SIZE 256
+
+static int g_tst_rcCompleted = VINF_SUCCESS;
+static int g_tst_cbCompleted = 0;
+static CLIPREADCBREQ *g_tst_pCompletedReq = NULL;
+static char g_tst_abCompletedBuf[TESTCASE_MAX_BUF_SIZE];
+
+static DECLCALLBACK(int) tstShClReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
+{
+ RT_NOREF(pCtx, pvUser);
+ g_tst_uX11Formats = fFormats;
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) tstShClOnRequestDataFromSourceCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
+{
+ RT_NOREF(pCtx, uFmt, pvUser);
+ *pcb = g_tst_cbDataVBox;
+ if (g_tst_pvDataVBox != NULL)
+ {
+ void *pv = RTMemDup(g_tst_pvDataVBox, g_tst_cbDataVBox);
+ *ppv = pv;
+ return pv != NULL ? g_tst_rcDataVBox : VERR_NO_MEMORY;
+ }
+ *ppv = NULL;
+ return g_tst_rcDataVBox;
+}
+
+static DECLCALLBACK(int) tstShClOnSendDataToDestCallback(PSHCLCONTEXT pCtx, void *pv, uint32_t cb, void *pvUser)
+{
+ RT_NOREF(pCtx);
+
+ PSHCLX11READDATAREQ pData = (PSHCLX11READDATAREQ)pvUser;
+
+ if (cb <= TESTCASE_MAX_BUF_SIZE)
+ {
+ g_tst_rcCompleted = pData->rcCompletion;
+ if (cb != 0)
+ memcpy(g_tst_abCompletedBuf, pv, cb);
+ }
+ else
+ g_tst_rcCompleted = VERR_BUFFER_OVERFLOW;
+ g_tst_cbCompleted = cb;
+ g_tst_pCompletedReq = pData->pReq;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Looks up the X11 format matching a given X11 atom text.
+ *
+ * @returns the format on success, NIL_CLIPX11FORMAT on failure
+ * @param pcszAtom Atom text to look up format for.
+ */
+static SHCLX11FMTIDX tstClipFindX11FormatByAtomText(const char *pcszAtom)
+{
+ const size_t j = clipReportMaxX11Formats();
+
+ for (unsigned i = 0; i < j; ++i)
+ {
+ if (!strcmp(g_aFormats[i].pcszAtom, pcszAtom))
+ return i;
+ }
+ return NIL_CLIPX11FORMAT;
+}
+
+static bool tstClipTextFormatConversion(PSHCLX11CTX pCtx)
+{
+ bool fSuccess = true;
+ SHCLX11FMTIDX targets[2];
+ SHCLX11FMTIDX x11Format;
+ targets[0] = tstClipFindX11FormatByAtomText("text/plain");
+ targets[1] = tstClipFindX11FormatByAtomText("image/bmp");
+ x11Format = clipGetTextFormatFromTargets(pCtx, targets, 2);
+ if (clipRealFormatForX11Format(x11Format) != SHCLX11FMT_TEXT)
+ fSuccess = false;
+ targets[0] = tstClipFindX11FormatByAtomText("UTF8_STRING");
+ targets[1] = tstClipFindX11FormatByAtomText("text/plain");
+ x11Format = clipGetTextFormatFromTargets(pCtx, targets, 2);
+ if (clipRealFormatForX11Format(x11Format) != SHCLX11FMT_UTF8)
+ fSuccess = false;
+ return fSuccess;
+}
+
+static void tstClipGetCompletedRequest(int *prc, char ** ppc, uint32_t *pcb, CLIPREADCBREQ **ppReq)
+{
+ *prc = g_tst_rcCompleted;
+ *ppc = g_tst_abCompletedBuf;
+ *pcb = g_tst_cbCompleted;
+ *ppReq = g_tst_pCompletedReq;
+}
+
+static void tstStringFromX11(RTTEST hTest, PSHCLX11CTX pCtx,
+ const char *pcszExp, int rcExp)
+{
+ bool retval = true;
+ tstClipSendTargetUpdate(pCtx);
+ if (tstClipQueryFormats() != VBOX_SHCL_FMT_UNICODETEXT)
+ {
+ RTTestFailed(hTest, "Wrong targets reported: %02X\n", tstClipQueryFormats());
+ }
+ else
+ {
+ char *pc;
+ CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)&pReq, *pReqRet = NULL;
+ ShClX11ReadDataFromX11(pCtx, VBOX_SHCL_FMT_UNICODETEXT, pReq);
+ int rc = VINF_SUCCESS;
+ uint32_t cbActual = 0;
+ tstClipGetCompletedRequest(&rc, &pc, &cbActual, &pReqRet);
+ if (rc != rcExp)
+ RTTestFailed(hTest, "Wrong return code, expected %Rrc, got %Rrc\n",
+ rcExp, rc);
+ else if (pReqRet != pReq)
+ RTTestFailed(hTest, "Wrong returned request data, expected %p, got %p\n",
+ pReq, pReqRet);
+ else if (RT_FAILURE(rcExp))
+ retval = true;
+ else
+ {
+ RTUTF16 wcExp[TESTCASE_MAX_BUF_SIZE / 2];
+ RTUTF16 *pwcExp = wcExp;
+ size_t cwc = 0;
+ rc = RTStrToUtf16Ex(pcszExp, RTSTR_MAX, &pwcExp,
+ RT_ELEMENTS(wcExp), &cwc);
+ size_t cbExp = cwc * 2 + 2;
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ if (cbActual != cbExp)
+ {
+ RTTestFailed(hTest, "Returned string is the wrong size, string \"%.*ls\", size %u, expected \"%s\", size %u\n",
+ RT_MIN(TESTCASE_MAX_BUF_SIZE, cbActual), pc, cbActual,
+ pcszExp, cbExp);
+ }
+ else
+ {
+ if (memcmp(pc, wcExp, cbExp) == 0)
+ retval = true;
+ else
+ RTTestFailed(hTest, "Returned string \"%.*ls\" does not match expected string \"%s\"\n",
+ TESTCASE_MAX_BUF_SIZE, pc, pcszExp);
+ }
+ }
+ }
+ }
+ if (!retval)
+ RTTestFailureDetails(hTest, "Expected: string \"%s\", rc %Rrc\n",
+ pcszExp, rcExp);
+}
+
+static void tstLatin1FromX11(RTTEST hTest, PSHCLX11CTX pCtx,
+ const char *pcszExp, int rcExp)
+{
+ bool retval = false;
+ tstClipSendTargetUpdate(pCtx);
+ if (tstClipQueryFormats() != VBOX_SHCL_FMT_UNICODETEXT)
+ RTTestFailed(hTest, "Wrong targets reported: %02X\n",
+ tstClipQueryFormats());
+ else
+ {
+ char *pc;
+ CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)&pReq, *pReqRet = NULL;
+ ShClX11ReadDataFromX11(pCtx, VBOX_SHCL_FMT_UNICODETEXT, pReq);
+ int rc = VINF_SUCCESS;
+ uint32_t cbActual = 0;
+ tstClipGetCompletedRequest(&rc, &pc, &cbActual, &pReqRet);
+ if (rc != rcExp)
+ RTTestFailed(hTest, "Wrong return code, expected %Rrc, got %Rrc\n",
+ rcExp, rc);
+ else if (pReqRet != pReq)
+ RTTestFailed(hTest, "Wrong returned request data, expected %p, got %p\n",
+ pReq, pReqRet);
+ else if (RT_FAILURE(rcExp))
+ retval = true;
+ else
+ {
+ RTUTF16 wcExp[TESTCASE_MAX_BUF_SIZE / 2];
+ //RTUTF16 *pwcExp = wcExp; - unused
+ size_t cwc;
+ for (cwc = 0; cwc == 0 || pcszExp[cwc - 1] != '\0'; ++cwc)
+ wcExp[cwc] = pcszExp[cwc];
+ size_t cbExp = cwc * 2;
+ if (cbActual != cbExp)
+ {
+ RTTestFailed(hTest, "Returned string is the wrong size, string \"%.*ls\", size %u, expected \"%s\", size %u\n",
+ RT_MIN(TESTCASE_MAX_BUF_SIZE, cbActual), pc, cbActual,
+ pcszExp, cbExp);
+ }
+ else
+ {
+ if (memcmp(pc, wcExp, cbExp) == 0)
+ retval = true;
+ else
+ RTTestFailed(hTest, "Returned string \"%.*ls\" does not match expected string \"%s\"\n",
+ TESTCASE_MAX_BUF_SIZE, pc, pcszExp);
+ }
+ }
+ }
+ if (!retval)
+ RTTestFailureDetails(hTest, "Expected: string \"%s\", rc %Rrc\n",
+ pcszExp, rcExp);
+}
+
+static void tstStringFromVBox(RTTEST hTest, PSHCLX11CTX pCtx, const char *pcszTarget, Atom typeExp, const char *valueExp)
+{
+ RT_NOREF(pCtx);
+ bool retval = false;
+ Atom type;
+ XtPointer value = NULL;
+ unsigned long length;
+ int format;
+ size_t lenExp = strlen(valueExp);
+ if (tstClipConvertSelection(pcszTarget, &type, &value, &length, &format))
+ {
+ if ( type != typeExp
+ || length != lenExp
+ || format != 8
+ || memcmp((const void *) value, (const void *)valueExp,
+ lenExp))
+ {
+ RTTestFailed(hTest, "Bad data: type %d, (expected %d), length %u, (expected %u), format %d (expected %d), value \"%.*s\" (expected \"%.*s\")\n",
+ type, typeExp, length, lenExp, format, 8,
+ RT_MIN(length, 20), value, RT_MIN(lenExp, 20), valueExp);
+ }
+ else
+ retval = true;
+ }
+ else
+ RTTestFailed(hTest, "Conversion failed\n");
+ XtFree((char *)value);
+ if (!retval)
+ RTTestFailureDetails(hTest, "Conversion to %s, expected \"%s\"\n",
+ pcszTarget, valueExp);
+}
+
+static void tstNoX11(PSHCLX11CTX pCtx, const char *pcszTestCtx)
+{
+ CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)&pReq;
+ int rc = ShClX11ReadDataFromX11(pCtx, VBOX_SHCL_FMT_UNICODETEXT, pReq);
+ RTTESTI_CHECK_MSG(rc == VERR_NO_DATA, ("context: %s\n", pcszTestCtx));
+}
+
+static void tstStringFromVBoxFailed(RTTEST hTest, PSHCLX11CTX pCtx, const char *pcszTarget)
+{
+ RT_NOREF(pCtx);
+ Atom type;
+ XtPointer value = NULL;
+ unsigned long length;
+ int format;
+ RTTEST_CHECK_MSG(hTest, !tstClipConvertSelection(pcszTarget, &type, &value,
+ &length, &format),
+ (hTest, "Conversion to target %s, should have failed but didn't, returned type %d, length %u, format %d, value \"%.*s\"\n",
+ pcszTarget, type, length, format, RT_MIN(length, 20),
+ value));
+ XtFree((char *)value);
+}
+
+static void tstNoSelectionOwnership(PSHCLX11CTX pCtx, const char *pcszTestCtx)
+{
+ RT_NOREF(pCtx);
+ RTTESTI_CHECK_MSG(!g_tst_fOwnsSel, ("context: %s\n", pcszTestCtx));
+}
+
+static void tstBadFormatRequestFromHost(RTTEST hTest, PSHCLX11CTX pCtx)
+{
+ tstClipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
+ sizeof("hello world"), 8);
+ tstClipSendTargetUpdate(pCtx);
+ if (tstClipQueryFormats() != VBOX_SHCL_FMT_UNICODETEXT)
+ RTTestFailed(hTest, "Wrong targets reported: %02X\n",
+ tstClipQueryFormats());
+ else
+ {
+ char *pc;
+ CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)&pReq, *pReqRet = NULL;
+ ShClX11ReadDataFromX11(pCtx, 0xF000 /* vboxFormat */, pReq); /* Bad format. */
+ int rc = VINF_SUCCESS;
+ uint32_t cbActual = 0;
+ tstClipGetCompletedRequest(&rc, &pc, &cbActual, &pReqRet);
+ if (rc != VERR_NOT_IMPLEMENTED)
+ RTTestFailed(hTest, "Wrong return code, expected VERR_NOT_IMPLEMENTED, got %Rrc\n",
+ rc);
+ tstClipSetSelectionValues("", XA_STRING, "", sizeof(""), 8);
+ tstClipSendTargetUpdate(pCtx);
+ if (tstClipQueryFormats() == VBOX_SHCL_FMT_UNICODETEXT)
+ RTTestFailed(hTest, "Failed to report targets after bad host request.\n");
+ }
+}
+
+int main()
+{
+ /*
+ * Init the runtime, test and say hello.
+ */
+ RTTEST hTest;
+ int rc = RTTestInitAndCreate("tstClipboardGH-X11", &hTest);
+ if (RT_FAILURE(rc))
+ return RTEXITCODE_FAILURE;
+ RTTestBanner(hTest);
+
+ /*
+ * Run the tests.
+ */
+ SHCLCALLBACKS Callbacks;
+ RT_ZERO(Callbacks);
+ Callbacks.pfnReportFormats = tstShClReportFormatsCallback;
+ Callbacks.pfnOnRequestDataFromSource = tstShClOnRequestDataFromSourceCallback;
+ Callbacks.pfnOnSendDataToDest = tstShClOnSendDataToDestCallback;
+
+ SHCLX11CTX X11Ctx;
+ rc = ShClX11Init(&X11Ctx, &Callbacks, NULL /* pParent */, false /* fHeadless */);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+ char *pc;
+ uint32_t cbActual;
+ CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)&pReq, *pReqRet = NULL;
+
+ /* UTF-8 from X11 */
+ RTTestSub(hTest, "reading UTF-8 from X11");
+ /* Simple test */
+ tstClipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
+ sizeof("hello world"), 8);
+ tstStringFromX11(hTest, &X11Ctx, "hello world", VINF_SUCCESS);
+ /* With an embedded carriage return */
+ tstClipSetSelectionValues("text/plain;charset=UTF-8", XA_STRING,
+ "hello\nworld", sizeof("hello\nworld"), 8);
+ tstStringFromX11(hTest, &X11Ctx, "hello\r\nworld", VINF_SUCCESS);
+ /* With an embedded CRLF */
+ tstClipSetSelectionValues("text/plain;charset=UTF-8", XA_STRING,
+ "hello\r\nworld", sizeof("hello\r\nworld"), 8);
+ tstStringFromX11(hTest, &X11Ctx, "hello\r\r\nworld", VINF_SUCCESS);
+ /* With an embedded LFCR */
+ tstClipSetSelectionValues("text/plain;charset=UTF-8", XA_STRING,
+ "hello\n\rworld", sizeof("hello\n\rworld"), 8);
+ tstStringFromX11(hTest, &X11Ctx, "hello\r\n\rworld", VINF_SUCCESS);
+ /* An empty string */
+ tstClipSetSelectionValues("text/plain;charset=utf-8", XA_STRING, "",
+ sizeof(""), 8);
+ tstStringFromX11(hTest, &X11Ctx, "", VINF_SUCCESS);
+ /* With an embedded UTF-8 character. */
+ tstClipSetSelectionValues("STRING", XA_STRING,
+ "100\xE2\x82\xAC" /* 100 Euro */,
+ sizeof("100\xE2\x82\xAC"), 8);
+ tstStringFromX11(hTest, &X11Ctx, "100\xE2\x82\xAC", VINF_SUCCESS);
+ /* A non-zero-terminated string */
+ tstClipSetSelectionValues("TEXT", XA_STRING,
+ "hello world", sizeof("hello world") - 1, 8);
+ tstStringFromX11(hTest, &X11Ctx, "hello world", VINF_SUCCESS);
+
+ /* Latin1 from X11 */
+ RTTestSub(hTest, "reading Latin1 from X11");
+ /* Simple test */
+ tstClipSetSelectionValues("STRING", XA_STRING, "Georges Dupr\xEA",
+ sizeof("Georges Dupr\xEA"), 8);
+ tstLatin1FromX11(hTest, &X11Ctx, "Georges Dupr\xEA", VINF_SUCCESS);
+ /* With an embedded carriage return */
+ tstClipSetSelectionValues("TEXT", XA_STRING, "Georges\nDupr\xEA",
+ sizeof("Georges\nDupr\xEA"), 8);
+ tstLatin1FromX11(hTest, &X11Ctx, "Georges\r\nDupr\xEA", VINF_SUCCESS);
+ /* With an embedded CRLF */
+ tstClipSetSelectionValues("TEXT", XA_STRING, "Georges\r\nDupr\xEA",
+ sizeof("Georges\r\nDupr\xEA"), 8);
+ tstLatin1FromX11(hTest, &X11Ctx, "Georges\r\r\nDupr\xEA", VINF_SUCCESS);
+ /* With an embedded LFCR */
+ tstClipSetSelectionValues("TEXT", XA_STRING, "Georges\n\rDupr\xEA",
+ sizeof("Georges\n\rDupr\xEA"), 8);
+ tstLatin1FromX11(hTest, &X11Ctx, "Georges\r\n\rDupr\xEA", VINF_SUCCESS);
+ /* A non-zero-terminated string */
+ tstClipSetSelectionValues("text/plain", XA_STRING,
+ "Georges Dupr\xEA!",
+ sizeof("Georges Dupr\xEA!") - 1, 8);
+ tstLatin1FromX11(hTest, &X11Ctx, "Georges Dupr\xEA!", VINF_SUCCESS);
+
+ /*
+ * Unknown X11 format
+ */
+ RTTestSub(hTest, "handling of an unknown X11 format");
+ tstClipInvalidateFormats();
+ tstClipSetSelectionValues("CLIPBOARD", XA_STRING, "Test",
+ sizeof("Test"), 8);
+ tstClipSendTargetUpdate(&X11Ctx);
+ RTTEST_CHECK_MSG(hTest, tstClipQueryFormats() == 0,
+ (hTest, "Failed to send a format update notification\n"));
+
+ /*
+ * Timeout from X11
+ */
+ RTTestSub(hTest, "X11 timeout");
+ tstClipSetSelectionValues("UTF8_STRING", XT_CONVERT_FAIL, NULL,0, 8);
+ tstStringFromX11(hTest, &X11Ctx, "", VERR_NO_DATA);
+
+ /*
+ * No data in X11 clipboard
+ */
+ RTTestSub(hTest, "a data request from an empty X11 clipboard");
+ tstClipSetSelectionValues("UTF8_STRING", XA_STRING, NULL,
+ 0, 8);
+ ShClX11ReadDataFromX11(&X11Ctx, VBOX_SHCL_FMT_UNICODETEXT, pReq);
+ tstClipGetCompletedRequest(&rc, &pc, &cbActual, &pReqRet);
+ RTTEST_CHECK_MSG(hTest, rc == VERR_NO_DATA,
+ (hTest, "Returned %Rrc instead of VERR_NO_DATA\n",
+ rc));
+ RTTEST_CHECK_MSG(hTest, pReqRet == pReq,
+ (hTest, "Wrong returned request data, expected %p, got %p\n",
+ pReq, pReqRet));
+
+ /*
+ * Ensure that VBox is notified when we return the CB to X11
+ */
+ RTTestSub(hTest, "notification of switch to X11 clipboard");
+ tstClipInvalidateFormats();
+ clipReportEmpty(&X11Ctx);
+ RTTEST_CHECK_MSG(hTest, tstClipQueryFormats() == 0,
+ (hTest, "Failed to send a format update (release) notification\n"));
+
+ /*
+ * Request for an invalid VBox format from X11
+ */
+ RTTestSub(hTest, "a request for an invalid VBox format from X11");
+ /* Testing for 0xffff will go into handling VBOX_SHCL_FMT_UNICODETEXT, where we don't have
+ * have any data at the moment so far, so this will return VERR_NO_DATA. */
+ ShClX11ReadDataFromX11(&X11Ctx, 0xffff /* vboxFormat */, pReq);
+ tstClipGetCompletedRequest(&rc, &pc, &cbActual, &pReqRet);
+ RTTEST_CHECK_MSG(hTest, rc == VERR_NO_DATA,
+ (hTest, "Returned %Rrc instead of VERR_NO_DATA\n",
+ rc));
+ RTTEST_CHECK_MSG(hTest, pReqRet == pReq,
+ (hTest, "Wrong returned request data, expected %p, got %p\n",
+ pReq, pReqRet));
+
+ /*
+ * Targets failure from X11
+ */
+ RTTestSub(hTest, "X11 targets conversion failure");
+ tstClipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
+ sizeof("hello world"), 8);
+ tstClipSetTargetsFailure();
+ Atom atom = XA_STRING;
+ long unsigned int cLen = 0;
+ int format = 8;
+ clipQueryX11TargetsCallback(NULL, (XtPointer) &X11Ctx, NULL, &atom, NULL, &cLen,
+ &format);
+ RTTEST_CHECK_MSG(hTest, tstClipQueryFormats() == 0,
+ (hTest, "Wrong targets reported: %02X\n",
+ tstClipQueryFormats()));
+
+ /*
+ * X11 text format conversion
+ */
+ RTTestSub(hTest, "handling of X11 selection targets");
+ RTTEST_CHECK_MSG(hTest, tstClipTextFormatConversion(&X11Ctx),
+ (hTest, "failed to select the right X11 text formats\n"));
+ /*
+ * UTF-8 from VBox
+ */
+ RTTestSub(hTest, "reading UTF-8 from VBox");
+ /* Simple test */
+ tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello world",
+ sizeof("hello world") * 2);
+ tstStringFromVBox(hTest, &X11Ctx, "UTF8_STRING",
+ clipGetAtom(&X11Ctx, "UTF8_STRING"), "hello world");
+ /* With an embedded carriage return */
+ tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello\r\nworld",
+ sizeof("hello\r\nworld") * 2);
+ tstStringFromVBox(hTest, &X11Ctx, "text/plain;charset=UTF-8",
+ clipGetAtom(&X11Ctx, "text/plain;charset=UTF-8"),
+ "hello\nworld");
+ /* With an embedded CRCRLF */
+ tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello\r\r\nworld",
+ sizeof("hello\r\r\nworld") * 2);
+ tstStringFromVBox(hTest, &X11Ctx, "text/plain;charset=UTF-8",
+ clipGetAtom(&X11Ctx, "text/plain;charset=UTF-8"),
+ "hello\r\nworld");
+ /* With an embedded CRLFCR */
+ tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello\r\n\rworld",
+ sizeof("hello\r\n\rworld") * 2);
+ tstStringFromVBox(hTest, &X11Ctx, "text/plain;charset=UTF-8",
+ clipGetAtom(&X11Ctx, "text/plain;charset=UTF-8"),
+ "hello\n\rworld");
+ /* An empty string */
+ tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "", 2);
+ tstStringFromVBox(hTest, &X11Ctx, "text/plain;charset=utf-8",
+ clipGetAtom(&X11Ctx, "text/plain;charset=utf-8"), "");
+ /* With an embedded UTF-8 character. */
+ tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "100\xE2\x82\xAC" /* 100 Euro */,
+ 10);
+ tstStringFromVBox(hTest, &X11Ctx, "STRING",
+ clipGetAtom(&X11Ctx, "STRING"), "100\xE2\x82\xAC");
+ /* A non-zero-terminated string */
+ tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello world",
+ sizeof("hello world") * 2 - 2);
+ tstStringFromVBox(hTest, &X11Ctx, "TEXT", clipGetAtom(&X11Ctx, "TEXT"),
+ "hello world");
+
+ /*
+ * Timeout from VBox
+ */
+ RTTestSub(hTest, "reading from VBox with timeout");
+ tstClipEmptyVBox(&X11Ctx, VERR_TIMEOUT);
+ tstStringFromVBoxFailed(hTest, &X11Ctx, "UTF8_STRING");
+
+ /*
+ * No data in VBox clipboard
+ */
+ RTTestSub(hTest, "an empty VBox clipboard");
+ tstClipSetSelectionValues("TEXT", XA_STRING, "", sizeof(""), 8);
+ tstClipEmptyVBox(&X11Ctx, VINF_SUCCESS);
+ RTTEST_CHECK_MSG(hTest, g_tst_fOwnsSel,
+ (hTest, "VBox grabbed the clipboard with no data and we ignored it\n"));
+ tstStringFromVBoxFailed(hTest, &X11Ctx, "UTF8_STRING");
+
+ /*
+ * An unknown VBox format
+ */
+ RTTestSub(hTest, "reading an unknown VBox format");
+ tstClipSetSelectionValues("TEXT", XA_STRING, "", sizeof(""), 8);
+ tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "", 2);
+ ShClX11ReportFormatsToX11(&X11Ctx, 0xa0000);
+ RTTEST_CHECK_MSG(hTest, g_tst_fOwnsSel,
+ (hTest, "VBox grabbed the clipboard with unknown data and we ignored it\n"));
+ tstStringFromVBoxFailed(hTest, &X11Ctx, "UTF8_STRING");
+
+ /*
+ * VBox requests a bad format
+ */
+ RTTestSub(hTest, "recovery from a bad format request");
+ tstBadFormatRequestFromHost(hTest, &X11Ctx);
+
+ ShClX11Destroy(&X11Ctx);
+
+ /*
+ * Headless clipboard tests
+ */
+ rc = ShClX11Init(&X11Ctx, &Callbacks, NULL /* pParent */, true /* fHeadless */);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+ /* Read from X11 */
+ RTTestSub(hTest, "reading from X11, headless clipboard");
+ /* Simple test */
+ tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "",
+ sizeof("") * 2);
+ tstClipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
+ sizeof("hello world"), 8);
+ tstNoX11(&X11Ctx, "reading from X11, headless clipboard");
+
+ /* Read from VBox */
+ RTTestSub(hTest, "reading from VBox, headless clipboard");
+ /* Simple test */
+ tstClipEmptyVBox(&X11Ctx, VERR_WRONG_ORDER);
+ tstClipSetSelectionValues("TEXT", XA_STRING, "", sizeof(""), 8);
+ tstClipSetVBoxUtf16(&X11Ctx, VINF_SUCCESS, "hello world",
+ sizeof("hello world") * 2);
+ tstNoSelectionOwnership(&X11Ctx, "reading from VBox, headless clipboard");
+
+ ShClX11Destroy(&X11Ctx);
+
+ return RTTestSummaryAndDestroy(hTest);
+}
+
diff --git a/src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardGH-X11Smoke.cpp b/src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardGH-X11Smoke.cpp
new file mode 100644
index 00000000..226c6fac
--- /dev/null
+++ b/src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardGH-X11Smoke.cpp
@@ -0,0 +1,103 @@
+/* $Id: tstClipboardGH-X11Smoke.cpp $ */
+/** @file
+ * Shared Clipboard guest/host X11 code smoke tests.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* This is a simple test case that just starts a copy of the X11 clipboard
+ * backend, checks the X11 clipboard and exits. If ever needed I will add an
+ * interactive mode in which the user can read and copy to the clipboard from
+ * the command line. */
+
+#include <iprt/assert.h>
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/test.h>
+
+#include <VBox/GuestHost/SharedClipboard.h>
+#include <VBox/GuestHost/SharedClipboard-x11.h>
+#include <VBox/GuestHost/clipboard-helper.h>
+
+
+static DECLCALLBACK(int) tstShClReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
+{
+ RT_NOREF(pCtx, fFormats, pvUser);
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) tstShClOnRequestDataFromSourceCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
+{
+ RT_NOREF(pCtx, uFmt, ppv, pcb, pvUser);
+ return VERR_NO_DATA;
+}
+
+static DECLCALLBACK(int) tstShClOnSendDataToDest(PSHCLCONTEXT pCtx, void *pv, uint32_t cb, void *pvUser)
+{
+ RT_NOREF(pCtx, pv, cb, pvUser);
+ return VINF_SUCCESS;
+}
+
+int main()
+{
+ /*
+ * Init the runtime, test and say hello.
+ */
+ RTTEST hTest;
+ int rc = RTTestInitAndCreate("tstClipboardGH-X11Smoke", &hTest);
+ if (rc)
+ return rc;
+ RTTestBanner(hTest);
+
+ /*
+ * Run the test.
+ */
+ rc = VINF_SUCCESS;
+ /* We can't test anything without an X session, so just return success
+ * in that case. */
+ if (!RTEnvExist("DISPLAY"))
+ {
+ RTTestPrintf(hTest, RTTESTLVL_INFO,
+ "X11 not available, not running test\n");
+ return RTTestSummaryAndDestroy(hTest);
+ }
+
+ SHCLCALLBACKS Callbacks;
+ RT_ZERO(Callbacks);
+ Callbacks.pfnReportFormats = tstShClReportFormatsCallback;
+ Callbacks.pfnOnRequestDataFromSource = tstShClOnRequestDataFromSourceCallback;
+ Callbacks.pfnOnSendDataToDest = tstShClOnSendDataToDest;
+
+ SHCLX11CTX X11Ctx;
+ rc = ShClX11Init(&X11Ctx, &Callbacks, NULL /* pParent */, false);
+ AssertRCReturn(rc, 1);
+ rc = ShClX11ThreadStart(&X11Ctx, false /* fGrab */);
+ AssertRCReturn(rc, 1);
+ /* Give the clipboard time to synchronise. */
+ RTThreadSleep(500);
+ rc = ShClX11ThreadStop(&X11Ctx);
+ AssertRCReturn(rc, 1);
+ ShClX11Destroy(&X11Ctx);
+ return RTTestSummaryAndDestroy(hTest);
+}
+
diff --git a/src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardQt/Makefile.kmk b/src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardQt/Makefile.kmk
new file mode 100644
index 00000000..bd9401a3
--- /dev/null
+++ b/src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardQt/Makefile.kmk
@@ -0,0 +1,52 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for tstClipboardQt.
+#
+
+#
+# Copyright (C) 2011-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+SUB_DEPTH = ../../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Targets and units.
+#
+USES += qt5
+PROGRAMS += tstClipboardQt
+
+#
+# tstClipboardQt
+#
+tstClipboardQt_TEMPLATE = VBoxQtGuiExe
+tstClipboardQt_NAME = tstClipboardQt
+tstClipboardQt_CXXFLAGS = \
+ $(VBOX_GCC_Wno-implicit-fallthrough) \
+ $(VBOX_GCC_Wno-deprecated-declarations)
+
+tstClipboardQt_SOURCES = \
+ tstClipboardQt.cpp
+
+# The Qt modules we're using.
+tstClipboardQt_QT_MODULES = Core Gui Widgets PrintSupport
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardQt/tstClipboardQt.cpp b/src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardQt/tstClipboardQt.cpp
new file mode 100644
index 00000000..ebf3e8fb
--- /dev/null
+++ b/src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardQt/tstClipboardQt.cpp
@@ -0,0 +1,47 @@
+/* $Id: tstClipboardQt.cpp $ */
+/** @file
+ * Shared Clipboard guest/host Qt code test cases.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include <QApplication>
+#include <QMainWindow>
+#include <QTextEdit>
+#include <QHBoxLayout>
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ QMainWindow window;
+ QWidget *pWidget = new QWidget;
+ window.setCentralWidget(pWidget);
+ QHBoxLayout *pLayout = new QHBoxLayout(pWidget);
+
+ QTextEdit *pTextEdit = new QTextEdit;
+ pLayout->addWidget(pTextEdit);
+
+
+ window.show();
+
+ app.exec();
+}