summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/x11/VBoxClient/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/Additions/x11/VBoxClient/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/Additions/x11/VBoxClient/testcase')
-rw-r--r--src/VBox/Additions/x11/VBoxClient/testcase/Makefile.kup0
-rw-r--r--src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11-auto.cpp791
-rw-r--r--src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11.cpp185
3 files changed, 976 insertions, 0 deletions
diff --git a/src/VBox/Additions/x11/VBoxClient/testcase/Makefile.kup b/src/VBox/Additions/x11/VBoxClient/testcase/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Additions/x11/VBoxClient/testcase/Makefile.kup
diff --git a/src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11-auto.cpp b/src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11-auto.cpp
new file mode 100644
index 00000000..0a90684d
--- /dev/null
+++ b/src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11-auto.cpp
@@ -0,0 +1,791 @@
+/* $Id: tstSeamlessX11-auto.cpp $ */
+/** @file
+ * Automated test of the X11 seamless Additions code.
+ * @todo Better separate test data from implementation details!
+ */
+
+/*
+ * Copyright (C) 2007-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 <stdlib.h> /* exit() */
+
+#include <X11/Xatom.h>
+#include <X11/Xmu/WinUtil.h>
+
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/semaphore.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+
+#include "../seamless.h"
+
+#undef DefaultRootWindow
+
+/******************************************************
+* Mock X11 functions needed by the seamless X11 class *
+******************************************************/
+
+int XFree(void *data)
+{
+ RTMemFree(data);
+ return 0;
+}
+
+#define TEST_DISPLAY ((Display *)0xffff)
+#define TEST_ROOT ((Window)1)
+
+void VBClLogError(const char *pszFormat, ...)
+{
+ va_list args;
+ va_start(args, pszFormat);
+ char *psz = NULL;
+ RTStrAPrintfV(&psz, pszFormat, args);
+ va_end(args);
+
+ AssertPtr(psz);
+ RTPrintf("Error: %s", psz);
+
+ RTStrFree(psz);
+}
+
+/** Exit with a fatal error. */
+void VBClLogFatalError(const char *pszFormat, ...)
+{
+ va_list args;
+ va_start(args, pszFormat);
+ char *psz = NULL;
+ RTStrAPrintfV(&psz, pszFormat, args);
+ va_end(args);
+
+ AssertPtr(psz);
+ RTPrintf("Fatal error: %s", psz);
+
+ RTStrFree(psz);
+
+ exit(1);
+}
+
+extern "C" Display *XOpenDisplay(const char *display_name);
+Display *XOpenDisplay(const char *display_name)
+{
+ RT_NOREF1(display_name);
+ return TEST_DISPLAY;
+}
+
+extern "C" int XCloseDisplay(Display *display);
+int XCloseDisplay(Display *display)
+{
+ RT_NOREF1(display);
+ Assert(display == TEST_DISPLAY);
+ return 0;
+}
+
+enum
+{
+ ATOM_PROP = 1,
+ ATOM_DESKTOP_PROP
+};
+
+extern "C" Atom XInternAtom(Display *display, const char *atom_name, Bool only_if_exists);
+Atom XInternAtom(Display *display, const char *atom_name, Bool only_if_exists)
+{
+ RT_NOREF2(only_if_exists, display);
+ Assert(display == TEST_DISPLAY);
+ if (!RTStrCmp(atom_name, WM_TYPE_PROP))
+ return (Atom) ATOM_PROP;
+ if (!RTStrCmp(atom_name, WM_TYPE_DESKTOP_PROP))
+ return (Atom) ATOM_DESKTOP_PROP;
+ AssertFailed();
+ return (Atom)0;
+}
+
+/** The window (if any) on which the WM_TYPE_PROP property is set to the
+ * WM_TYPE_DESKTOP_PROP atom. */
+static Window g_hSmlsDesktopWindow = 0;
+
+extern "C" int XGetWindowProperty(Display *display, Window w, Atom property,
+ long long_offset, long long_length,
+ Bool delProp, Atom req_type,
+ Atom *actual_type_return,
+ int *actual_format_return,
+ unsigned long *nitems_return,
+ unsigned long *bytes_after_return,
+ unsigned char **prop_return);
+int XGetWindowProperty(Display *display, Window w, Atom property,
+ long long_offset, long long_length, Bool delProp,
+ Atom req_type, Atom *actual_type_return,
+ int *actual_format_return,
+ unsigned long *nitems_return,
+ unsigned long *bytes_after_return,
+ unsigned char **prop_return)
+{
+ RT_NOREF2(display, long_length);
+ Assert(display == TEST_DISPLAY);
+ Atom atomType = XInternAtom (display, WM_TYPE_PROP, true);
+ Atom atomTypeDesktop = XInternAtom (display, WM_TYPE_DESKTOP_PROP, true);
+ /* We only handle things we expect. */
+ AssertReturn((req_type == XA_ATOM) || (req_type == AnyPropertyType),
+ 0xffff);
+ AssertReturn(property == atomType, 0xffff);
+ *actual_type_return = XA_ATOM;
+ *actual_format_return = sizeof(Atom) * 8;
+ *nitems_return = 0;
+ *bytes_after_return = sizeof(Atom);
+ *prop_return = NULL;
+ if ((w != g_hSmlsDesktopWindow) || (g_hSmlsDesktopWindow == 0))
+ return Success;
+ AssertReturn(long_offset == 0, 0);
+ AssertReturn(delProp == false, 0);
+ unsigned char *pProp;
+ pProp = (unsigned char *)RTMemDup(&atomTypeDesktop,
+ sizeof(atomTypeDesktop));
+ AssertReturn(pProp, 0xffff);
+ *nitems_return = 1;
+ *prop_return = pProp;
+ *bytes_after_return = 0;
+ return 0;
+}
+
+#if 0 /* unused */
+/** Sets the current set of properties for all mock X11 windows */
+static void smlsSetDesktopWindow(Window hWin)
+{
+ g_hSmlsDesktopWindow = hWin;
+}
+#endif
+
+extern "C" Bool XShapeQueryExtension(Display *dpy, int *event_basep, int *error_basep);
+Bool XShapeQueryExtension(Display *dpy, int *event_basep, int *error_basep)
+{
+ RT_NOREF3(dpy, event_basep, error_basep);
+ Assert(dpy == TEST_DISPLAY);
+ return true;
+}
+
+/* We silently ignore this for now. */
+extern "C" int XSelectInput(Display *display, Window w, long event_mask);
+int XSelectInput(Display *display, Window w, long event_mask)
+{
+ RT_NOREF3(display, w, event_mask);
+ Assert(display == TEST_DISPLAY);
+ return 0;
+}
+
+/* We silently ignore this for now. */
+extern "C" void XShapeSelectInput(Display *display, Window w, unsigned long event_mask);
+void XShapeSelectInput(Display *display, Window w, unsigned long event_mask)
+{
+ RT_NOREF3(display, w, event_mask);
+ Assert(display == TEST_DISPLAY);
+}
+
+extern "C" Window XDefaultRootWindow(Display *display);
+Window XDefaultRootWindow(Display *display)
+{
+ RT_NOREF1(display);
+ Assert(display == TEST_DISPLAY);
+ return TEST_ROOT;
+}
+
+static unsigned g_cSmlsWindows = 0;
+static Window *g_paSmlsWindows = NULL;
+static XWindowAttributes *g_paSmlsWinAttribs = NULL;
+static const char **g_papszSmlsWinNames = NULL;
+
+extern "C" Status XQueryTree(Display *display, Window w, Window *root_return,
+ Window *parent_return, Window **children_return,
+ unsigned int *nchildren_return);
+Status XQueryTree(Display *display, Window w, Window *root_return,
+ Window *parent_return, Window **children_return,
+ unsigned int *nchildren_return)
+{
+ RT_NOREF1(display);
+ Assert(display == TEST_DISPLAY);
+ AssertReturn(w == TEST_ROOT, False); /* We support nothing else */
+ AssertPtrReturn(children_return, False);
+ AssertReturn(g_paSmlsWindows, False);
+ if (root_return)
+ *root_return = TEST_ROOT;
+ if (parent_return)
+ *parent_return = TEST_ROOT;
+ *children_return = (Window *)RTMemDup(g_paSmlsWindows,
+ g_cSmlsWindows * sizeof(Window));
+ if (nchildren_return)
+ *nchildren_return = g_cSmlsWindows;
+ return (g_cSmlsWindows != 0);
+}
+
+extern "C" Window XmuClientWindow(Display *dpy, Window win);
+Window XmuClientWindow(Display *dpy, Window win)
+{
+ RT_NOREF1(dpy);
+ Assert(dpy == TEST_DISPLAY);
+ return win;
+}
+
+extern "C" Status XGetWindowAttributes(Display *display, Window w,
+ XWindowAttributes *window_attributes_return);
+Status XGetWindowAttributes(Display *display, Window w,
+ XWindowAttributes *window_attributes_return)
+{
+ RT_NOREF1(display);
+ Assert(display == TEST_DISPLAY);
+ AssertPtrReturn(window_attributes_return, 1);
+ for (unsigned i = 0; i < g_cSmlsWindows; ++i)
+ if (g_paSmlsWindows[i] == w)
+ {
+ *window_attributes_return = g_paSmlsWinAttribs[i];
+ return 1;
+ }
+ return 0;
+}
+
+extern "C" Status XGetWMNormalHints(Display *display, Window w,
+ XSizeHints *hints_return,
+ long *supplied_return);
+
+Status XGetWMNormalHints(Display *display, Window w,
+ XSizeHints *hints_return, long *supplied_return)
+{
+ RT_NOREF4(display, w, hints_return, supplied_return);
+ Assert(display == TEST_DISPLAY);
+ return 1;
+}
+
+static void smlsSetWindowAttributes(XWindowAttributes *pAttribs,
+ Window *pWindows, unsigned cAttribs,
+ const char **paNames)
+{
+ g_paSmlsWinAttribs = pAttribs;
+ g_paSmlsWindows = pWindows;
+ g_cSmlsWindows = cAttribs;
+ g_papszSmlsWinNames = paNames;
+}
+
+static Window g_SmlsShapedWindow = 0;
+static int g_cSmlsShapeRectangles = 0;
+static XRectangle *g_pSmlsShapeRectangles = NULL;
+
+extern "C" XRectangle *XShapeGetRectangles (Display *dpy, Window window,
+ int kind, int *count,
+ int *ordering);
+XRectangle *XShapeGetRectangles (Display *dpy, Window window, int kind,
+ int *count, int *ordering)
+{
+ RT_NOREF2(dpy, kind);
+ Assert(dpy == TEST_DISPLAY);
+ if ((window != g_SmlsShapedWindow) || (window == 0))
+ return NULL; /* Probably not correct, but works for us. */
+ *count = g_cSmlsShapeRectangles;
+ *ordering = 0;
+ return (XRectangle *)RTMemDup(g_pSmlsShapeRectangles,
+ sizeof(XRectangle)
+ * g_cSmlsShapeRectangles);
+}
+
+static void smlsSetShapeRectangles(Window window, int cRects,
+ XRectangle *pRects)
+{
+ g_SmlsShapedWindow = window;
+ g_cSmlsShapeRectangles = cRects;
+ g_pSmlsShapeRectangles = pRects;
+}
+
+static int g_SmlsEventType = 0;
+static Window g_SmlsEventWindow = 0;
+
+/* This should not be needed in the bits of the code we test. */
+extern "C" int XNextEvent(Display *display, XEvent *event_return);
+int XNextEvent(Display *display, XEvent *event_return)
+{
+ RT_NOREF1(display);
+ Assert(display == TEST_DISPLAY);
+ event_return->xany.type = g_SmlsEventType;
+ event_return->xany.window = g_SmlsEventWindow;
+ event_return->xmap.window = g_SmlsEventWindow;
+ return True;
+}
+
+/* Mock XPending(): this also should not be needed. Just in case, always
+ * return that at least one event is pending to be processed. */
+extern "C" int XPending(Display *display);
+int XPending(Display *display)
+{
+ RT_NOREF1(display);
+ return 1;
+}
+
+static void smlsSetNextEvent(int type, Window window)
+{
+ g_SmlsEventType = type;
+ g_SmlsEventWindow = window;
+}
+
+/* This should not be needed in the bits of the code we test. */
+extern "C" Status XSendEvent(Display *display, Window w, Bool propagate,
+ long event_mask, XEvent *event_send);
+Status XSendEvent(Display *display, Window w, Bool propagate,
+ long event_mask, XEvent *event_send)
+{
+ RT_NOREF5(display, w, propagate, event_mask, event_send);
+ Assert(display == TEST_DISPLAY);
+ AssertFailedReturn(0);
+}
+
+/* This should not be needed in the bits of the code we test. */
+extern "C" int XFlush(Display *display);
+int XFlush(Display *display)
+{
+ RT_NOREF1(display);
+ Assert(display == TEST_DISPLAY);
+ AssertFailedReturn(0);
+}
+
+/** Global "received a notification" flag. */
+static bool g_fNotified = false;
+
+/** Dummy host call-back. */
+static void sendRegionUpdate(RTRECT *pRects, size_t cRects)
+{
+ RT_NOREF2(pRects, cRects);
+ g_fNotified = true;
+}
+
+static bool gotNotification(void)
+{
+ if (!g_fNotified)
+ return false;
+ g_fNotified = false;
+ return true;
+}
+
+/*****************************
+* The actual tests to be run *
+*****************************/
+
+/** The name of the unit test */
+static const char *g_pszTestName = NULL;
+
+/*** Test fixture data and data structures ***/
+
+/** A structure describing a test fixture to be run through. Each fixture
+ * describes the state of the windows visible (and unmapped) on the X server
+ * before and after a particular event is delivered, and the expected
+ * on-screen positions of all interesting visible windows at the end of the
+ * fixture as reported by the code (currently in the order it is likely to
+ * report them in, @todo sort this). We expect that the set of visible
+ * windows will be the same whether we start the code before the event and
+ * handle it or start the code after the event.
+ */
+struct SMLSFIXTURE
+{
+ /** The number of windows visible before the event */
+ unsigned cWindowsBefore;
+ /** An array of Window IDs for the visible and unmapped windows before
+ * the event */
+ Window *pahWindowsBefore;
+ /** The window attributes matching the windows in @a paWindowsBefore */
+ XWindowAttributes *paAttribsBefore;
+ /** The window names matching the windows in @a paWindowsBefore */
+ const char **papszNamesBefore;
+ /** The shaped window before the event - we allow at most one of these.
+ * Zero for none. */
+ Window hShapeWindowBefore;
+ /** The number of rectangles in the shaped window before the event. */
+ int cShapeRectsBefore;
+ /** The rectangles in the shaped window before the event */
+ XRectangle *paShapeRectsBefore;
+ /** The number of windows visible after the event */
+ unsigned cWindowsAfter;
+ /** An array of Window IDs for the visible and unmapped windows after
+ * the event */
+ Window *pahWindowsAfter;
+ /** The window attributes matching the windows in @a paWindowsAfter */
+ XWindowAttributes *paAttribsAfter;
+ /** The window names matching the windows in @a paWindowsAfter */
+ const char **papszNamesAfter;
+ /** The shaped window after the event - we allow at most one of these.
+ * Zero for none. */
+ Window hShapeWindowAfter;
+ /** The number of rectangles in the shaped window after the event. */
+ int cShapeRectsAfter;
+ /** The rectangles in the shaped window after the event */
+ XRectangle *paShapeRectsAfter;
+ /** The event to delivered */
+ int x11EventType;
+ /** The window for which the event in @enmEvent is delivered */
+ Window hEventWindow;
+ /** The number of windows expected to be reported at the end of the
+ * fixture */
+ unsigned cReportedRects;
+ /** The onscreen positions of those windows. */
+ RTRECT *paReportedRects;
+ /** Do we expect notification after the event? */
+ bool fExpectNotification;
+};
+
+/*** Test fixture to test the code against X11 configure (move) events ***/
+
+static Window g_ahWin1[] = { 20 };
+static XWindowAttributes g_aAttrib1Before[] =
+{ { 100, 200, 200, 300, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, IsViewable }
+};
+static XRectangle g_aRectangle1[] =
+{
+ { 0, 0, 50, 50 },
+ { 50, 50, 150, 250 }
+};
+static XWindowAttributes g_aAttrib1After[] =
+{ { 200, 300, 200, 300, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, IsViewable }
+};
+static const char *g_apszNames1[] = { "Test Window" };
+
+AssertCompile(RT_ELEMENTS(g_ahWin1) == RT_ELEMENTS(g_aAttrib1Before));
+AssertCompile(RT_ELEMENTS(g_ahWin1) == RT_ELEMENTS(g_aAttrib1After));
+AssertCompile(RT_ELEMENTS(g_ahWin1) == RT_ELEMENTS(g_apszNames1));
+
+static RTRECT g_aRects1[] =
+{
+ { 200, 300, 250, 350 },
+ { 250, 350, 400, 600 }
+};
+
+static SMLSFIXTURE g_testMove =
+{
+ RT_ELEMENTS(g_ahWin1),
+ g_ahWin1,
+ g_aAttrib1Before,
+ g_apszNames1,
+ 20,
+ RT_ELEMENTS(g_aRectangle1),
+ g_aRectangle1,
+ RT_ELEMENTS(g_ahWin1),
+ g_ahWin1,
+ g_aAttrib1After,
+ g_apszNames1,
+ 20,
+ RT_ELEMENTS(g_aRectangle1),
+ g_aRectangle1,
+ ConfigureNotify,
+ 20,
+ RT_ELEMENTS(g_aRects1),
+ g_aRects1,
+ true
+};
+
+/*** Test fixture to test the code against X11 configure (resize) events ***/
+
+static XWindowAttributes g_aAttrib2Before[] =
+{ { 100, 200, 200, 300, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, IsViewable }
+};
+static XRectangle g_aRectangle2Before[] =
+{
+ { 0, 0, 50, 50 },
+ { 50, 50, 100, 100 }
+};
+
+AssertCompile(RT_ELEMENTS(g_ahWin1) == RT_ELEMENTS(g_aAttrib2Before));
+
+static SMLSFIXTURE g_testResize =
+{
+ RT_ELEMENTS(g_ahWin1),
+ g_ahWin1,
+ g_aAttrib2Before,
+ g_apszNames1,
+ 20,
+ RT_ELEMENTS(g_aRectangle2Before),
+ g_aRectangle2Before,
+ RT_ELEMENTS(g_ahWin1),
+ g_ahWin1,
+ g_aAttrib1After,
+ g_apszNames1,
+ 20,
+ RT_ELEMENTS(g_aRectangle1),
+ g_aRectangle1,
+ ConfigureNotify,
+ 20,
+ RT_ELEMENTS(g_aRects1),
+ g_aRects1,
+ true
+};
+
+/*** Test fixture to test the code against X11 map events ***/
+
+static XWindowAttributes g_aAttrib3Before[] =
+{ { 200, 300, 200, 300, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, IsUnmapped }
+};
+
+AssertCompile(RT_ELEMENTS(g_ahWin1) == RT_ELEMENTS(g_aAttrib3Before));
+
+static SMLSFIXTURE g_testMap =
+{
+ RT_ELEMENTS(g_ahWin1),
+ g_ahWin1,
+ g_aAttrib3Before,
+ g_apszNames1,
+ 20,
+ RT_ELEMENTS(g_aRectangle1),
+ g_aRectangle1,
+ RT_ELEMENTS(g_ahWin1),
+ g_ahWin1,
+ g_aAttrib1After,
+ g_apszNames1,
+ 20,
+ RT_ELEMENTS(g_aRectangle1),
+ g_aRectangle1,
+ MapNotify,
+ 20,
+ RT_ELEMENTS(g_aRects1),
+ g_aRects1,
+ true
+};
+
+/*** Test fixtures to test the code against X11 unmap events ***/
+
+static XWindowAttributes g_aAttrib4After[] =
+{ { 100, 200, 300, 400, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, IsUnmapped }
+};
+
+AssertCompile(RT_ELEMENTS(g_ahWin1) == RT_ELEMENTS(g_aAttrib4After));
+
+static SMLSFIXTURE g_testUnmap =
+{
+ RT_ELEMENTS(g_ahWin1),
+ g_ahWin1,
+ g_aAttrib1Before,
+ g_apszNames1,
+ 20,
+ RT_ELEMENTS(g_aRectangle1),
+ g_aRectangle1,
+ RT_ELEMENTS(g_ahWin1),
+ g_ahWin1,
+ g_aAttrib4After,
+ g_apszNames1,
+ 20,
+ RT_ELEMENTS(g_aRectangle1),
+ g_aRectangle1,
+ UnmapNotify,
+ 20,
+ 0,
+ NULL,
+ true
+};
+
+/*** A window we are not monitoring has been unmapped. Nothing should
+ *** happen, especially nothing bad. ***/
+
+static RTRECT g_aRects2[] =
+{
+ { 100, 200, 150, 250 },
+ { 150, 250, 300, 500 }
+};
+
+static SMLSFIXTURE g_testUnmapOther =
+{
+ RT_ELEMENTS(g_ahWin1),
+ g_ahWin1,
+ g_aAttrib1Before,
+ g_apszNames1,
+ 20,
+ RT_ELEMENTS(g_aRectangle1),
+ g_aRectangle1,
+ RT_ELEMENTS(g_ahWin1),
+ g_ahWin1,
+ g_aAttrib1Before,
+ g_apszNames1,
+ 20,
+ RT_ELEMENTS(g_aRectangle1),
+ g_aRectangle1,
+ UnmapNotify,
+ 21,
+ RT_ELEMENTS(g_aRects2),
+ g_aRects2,
+ false
+};
+
+/*** Test fixture to test the code against X11 shape events ***/
+
+static XRectangle g_aRectangle5Before[] =
+{
+ { 0, 0, 200, 200 }
+};
+
+static SMLSFIXTURE g_testShape =
+{
+ RT_ELEMENTS(g_ahWin1),
+ g_ahWin1,
+ g_aAttrib1After,
+ g_apszNames1,
+ 20,
+ RT_ELEMENTS(g_aRectangle5Before),
+ g_aRectangle5Before,
+ RT_ELEMENTS(g_ahWin1),
+ g_ahWin1,
+ g_aAttrib1After,
+ g_apszNames1,
+ 20,
+ RT_ELEMENTS(g_aRectangle1),
+ g_aRectangle1,
+ VBoxShapeNotify,
+ 20,
+ RT_ELEMENTS(g_aRects1),
+ g_aRects1,
+ true
+};
+
+/*** And the test code proper ***/
+
+/** Compare two RTRECT structures */
+static bool smlsCompRect(RTRECT *pFirst, RTRECT *pSecond)
+{
+ return ( (pFirst->xLeft == pSecond->xLeft)
+ && (pFirst->yTop == pSecond->yTop)
+ && (pFirst->xRight == pSecond->xRight)
+ && (pFirst->yBottom == pSecond->yBottom));
+}
+
+static void smlsPrintDiffRects(RTRECT *pExp, RTRECT *pGot)
+{
+ RTPrintf(" Expected: %d, %d, %d, %d. Got: %d, %d, %d, %d\n",
+ pExp->xLeft, pExp->yTop, pExp->xRight, pExp->yBottom,
+ pGot->xLeft, pGot->yTop, pGot->xRight, pGot->yBottom);
+}
+
+/** Run through a test fixture */
+static unsigned smlsDoFixture(SMLSFIXTURE *pFixture, const char *pszDesc)
+{
+ SeamlessX11 subject;
+ unsigned cErrs = 0;
+
+ subject.init(sendRegionUpdate);
+ smlsSetWindowAttributes(pFixture->paAttribsBefore,
+ pFixture->pahWindowsBefore,
+ pFixture->cWindowsBefore,
+ pFixture->papszNamesBefore);
+ smlsSetShapeRectangles(pFixture->hShapeWindowBefore,
+ pFixture->cShapeRectsBefore,
+ pFixture->paShapeRectsBefore);
+ subject.start();
+ smlsSetWindowAttributes(pFixture->paAttribsAfter,
+ pFixture->pahWindowsAfter,
+ pFixture->cWindowsAfter,
+ pFixture->papszNamesAfter);
+ smlsSetShapeRectangles(pFixture->hShapeWindowAfter,
+ pFixture->cShapeRectsAfter,
+ pFixture->paShapeRectsAfter);
+ smlsSetNextEvent(pFixture->x11EventType, pFixture->hEventWindow);
+ if (gotNotification()) /* Initial window tree rebuild */
+ {
+ RTPrintf("%s: fixture: %s. Notification was set before the first event!!!\n",
+ g_pszTestName, pszDesc);
+ ++cErrs;
+ }
+ subject.nextConfigurationEvent();
+ if (!gotNotification())
+ {
+ RTPrintf("%s: fixture: %s. No notification was sent for the initial window tree rebuild.\n",
+ g_pszTestName, pszDesc);
+ ++cErrs;
+ }
+ smlsSetNextEvent(0, 0);
+ subject.nextConfigurationEvent();
+ if (pFixture->fExpectNotification && !gotNotification())
+ {
+ RTPrintf("%s: fixture: %s. No notification was sent after the event.\n",
+ g_pszTestName, pszDesc);
+ ++cErrs;
+ }
+ RTRECT *pRects = subject.getRects();
+ size_t cRects = subject.getRectCount();
+ if (cRects != pFixture->cReportedRects)
+ {
+ RTPrintf("%s: fixture: %s. Wrong number of rectangles reported after processing event (expected %u, got %u).\n",
+ g_pszTestName, pszDesc, pFixture->cReportedRects,
+ cRects);
+ ++cErrs;
+ }
+ else
+ for (unsigned i = 0; i < cRects; ++i)
+ if (!smlsCompRect(&pRects[i], &pFixture->paReportedRects[i]))
+ {
+ RTPrintf("%s: fixture: %s. Rectangle %u wrong after processing event.\n",
+ g_pszTestName, pszDesc, i);
+ smlsPrintDiffRects(&pFixture->paReportedRects[i],
+ &pRects[i]);
+ ++cErrs;
+ break;
+ }
+ subject.stop();
+ subject.start();
+ if (cRects != pFixture->cReportedRects)
+ {
+ RTPrintf("%s: fixture: %s. Wrong number of rectangles reported without processing event (expected %u, got %u).\n",
+ g_pszTestName, pszDesc, pFixture->cReportedRects,
+ cRects);
+ ++cErrs;
+ }
+ else
+ for (unsigned i = 0; i < cRects; ++i)
+ if (!smlsCompRect(&pRects[i], &pFixture->paReportedRects[i]))
+ {
+ RTPrintf("%s: fixture: %s. Rectangle %u wrong without processing event.\n",
+ g_pszTestName, pszDesc, i);
+ smlsPrintDiffRects(&pFixture->paReportedRects[i],
+ &pRects[i]);
+ ++cErrs;
+ break;
+ }
+ return cErrs;
+}
+
+int main(int argc, char **argv)
+{
+ RTR3InitExe(argc, &argv, 0);
+ unsigned cErrs = 0;
+ g_pszTestName = RTPathFilename(argv[0]);
+
+ RTPrintf("%s: TESTING\n", g_pszTestName);
+
+/** @todo r=bird: This testcase is broken and we didn't notice because we
+ * don't run it on the testboxes! @bugref{9842} */
+if (argc == 1)
+{
+ RTPrintf("%s: Note! This testcase is broken, skipping!\n", g_pszTestName);
+ return RTEXITCODE_SUCCESS;
+}
+
+ cErrs += smlsDoFixture(&g_testMove,
+ "ConfigureNotify event (window moved)");
+ // Currently not working
+ cErrs += smlsDoFixture(&g_testResize,
+ "ConfigureNotify event (window resized)");
+ cErrs += smlsDoFixture(&g_testMap, "MapNotify event");
+ cErrs += smlsDoFixture(&g_testUnmap, "UnmapNotify event");
+ cErrs += smlsDoFixture(&g_testUnmapOther,
+ "UnmapNotify event for unmonitored window");
+ cErrs += smlsDoFixture(&g_testShape, "ShapeNotify event");
+ if (cErrs > 0)
+ RTPrintf("%u errors\n", cErrs);
+ return cErrs == 0 ? 0 : 1;
+}
diff --git a/src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11.cpp b/src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11.cpp
new file mode 100644
index 00000000..06bd5c8e
--- /dev/null
+++ b/src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11.cpp
@@ -0,0 +1,185 @@
+/* $Id: tstSeamlessX11.cpp $ */
+/** @file
+ * Linux seamless guest additions simulator in host.
+ */
+
+/*
+ * Copyright (C) 2007-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 <stdlib.h> /* exit() */
+
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <VBox/VBoxGuestLib.h>
+
+#include "../seamless.h"
+
+static RTSEMEVENT eventSem;
+
+void VBClLogError(const char *pszFormat, ...)
+{
+ va_list args;
+ va_start(args, pszFormat);
+ char *psz = NULL;
+ RTStrAPrintfV(&psz, pszFormat, args);
+ va_end(args);
+
+ AssertPtr(psz);
+ RTPrintf("Error: %s", psz);
+
+ RTStrFree(psz);
+}
+
+/** Exit with a fatal error. */
+void VBClLogFatalError(const char *pszFormat, ...)
+{
+ va_list args;
+ va_start(args, pszFormat);
+ char *psz = NULL;
+ RTStrAPrintfV(&psz, pszFormat, args);
+ va_end(args);
+
+ AssertPtr(psz);
+ RTPrintf("Fatal error: %s", psz);
+
+ RTStrFree(psz);
+
+ exit(1);
+}
+
+void VBClLogVerbose(unsigned iLevel, const char *pszFormat, ...)
+{
+ RT_NOREF(iLevel);
+
+ va_list va;
+ va_start(va, pszFormat);
+ RTPrintf("%s", pszFormat);
+ va_end(va);
+}
+
+int VBClStartVTMonitor()
+{
+ return VINF_SUCCESS;
+}
+
+int VbglR3SeamlessSendRects(uint32_t cRects, PRTRECT pRects)
+{
+ RTPrintf("Received rectangle update (%u rectangles):\n", cRects);
+ for (unsigned i = 0; i < cRects; ++i)
+ {
+ RTPrintf(" xLeft: %d yTop: %d xRight: %d yBottom: %d\n",
+ pRects[i].xLeft, pRects[i].yTop, pRects[i].xRight,
+ pRects[i].yBottom);
+ }
+ return VINF_SUCCESS;
+}
+
+int VbglR3SeamlessSetCap(bool bState)
+{
+ RTPrintf("%s\n", bState ? "Seamless capability set"
+ : "Seamless capability unset");
+ return VINF_SUCCESS;
+}
+
+int VbglR3CtlFilterMask(uint32_t u32OrMask, uint32_t u32NotMask)
+{
+ RTPrintf("IRQ filter mask changed. Or mask: 0x%x. Not mask: 0x%x\n",
+ u32OrMask, u32NotMask);
+ return VINF_SUCCESS;
+}
+
+int VbglR3SeamlessWaitEvent(VMMDevSeamlessMode *pMode)
+{
+ static bool active = false;
+
+ int rc = VINF_SUCCESS;
+ if (!active)
+ {
+ active = true;
+ *pMode = VMMDev_Seamless_Visible_Region;
+ }
+ else
+ rc = RTSemEventWait(eventSem, RT_INDEFINITE_WAIT);
+ return rc;
+}
+
+VBGLR3DECL(int) VbglR3InitUser(void) { return VINF_SUCCESS; }
+VBGLR3DECL(void) VbglR3Term(void) {}
+
+/**
+ * Xlib error handler for certain errors that we can't avoid.
+ */
+int vboxClientXLibErrorHandler(Display *pDisplay, XErrorEvent *pError)
+{
+ char errorText[1024];
+
+ if (pError->error_code == BadWindow)
+ {
+ /* This can be triggered if a guest application destroys a window before we notice. */
+ RTPrintf("ignoring BadAtom error and returning\n");
+ return 0;
+ }
+ XGetErrorText(pDisplay, pError->error_code, errorText, sizeof(errorText));
+ RTPrintf("An X Window protocol error occurred: %s\n"
+ " Request code: %d\n"
+ " Minor code: %d\n"
+ " Serial number of the failed request: %d\n\n"
+ "exiting.\n",
+ errorText, (int)pError->request_code, (int)pError->minor_code,
+ (int)pError->serial);
+ exit(1);
+}
+
+int main( int argc, char **argv)
+{
+ int rc = VINF_SUCCESS;
+
+ RTR3InitExe(argc, &argv, 0);
+ RTPrintf("VirtualBox guest additions X11 seamless mode testcase\n");
+ if (0 == XInitThreads())
+ {
+ RTPrintf("Failed to initialise X11 threading, exiting.\n");
+ exit(1);
+ }
+ /* Set an X11 error handler, so that we don't die when we get unavoidable errors. */
+ XSetErrorHandler(vboxClientXLibErrorHandler);
+ RTPrintf("\nType Ctrl-C to exit...\n");
+ RTSemEventCreate(&eventSem);
+ /** Our instance of the seamless class. */
+ SeamlessMain seamless;
+ LogRel(("Starting seamless Guest Additions...\n"));
+ rc = seamless.init();
+ if (rc != VINF_SUCCESS)
+ {
+ RTPrintf("Failed to initialise seamless Additions, rc = %Rrc\n", rc);
+ }
+ bool fShutdown = false;
+ rc = seamless.worker(&fShutdown);
+ if (rc != VINF_SUCCESS)
+ {
+ RTPrintf("Failed to run seamless Additions, rc = %Rrc\n", rc);
+ }
+ return rc;
+}