From fda9428d4e791370e16fcaffebea898895a3d62c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Tue, 6 May 2014 09:53:37 +0200 Subject: [PATCH] [screenlocker] Add unit test for LockWindow So far the unit test verifies that: * screen gets blanked as soon as the first lock window is mapped * screen stays blanked when the lock window gets destroyed * screen stays blanked if a window is raised on top of the stack REVIEW: 118012 --- .../screenlocker/autotests/CMakeLists.txt | 8 ++ .../screenlocker/autotests/lockwindowtest.cpp | 134 ++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 ksmserver/screenlocker/autotests/lockwindowtest.cpp diff --git a/ksmserver/screenlocker/autotests/CMakeLists.txt b/ksmserver/screenlocker/autotests/CMakeLists.txt index 24c5ec3dc..b31bbf799 100644 --- a/ksmserver/screenlocker/autotests/CMakeLists.txt +++ b/ksmserver/screenlocker/autotests/CMakeLists.txt @@ -35,3 +35,11 @@ ecm_mark_as_test(keyboardGrabber) add_executable(pointerGrabber pointergrabber.cpp) target_link_libraries(pointerGrabber Qt5::Core XCB::XCB) ecm_mark_as_test(pointerGrabber) + +####################################### +# LockWindowTest +####################################### +add_executable(lockWindowTest lockwindowtest.cpp) +target_link_libraries(lockWindowTest Qt5::Test screenlocker_static) +add_test(ksmserver-lockWindowTest lockWindowTest) +ecm_mark_as_test(lockWindowTest) diff --git a/ksmserver/screenlocker/autotests/lockwindowtest.cpp b/ksmserver/screenlocker/autotests/lockwindowtest.cpp new file mode 100644 index 000000000..618f8347b --- /dev/null +++ b/ksmserver/screenlocker/autotests/lockwindowtest.cpp @@ -0,0 +1,134 @@ +/******************************************************************** + KSld - the KDE Screenlocker Daemon + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +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; either version 2 of the License, or +(at your option) any later version. + +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 . +*********************************************************************/ +// own +#include "../lockwindow.h" +// Qt +#include +#include +#include +// xcb +#include + +template using ScopedCPointer = QScopedPointer; + +class LockWindowTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void testBlankScreen(); +}; + +xcb_screen_t *defaultScreen() +{ + int screen = QX11Info::appScreen(); + for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(QX11Info::connection())); + it.rem; + --screen, xcb_screen_next(&it)) { + if (screen == 0) { + return it.data; + } + } + return nullptr; +} + +bool isBlack() +{ + xcb_connection_t *c = QX11Info::connection(); + xcb_screen_t *screen = defaultScreen(); + const int width = screen->width_in_pixels; + const int height = screen->height_in_pixels; + const auto cookie = xcb_get_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, QX11Info::appRootWindow(), + 0, 0, width, height, ~0); + ScopedCPointer xImage(xcb_get_image_reply(c, cookie, nullptr)); + if (xImage.isNull()) { + return false; + } + + // this operates on the assumption that X server default depth matches Qt's image format + QImage image(xcb_get_image_data(xImage.data()), width, height, + xcb_get_image_data_length(xImage.data()) / height, QImage::Format_ARGB32_Premultiplied); + + for (int i = 0; i < image.width(); i++) { + for (int j = 0; j < image.height(); j++) { + if (image.pixel(i, j) != qRgb(0, 0, 0)) { + return false; + } + } + } + return true; +} + +xcb_atom_t screenLockerAtom() +{ + const QByteArray atomName = QByteArrayLiteral("_KDE_SCREEN_LOCKER"); + xcb_connection_t *c = QX11Info::connection(); + const auto cookie = xcb_intern_atom(c, false, atomName.length(), atomName.constData()); + ScopedCPointer atom(xcb_intern_atom_reply(c, cookie, nullptr)); + if (atom.isNull()) { + return XCB_ATOM_NONE; + } + return atom->atom; +} + +void LockWindowTest::testBlankScreen() +{ + ScreenLocker::LockWindow lockWindow; + lockWindow.showLockWindow(); + + // the screen will be blanked once the first lock window gets mapped, so let's create one + QWindow fakeWindow; + fakeWindow.setFlags(Qt::X11BypassWindowManagerHint); + // it's on purpose outside the visual area + fakeWindow.setGeometry(-1, -1, 1, 1); + fakeWindow.create(); + xcb_atom_t atom = screenLockerAtom(); + QVERIFY(atom != XCB_ATOM_NONE); + xcb_connection_t *c = QX11Info::connection(); + xcb_change_property(c, XCB_PROP_MODE_REPLACE, fakeWindow.winId(), atom, atom, 32, 0, nullptr); + xcb_flush(c); + fakeWindow.show(); + + // give it time to be shown + QTest::qWait(1000); + + // now lets try to get a screen grab and verify it's black + QVERIFY(isBlack()); + + // destorying the fakeWindow should not remove the blanked screen + fakeWindow.destroy(); + QTest::qWait(1000); + QVERIFY(isBlack()); + + // let's create another window and try to raise it above the lockWindow + // using a QWidget to get proper content which won't be black + QWidget widgetWindow; + widgetWindow.setGeometry(10, 10, 100, 100); + widgetWindow.show(); + const uint32_t values[] = { XCB_STACK_MODE_ABOVE }; + xcb_configure_window(c, widgetWindow.winId(), XCB_CONFIG_WINDOW_STACK_MODE, values); + xcb_flush(c); + QTest::qWait(1000); + QVERIFY(isBlack()); + + lockWindow.hideLockWindow(); +} + +QTEST_MAIN(LockWindowTest) +#include "lockwindowtest.moc"