diff options
author | Daniel d'Andrada <daniel.dandrada@canonical.com> | 2015-04-10 11:54:44 -0300 |
---|---|---|
committer | Daniel d'Andrada <daniel.dandrada@canonical.com> | 2015-04-10 11:54:44 -0300 |
commit | 0774ce8ea08d3298a98af7660104d7ea05589ec7 (patch) | |
tree | b1987ad83b0b21ec6b4795ac05b3d1614a2a3b63 /tests | |
parent | f7fdcfffd0e41f124c296801214b779dae33acb6 (diff) | |
parent | b9e9716a8d7e3cc8e38e58d94622eed5a68cc302 (diff) |
Merge trunk
Diffstat (limited to 'tests')
-rw-r--r-- | tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h | 3 | ||||
-rw-r--r-- | tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp | 35 | ||||
-rw-r--r-- | tests/mirserver/Screen/screen_test.cpp | 5 | ||||
-rw-r--r-- | tests/modules/Application/application_test.cpp | 14 | ||||
-rw-r--r-- | tests/modules/ApplicationManager/application_manager_test.cpp | 80 | ||||
-rw-r--r-- | tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp | 58 | ||||
-rw-r--r-- | tests/modules/SharedWakelock/CMakeLists.txt | 6 | ||||
-rw-r--r-- | tests/modules/SharedWakelock/sharedwakelock_test.cpp | 405 | ||||
-rw-r--r-- | tests/modules/common/mock_shared_wakelock.h | 41 |
9 files changed, 486 insertions, 161 deletions
diff --git a/tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h b/tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h index eece027..ec4a80d 100644 --- a/tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h +++ b/tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Canonical, Ltd. + * Copyright (C) 2014-2015 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by @@ -34,6 +34,7 @@ public: MOCK_METHOD4(handleTouchEvent, void(ulong timestamp, QTouchDevice *device, const QList<struct QWindowSystemInterface::TouchPoint> &points, Qt::KeyboardModifiers mods)); + MOCK_METHOD4(handleMouseEvent, void(ulong, QPointF, Qt::MouseButton, Qt::KeyboardModifiers)); }; namespace testing diff --git a/tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp b/tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp index 0514827..30c4d10 100644 --- a/tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp +++ b/tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Canonical, Ltd. + * Copyright (C) 2014-2015 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by @@ -15,6 +15,8 @@ * */ +#define MIR_INCLUDE_DEPRECATED_EVENT_HEADER + #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -191,34 +193,3 @@ TEST_F(QtEventFeederTest, PressSameTouchTwice) ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockWindowSystem)); } -TEST_F(QtEventFeederTest, IgnoreHovering) -{ - setIrrelevantMockWindowSystemExpectations(); - EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,_,_)).Times(0); - - MirEvent mirEvent; - mirEvent.type = mir_event_type_motion; - mirEvent.motion.pointer_count = 1; - mirEvent.motion.pointer_coordinates[0].id = 0; - mirEvent.motion.pointer_coordinates[0].x = 10; - mirEvent.motion.pointer_coordinates[0].y = 10; - mirEvent.motion.pointer_coordinates[0].touch_major = 1; - mirEvent.motion.pointer_coordinates[0].touch_minor = 1; - mirEvent.motion.pointer_coordinates[0].pressure = 10; - mirEvent.motion.action = mir_motion_action_hover_enter; - mirEvent.motion.event_time = 123 * 1000000; - - qtEventFeeder->dispatch(mirEvent); - - mirEvent.motion.pointer_coordinates[0].x = 20; - mirEvent.motion.pointer_coordinates[0].y = 20; - mirEvent.motion.action = mir_motion_action_hover_move; - mirEvent.motion.event_time = 125 * 1000000; - - qtEventFeeder->dispatch(mirEvent); - - mirEvent.motion.action = mir_motion_action_hover_exit; - mirEvent.motion.event_time = 127 * 1000000; - - qtEventFeeder->dispatch(mirEvent); -} diff --git a/tests/mirserver/Screen/screen_test.cpp b/tests/mirserver/Screen/screen_test.cpp index 3fe4c9e..be0461d 100644 --- a/tests/mirserver/Screen/screen_test.cpp +++ b/tests/mirserver/Screen/screen_test.cpp @@ -51,6 +51,11 @@ mg::DisplayConfigurationOutput const fake_output TEST(ScreenTest, OrientationSensor) { + if (!qEnvironmentVariableIsSet("QT_ACCEL_FILEPATH")) { + // Trick Qt >= 5.4.1 to load the generic sensors + qputenv("QT_ACCEL_FILEPATH", "dummy"); + } + Screen::skipDBusRegistration = true; Screen *screen = new Screen(fake_output); diff --git a/tests/modules/Application/application_test.cpp b/tests/modules/Application/application_test.cpp index 488a598..b201917 100644 --- a/tests/modules/Application/application_test.cpp +++ b/tests/modules/Application/application_test.cpp @@ -34,7 +34,7 @@ TEST_F(ApplicationTests, checkFocusAcquiresWakeLock) { using namespace ::testing; - EXPECT_CALL(sharedWakelock, createWakelock()).Times(1); + EXPECT_CALL(sharedWakelock, acquire(_)).Times(1); startApplication(123, "app"); applicationManager.focusApplication("app"); @@ -50,14 +50,14 @@ TEST_F(ApplicationTests, checkSuspendReleasesWakeLock) applicationManager.focusApplication("app"); Q_EMIT session->suspended(); - EXPECT_FALSE(sharedWakelock.wakelockHeld()); + EXPECT_FALSE(sharedWakelock.enabled()); } TEST_F(ApplicationTests, checkResumeAcquiresWakeLock) { using namespace ::testing; - EXPECT_CALL(sharedWakelock, createWakelock()).Times(1); + EXPECT_CALL(sharedWakelock, acquire(_)).Times(1); auto app = startApplication(123, "app"); auto session = app->session(); @@ -69,7 +69,7 @@ TEST_F(ApplicationTests, checkRespawnAcquiresWakeLock) { using namespace ::testing; - EXPECT_CALL(sharedWakelock, createWakelock()).Times(1); + EXPECT_CALL(sharedWakelock, acquire(_)).Times(1); const QString appId = "app"; auto app = startApplication(123, "app"); @@ -89,7 +89,7 @@ TEST_F(ApplicationTests, checkDashFocusDoesNotAcquireWakeLock) { using namespace ::testing; - EXPECT_CALL(sharedWakelock, createWakelock()).Times(0); + EXPECT_CALL(sharedWakelock, acquire(_)).Times(0); startApplication(123, "unity8-dash"); applicationManager.focusApplication("unity8-dash"); @@ -105,14 +105,14 @@ TEST_F(ApplicationTests, checkDashSuspendDoesNotImpactWakeLock) applicationManager.focusApplication("unity8-dash"); Q_EMIT session->suspended(); - EXPECT_FALSE(sharedWakelock.wakelockHeld()); + EXPECT_FALSE(sharedWakelock.enabled()); } TEST_F(ApplicationTests, checkDashResumeDoesNotAcquireWakeLock) { using namespace ::testing; - EXPECT_CALL(sharedWakelock, createWakelock()).Times(0); + EXPECT_CALL(sharedWakelock, acquire(_)).Times(0); auto app = startApplication(123, "unity8-dash"); auto session = app->session(); diff --git a/tests/modules/ApplicationManager/application_manager_test.cpp b/tests/modules/ApplicationManager/application_manager_test.cpp index bd6008a..fa73b81 100644 --- a/tests/modules/ApplicationManager/application_manager_test.cpp +++ b/tests/modules/ApplicationManager/application_manager_test.cpp @@ -15,6 +15,8 @@ * */ +#define MIR_INCLUDE_DEPRECATED_EVENT_HEADER + #include <thread> #include <condition_variable> #include <QSignalSpy> @@ -2232,3 +2234,81 @@ TEST_F(ApplicationManagerTests, focusMainStageAfterSideStage) .Times(0); applicationManager.focusApplication(webbrowserAppId); } + +/* + * Test lifecycle exempt applications have their wakelocks released when shell tries to suspend them + */ +TEST_F(ApplicationManagerTests,lifecycleExemptAppsHaveWakelockReleasedOnAttemptedSuspend) +{ + using namespace ::testing; + + const QString appId("com.ubuntu.music"); // member of lifecycle exemption list + const quint64 procId = 12345; + + // Set up Mocks & signal watcher + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); + + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); + + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) + .Times(1) + .WillOnce(Return(true)); + + auto application = applicationManager.startApplication(appId, ApplicationManager::NoFlag); + applicationManager.onProcessStarting(appId); + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); + bool authed = true; + applicationManager.authorizeSession(procId, authed); + onSessionStarting(session); + + // App creates surface, focuses it so state is running + std::shared_ptr<mir::scene::Surface> surface(nullptr); + applicationManager.onSessionCreatedSurface(session.get(), surface); + applicationManager.focusApplication(appId); + + applicationManager.unfocusCurrentApplication(); + + EXPECT_FALSE(sharedWakelock.enabled()); + EXPECT_EQ(application->state(), Application::Running); +} + +/* + * Test lifecycle exempt applications have their wakelocks released on suspend + */ +TEST_F(ApplicationManagerTests,lifecycleExemptAppsHaveWakelockReleasedOnUnSuspend) +{ + using namespace ::testing; + + const QString appId("com.ubuntu.music"); // member of lifecycle exemption list + const quint64 procId = 12345; + + // Set up Mocks & signal watcher + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); + + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); + + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) + .Times(1) + .WillOnce(Return(true)); + + auto application = applicationManager.startApplication(appId, ApplicationManager::NoFlag); + applicationManager.onProcessStarting(appId); + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); + bool authed = true; + applicationManager.authorizeSession(procId, authed); + onSessionStarting(session); + + // App creates surface, focuses it so state is running + std::shared_ptr<mir::scene::Surface> surface(nullptr); + applicationManager.onSessionCreatedSurface(session.get(), surface); + applicationManager.focusApplication(appId); + + applicationManager.setSuspended(true); + + EXPECT_FALSE(sharedWakelock.enabled()); + EXPECT_EQ(application->state(), Application::Running); +} diff --git a/tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp b/tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp index 9fa237c..7d3b727 100644 --- a/tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp +++ b/tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Canonical, Ltd. + * Copyright (C) 2014-2015 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by @@ -15,6 +15,8 @@ * */ +#define MIR_INCLUDE_DEPRECATED_EVENT_HEADER + #include <gtest/gtest.h> #include <QLoggingCategory> @@ -60,29 +62,43 @@ TEST(MirSurfaceItemTest, MissingTouchEnd) EXPECT_CALL(*mockSurface, type()).Times(AnyNumber()).WillRepeatedly(Return(mir_surface_type_normal)); EXPECT_CALL(*mockSession, setSurface(_)).Times(AnyNumber()); + auto getTouchEvent = [](MirEvent const& event) -> MirTouchInputEvent const* + { + if (mir_event_get_type(&event) != mir_event_type_input) + return nullptr; + auto const* input_event = mir_event_get_input_event(&event); + if (mir_input_event_get_type(input_event) != mir_input_event_type_touch) + return nullptr; + return mir_input_event_get_touch_input_event(input_event); + }; + + auto eventMatches = [&](MirEvent const& event, + int touch_count, + MirTouchInputEventTouchAction action, + MirTouchInputEventTouchId touch_id) ->void + { + auto const* touch_event = getTouchEvent(event); + ASSERT_NE(touch_event, nullptr); + ASSERT_EQ(touch_count, mir_touch_input_event_get_touch_count(touch_event)); + ASSERT_EQ(action, mir_touch_input_event_get_touch_action(touch_event,0)); + ASSERT_EQ(touch_id, mir_touch_input_event_get_touch_id(touch_event,0)); + }; + // The touch event sequence we expect mir::input::surface to receive from MirSurfaceItem. // It should properly finish the sequence for touch 0 ('down', 'move' and 'up') before starting // the sequence for touch 1. EXPECT_CALL(*mockSurface, consume(_)) - .WillOnce(Invoke([] (MirEvent const& mirEvent) { - ASSERT_EQ(mir_motion_action_down, mirEvent.motion.action); - ASSERT_EQ(1, mirEvent.motion.pointer_count); - ASSERT_EQ(0, mirEvent.motion.pointer_coordinates[0].id); + .WillOnce(Invoke([&] (MirEvent const& mirEvent) { + eventMatches(mirEvent, 1, mir_touch_input_event_action_down, 0); })) - .WillOnce(Invoke([] (MirEvent const& mirEvent) { - ASSERT_EQ(mir_motion_action_move, mirEvent.motion.action); - ASSERT_EQ(1, mirEvent.motion.pointer_count); - ASSERT_EQ(0, mirEvent.motion.pointer_coordinates[0].id); + .WillOnce(Invoke([&] (MirEvent const& mirEvent) { + eventMatches(mirEvent, 1, mir_touch_input_event_action_change, 0); })) - .WillOnce(Invoke([] (MirEvent const& mirEvent) { - ASSERT_EQ(mir_motion_action_up, mirEvent.motion.action); - ASSERT_EQ(1, mirEvent.motion.pointer_count); - ASSERT_EQ(0, mirEvent.motion.pointer_coordinates[0].id); + .WillOnce(Invoke([&] (MirEvent const& mirEvent) { + eventMatches(mirEvent, 1, mir_touch_input_event_action_up, 0); })) - .WillOnce(Invoke([] (MirEvent const& mirEvent) { - ASSERT_EQ(mir_motion_action_down, mirEvent.motion.action); - ASSERT_EQ(1, mirEvent.motion.pointer_count); - ASSERT_EQ(1, mirEvent.motion.pointer_coordinates[0].id); + .WillOnce(Invoke([&] (MirEvent const& mirEvent) { + eventMatches(mirEvent, 1, mir_touch_input_event_action_down, 1); })); @@ -95,19 +111,19 @@ TEST(MirSurfaceItemTest, MissingTouchEnd) touchPoints[0].setId(0); touchPoints[0].setState(Qt::TouchPointPressed); surfaceItem->processTouchEvent(QEvent::TouchBegin, - timestamp, touchPoints, touchPoints[0].state()); + timestamp, Qt::NoModifier, touchPoints, touchPoints[0].state()); touchPoints[0].setState(Qt::TouchPointMoved); surfaceItem->processTouchEvent(QEvent::TouchUpdate, - timestamp + 10, touchPoints, touchPoints[0].state()); + timestamp + 10, Qt::NoModifier, touchPoints, touchPoints[0].state()); // Starting a new touch sequence (with touch 1) without ending the current one // (wich has touch 0). touchPoints[0].setId(1); touchPoints[0].setState(Qt::TouchPointPressed); surfaceItem->processTouchEvent(QEvent::TouchBegin, - timestamp + 20, touchPoints, touchPoints[0].state()); - + timestamp + 20, Qt::NoModifier, touchPoints, touchPoints[0].state()); + delete surfaceItem; delete mockSession; } diff --git a/tests/modules/SharedWakelock/CMakeLists.txt b/tests/modules/SharedWakelock/CMakeLists.txt index 99838a4..6dab098 100644 --- a/tests/modules/SharedWakelock/CMakeLists.txt +++ b/tests/modules/SharedWakelock/CMakeLists.txt @@ -6,6 +6,8 @@ set( include_directories( ${CMAKE_SOURCE_DIR}/src/modules ${CMAKE_SOURCE_DIR}/tests/modules/common + ${QTDBUSTEST_INCLUDE_DIRS} + ${QTDBUSMOCK_INCLUDE_DIRS} ) add_executable(sharedwakelock_test ${SHARED_WAKELOCK_TEST_SOURCES}) @@ -15,8 +17,12 @@ target_link_libraries( unityapplicationplugin + Qt5::Test + ${GTEST_BOTH_LIBRARIES} ${GMOCK_LIBRARIES} + ${QTDBUSTEST_LIBRARIES} + ${QTDBUSMOCK_LIBRARIES} ) add_test(SharedWakelock, sharedwakelock_test) diff --git a/tests/modules/SharedWakelock/sharedwakelock_test.cpp b/tests/modules/SharedWakelock/sharedwakelock_test.cpp index 4242ee5..3ad5eba 100644 --- a/tests/modules/SharedWakelock/sharedwakelock_test.cpp +++ b/tests/modules/SharedWakelock/sharedwakelock_test.cpp @@ -17,154 +17,373 @@ #include <Unity/Application/sharedwakelock.h> -#include "mock_shared_wakelock.h" +#include <libqtdbusmock/DBusMock.h> -#include <gmock/gmock.h> +#include <QCoreApplication> +#include <QSignalSpy> #include <gtest/gtest.h> using namespace qtmir; -using testing::MockSharedWakelock; +using namespace testing; +using namespace QtDBusTest; +using namespace QtDBusMock; -TEST(SharedWakelock, acquireCreatesAWakelock) +const char POWERD_NAME[] = "com.canonical.powerd"; +const char POWERD_PATH[] = "/com/canonical/powerd"; +const char POWERD_INTERFACE[] = "com.canonical.powerd"; + +class SharedWakelockTest: public Test +{ +protected: + SharedWakelockTest() + : mock(dbus) + { + mock.registerCustomMock(POWERD_NAME, + POWERD_PATH, + POWERD_INTERFACE, + QDBusConnection::SystemBus); + + dbus.startServices(); + } + + virtual ~SharedWakelockTest() + {} + + virtual OrgFreedesktopDBusMockInterface& powerdMockInterface() + { + return mock.mockInterface(POWERD_NAME, + POWERD_PATH, + POWERD_INTERFACE, + QDBusConnection::SystemBus); + } + + void implementRequestSysState() { + // Defines the mock impementation of this DBus method + powerdMockInterface().AddMethod("com.canonical.powerd", + "requestSysState", "si", "s", "ret = 'cookie'").waitForFinished(); + } + + void implementClearSysState() { + powerdMockInterface().AddMethod("com.canonical.powerd", + "clearSysState", "s", "", "").waitForFinished(); + } + + void EXPECT_CALL(const QList<QVariantList> &spy, int index, + const QString &name, const QVariantList &args) + { + QVariant args2(QVariant::fromValue(args)); + ASSERT_LT(index, spy.size()); + const QVariantList &call(spy.at(index)); + EXPECT_EQ(name, call.at(0).toString()); + EXPECT_EQ(args2.toString().toStdString(), call.at(1).toString().toStdString()); + } + + DBusTestRunner dbus; + DBusMock mock; +}; + +TEST_F(SharedWakelockTest, acquireCreatesAWakelock) +{ + implementRequestSysState(); + + /* Note: we pass the DBusTestRunner constructed system DBus connection instead of letting + * Qt create one. This is done as Qt has a non-testing friendly way of handling the built-in + * system and session connections, and as DBusTestRunner restarts the DBus daemon for each + * testcase, it unfortunately means Qt would end up pointing to a dead bus after one testcase. */ + SharedWakelock wakelock(dbus.systemConnection()); + + // Verify the DBus method is called & wakelock + QSignalSpy wakelockDBusMethodSpy(&powerdMockInterface(), SIGNAL(MethodCalled(const QString &, const QVariantList &))); + QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) )); + + QScopedPointer<QObject> object(new QObject); + wakelock.acquire(object.data()); + wakelockDBusMethodSpy.wait(); + + EXPECT_FALSE(wakelockDBusMethodSpy.empty()); + EXPECT_CALL(wakelockDBusMethodSpy, 0, "requestSysState", + QVariantList() << QString("active") << 1); + + // Ensure a wakelock created + wakelockEnabledSpy.wait(); + EXPECT_FALSE(wakelockEnabledSpy.empty()); + EXPECT_TRUE(wakelock.enabled()); +} + +TEST_F(SharedWakelockTest, acquireThenReleaseDestroysTheWakelock) { - using namespace ::testing; + implementRequestSysState(); + implementClearSysState(); + + SharedWakelock wakelock(dbus.systemConnection()); - testing::NiceMock<MockSharedWakelock> sharedWakelock; - QScopedPointer<QObject> app1(new QObject); + QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) )); - EXPECT_CALL(sharedWakelock, createWakelock()).Times(1); - sharedWakelock.acquire(app1.data()); + QScopedPointer<QObject> object(new QObject); + wakelock.acquire(object.data()); + wakelockEnabledSpy.wait(); + + // Verify the DBus method is called + QSignalSpy wakelockDBusMethodSpy(&powerdMockInterface(), SIGNAL(MethodCalled(const QString &, const QVariantList &))); + wakelock.release(object.data()); + wakelockDBusMethodSpy.wait(); + + EXPECT_FALSE(wakelockDBusMethodSpy.empty()); + EXPECT_CALL(wakelockDBusMethodSpy, 0, "clearSysState", + QVariantList() << QString("cookie")); + + EXPECT_FALSE(wakelock.enabled()); } -TEST(SharedWakelock, acquireThenReleaseDestroysTheWakelock) +TEST_F(SharedWakelockTest, doubleAcquireBySameOwnerOnlyCreatesASingleWakelock) { - using namespace ::testing; + implementRequestSysState(); + + SharedWakelock wakelock(dbus.systemConnection()); + + // Verify the DBus method is called & wakelock + QSignalSpy wakelockDBusMethodSpy(&powerdMockInterface(), SIGNAL(MethodCalled(const QString &, const QVariantList &))); - testing::NiceMock<MockSharedWakelock> sharedWakelock; - QScopedPointer<QObject> app1(new QObject); + QScopedPointer<QObject> object(new QObject); + wakelock.acquire(object.data()); + wakelock.acquire(object.data()); + wakelockDBusMethodSpy.wait(); - sharedWakelock.acquire(app1.data()); - sharedWakelock.release(app1.data()); - EXPECT_FALSE(sharedWakelock.wakelockHeld()); + EXPECT_EQ(wakelockDBusMethodSpy.count(), 1); } -TEST(SharedWakelock, doubleAcquireBySameOwnerOnlyCreatesASingleWakelock) +TEST_F(SharedWakelockTest, doubleAcquireThenReleaseBySameOwnerDestroysWakelock) { - using namespace ::testing; + implementRequestSysState(); + implementClearSysState(); - testing::NiceMock<MockSharedWakelock> sharedWakelock; - QScopedPointer<QObject> app1(new QObject); + SharedWakelock wakelock(dbus.systemConnection()); - EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject)); - sharedWakelock.acquire(app1.data()); - sharedWakelock.acquire(app1.data()); + QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) )); + QScopedPointer<QObject> object(new QObject); + + wakelock.acquire(object.data()); + wakelock.acquire(object.data()); + wakelock.release(object.data()); + wakelockEnabledSpy.wait(); + EXPECT_FALSE(wakelock.enabled()); } -TEST(SharedWakelock, doubleAcquireThenReleaseBySameOwnerDestroysWakelock) +TEST_F(SharedWakelockTest, acquireByDifferentOwnerOnlyCreatesASingleWakelock) { - using namespace ::testing; + implementRequestSysState(); + + SharedWakelock wakelock(dbus.systemConnection()); + + // Verify the DBus method is called & wakelock + QSignalSpy wakelockDBusMethodSpy(&powerdMockInterface(), SIGNAL(MethodCalled(const QString &, const QVariantList &))); - testing::NiceMock<MockSharedWakelock> sharedWakelock; - QScopedPointer<QObject> app1(new QObject); + QScopedPointer<QObject> object1(new QObject); + QScopedPointer<QObject> object2(new QObject); + wakelock.acquire(object1.data()); + wakelock.acquire(object2.data()); - EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject)); - sharedWakelock.acquire(app1.data()); - sharedWakelock.acquire(app1.data()); - sharedWakelock.release(app1.data()); - EXPECT_FALSE(sharedWakelock.wakelockHeld()); + wakelockDBusMethodSpy.wait(); + EXPECT_EQ(wakelockDBusMethodSpy.count(), 1); } -TEST(SharedWakelock, acquireByDifferentOwnerOnlyCreatesASingleWakelock) +TEST_F(SharedWakelockTest, twoOwnersWhenBothReleaseWakelockReleased) { - using namespace ::testing; + implementRequestSysState(); + + SharedWakelock wakelock(dbus.systemConnection()); + + QScopedPointer<QObject> object1(new QObject); + QScopedPointer<QObject> object2(new QObject); + wakelock.acquire(object1.data()); + wakelock.acquire(object2.data()); - testing::NiceMock<MockSharedWakelock> sharedWakelock; - QScopedPointer<QObject> app1(new QObject); - QScopedPointer<QObject> app2(new QObject); + QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) )); - EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject)); - sharedWakelock.acquire(app1.data()); - sharedWakelock.acquire(app2.data()); + wakelock.release(object1.data()); + wakelock.release(object2.data()); + + wakelockEnabledSpy.wait(); + EXPECT_FALSE(wakelock.enabled()); } -TEST(SharedWakelock, twoOwnersWhenOneReleasesStillHoldWakelock) +TEST_F(SharedWakelockTest, doubleReleaseOfSingleOwnerIgnored) { - using namespace ::testing; + implementRequestSysState(); + + SharedWakelock wakelock(dbus.systemConnection()); + + QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) )); + + QScopedPointer<QObject> object1(new QObject); + QScopedPointer<QObject> object2(new QObject); + wakelock.acquire(object1.data()); + wakelock.acquire(object2.data()); + wakelock.release(object1.data()); + + wakelockEnabledSpy.wait(); - testing::NiceMock<MockSharedWakelock> sharedWakelock; - QScopedPointer<QObject> app1(new QObject); - QScopedPointer<QObject> app2(new QObject); + wakelock.release(object1.data()); - EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject)); - sharedWakelock.acquire(app1.data()); - sharedWakelock.acquire(app2.data()); - sharedWakelock.release(app1.data()); - EXPECT_TRUE(sharedWakelock.wakelockHeld()); + EXPECT_TRUE(wakelock.enabled()); } -TEST(SharedWakelock, twoOwnersWhenBothReleaseWakelockReleased) +TEST_F(SharedWakelockTest, wakelockAcquireReleaseFlood) { - using namespace ::testing; - - testing::NiceMock<MockSharedWakelock> sharedWakelock; - QScopedPointer<QObject> app1(new QObject); - QScopedPointer<QObject> app2(new QObject); - - EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject)); - sharedWakelock.acquire(app1.data()); - sharedWakelock.acquire(app2.data()); - sharedWakelock.release(app2.data()); - sharedWakelock.release(app1.data()); - EXPECT_FALSE(sharedWakelock.wakelockHeld()); + implementRequestSysState(); + implementClearSysState(); + + SharedWakelock wakelock(dbus.systemConnection()); + + QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) )); + + QScopedPointer<QObject> object(new QObject); + wakelock.acquire(object.data()); + wakelock.release(object.data()); + wakelock.acquire(object.data()); + wakelock.release(object.data()); + wakelock.acquire(object.data()); + wakelock.release(object.data()); + while (wakelockEnabledSpy.wait(100)) {} + EXPECT_FALSE(wakelock.enabled()); } -TEST(SharedWakelock, doubleReleaseOfSingleOwnerIgnored) +TEST_F(SharedWakelockTest, wakelockAcquireReleaseAcquireWithDelays) { - using namespace ::testing; + powerdMockInterface().AddMethod("com.canonical.powerd", + "requestSysState", "si", "s", + "i=100000\n" // delay the response from the mock dbus instance + "while i>0:\n" + " i-=1\n" + "ret = 'cookie'").waitForFinished(); + + implementClearSysState(); + + SharedWakelock wakelock(dbus.systemConnection()); + + QSignalSpy wakelockDBusMethodSpy(&powerdMockInterface(), SIGNAL(MethodCalled(const QString &, const QVariantList &))); + + QScopedPointer<QObject> object(new QObject); + wakelock.acquire(object.data()); + wakelock.release(object.data()); + wakelock.acquire(object.data()); + + while (wakelockDBusMethodSpy.wait()) {} + EXPECT_TRUE(wakelock.enabled()); + + // there must be at least one clearSysState call, but is not necessarily the second call + // should the dbus response be slow enough, it may be the third call. Is hard to reliably + // reproduce the racey condition for all platforms. + bool found = false; + for (int i=0; i<wakelockDBusMethodSpy.count(); i++) { + const QVariantList &call(wakelockDBusMethodSpy.at(i)); + + if (call.at(0).toString() == "clearSysState") { + found = true; + } + } + EXPECT_TRUE(found); +} + +TEST_F(SharedWakelockTest, nullOwnerAcquireIgnored) +{ + implementRequestSysState(); + + SharedWakelock wakelock(dbus.systemConnection()); + + QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) )); - testing::NiceMock<MockSharedWakelock> sharedWakelock; - QScopedPointer<QObject> app1(new QObject); - QScopedPointer<QObject> app2(new QObject); + wakelock.acquire(nullptr); - EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject)); - sharedWakelock.acquire(app1.data()); - sharedWakelock.acquire(app2.data()); - sharedWakelock.release(app1.data()); - EXPECT_TRUE(sharedWakelock.wakelockHeld()); + wakelockEnabledSpy.wait(200); // have to wait to see if anything happens - sharedWakelock.release(app1.data()); - EXPECT_TRUE(sharedWakelock.wakelockHeld()); + EXPECT_FALSE(wakelock.enabled()); } -TEST(SharedWakelock, nullOwnerAcquireIgnored) +TEST_F(SharedWakelockTest, nullReleaseAcquireIgnored) { - using namespace ::testing; + implementRequestSysState(); - testing::NiceMock<MockSharedWakelock> sharedWakelock; + SharedWakelock wakelock(dbus.systemConnection()); - EXPECT_CALL(sharedWakelock, createWakelock()).Times(0); - sharedWakelock.acquire(nullptr); + wakelock.release(nullptr); + + EXPECT_FALSE(wakelock.enabled()); } -TEST(SharedWakelock, nullOwnerReleaseIgnored) +TEST_F(SharedWakelockTest, ifOwnerDestroyedWakelockReleased) { - using namespace ::testing; + implementRequestSysState(); + implementClearSysState(); + + SharedWakelock wakelock(dbus.systemConnection()); + + QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) )); + + QScopedPointer<QObject> object(new QObject); + wakelock.acquire(object.data()); + wakelockEnabledSpy.wait(); + + // Verify the DBus method is called + QSignalSpy wakelockDBusMethodSpy(&powerdMockInterface(), SIGNAL(MethodCalled(const QString &, const QVariantList &))); - testing::NiceMock<MockSharedWakelock> sharedWakelock; + object.reset(); + wakelockDBusMethodSpy.wait(); - EXPECT_CALL(sharedWakelock, createWakelock()).Times(0); - sharedWakelock.release(nullptr); + EXPECT_FALSE(wakelockDBusMethodSpy.empty()); + EXPECT_CALL(wakelockDBusMethodSpy, 0, "clearSysState", + QVariantList() << QString("cookie")); + + EXPECT_FALSE(wakelock.enabled()); } -TEST(SharedWakelock, ifOwnerDestroyedWakelockReleased) +TEST_F(SharedWakelockTest, reloadCachedWakelockCookie) { - using namespace ::testing; + const char cookieFile[] = "/tmp/qtmir_powerd_cookie"; + + // Custom Deleter for QScopedPointer to delete the qtmir cookie file no matter what + struct ScopedQFileCustomDeleter + { + static inline void cleanup(QFile *file) + { + file->remove(); + delete file; + } + }; + QScopedPointer<QFile, ScopedQFileCustomDeleter> cookieCache(new QFile(cookieFile)); + cookieCache->open(QFile::WriteOnly | QFile::Text); + cookieCache->write("myCookie"); + + SharedWakelock wakelock(dbus.systemConnection()); + EXPECT_TRUE(wakelock.enabled()); +} + +TEST_F(SharedWakelockTest, wakelockReleasedOnSharedWakelockDestroyed) +{ + implementRequestSysState(); + implementClearSysState(); + + auto wakelock = new SharedWakelock(dbus.systemConnection()); + + QSignalSpy wakelockEnabledSpy(wakelock, SIGNAL( enabledChanged(bool) )); + + QScopedPointer<QObject> object(new QObject); + wakelock->acquire(object.data()); + wakelockEnabledSpy.wait(); // wait for wakelock to be enabled + + QSignalSpy wakelockDBusMethodSpy(&powerdMockInterface(), SIGNAL(MethodCalled(const QString &, const QVariantList &))); + delete wakelock; + wakelockDBusMethodSpy.wait(); + + EXPECT_FALSE(wakelockDBusMethodSpy.empty()); + EXPECT_CALL(wakelockDBusMethodSpy, 0, "clearSysState", + QVariantList() << QString("cookie")); +} - testing::NiceMock<MockSharedWakelock> sharedWakelock; - QScopedPointer<QObject> app1(new QObject); - EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject)); - sharedWakelock.acquire(app1.data()); - app1.reset(); - EXPECT_FALSE(sharedWakelock.wakelockHeld()); +// Define custom main() as these tests rely on a QEventLoop +int main(int argc, char** argv) { + QCoreApplication app(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff --git a/tests/modules/common/mock_shared_wakelock.h b/tests/modules/common/mock_shared_wakelock.h index 78d3281..0e99a53 100644 --- a/tests/modules/common/mock_shared_wakelock.h +++ b/tests/modules/common/mock_shared_wakelock.h @@ -23,21 +23,48 @@ namespace testing { -struct MockSharedWakelock : public qtmir::SharedWakelock +class MockSharedWakelock : public qtmir::SharedWakelock { - MockSharedWakelock() +public: + MockSharedWakelock(const QDBusConnection& /*connection*/= QDBusConnection::systemBus()) { - ON_CALL(*this, createWakelock()).WillByDefault(Invoke(this, &MockSharedWakelock::doCreateWakelock)); + ON_CALL(*this, enabled()).WillByDefault(Invoke(this, &MockSharedWakelock::doEnabled)); + ON_CALL(*this, acquire(_)).WillByDefault(Invoke(this, &MockSharedWakelock::doAcquire)); + ON_CALL(*this, release(_)).WillByDefault(Invoke(this, &MockSharedWakelock::doRelease)); } - MOCK_METHOD0(createWakelock, QObject*()); - bool wakelockHeld() { return m_wakelock; } + MOCK_CONST_METHOD0(enabled, bool()); + MOCK_METHOD1(acquire, void(const QObject *)); + MOCK_METHOD1(release, void(const QObject *)); + bool doEnabled() + { + return !m_owners.isEmpty(); + } + + void doAcquire(const QObject *object) + { + if (m_owners.contains(object)) { + return; + } + m_owners.insert(object); + if (m_owners.size() == 1) { + Q_EMIT enabledChanged(true); + } + } - QObject* doCreateWakelock() const + void doRelease(const QObject *object) { - return new QObject; + if (!m_owners.remove(object)) { + return; + } + if (m_owners.isEmpty()) { + Q_EMIT enabledChanged(false); + } } + +private: + QSet<const QObject *> m_owners; }; } // namespace testing |