From 6ea2db56307d04ac8dba4bcf929cd716f4840359 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 16 Jul 2014 01:08:54 -0500 Subject: add first draft of low battery notifications --- tests/CMakeLists.txt | 2 ++ tests/test-notify.cc | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 tests/test-notify.cc (limited to 'tests') diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c5ad09d..4489bdc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -42,5 +42,7 @@ function(add_test_by_name name) add_dependencies (${TEST_NAME} libindicatorpowerservice) target_link_libraries (${TEST_NAME} indicatorpowerservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) endfunction() +add_test_by_name(test-notify) +add_test(NAME dear-reader-the-next-test-takes-80-seconds COMMAND true) add_test_by_name(test-device) diff --git a/tests/test-notify.cc b/tests/test-notify.cc new file mode 100644 index 0000000..0b75177 --- /dev/null +++ b/tests/test-notify.cc @@ -0,0 +1,59 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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 . + * + * Authors: + * Charles Kerr + */ + +#include "device.h" +#include "service.h" + +#include + +#include + +class NotifyTest : public ::testing::Test +{ + private: + + typedef ::testing::Test super; + + protected: + + virtual void SetUp() + { + super::SetUp(); + } + + virtual void TearDown() + { + super::TearDown(); + } +}; + +/*** +**** +***/ + +// mock device provider + +// send notifications of a device going down from 50% to 3% by 1% increments + +// popup should appear exactly twice: once at 10%, once at 5% + +TEST_F(NotifyTest, HelloWorld) +{ +} + -- cgit v1.2.3 From d5b42752328be0a217c1271656523399b8056f58 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 21 Jul 2014 12:21:54 -0500 Subject: add dbus-test-runner as a build dependency for tests --- tests/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4489bdc..64b8ed8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,6 +5,11 @@ add_library (gtest STATIC set_target_properties (gtest PROPERTIES INCLUDE_DIRECTORIES ${INCLUDE_DIRECTORIES} ${GTEST_INCLUDE_DIR}) set_target_properties (gtest PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} -w) +# dbustest +pkg_check_modules(DBUSTEST REQUIRED + dbustest-1>=14.04.0) +include_directories (SYSTEM ${DBUSTEST_INCLUDE_DIRS}) + # add warnings set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${C_WARNING_ARGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-weak-vtables -Wno-global-constructors") # Google Test @@ -40,7 +45,7 @@ function(add_test_by_name name) add_executable (${TEST_NAME} ${TEST_NAME}.cc gschemas.compiled) add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatorpowerservice) - target_link_libraries (${TEST_NAME} indicatorpowerservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) + target_link_libraries (${TEST_NAME} indicatorpowerservice gtest ${DBUSTEST_LIBRARIES} ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) endfunction() add_test_by_name(test-notify) add_test(NAME dear-reader-the-next-test-takes-80-seconds COMMAND true) -- cgit v1.2.3 From 125f6f6d02e195a668704ac8f8c7ca87940432b9 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 21 Jul 2014 12:32:49 -0500 Subject: copy the dbus-test-runner/dbusmock scaffolding for freedesktop Notifications from the indicator-datetime tests --- tests/glib-fixture.h | 138 ++++++++++++++++++++++++++++++++++++ tests/test-notify.cc | 192 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 316 insertions(+), 14 deletions(-) create mode 100644 tests/glib-fixture.h (limited to 'tests') diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h new file mode 100644 index 0000000..46b9640 --- /dev/null +++ b/tests/glib-fixture.h @@ -0,0 +1,138 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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 . + */ + +#include + +#include +#include +#include + +#include + +#include // setlocale() + +class GlibFixture : public ::testing::Test +{ + private: + + //GLogFunc realLogHandler; + + protected: + + std::map logCounts; + + void testLogCount(GLogLevelFlags log_level, int /*expected*/) + { +#if 0 + EXPECT_EQ(expected, logCounts[log_level]); +#endif + + logCounts.erase(log_level); + } + + private: + + static void default_log_handler(const gchar * log_domain, + GLogLevelFlags log_level, + const gchar * message, + gpointer self) + { + g_print("%s - %d - %s\n", log_domain, (int)log_level, message); + static_cast(self)->logCounts[log_level]++; + } + + protected: + + virtual void SetUp() + { + setlocale(LC_ALL, "C.UTF-8"); + + loop = g_main_loop_new(nullptr, false); + + //g_log_set_default_handler(default_log_handler, this); + + // only use local, temporary settings + g_assert(g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, true)); + g_assert(g_setenv("GSETTINGS_BACKEND", "memory", true)); + g_debug("SCHEMA_DIR is %s", SCHEMA_DIR); + + g_unsetenv("DISPLAY"); + + } + + virtual void TearDown() + { +#if 0 + // confirm there aren't any unexpected log messages + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_ERROR]); + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_CRITICAL]); + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_WARNING]); + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_MESSAGE]); + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_INFO]); +#endif + + // revert to glib's log handler + //g_log_set_default_handler(realLogHandler, this); + + g_clear_pointer(&loop, g_main_loop_unref); + } + + private: + + static gboolean + wait_for_signal__timeout(gpointer name) + { + g_error("%s: timed out waiting for signal '%s'", G_STRLOC, (char*)name); + return G_SOURCE_REMOVE; + } + + static gboolean + wait_msec__timeout(gpointer loop) + { + g_main_loop_quit(static_cast(loop)); + return G_SOURCE_CONTINUE; + } + + protected: + + /* convenience func to loop while waiting for a GObject's signal */ + void wait_for_signal(gpointer o, const gchar * signal, unsigned int timeout_seconds=5u) + { + // wait for the signal or for timeout, whichever comes first + const auto handler_id = g_signal_connect_swapped(o, signal, + G_CALLBACK(g_main_loop_quit), + loop); + const auto timeout_id = g_timeout_add_seconds(timeout_seconds, + wait_for_signal__timeout, + loop); + g_main_loop_run(loop); + g_source_remove(timeout_id); + g_signal_handler_disconnect(o, handler_id); + } + + /* convenience func to loop for N msec */ + void wait_msec(unsigned int msec=50u) + { + const auto id = g_timeout_add(msec, wait_msec__timeout, loop); + g_main_loop_run(loop); + g_source_remove(id); + } + + GMainLoop * loop; +}; diff --git a/tests/test-notify.cc b/tests/test-notify.cc index 0b75177..6ac3d82 100644 --- a/tests/test-notify.cc +++ b/tests/test-notify.cc @@ -17,30 +17,119 @@ * Charles Kerr */ + +#include "glib-fixture.h" + #include "device.h" -#include "service.h" +#include "notifier.h" #include +#include + +#include + +#include #include -class NotifyTest : public ::testing::Test +/*** +**** +***/ + +class NotifyFixture: public GlibFixture { - private: +private: + + typedef GlibFixture super; + + static constexpr char const * NOTIFY_BUSNAME {"org.freedesktop.Notifications"}; + static constexpr char const * NOTIFY_INTERFACE {"org.freedesktop.Notifications"}; + static constexpr char const * NOTIFY_PATH {"/org/freedesktop/Notifications"}; + + DbusTestService * service = nullptr; + DbusTestDbusMock * mock = nullptr; + DbusTestDbusMockObject * obj = nullptr; + GDBusConnection * bus = nullptr; + +protected: + + static constexpr int NOTIFY_ID {1234}; + + static constexpr int NOTIFICATION_CLOSED_EXPIRED {1}; + static constexpr int NOTIFICATION_CLOSED_DISMISSED {2}; + static constexpr int NOTIFICATION_CLOSED_API {3}; + static constexpr int NOTIFICATION_CLOSED_UNDEFINED {4}; + + static constexpr char const * APP_NAME {"indicator-power-service"}; + + static constexpr char const * METHOD_NOTIFY {"Notify"}; + static constexpr char const * METHOD_GET_CAPS {"GetCapabilities"}; + static constexpr char const * METHOD_GET_INFO {"GetServerInformation"}; + + static constexpr char const * HINT_TIMEOUT {"x-canonical-snap-decisions-timeout"}; + +protected: + + void SetUp() + { + super::SetUp(); + + // init DBusMock / dbus-test-runner + + service = dbus_test_service_new(NULL); + + GError * error = NULL; + mock = dbus_test_dbus_mock_new(NOTIFY_BUSNAME); + obj = dbus_test_dbus_mock_get_object(mock, NOTIFY_PATH, NOTIFY_INTERFACE, &error); + g_assert_no_error (error); + + dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_GET_INFO, + NULL, + G_VARIANT_TYPE("(ssss)"), + "ret = ('mock-notify', 'test vendor', '1.0', '1.1')", // python + &error); + g_assert_no_error (error); + + auto python_str = g_strdup_printf ("ret = %d", NOTIFY_ID); + dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_NOTIFY, + G_VARIANT_TYPE("(susssasa{sv}i)"), + G_VARIANT_TYPE_UINT32, + python_str, + &error); + g_free (python_str); + g_assert_no_error (error); + + dbus_test_service_add_task(service, DBUS_TEST_TASK(mock)); + dbus_test_service_start_tasks(service); + + bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); + g_dbus_connection_set_exit_on_close(bus, FALSE); + g_object_add_weak_pointer(G_OBJECT(bus), (gpointer *)&bus); - typedef ::testing::Test super; + notify_init(APP_NAME); + } - protected: + virtual void TearDown() + { + notify_uninit(); - virtual void SetUp() - { - super::SetUp(); - } + g_clear_object(&mock); + g_clear_object(&service); + g_object_unref(bus); - virtual void TearDown() - { - super::TearDown(); - } + // wait a little while for the scaffolding to shut down, + // but don't block on it forever... + unsigned int cleartry = 0; + while ((bus != NULL) && (cleartry < 50)) + { + g_usleep(100000); + while (g_main_pending()) + g_main_iteration(true); + cleartry++; + } + + super::TearDown(); + } }; /*** @@ -53,7 +142,82 @@ class NotifyTest : public ::testing::Test // popup should appear exactly twice: once at 10%, once at 5% -TEST_F(NotifyTest, HelloWorld) +TEST_F(NotifyFixture, HelloWorld) { } +#if 0 +using namespace unity::indicator::datetime; + +/*** +**** +***/ + +using namespace unity::indicator::datetime; + +class SnapFixture: public GlibFixture +{ + +/*** +**** +***/ + +namespace +{ + gboolean quit_idle (gpointer gloop) + { + g_main_loop_quit(static_cast(gloop)); + return G_SOURCE_REMOVE; + }; +} + +TEST_F(SnapFixture, InteractiveDuration) +{ + static constexpr int duration_minutes = 120; + auto settings = std::make_shared(); + settings->alarm_duration.set(duration_minutes); + auto timezones = std::make_shared(); + auto clock = std::make_shared(timezones); + Snap snap (clock, settings); + + // GetCapabilities returns an array containing 'actions', + // so our snap decision will be interactive. + // For this test, it means we should get a timeout Notify Hint + // that matches duration_minutes + GError * error = nullptr; + dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_GET_CAPS, nullptr, G_VARIANT_TYPE_STRING_ARRAY, "ret = ['actions', 'body']", &error); + g_assert_no_error (error); + + // call the Snap Decision + auto func = [this](const Appointment&){g_idle_add(quit_idle, loop);}; + snap(appt, func, func); + + // confirm that Notify got called once + guint len = 0; + auto calls = dbus_test_dbus_mock_object_get_method_calls (mock, obj, METHOD_NOTIFY, &len, &error); + g_assert_no_error(error); + ASSERT_EQ(1, len); + + // confirm that the app_name passed to Notify was APP_NAME + const auto& params = calls[0].params; + ASSERT_EQ(8, g_variant_n_children(params)); + const char * str = nullptr; + g_variant_get_child (params, 0, "&s", &str); + ASSERT_STREQ(APP_NAME, str); + + // confirm that the icon passed to Notify was "alarm-clock" + g_variant_get_child (params, 2, "&s", &str); + ASSERT_STREQ("alarm-clock", str); + + // confirm that the hints passed to Notify included a timeout matching duration_minutes + int32_t i32; + bool b; + auto hints = g_variant_get_child_value (params, 6); + b = g_variant_lookup (hints, HINT_TIMEOUT, "i", &i32); + EXPECT_TRUE(b); + const auto duration = std::chrono::minutes(duration_minutes); + EXPECT_EQ(std::chrono::duration_cast(duration).count(), i32); + g_variant_unref(hints); +} +#endif + -- cgit v1.2.3 From 2cb851b018c5e7a0278dab75f73bb031c7c42422 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 21 Jul 2014 16:08:29 -0500 Subject: add tests to confirm that the DBus object's PowerLevel property changes at the right times (and only at the right times) when the battery is draining --- tests/test-notify.cc | 208 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 131 insertions(+), 77 deletions(-) (limited to 'tests') diff --git a/tests/test-notify.cc b/tests/test-notify.cc index 6ac3d82..cf1f9d3 100644 --- a/tests/test-notify.cc +++ b/tests/test-notify.cc @@ -20,6 +20,7 @@ #include "glib-fixture.h" +#include "dbus-shared.h" #include "device.h" #include "notifier.h" @@ -49,10 +50,11 @@ private: DbusTestService * service = nullptr; DbusTestDbusMock * mock = nullptr; DbusTestDbusMockObject * obj = nullptr; - GDBusConnection * bus = nullptr; protected: + GDBusConnection * bus = nullptr; + static constexpr int NOTIFY_ID {1234}; static constexpr int NOTIFICATION_CLOSED_EXPIRED {1}; @@ -76,15 +78,15 @@ protected: // init DBusMock / dbus-test-runner - service = dbus_test_service_new(NULL); + service = dbus_test_service_new(nullptr); - GError * error = NULL; + GError * error = nullptr; mock = dbus_test_dbus_mock_new(NOTIFY_BUSNAME); obj = dbus_test_dbus_mock_get_object(mock, NOTIFY_PATH, NOTIFY_INTERFACE, &error); g_assert_no_error (error); dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_GET_INFO, - NULL, + nullptr, G_VARIANT_TYPE("(ssss)"), "ret = ('mock-notify', 'test vendor', '1.0', '1.1')", // python &error); @@ -102,7 +104,7 @@ protected: dbus_test_service_add_task(service, DBUS_TEST_TASK(mock)); dbus_test_service_start_tasks(service); - bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); + bus = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr); g_dbus_connection_set_exit_on_close(bus, FALSE); g_object_add_weak_pointer(G_OBJECT(bus), (gpointer *)&bus); @@ -120,7 +122,7 @@ protected: // wait a little while for the scaffolding to shut down, // but don't block on it forever... unsigned int cleartry = 0; - while ((bus != NULL) && (cleartry < 50)) + while ((bus != nullptr) && (cleartry < 50)) { g_usleep(100000); while (g_main_pending()) @@ -136,88 +138,140 @@ protected: **** ***/ -// mock device provider - -// send notifications of a device going down from 50% to 3% by 1% increments - -// popup should appear exactly twice: once at 10%, once at 5% - +// simple test to confirm the NotifyFixture plumbing all works TEST_F(NotifyFixture, HelloWorld) { } -#if 0 -using namespace unity::indicator::datetime; - -/*** -**** -***/ - -using namespace unity::indicator::datetime; - -class SnapFixture: public GlibFixture -{ - -/*** -**** -***/ +// scaffolding to listen for PropertyChanged signals and remember them namespace { - gboolean quit_idle (gpointer gloop) + enum { - g_main_loop_quit(static_cast(gloop)); - return G_SOURCE_REMOVE; + FIELD_POWER_LEVEL = (1<<0), + FIELD_IS_WARNING = (1<<1) }; + + struct ChangedParams + { + int32_t power_level = POWER_LEVEL_OK; + bool is_warning = false; + uint32_t fields = 0; + }; + + void on_battery_property_changed (GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender_name G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *signal_name G_GNUC_UNUSED, + GVariant *parameters, + gpointer gchanged_params) + { + g_return_if_fail (g_variant_n_children (parameters) == 3); + auto changed_properties = g_variant_get_child_value (parameters, 1); + g_return_if_fail (g_variant_is_of_type (changed_properties, G_VARIANT_TYPE_DICTIONARY)); + auto changed_params = static_cast(gchanged_params); + + gint32 power_level; + if (g_variant_lookup (changed_properties, "PowerLevel", "i", &power_level, nullptr)) + { + changed_params->power_level = power_level; + changed_params->fields |= FIELD_POWER_LEVEL; + } + + gboolean is_warning; + if (g_variant_lookup (changed_properties, "IsWarning", "b", &is_warning, nullptr)) + { + changed_params->is_warning = is_warning; + changed_params->fields |= FIELD_IS_WARNING; + } + + g_variant_unref (changed_properties); + } +} + +TEST_F(NotifyFixture, PercentageToLevel) +{ + auto battery = indicator_power_device_new ("/object/path", + UP_DEVICE_KIND_BATTERY, + 50.0, + UP_DEVICE_STATE_DISCHARGING, + 30); + + // confirm that the power levels trigger at the right percentages + for (int i=100; i>=0; --i) + { + g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, (gdouble)i, nullptr); + const auto level = indicator_power_notifier_get_power_level(battery); + + if (i <= 2) + EXPECT_EQ (POWER_LEVEL_CRITICAL, level); + else if (i <= 5) + EXPECT_EQ (POWER_LEVEL_VERY_LOW, level); + else if (i <= 10) + EXPECT_EQ (POWER_LEVEL_LOW, level); + else + EXPECT_EQ (POWER_LEVEL_OK, level); + } + + g_object_unref (battery); } -TEST_F(SnapFixture, InteractiveDuration) + +TEST_F(NotifyFixture, LevelsDuringBatteryDrain) { - static constexpr int duration_minutes = 120; - auto settings = std::make_shared(); - settings->alarm_duration.set(duration_minutes); - auto timezones = std::make_shared(); - auto clock = std::make_shared(timezones); - Snap snap (clock, settings); - - // GetCapabilities returns an array containing 'actions', - // so our snap decision will be interactive. - // For this test, it means we should get a timeout Notify Hint - // that matches duration_minutes - GError * error = nullptr; - dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_GET_CAPS, nullptr, G_VARIANT_TYPE_STRING_ARRAY, "ret = ['actions', 'body']", &error); - g_assert_no_error (error); - - // call the Snap Decision - auto func = [this](const Appointment&){g_idle_add(quit_idle, loop);}; - snap(appt, func, func); - - // confirm that Notify got called once - guint len = 0; - auto calls = dbus_test_dbus_mock_object_get_method_calls (mock, obj, METHOD_NOTIFY, &len, &error); - g_assert_no_error(error); - ASSERT_EQ(1, len); - - // confirm that the app_name passed to Notify was APP_NAME - const auto& params = calls[0].params; - ASSERT_EQ(8, g_variant_n_children(params)); - const char * str = nullptr; - g_variant_get_child (params, 0, "&s", &str); - ASSERT_STREQ(APP_NAME, str); - - // confirm that the icon passed to Notify was "alarm-clock" - g_variant_get_child (params, 2, "&s", &str); - ASSERT_STREQ("alarm-clock", str); - - // confirm that the hints passed to Notify included a timeout matching duration_minutes - int32_t i32; - bool b; - auto hints = g_variant_get_child_value (params, 6); - b = g_variant_lookup (hints, HINT_TIMEOUT, "i", &i32); - EXPECT_TRUE(b); - const auto duration = std::chrono::minutes(duration_minutes); - EXPECT_EQ(std::chrono::duration_cast(duration).count(), i32); - g_variant_unref(hints); + auto battery = indicator_power_device_new ("/object/path", + UP_DEVICE_KIND_BATTERY, + 50.0, + UP_DEVICE_STATE_DISCHARGING, + 30); + + // set up a notifier and give it the battery so changing the battery's + // charge should show up on the bus. + auto notifier = indicator_power_notifier_new (); + indicator_power_notifier_set_battery (notifier, battery); + indicator_power_notifier_set_bus (notifier, bus); + wait_msec(); + + ChangedParams changed_params; + auto subscription_tag = g_dbus_connection_signal_subscribe (bus, + nullptr, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + BUS_PATH"/Battery", + nullptr, + G_DBUS_SIGNAL_FLAGS_NONE, + on_battery_property_changed, + &changed_params, + nullptr); + + // confirm that draining the battery puts + // the power_level change through its paces + for (int i=100; i>=0; --i) + { + changed_params = ChangedParams(); + EXPECT_TRUE (changed_params.fields == 0); + + const auto old_level = indicator_power_notifier_get_power_level(battery); + g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, (gdouble)i, nullptr); + const auto new_level = indicator_power_notifier_get_power_level(battery); + wait_msec(); + + if (old_level == new_level) + { + EXPECT_EQ (0, changed_params.fields); + } + else + { + EXPECT_EQ (FIELD_POWER_LEVEL, changed_params.fields); + EXPECT_EQ (new_level, changed_params.power_level); + } + } + + // cleanup + g_dbus_connection_signal_unsubscribe (bus, subscription_tag); + g_object_unref (notifier); + g_object_unref (battery); } -#endif -- cgit v1.2.3 From 1bd1b700892a786fd01410a4b81b2f2cc93a89c1 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 21 Jul 2014 17:13:15 -0500 Subject: add tests for events that change whether or not a 'low battery' notification is being shown --- tests/test-notify.cc | 97 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/test-notify.cc b/tests/test-notify.cc index cf1f9d3..d5d7e9f 100644 --- a/tests/test-notify.cc +++ b/tests/test-notify.cc @@ -47,12 +47,11 @@ private: static constexpr char const * NOTIFY_INTERFACE {"org.freedesktop.Notifications"}; static constexpr char const * NOTIFY_PATH {"/org/freedesktop/Notifications"}; +protected: + DbusTestService * service = nullptr; DbusTestDbusMock * mock = nullptr; DbusTestDbusMockObject * obj = nullptr; - -protected: - GDBusConnection * bus = nullptr; static constexpr int NOTIFY_ID {1234}; @@ -189,6 +188,10 @@ namespace g_variant_unref (changed_properties); } + + static const double percent_critical = 2.0; + static const double percent_very_low = 5.0; + static const double percent_low = 10.0; } TEST_F(NotifyFixture, PercentageToLevel) @@ -205,11 +208,11 @@ TEST_F(NotifyFixture, PercentageToLevel) g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, (gdouble)i, nullptr); const auto level = indicator_power_notifier_get_power_level(battery); - if (i <= 2) + if (i <= percent_critical) EXPECT_EQ (POWER_LEVEL_CRITICAL, level); - else if (i <= 5) + else if (i <= percent_very_low) EXPECT_EQ (POWER_LEVEL_VERY_LOW, level); - else if (i <= 10) + else if (i <= percent_low) EXPECT_EQ (POWER_LEVEL_LOW, level); else EXPECT_EQ (POWER_LEVEL_OK, level); @@ -275,3 +278,85 @@ TEST_F(NotifyFixture, LevelsDuringBatteryDrain) g_object_unref (battery); } + +// confirm that notifications pop up +// when a discharging battery's power level drops +TEST_F(NotifyFixture, EventsThatChangeNotifications) +{ + // GetCapabilities returns an array containing 'actions', so that we'll + // get snap decisions and the 'IsWarning' property + GError * error = nullptr; + dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_GET_CAPS, nullptr, G_VARIANT_TYPE_STRING_ARRAY, "ret = ['actions', 'body']", &error); + g_assert_no_error (error); + + auto battery = indicator_power_device_new ("/object/path", + UP_DEVICE_KIND_BATTERY, + percent_low + 1.0, + UP_DEVICE_STATE_DISCHARGING, + 30); + + // set up a notifier and give it the battery so changing the battery's + // charge should show up on the bus. + auto notifier = indicator_power_notifier_new (); + indicator_power_notifier_set_battery (notifier, battery); + indicator_power_notifier_set_bus (notifier, bus); + ChangedParams changed_params; + auto subscription_tag = g_dbus_connection_signal_subscribe (bus, + nullptr, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + BUS_PATH"/Battery", + nullptr, + G_DBUS_SIGNAL_FLAGS_NONE, + on_battery_property_changed, + &changed_params, + nullptr); + + // test setup case + wait_msec(); + EXPECT_EQ (0, changed_params.power_level); + + // change the percent past the 'low' threshold and confirm that + // a) the power level changes + // b) we get a notification + changed_params = ChangedParams(); + g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, (gdouble)percent_low, nullptr); + wait_msec(); + EXPECT_EQ (FIELD_POWER_LEVEL|FIELD_IS_WARNING, changed_params.fields); + EXPECT_EQ (indicator_power_notifier_get_power_level(battery), changed_params.power_level); + EXPECT_TRUE (changed_params.is_warning); + + // now test that the warning changes if the level goes down even lower... + changed_params = ChangedParams(); + g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, (gdouble)percent_very_low, nullptr); + wait_msec(); + EXPECT_EQ (FIELD_POWER_LEVEL, changed_params.fields); + EXPECT_EQ (POWER_LEVEL_VERY_LOW, changed_params.power_level); + + // ...and that the warning is taken down if the battery is plugged back in... + changed_params = ChangedParams(); + g_object_set (battery, INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_CHARGING, nullptr); + wait_msec(); + EXPECT_EQ (FIELD_IS_WARNING, changed_params.fields); + EXPECT_FALSE (changed_params.is_warning); + + // ...and that it comes back if we unplug again... + changed_params = ChangedParams(); + g_object_set (battery, INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_DISCHARGING, nullptr); + wait_msec(); + EXPECT_EQ (FIELD_IS_WARNING, changed_params.fields); + EXPECT_TRUE (changed_params.is_warning); + + // ...and that it's taken down if the power level is OK + changed_params = ChangedParams(); + g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, (gdouble)percent_low+1, nullptr); + wait_msec(); + EXPECT_EQ (FIELD_POWER_LEVEL|FIELD_IS_WARNING, changed_params.fields); + EXPECT_EQ (POWER_LEVEL_OK, changed_params.power_level); + EXPECT_FALSE (changed_params.is_warning); + + // cleanup + g_dbus_connection_signal_unsubscribe (bus, subscription_tag); + g_object_unref (notifier); + g_object_unref (battery); +} -- cgit v1.2.3 From 9c3d863d5e0ebe35ae69dedb5219519f0ced9339 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 22 Jul 2014 09:53:53 -0500 Subject: copyediting: code cleanup --- tests/test-notify.cc | 163 +++++++++++++++++++++++++++++---------------------- 1 file changed, 94 insertions(+), 69 deletions(-) (limited to 'tests') diff --git a/tests/test-notify.cc b/tests/test-notify.cc index d5d7e9f..74a08dc 100644 --- a/tests/test-notify.cc +++ b/tests/test-notify.cc @@ -81,13 +81,16 @@ protected: GError * error = nullptr; mock = dbus_test_dbus_mock_new(NOTIFY_BUSNAME); - obj = dbus_test_dbus_mock_get_object(mock, NOTIFY_PATH, NOTIFY_INTERFACE, &error); + obj = dbus_test_dbus_mock_get_object(mock, + NOTIFY_PATH, + NOTIFY_INTERFACE, + &error); g_assert_no_error (error); dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_GET_INFO, nullptr, G_VARIANT_TYPE("(ssss)"), - "ret = ('mock-notify', 'test vendor', '1.0', '1.1')", // python + "ret = ('mock-notify', 'test vendor', '1.0', '1.1')", &error); g_assert_no_error (error); @@ -142,8 +145,55 @@ TEST_F(NotifyFixture, HelloWorld) { } +/*** +**** +***/ + + +namespace +{ + static constexpr double percent_critical {2.0}; + static constexpr double percent_very_low {5.0}; + static constexpr double percent_low {10.0}; + + void set_battery_percentage (IndicatorPowerDevice * battery, gdouble p) + { + g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, p, nullptr); + } +} + +TEST_F(NotifyFixture, PercentageToLevel) +{ + auto battery = indicator_power_device_new ("/object/path", + UP_DEVICE_KIND_BATTERY, + 50.0, + UP_DEVICE_STATE_DISCHARGING, + 30); + + // confirm that the power levels trigger at the right percentages + for (int i=100; i>=0; --i) + { + set_battery_percentage (battery, i); + const auto level = indicator_power_notifier_get_power_level(battery); + + if (i <= percent_critical) + EXPECT_EQ (POWER_LEVEL_CRITICAL, level); + else if (i <= percent_very_low) + EXPECT_EQ (POWER_LEVEL_VERY_LOW, level); + else if (i <= percent_low) + EXPECT_EQ (POWER_LEVEL_LOW, level); + else + EXPECT_EQ (POWER_LEVEL_OK, level); + } + + g_object_unref (battery); +} + +/*** +**** +***/ -// scaffolding to listen for PropertyChanged signals and remember them +// scaffolding to monitor PropertyChanged signals namespace { enum @@ -168,60 +218,28 @@ namespace gpointer gchanged_params) { g_return_if_fail (g_variant_n_children (parameters) == 3); - auto changed_properties = g_variant_get_child_value (parameters, 1); - g_return_if_fail (g_variant_is_of_type (changed_properties, G_VARIANT_TYPE_DICTIONARY)); + auto dict = g_variant_get_child_value (parameters, 1); + g_return_if_fail (g_variant_is_of_type (dict, G_VARIANT_TYPE_DICTIONARY)); auto changed_params = static_cast(gchanged_params); gint32 power_level; - if (g_variant_lookup (changed_properties, "PowerLevel", "i", &power_level, nullptr)) + if (g_variant_lookup (dict, "PowerLevel", "i", &power_level, nullptr)) { changed_params->power_level = power_level; changed_params->fields |= FIELD_POWER_LEVEL; } gboolean is_warning; - if (g_variant_lookup (changed_properties, "IsWarning", "b", &is_warning, nullptr)) + if (g_variant_lookup (dict, "IsWarning", "b", &is_warning, nullptr)) { changed_params->is_warning = is_warning; changed_params->fields |= FIELD_IS_WARNING; } - g_variant_unref (changed_properties); + g_variant_unref (dict); } - - static const double percent_critical = 2.0; - static const double percent_very_low = 5.0; - static const double percent_low = 10.0; -} - -TEST_F(NotifyFixture, PercentageToLevel) -{ - auto battery = indicator_power_device_new ("/object/path", - UP_DEVICE_KIND_BATTERY, - 50.0, - UP_DEVICE_STATE_DISCHARGING, - 30); - - // confirm that the power levels trigger at the right percentages - for (int i=100; i>=0; --i) - { - g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, (gdouble)i, nullptr); - const auto level = indicator_power_notifier_get_power_level(battery); - - if (i <= percent_critical) - EXPECT_EQ (POWER_LEVEL_CRITICAL, level); - else if (i <= percent_very_low) - EXPECT_EQ (POWER_LEVEL_VERY_LOW, level); - else if (i <= percent_low) - EXPECT_EQ (POWER_LEVEL_LOW, level); - else - EXPECT_EQ (POWER_LEVEL_OK, level); - } - - g_object_unref (battery); } - TEST_F(NotifyFixture, LevelsDuringBatteryDrain) { auto battery = indicator_power_device_new ("/object/path", @@ -238,16 +256,16 @@ TEST_F(NotifyFixture, LevelsDuringBatteryDrain) wait_msec(); ChangedParams changed_params; - auto subscription_tag = g_dbus_connection_signal_subscribe (bus, - nullptr, - "org.freedesktop.DBus.Properties", - "PropertiesChanged", - BUS_PATH"/Battery", - nullptr, - G_DBUS_SIGNAL_FLAGS_NONE, - on_battery_property_changed, - &changed_params, - nullptr); + auto sub_tag = g_dbus_connection_signal_subscribe (bus, + nullptr, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + BUS_PATH"/Battery", + nullptr, + G_DBUS_SIGNAL_FLAGS_NONE, + on_battery_property_changed, + &changed_params, + nullptr); // confirm that draining the battery puts // the power_level change through its paces @@ -257,7 +275,7 @@ TEST_F(NotifyFixture, LevelsDuringBatteryDrain) EXPECT_TRUE (changed_params.fields == 0); const auto old_level = indicator_power_notifier_get_power_level(battery); - g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, (gdouble)i, nullptr); + set_battery_percentage (battery, i); const auto new_level = indicator_power_notifier_get_power_level(battery); wait_msec(); @@ -273,20 +291,27 @@ TEST_F(NotifyFixture, LevelsDuringBatteryDrain) } // cleanup - g_dbus_connection_signal_unsubscribe (bus, subscription_tag); + g_dbus_connection_signal_unsubscribe (bus, sub_tag); g_object_unref (notifier); g_object_unref (battery); } +/*** +**** +***/ -// confirm that notifications pop up -// when a discharging battery's power level drops TEST_F(NotifyFixture, EventsThatChangeNotifications) { // GetCapabilities returns an array containing 'actions', so that we'll // get snap decisions and the 'IsWarning' property GError * error = nullptr; - dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_GET_CAPS, nullptr, G_VARIANT_TYPE_STRING_ARRAY, "ret = ['actions', 'body']", &error); + dbus_test_dbus_mock_object_add_method (mock, + obj, + METHOD_GET_CAPS, + nullptr, + G_VARIANT_TYPE_STRING_ARRAY, + "ret = ['actions', 'body']", + &error); g_assert_no_error (error); auto battery = indicator_power_device_new ("/object/path", @@ -301,16 +326,16 @@ TEST_F(NotifyFixture, EventsThatChangeNotifications) indicator_power_notifier_set_battery (notifier, battery); indicator_power_notifier_set_bus (notifier, bus); ChangedParams changed_params; - auto subscription_tag = g_dbus_connection_signal_subscribe (bus, - nullptr, - "org.freedesktop.DBus.Properties", - "PropertiesChanged", - BUS_PATH"/Battery", - nullptr, - G_DBUS_SIGNAL_FLAGS_NONE, - on_battery_property_changed, - &changed_params, - nullptr); + auto sub_tag = g_dbus_connection_signal_subscribe (bus, + nullptr, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + BUS_PATH"/Battery", + nullptr, + G_DBUS_SIGNAL_FLAGS_NONE, + on_battery_property_changed, + &changed_params, + nullptr); // test setup case wait_msec(); @@ -320,7 +345,7 @@ TEST_F(NotifyFixture, EventsThatChangeNotifications) // a) the power level changes // b) we get a notification changed_params = ChangedParams(); - g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, (gdouble)percent_low, nullptr); + set_battery_percentage (battery, percent_low); wait_msec(); EXPECT_EQ (FIELD_POWER_LEVEL|FIELD_IS_WARNING, changed_params.fields); EXPECT_EQ (indicator_power_notifier_get_power_level(battery), changed_params.power_level); @@ -328,7 +353,7 @@ TEST_F(NotifyFixture, EventsThatChangeNotifications) // now test that the warning changes if the level goes down even lower... changed_params = ChangedParams(); - g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, (gdouble)percent_very_low, nullptr); + set_battery_percentage (battery, percent_very_low); wait_msec(); EXPECT_EQ (FIELD_POWER_LEVEL, changed_params.fields); EXPECT_EQ (POWER_LEVEL_VERY_LOW, changed_params.power_level); @@ -349,14 +374,14 @@ TEST_F(NotifyFixture, EventsThatChangeNotifications) // ...and that it's taken down if the power level is OK changed_params = ChangedParams(); - g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, (gdouble)percent_low+1, nullptr); + set_battery_percentage (battery, percent_low+1); wait_msec(); EXPECT_EQ (FIELD_POWER_LEVEL|FIELD_IS_WARNING, changed_params.fields); EXPECT_EQ (POWER_LEVEL_OK, changed_params.power_level); EXPECT_FALSE (changed_params.is_warning); // cleanup - g_dbus_connection_signal_unsubscribe (bus, subscription_tag); + g_dbus_connection_signal_unsubscribe (bus, sub_tag); g_object_unref (notifier); g_object_unref (battery); } -- cgit v1.2.3 From 399eab24252eb7c83042de726ebbc4cf9fbb7637 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 22 Jul 2014 09:58:29 -0500 Subject: add indicator-power-service-cmdline-battery, a manual test utility to test the indicator's behavior when a battery's state changes --- tests/CMakeLists.txt | 8 ++ tests/device-provider-mock.c | 107 +++++++++++++++++++ tests/device-provider-mock.h | 79 +++++++++++++++ tests/indicator-power-service-cmdline-battery.cc | 124 +++++++++++++++++++++++ 4 files changed, 318 insertions(+) create mode 100644 tests/device-provider-mock.c create mode 100644 tests/device-provider-mock.h create mode 100644 tests/indicator-power-service-cmdline-battery.cc (limited to 'tests') diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 64b8ed8..a0d24af 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -51,3 +51,11 @@ add_test_by_name(test-notify) add_test(NAME dear-reader-the-next-test-takes-80-seconds COMMAND true) add_test_by_name(test-device) +### +### + +set (APP_NAME indicator-power-service-cmdline-battery) +add_executable (${APP_NAME} ${APP_NAME}.cc device-provider-mock.c) +add_dependencies (${APP_NAME} libindicatorpowerservice) +target_link_libraries (${APP_NAME} indicatorpowerservice ${SERVICE_DEPS_LIBRARIES}) + diff --git a/tests/device-provider-mock.c b/tests/device-provider-mock.c new file mode 100644 index 0000000..afca178 --- /dev/null +++ b/tests/device-provider-mock.c @@ -0,0 +1,107 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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 . + * + * Authors: + * Charles Kerr + */ + +#include "device.h" +#include "device-provider.h" +#include "device-provider-mock.h" + +/*** +**** GObject boilerplate +***/ + +static void indicator_power_device_provider_interface_init ( + IndicatorPowerDeviceProviderInterface * iface); + +G_DEFINE_TYPE_WITH_CODE ( + IndicatorPowerDeviceProviderMock, + indicator_power_device_provider_mock, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (INDICATOR_TYPE_POWER_DEVICE_PROVIDER, + indicator_power_device_provider_interface_init)) + +/*** +**** IndicatorPowerDeviceProvider virtual functions +***/ + +static GList * +my_get_devices (IndicatorPowerDeviceProvider * provider) +{ + IndicatorPowerDeviceProviderMock * self = INDICATOR_POWER_DEVICE_PROVIDER_MOCK(provider); + + return g_list_copy_deep (self->devices, (GCopyFunc)g_object_ref, NULL); +} + +/*** +**** GObject virtual functions +***/ + +static void +my_dispose (GObject * o) +{ + IndicatorPowerDeviceProviderMock * self = INDICATOR_POWER_DEVICE_PROVIDER_MOCK(o); + + g_list_free_full (self->devices, g_object_unref); + + G_OBJECT_CLASS (indicator_power_device_provider_mock_parent_class)->dispose (o); +} + +/*** +**** Instantiation +***/ + +static void +indicator_power_device_provider_mock_class_init (IndicatorPowerDeviceProviderMockClass * klass) +{ + GObjectClass * object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = my_dispose; +} + +static void +indicator_power_device_provider_interface_init (IndicatorPowerDeviceProviderInterface * iface) +{ + iface->get_devices = my_get_devices; +} + +static void +indicator_power_device_provider_mock_init (IndicatorPowerDeviceProviderMock * self) +{ +} + +/*** +**** Public API +***/ + +IndicatorPowerDeviceProvider * +indicator_power_device_provider_mock_new (void) +{ + gpointer o = g_object_new (INDICATOR_TYPE_POWER_DEVICE_PROVIDER_MOCK, NULL); + + return INDICATOR_POWER_DEVICE_PROVIDER (o); +} + +void +indicator_power_device_provider_add_device (IndicatorPowerDeviceProviderMock * provider, + IndicatorPowerDevice * device) +{ + provider->devices = g_list_append (provider->devices, g_object_ref(device)); + + g_signal_connect_swapped (device, "notify", G_CALLBACK(indicator_power_device_provider_emit_devices_changed), provider); +} diff --git a/tests/device-provider-mock.h b/tests/device-provider-mock.h new file mode 100644 index 0000000..4d06924 --- /dev/null +++ b/tests/device-provider-mock.h @@ -0,0 +1,79 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef __INDICATOR_POWER_DEVICE_PROVIDER_MOCK__H__ +#define __INDICATOR_POWER_DEVICE_PROVIDER_MOCK__H__ + +#include /* parent class */ + +#include "device.h" +#include "device-provider.h" + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_POWER_DEVICE_PROVIDER_MOCK \ + (indicator_power_device_provider_mock_get_type()) + +#define INDICATOR_POWER_DEVICE_PROVIDER_MOCK(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), \ + INDICATOR_TYPE_POWER_DEVICE_PROVIDER_MOCK, \ + IndicatorPowerDeviceProviderMock)) + +#define INDICATOR_POWER_DEVICE_PROVIDER_MOCK_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), \ + INDICATOR_TYPE_POWER_DEVICE_PROVIDER_MOCK, \ + IndicatorPowerDeviceProviderMockClass)) + +#define INDICATOR_IS_POWER_DEVICE_PROVIDER_MOCK(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), \ + INDICATOR_TYPE_POWER_DEVICE_PROVIDER_MOCK)) + +typedef struct _IndicatorPowerDeviceProviderMock + IndicatorPowerDeviceProviderMock; +typedef struct _IndicatorPowerDeviceProviderMockPriv + IndicatorPowerDeviceProviderMockPriv; +typedef struct _IndicatorPowerDeviceProviderMockClass + IndicatorPowerDeviceProviderMockClass; + +/** + * An IndicatorPowerDeviceProvider which gets its devices from Mock. + */ +struct _IndicatorPowerDeviceProviderMock +{ + GObject parent_instance; + + /*< private >*/ + GList * devices; +}; + +struct _IndicatorPowerDeviceProviderMockClass +{ + GObjectClass parent_class; +}; + +GType indicator_power_device_provider_mock_get_type (void); + +IndicatorPowerDeviceProvider * indicator_power_device_provider_mock_new (void); + +void indicator_power_device_provider_add_device (IndicatorPowerDeviceProviderMock * provider, + IndicatorPowerDevice * device); + +G_END_DECLS + +#endif /* __INDICATOR_POWER_DEVICE_PROVIDER_MOCK__H__ */ diff --git a/tests/indicator-power-service-cmdline-battery.cc b/tests/indicator-power-service-cmdline-battery.cc new file mode 100644 index 0000000..a7a86a1 --- /dev/null +++ b/tests/indicator-power-service-cmdline-battery.cc @@ -0,0 +1,124 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#include // setlocale() +#include // bindtextdomain() +#include // STDIN_FILENO + +#include + +#include "device-provider-mock.h" + +#include "service.h" + +/*** +**** +***/ + +static void +on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop) +{ + g_message ("exiting: service couldn't acquire or lost ownership of busname"); + g_main_loop_quit ((GMainLoop*)loop); +} + +static IndicatorPowerDevice * battery = nullptr; + +static GMainLoop * loop = nullptr; + +static gboolean on_command_stream_available (GIOChannel *source, + GIOCondition /*condition*/, + gpointer /*user_data*/) +{ + gchar * str = nullptr; + GError * error = nullptr; + auto status = g_io_channel_read_line (source, &str, nullptr, nullptr, &error); + g_assert_no_error (error); + + if (status == G_IO_STATUS_NORMAL) + { + g_strstrip (str); + + if (!g_strcmp0 (str, "charging")) + { + g_object_set (battery, INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_CHARGING, nullptr); + } + else if (!g_strcmp0 (str, "discharging")) + { + g_object_set (battery, INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_DISCHARGING, nullptr); + } + else + { + g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, atof(str), nullptr); + } + } + else if (status == G_IO_STATUS_EOF) + { + g_main_loop_quit (loop); + } + + g_free (str); + return G_SOURCE_CONTINUE; +} + +/* this is basically indicator-power-service with a custom provider */ +int +main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED) +{ + g_message ("This app is basically the same as indicator-power-service but,\n" + "instead of the system's real devices, sees a single fake battery\n" + "which can be manipulated by typing commands:\n" + "'charging', 'discharging', a charge percentage, or ctrl-c."); + + IndicatorPowerDeviceProvider * device_provider; + IndicatorPowerService * service; + + g_assert(g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, true)); + g_assert(g_setenv("GSETTINGS_BACKEND", "memory", true)); + + /* boilerplate i18n */ + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain (GETTEXT_PACKAGE); + + /* read lines from the command line */ + auto channel = g_io_channel_unix_new (STDIN_FILENO); + auto watch_tag = g_io_add_watch (channel, G_IO_IN, on_command_stream_available, nullptr); + + /* run */ + battery = indicator_power_device_new ("/some/path", UP_DEVICE_KIND_BATTERY, 50.0, UP_DEVICE_STATE_DISCHARGING, 30*60); + device_provider = indicator_power_device_provider_mock_new (); + indicator_power_device_provider_add_device (INDICATOR_POWER_DEVICE_PROVIDER_MOCK(device_provider), battery); + service = indicator_power_service_new (device_provider); + loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (service, INDICATOR_POWER_SERVICE_SIGNAL_NAME_LOST, + G_CALLBACK(on_name_lost), loop); + g_main_loop_run (loop); + + /* cleanup */ + g_main_loop_unref (loop); + g_source_remove (watch_tag); + g_io_channel_unref (channel); + g_clear_object (&service); + g_clear_object (&device_provider); + g_clear_object (&battery); + return 0; +} -- cgit v1.2.3 From 584aa80f415597a2970bfd29fa0d48b9a8842086 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 22 Jul 2014 11:59:03 -0500 Subject: Make the when-does-power-level-change tests work whether or not Ephemeral notifications are being used. --- tests/test-notify.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/test-notify.cc b/tests/test-notify.cc index 74a08dc..1fc843e 100644 --- a/tests/test-notify.cc +++ b/tests/test-notify.cc @@ -281,11 +281,11 @@ TEST_F(NotifyFixture, LevelsDuringBatteryDrain) if (old_level == new_level) { - EXPECT_EQ (0, changed_params.fields); + EXPECT_EQ (0, (changed_params.fields & FIELD_POWER_LEVEL)); } else { - EXPECT_EQ (FIELD_POWER_LEVEL, changed_params.fields); + EXPECT_EQ (FIELD_POWER_LEVEL, (changed_params.fields & FIELD_POWER_LEVEL)); EXPECT_EQ (new_level, changed_params.power_level); } } -- cgit v1.2.3 From dd322344959c31ebbda939bf74547ed85e3233f9 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 22 Jul 2014 12:01:45 -0500 Subject: add manual test case for using the indicator-power-service-cmdline-battery test utility --- tests/manual | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'tests') diff --git a/tests/manual b/tests/manual index d3a22e1..a542cac 100644 --- a/tests/manual +++ b/tests/manual @@ -22,3 +22,20 @@ Test-case indicator-power/unity8-items-check
The menu is populated with items
+Test-case indicator-power/low-battery-popups +
+
Open a terminal
+
Stop the currently-running power indicator: "stop indicator-power"
+
Start the fake battery harness in the tests/build/ directory: "indicator-power-service-cmdline-battery"
+
Battery indicator should update, showing a discharging battery with a 50% charge
+
Type: "10" (no quotes) and press Enter
+
A popup should appear saying 'Battery low - 10% charge remaining'
+
Battery indicator's icon should show a low charge
+
Battery indicator's "Charge level" menuitem should show a 10% charge
+
Type: "9" (no quotes) and press Enter
+
The 'Battery low' popup should NOT appear, since we've already been notified
+
Battery indicator's "Charge level" menuitem should show a 9% charge
+
Type: "5" (no quotes) and press Enter
+
No 'Battery low' popup SHOULD appear, since 5% is the next warning threshold
+
Battery indicator's "Charge level" menuitem should show a 5% charge
+
-- cgit v1.2.3 From 8b9a80174263c601952d6c31cbcd344c85d096dd Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 24 Jul 2014 09:20:35 -0500 Subject: in tests/glib-fixture.h, sync with the glib-fixture.h from indicator-transfer, it's got less '#if 0' cruft --- tests/glib-fixture.h | 77 +++++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 37 deletions(-) (limited to 'tests') diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h index 46b9640..4c53244 100644 --- a/tests/glib-fixture.h +++ b/tests/glib-fixture.h @@ -1,8 +1,5 @@ /* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr + * Copyright 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published @@ -15,6 +12,9 @@ * * You should have received a copy of the GNU General Public License along * with this program. If not, see . + * + * Authors: + * Charles Kerr */ #include @@ -31,64 +31,67 @@ class GlibFixture : public ::testing::Test { private: - //GLogFunc realLogHandler; - - protected: + GLogFunc realLogHandler; - std::map logCounts; + std::map expected_log; + std::map> log; - void testLogCount(GLogLevelFlags log_level, int /*expected*/) + void test_log_counts() { -#if 0 - EXPECT_EQ(expected, logCounts[log_level]); -#endif + const GLogLevelFlags levels_to_test[] = { G_LOG_LEVEL_ERROR, + G_LOG_LEVEL_CRITICAL, + G_LOG_LEVEL_MESSAGE, + G_LOG_LEVEL_WARNING }; - logCounts.erase(log_level); - } + for(const auto& level : levels_to_test) + { + const auto& v = log[level]; + const auto n = v.size(); - private: + EXPECT_EQ(expected_log[level], n); + + if (expected_log[level] != n) + for (size_t i=0; i(self)->logCounts[log_level]++; + auto tmp = g_strdup_printf ("%s:%d \"%s\"", log_domain, (int)log_level, message); + static_cast(self)->log[log_level].push_back(tmp); + g_free(tmp); } protected: + void increment_expected_errors(GLogLevelFlags level, size_t n=1) + { + expected_log[level] += n; + } + virtual void SetUp() { setlocale(LC_ALL, "C.UTF-8"); loop = g_main_loop_new(nullptr, false); - //g_log_set_default_handler(default_log_handler, this); - - // only use local, temporary settings - g_assert(g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, true)); - g_assert(g_setenv("GSETTINGS_BACKEND", "memory", true)); - g_debug("SCHEMA_DIR is %s", SCHEMA_DIR); + g_log_set_default_handler(default_log_handler, this); g_unsetenv("DISPLAY"); - } virtual void TearDown() { -#if 0 - // confirm there aren't any unexpected log messages - EXPECT_EQ(0, logCounts[G_LOG_LEVEL_ERROR]); - EXPECT_EQ(0, logCounts[G_LOG_LEVEL_CRITICAL]); - EXPECT_EQ(0, logCounts[G_LOG_LEVEL_WARNING]); - EXPECT_EQ(0, logCounts[G_LOG_LEVEL_MESSAGE]); - EXPECT_EQ(0, logCounts[G_LOG_LEVEL_INFO]); -#endif - - // revert to glib's log handler - //g_log_set_default_handler(realLogHandler, this); + test_log_counts(); + + g_log_set_default_handler(realLogHandler, this); g_clear_pointer(&loop, g_main_loop_unref); } @@ -112,7 +115,7 @@ class GlibFixture : public ::testing::Test protected: /* convenience func to loop while waiting for a GObject's signal */ - void wait_for_signal(gpointer o, const gchar * signal, unsigned int timeout_seconds=5u) + void wait_for_signal(gpointer o, const gchar * signal, const guint timeout_seconds=5) { // wait for the signal or for timeout, whichever comes first const auto handler_id = g_signal_connect_swapped(o, signal, @@ -127,7 +130,7 @@ class GlibFixture : public ::testing::Test } /* convenience func to loop for N msec */ - void wait_msec(unsigned int msec=50u) + void wait_msec(guint msec=50) { const auto id = g_timeout_add(msec, wait_msec__timeout, loop); g_main_loop_run(loop); -- cgit v1.2.3 From f7f949eb2d04e8dd4493cbd8e55f34c896efd9ea Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 24 Jul 2014 16:25:32 -0500 Subject: d'oh --- tests/glib-fixture.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h index 4c53244..d333ab2 100644 --- a/tests/glib-fixture.h +++ b/tests/glib-fixture.h @@ -52,7 +52,7 @@ class GlibFixture : public ::testing::Test if (expected_log[level] != n) for (size_t i=0; i Date: Thu, 24 Jul 2014 22:51:03 -0500 Subject: in notifier.c, fix potential callchain loop when closing a notification --- tests/test-notify.cc | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/test-notify.cc b/tests/test-notify.cc index 1fc843e..a8d66d3 100644 --- a/tests/test-notify.cc +++ b/tests/test-notify.cc @@ -54,7 +54,7 @@ protected: DbusTestDbusMockObject * obj = nullptr; GDBusConnection * bus = nullptr; - static constexpr int NOTIFY_ID {1234}; + static constexpr int FIRST_NOTIFY_ID {1234}; static constexpr int NOTIFICATION_CLOSED_EXPIRED {1}; static constexpr int NOTIFICATION_CLOSED_DISMISSED {2}; @@ -63,9 +63,11 @@ protected: static constexpr char const * APP_NAME {"indicator-power-service"}; + static constexpr char const * METHOD_CLOSE {"CloseNotification"}; static constexpr char const * METHOD_NOTIFY {"Notify"}; static constexpr char const * METHOD_GET_CAPS {"GetCapabilities"}; static constexpr char const * METHOD_GET_INFO {"GetServerInformation"}; + static constexpr char const * SIGNAL_CLOSED {"NotificationClosed"}; static constexpr char const * HINT_TIMEOUT {"x-canonical-snap-decisions-timeout"}; @@ -86,7 +88,8 @@ protected: NOTIFY_INTERFACE, &error); g_assert_no_error (error); - + + // METHOD_GET_INFO dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_GET_INFO, nullptr, G_VARIANT_TYPE("(ssss)"), @@ -94,14 +97,34 @@ protected: &error); g_assert_no_error (error); - auto python_str = g_strdup_printf ("ret = %d", NOTIFY_ID); + // METHOD_NOTIFY + auto str = g_strdup_printf("try:\n" + " self.NextNotifyId\n" + "except AttributeError:\n" + " self.NextNotifyId = %d\n" + "ret = self.NextNotifyId\n" + "self.NextNotifyId += 1\n", + FIRST_NOTIFY_ID); dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_NOTIFY, G_VARIANT_TYPE("(susssasa{sv}i)"), G_VARIANT_TYPE_UINT32, - python_str, + str, &error); - g_free (python_str); g_assert_no_error (error); + g_free (str); + + // METHOD_CLOSE + str = g_strdup_printf("self.EmitSignal('%s', '%s', 'uu', [ args[0], %d ])", + NOTIFY_INTERFACE, + SIGNAL_CLOSED, + NOTIFICATION_CLOSED_API); + dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_CLOSE, + G_VARIANT_TYPE("(u)"), + nullptr, + str, + &error); + g_assert_no_error (error); + g_free (str); dbus_test_service_add_task(service, DBUS_TEST_TASK(mock)); dbus_test_service_start_tasks(service); -- cgit v1.2.3 From f15482d3f189c378d4e4cf2dfc69eefa522b30aa Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 24 Jul 2014 23:31:11 -0500 Subject: on the bus, publish the battery's power_level as strings rather than ints --- tests/test-notify.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/tests/test-notify.cc b/tests/test-notify.cc index a8d66d3..b5166a0 100644 --- a/tests/test-notify.cc +++ b/tests/test-notify.cc @@ -200,13 +200,13 @@ TEST_F(NotifyFixture, PercentageToLevel) const auto level = indicator_power_notifier_get_power_level(battery); if (i <= percent_critical) - EXPECT_EQ (POWER_LEVEL_CRITICAL, level); + EXPECT_STREQ (POWER_LEVEL_STR_CRITICAL, level); else if (i <= percent_very_low) - EXPECT_EQ (POWER_LEVEL_VERY_LOW, level); + EXPECT_STREQ (POWER_LEVEL_STR_VERY_LOW, level); else if (i <= percent_low) - EXPECT_EQ (POWER_LEVEL_LOW, level); + EXPECT_STREQ (POWER_LEVEL_STR_LOW, level); else - EXPECT_EQ (POWER_LEVEL_OK, level); + EXPECT_STREQ (POWER_LEVEL_STR_OK, level); } g_object_unref (battery); @@ -227,7 +227,7 @@ namespace struct ChangedParams { - int32_t power_level = POWER_LEVEL_OK; + std::string power_level = POWER_LEVEL_STR_OK; bool is_warning = false; uint32_t fields = 0; }; @@ -245,8 +245,8 @@ namespace g_return_if_fail (g_variant_is_of_type (dict, G_VARIANT_TYPE_DICTIONARY)); auto changed_params = static_cast(gchanged_params); - gint32 power_level; - if (g_variant_lookup (dict, "PowerLevel", "i", &power_level, nullptr)) + const char * power_level; + if (g_variant_lookup (dict, "PowerLevel", "&s", &power_level, nullptr)) { changed_params->power_level = power_level; changed_params->fields |= FIELD_POWER_LEVEL; @@ -362,7 +362,7 @@ TEST_F(NotifyFixture, EventsThatChangeNotifications) // test setup case wait_msec(); - EXPECT_EQ (0, changed_params.power_level); + EXPECT_STREQ (POWER_LEVEL_STR_OK, changed_params.power_level.c_str()); // change the percent past the 'low' threshold and confirm that // a) the power level changes @@ -379,7 +379,7 @@ TEST_F(NotifyFixture, EventsThatChangeNotifications) set_battery_percentage (battery, percent_very_low); wait_msec(); EXPECT_EQ (FIELD_POWER_LEVEL, changed_params.fields); - EXPECT_EQ (POWER_LEVEL_VERY_LOW, changed_params.power_level); + EXPECT_STREQ (POWER_LEVEL_STR_VERY_LOW, changed_params.power_level.c_str()); // ...and that the warning is taken down if the battery is plugged back in... changed_params = ChangedParams(); @@ -400,7 +400,7 @@ TEST_F(NotifyFixture, EventsThatChangeNotifications) set_battery_percentage (battery, percent_low+1); wait_msec(); EXPECT_EQ (FIELD_POWER_LEVEL|FIELD_IS_WARNING, changed_params.fields); - EXPECT_EQ (POWER_LEVEL_OK, changed_params.power_level); + EXPECT_STREQ (POWER_LEVEL_STR_OK, changed_params.power_level.c_str()); EXPECT_FALSE (changed_params.is_warning); // cleanup -- cgit v1.2.3