diff options
-rw-r--r-- | CMakeLists.txt | 5 | ||||
-rw-r--r-- | data/CMakeLists.txt | 2 | ||||
-rw-r--r-- | data/com.canonical.indicator.display | 11 | ||||
-rw-r--r-- | data/com.canonical.indicator.rotation_lock | 11 | ||||
-rw-r--r-- | debian/changelog | 6 | ||||
-rw-r--r-- | debian/control | 1 | ||||
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/exporter.cpp | 217 | ||||
-rw-r--r-- | src/exporter.h (renamed from tests/test-hello-world.cpp) | 38 | ||||
-rw-r--r-- | src/indicator.h | 88 | ||||
-rw-r--r-- | src/main.cpp | 25 | ||||
-rw-r--r-- | src/rotation-lock.cpp | 178 | ||||
-rw-r--r-- | src/rotation-lock.h | 42 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 4 | ||||
-rw-r--r-- | tests/glib-fixture.h | 2 | ||||
-rw-r--r-- | tests/gtestdbus-fixture.h | 102 | ||||
-rw-r--r-- | tests/manual | 24 | ||||
-rw-r--r-- | tests/test-rotation-lock.cpp | 61 |
18 files changed, 754 insertions, 64 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 02ecacb..8e890ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,9 +43,8 @@ include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS}) ## ## -set (CMAKE_INCLUDE_CURRENT_DIR ON) -include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include) -include_directories (${CMAKE_CURRENT_BINARY_DIR}/include) +set (CMAKE_INCLUDE_CURRENT_DIR OFF) +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) # set the compiler warnings if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 815acbd..7edaa44 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -67,7 +67,7 @@ install (FILES "${UPSTART_XDG_AUTOSTART_FILE}" set (UNITY_INDICATOR_DIR "${CMAKE_INSTALL_FULL_DATAROOTDIR}/unity/indicators") message (STATUS "${UNITY_INDICATOR_DIR} is the Unity Indicator install dir") -set (UNITY_INDICATOR_NAME "com.canonical.indicator.display") +set (UNITY_INDICATOR_NAME "com.canonical.indicator.rotation_lock") set (UNITY_INDICATOR_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${UNITY_INDICATOR_NAME}") install (FILES "${UNITY_INDICATOR_FILE}" diff --git a/data/com.canonical.indicator.display b/data/com.canonical.indicator.display deleted file mode 100644 index e94405a..0000000 --- a/data/com.canonical.indicator.display +++ /dev/null @@ -1,11 +0,0 @@ -[Indicator Service] -Name=indicator-display -ObjectPath=/com/canonical/indicator/display -Position=90 - -[phone] -ObjectPath=/com/canonical/indicator/display/phone - -[phone_greeter] -ObjectPath=/com/canonical/indicator/display/phone - diff --git a/data/com.canonical.indicator.rotation_lock b/data/com.canonical.indicator.rotation_lock new file mode 100644 index 0000000..7740db7 --- /dev/null +++ b/data/com.canonical.indicator.rotation_lock @@ -0,0 +1,11 @@ +[Indicator Service] +Name=indicator-rotation-lock +ObjectPath=/com/canonical/indicator/rotation_lock +Position=90 + +[phone] +ObjectPath=/com/canonical/indicator/rotation_lock/phone + +[phone_greeter] +ObjectPath=/com/canonical/indicator/rotation_lock/phone + diff --git a/debian/changelog b/debian/changelog index e69de29..58b5072 100644 --- a/debian/changelog +++ b/debian/changelog @@ -0,0 +1,6 @@ +indicator-display (0.1-0ubuntu1) utopic; urgency=medium + + * Initial release. + + -- Charles Kerr <charles.kerr@canonical.com> Wed, 20 Aug 2014 15:29:27 -0500 + diff --git a/debian/control b/debian/control index fe33f52..a972cc9 100644 --- a/debian/control +++ b/debian/control @@ -8,6 +8,7 @@ Build-Depends: cmake, g++-4.9, libglib2.0-dev (>= 2.36), liburl-dispatcher1-deva + libproperties-cpp-dev, # for coverage reports lcov, # for tests diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 16586bf..982aa49 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,7 @@ add_definitions (-DG_LOG_DOMAIN="${CMAKE_PROJECT_NAME}") # handwritten source code... set (SERVICE_LIB_HANDWRITTEN_SOURCES + exporter.cpp rotation-lock.cpp) add_library (${SERVICE_LIB} STATIC diff --git a/src/exporter.cpp b/src/exporter.cpp new file mode 100644 index 0000000..8288b9a --- /dev/null +++ b/src/exporter.cpp @@ -0,0 +1,217 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#include <src/exporter.h> + +class Exporter::Impl +{ +public: + + Impl(const std::shared_ptr<Indicator>& indicator): + m_indicator(indicator) + { + auto bus_name = g_strdup_printf("com.canonical.indicator.%s", indicator->name()); + m_own_id = g_bus_own_name(G_BUS_TYPE_SESSION, + bus_name, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, + on_bus_acquired, + nullptr, + on_name_lost, + this, + nullptr); + + g_free(bus_name); + } + + ~Impl() + { + if (m_bus != nullptr) + { + for(const auto& id : m_exported_menu_ids) + g_dbus_connection_unexport_menu_model(m_bus, id); + + if (m_exported_actions_id) + g_dbus_connection_unexport_action_group(m_bus, m_exported_actions_id); + } + + if (m_own_id) + g_bus_unown_name(m_own_id); + + g_clear_object(&m_bus); + } + + core::Signal<std::string>& name_lost() + { + return m_name_lost; + } + +private: + + void emit_name_lost(const char* bus_name) + { + m_name_lost(bus_name); + } + + static void on_bus_acquired(GDBusConnection * connection, + const gchar * name, + gpointer gself) + { + static_cast<Impl*>(gself)->on_bus_acquired(connection, name); + } + + void on_bus_acquired(GDBusConnection* connection, const gchar* /*name*/) + { + m_bus = G_DBUS_CONNECTION(g_object_ref(G_OBJECT(connection))); + + export_actions(m_indicator); + + for (auto& profile : m_indicator->profiles()) + export_profile(m_indicator, profile); + } + + void export_actions(const std::shared_ptr<Indicator>& indicator) + { + GError* error; + char* object_path; + guint id; + + // export the actions + + error = nullptr; + object_path = g_strdup_printf("/com/canonical/indicator/%s", indicator->name()); + id = g_dbus_connection_export_action_group(m_bus, + object_path, + G_ACTION_GROUP(indicator->action_group()), + &error); + if (id) + m_exported_actions_id = id; + else + g_warning("Can't export action group to '%s': %s", object_path, error->message); + + g_clear_error(&error); + g_free(object_path); + } + + static GVariant* create_header_state(const Header& h) + { + GVariantBuilder b; + g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT); + + g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean(h.is_visible)); + + if (!h.title.empty()) + g_variant_builder_add(&b, "{sv}", "title", g_variant_new_string(h.title.c_str())); + + if (!h.label.empty()) + g_variant_builder_add(&b, "{sv}", "label", g_variant_new_string(h.label.c_str())); + + if (!h.title.empty() || !h.label.empty()) + g_variant_builder_add(&b, "{sv}", "accessible-desc", g_variant_new_string(!h.label.empty() ? h.label.c_str() : h.title.c_str())); + + if (h.icon) + g_variant_builder_add(&b, "{sv}", "icon", g_icon_serialize(h.icon.get())); + + return g_variant_builder_end (&b); + } + + void export_profile(const std::shared_ptr<Indicator>& indicator, + const std::shared_ptr<Profile>& profile) + { + // build the header action + auto action_group = indicator->action_group(); + std::string action_name = profile->name() + "-header"; + auto a = g_simple_action_new_stateful(action_name.c_str(), nullptr, create_header_state(profile->header())); + g_action_map_add_action(G_ACTION_MAP(action_group), G_ACTION(a)); + profile->header().changed().connect([action_group,action_name](const Header& header){ + auto state = create_header_state(header); + auto tmp = g_variant_print(state, true); + g_message("header changed; updating action state to '%s'", tmp); + g_action_group_change_action_state(G_ACTION_GROUP(action_group), + action_name.c_str(), + create_header_state(header)); + g_free(tmp); + }); + + // build the header menu + auto detailed_action = g_strdup_printf("indicator.%s", action_name.c_str()); + GMenuItem* header = g_menu_item_new(nullptr, detailed_action); + g_menu_item_set_attribute(header, "x-canonical-type", "s", "com.canonical.indicator.root"); + g_menu_item_set_submenu(header, profile->menu_model().get()); + g_free(detailed_action); + + // build the menu + auto menu = g_menu_new(); + g_menu_append_item(menu, header); + g_object_unref(header); + + // export the menu + auto object_path = g_strdup_printf("/com/canonical/indicator/%s/%s", + indicator->name(), + profile->name().c_str()); + GError* error = nullptr; + auto id = g_dbus_connection_export_menu_model(m_bus, object_path, G_MENU_MODEL(menu), &error); + if (id) + m_exported_menu_ids.insert(id); + else if (error != nullptr) + g_warning("cannot export '%s': %s", object_path, error->message); + + g_free(object_path); + g_clear_error(&error); + //g_object_unref(menu); + } + + static void on_name_lost(GDBusConnection * /*connection*/, + const gchar * name, + gpointer gthis) + { + static_cast<Impl*>(gthis)->emit_name_lost(name); + } + + const std::string m_bus_name; + core::Signal<std::string> m_name_lost; + std::shared_ptr<Indicator> m_indicator; + std::set<guint> m_exported_menu_ids; + guint m_own_id = 0; + guint m_exported_actions_id = 0; + GDBusConnection * m_bus = nullptr; +}; + +/*** +**** +***/ + +Exporter::Exporter(const std::shared_ptr<Indicator>& indicator): + impl(new Impl(indicator)) +{ +} + +Exporter::~Exporter() +{ +} + +core::Signal<std::string>& +Exporter::name_lost() +{ + return impl->name_lost(); +} + +/*** +**** +***/ + diff --git a/tests/test-hello-world.cpp b/src/exporter.h index 5d9c308..6367f3a 100644 --- a/tests/test-hello-world.cpp +++ b/src/exporter.h @@ -17,32 +17,24 @@ * Charles Kerr <charles.kerr@canonical.com> */ -#include "glib-fixture.h" +#include <src/indicator.h> -class HelloWorldFixture: public GlibFixture -{ -private: - typedef GlibFixture super; +#include <core/signal.h> -protected: +#include <memory> - void SetUp() - { - super::SetUp(); - } - - void TearDown() - { - super::TearDown(); - } -}; +class Exporter +{ +public: + Exporter(const std::shared_ptr<Indicator>& indicator); + ~Exporter(); + core::Signal<std::string>& name_lost(); -/*** -**** -***/ +private: + class Impl; + std::unique_ptr<Impl> impl; -TEST_F(HelloWorldFixture, HelloWorld) -{ - EXPECT_TRUE(true); -} + Exporter(const Exporter&) =delete; + Exporter& operator=(const Exporter&) =delete; +}; diff --git a/src/indicator.h b/src/indicator.h new file mode 100644 index 0000000..dc4df09 --- /dev/null +++ b/src/indicator.h @@ -0,0 +1,88 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#ifndef INDICATOR_DISPLAY_INDICATOR_H +#define INDICATOR_DISPLAY_INDICATOR_H + +#include <core/property.h> + +#include <gio/gio.h> // GIcon + +#include <string> +#include <vector> + +struct Header +{ + bool is_visible = false; + std::string title; + std::string label; + std::string a11y; + std::shared_ptr<GIcon> icon; + + bool operator== (const Header& that) const { + return (is_visible == that.is_visible) && + (title == that.title) && + (label == that.label) && + (a11y == that.a11y) && + (icon == that.icon); + } + bool operator!= (const Header& that) const { return !(*this == that);} +}; + + +class Profile +{ +public: + virtual std::string name() const =0; + virtual const core::Property<Header>& header() const =0; + virtual std::shared_ptr<GMenuModel> menu_model() const =0; + +protected: + Profile() =default; +}; + + +class SimpleProfile: public Profile +{ +public: + SimpleProfile(const char* name, const std::shared_ptr<GMenuModel>& menu): m_name(name), m_menu(menu) {} + + std::string name() const {return m_name;} + core::Property<Header>& header() {return m_header;} + const core::Property<Header>& header() const {return m_header;} + std::shared_ptr<GMenuModel> menu_model() const {return m_menu;} + +protected: + const std::string m_name; + core::Property<Header> m_header; + std::shared_ptr<GMenuModel> m_menu; +}; + + +class Indicator +{ +public: + virtual ~Indicator() =default; + + virtual const char* name() const =0; + virtual GSimpleActionGroup* action_group() const =0; + virtual std::vector<std::shared_ptr<Profile>> profiles() const =0; +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp index 5cd3581..86bdeb3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,9 @@ * Charles Kerr <charles.kerr@canonical.com> */ +#include <src/exporter.h> +#include <src/rotation-lock.h> + #include <glib/gi18n.h> // bindtextdomain() #include <gio/gio.h> @@ -35,14 +38,22 @@ main(int /*argc*/, char** /*argv*/) textdomain(GETTEXT_PACKAGE); auto loop = g_main_loop_new(nullptr, false); + auto on_name_lost = [loop](const std::string& name){ + g_warning("busname lost: '%s'", name.c_str()); + g_main_loop_quit(loop); + }; + + // build all our indicators. + // Right now we've only got one -- rotation lock -- but hey, we can dream. + std::vector<std::shared_ptr<Indicator>> indicators; + std::vector<std::shared_ptr<Exporter>> exporters; + indicators.push_back(std::make_shared<RotationLockIndicator>()); + for (auto& indicator : indicators) { + auto exporter = std::make_shared<Exporter>(indicator); + exporter->name_lost().connect(on_name_lost); + exporters.push_back(exporter); + } - // run until we lose the busname -// auto model = std::make_shared<MutableModel>(); - // auto world = std::shared_ptr<World>(new DBusWorld(model)); - // auto controller = std::make_shared<Controller>(model, world); - // GMenuView menu_view (model, controller); - // FIXME: listen for busname-lost - g_message("entering GMainLoop that does nothing! Woo!"); g_main_loop_run(loop); // cleanup diff --git a/src/rotation-lock.cpp b/src/rotation-lock.cpp index 0a80085..3bbe12a 100644 --- a/src/rotation-lock.cpp +++ b/src/rotation-lock.cpp @@ -17,3 +17,181 @@ * Charles Kerr <charles.kerr@canonical.com> */ +#include <src/rotation-lock.h> + +#include <glib/gi18n.h> + +class RotationLockIndicator::Impl +{ +public: + + Impl(): + m_settings(g_settings_new(m_schema_name)), + m_action_group(create_action_group()) + { + // build the rotation lock icon + auto icon = g_themed_icon_new_with_default_fallbacks("orientation-lock"); + auto icon_deleter = [](GIcon* o){g_object_unref(G_OBJECT(o));}; + m_icon.reset(icon, icon_deleter); + + // build the phone profile + auto menu_model_deleter = [](GMenuModel* o){g_object_unref(G_OBJECT(o));}; + std::shared_ptr<GMenuModel> phone_menu (create_phone_menu(), menu_model_deleter); + m_phone = std::make_shared<SimpleProfile>("phone", phone_menu); + update_phone_header(); + } + + ~Impl() + { + g_clear_object(&m_action_group); + g_clear_object(&m_settings); + } + + GSimpleActionGroup* action_group() const + { + return m_action_group; + } + + std::vector<std::shared_ptr<Profile>> profiles() + { + std::vector<std::shared_ptr<Profile>> ret; + ret.push_back(m_phone); + return ret; + } + +private: + + /*** + **** Actions + ***/ + + static gboolean settings_to_action_state(GValue *value, + GVariant *variant, + gpointer /*unused*/) + { + bool is_locked = g_strcmp0(g_variant_get_string(variant, nullptr), "none"); + g_value_set_variant(value, g_variant_new_boolean(is_locked)); + return TRUE; + } + + static GVariant* action_state_to_settings(const GValue *value, + const GVariantType * /*expected_type*/, + gpointer /*unused*/) + { + // Toggling to 'on' *should* lock to the screen's current orientation. + // We don't have any way of knowing Screen.orientation in this service, + // so just pick one at random to satisfy the binding's needs. + // + // In practice this doesn't matter since the indicator isn't visible + // when the lock mode is 'none' so the end user won't ever be able + // to toggle the menuitem from None to anything else. + + auto state_is_true = g_variant_get_boolean(g_value_get_variant(value)); + return g_variant_new_string(state_is_true ? "PrimaryOrientation" : "none"); + } + + GSimpleActionGroup* create_action_group() + { + GSimpleActionGroup* group; + GSimpleAction* action; + + group = g_simple_action_group_new(); + action = g_simple_action_new_stateful("rotation-lock", + nullptr, + g_variant_new_boolean(false)); + g_settings_bind_with_mapping(m_settings, "orientation-lock", + action, "state", + G_SETTINGS_BIND_DEFAULT, + settings_to_action_state, + action_state_to_settings, + nullptr, + nullptr); + g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(action)); + g_object_unref(G_OBJECT(action)); + g_signal_connect_swapped(m_settings, "changed::orientation-lock", + G_CALLBACK(on_orientation_lock_setting_changed), this); + + return group; + } + + /*** + **** Phone profile + ***/ + + static void on_orientation_lock_setting_changed (gpointer gself) + { + static_cast<Impl*>(gself)->update_phone_header(); + } + + GMenuModel* create_phone_menu() + { + GMenu* menu; + GMenuItem* menu_item; + + menu = g_menu_new(); + + menu_item = g_menu_item_new(_("Rotation Lock"), "indicator.rotation-lock"); + g_menu_item_set_attribute(menu_item, "x-canonical-type", "s", "com.canonical.indicator.switch"); + g_menu_append_item(menu, menu_item); + g_object_unref(menu_item); + + return G_MENU_MODEL(menu); + } + + void update_phone_header() + { + Header h; + h.title = _("Rotation lock"); + h.a11y = h.title; + h.is_visible = g_settings_get_enum(m_settings, "orientation-lock") != 0; + h.icon = m_icon; + m_phone->header().set(h); + } + + /*** + **** + ***/ + + static constexpr char const * m_schema_name {"com.ubuntu.touch.system"}; + static constexpr char const * m_orientation_lock_icon_name {"orientation-lock"}; + GSettings* m_settings = nullptr; + GSimpleActionGroup* m_action_group = nullptr; + std::shared_ptr<SimpleProfile> m_phone; + std::shared_ptr<GIcon> m_icon; +}; + +/*** +**** +***/ + +RotationLockIndicator::RotationLockIndicator(): + impl(new Impl()) +{ +} + +RotationLockIndicator::~RotationLockIndicator() +{ +} + +std::vector<std::shared_ptr<Profile>> +RotationLockIndicator::profiles() const +{ + return impl->profiles(); +} + +GSimpleActionGroup* +RotationLockIndicator::action_group() const +{ + return impl->action_group(); +} + +const char* +RotationLockIndicator::name() const +{ + return "rotation_lock"; +} + +/*** +**** +***/ + diff --git a/src/rotation-lock.h b/src/rotation-lock.h new file mode 100644 index 0000000..2354c4a --- /dev/null +++ b/src/rotation-lock.h @@ -0,0 +1,42 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#ifndef INDICATOR_DISPLAY_ROTATION_LOCK_H +#define INDICATOR_DISPLAY_ROTATION_LOCK_H + +#include <src/indicator.h> + +#include <memory> // std::unique_ptr + +class RotationLockIndicator: public Indicator +{ +public: + RotationLockIndicator(); + ~RotationLockIndicator(); + + const char* name() const; + GSimpleActionGroup* action_group() const; + std::vector<std::shared_ptr<Profile>> profiles() const; + +protected: + class Impl; + std::unique_ptr<Impl> impl; +}; + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 38c315c..054a676 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -29,7 +29,7 @@ function(add_test_by_name name) add_dependencies (${TEST_NAME} libindicatordisplayservice) target_link_libraries (${TEST_NAME} indicatordisplayservice ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES}) endfunction() -add_test_by_name(test-hello-world) +add_test_by_name(test-rotation-lock) -add_test (cppcheck cppcheck --enable=all -q --error-exitcode=2 --inline-suppr -I${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests) +add_test (cppcheck cppcheck --enable=all -q --error-exitcode=2 --inline-suppr -I${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests) diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h index 4c53244..a8f3a76 100644 --- a/tests/glib-fixture.h +++ b/tests/glib-fixture.h @@ -84,6 +84,8 @@ class GlibFixture : public ::testing::Test g_log_set_default_handler(default_log_handler, this); + g_assert(g_setenv("GSETTINGS_BACKEND", "memory", true)); + g_unsetenv("DISPLAY"); } diff --git a/tests/gtestdbus-fixture.h b/tests/gtestdbus-fixture.h new file mode 100644 index 0000000..541050e --- /dev/null +++ b/tests/gtestdbus-fixture.h @@ -0,0 +1,102 @@ +/* + * Copyright 2013 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#include "glib-fixture.h" + +/*** +**** +***/ + +class GTestDBusFixture: public GlibFixture +{ + public: + + GTestDBusFixture() {} + + GTestDBusFixture(const std::vector<std::string>& service_dirs_in): service_dirs(service_dirs_in) {} + + private: + + typedef GlibFixture super; + + static void + on_bus_opened (GObject* /*object*/, GAsyncResult * res, gpointer gself) + { + auto self = static_cast<GTestDBusFixture*>(gself); + + GError * err = 0; + self->bus = g_bus_get_finish (res, &err); + g_assert_no_error (err); + + g_main_loop_quit (self->loop); + } + + static void + on_bus_closed (GObject* /*object*/, GAsyncResult * res, gpointer gself) + { + auto self = static_cast<GTestDBusFixture*>(gself); + + GError * err = 0; + g_dbus_connection_close_finish (self->bus, res, &err); + g_assert_no_error (err); + + g_main_loop_quit (self->loop); + } + + protected: + + GTestDBus * test_dbus = nullptr; + GDBusConnection * bus = nullptr; + const std::vector<std::string> service_dirs; + + virtual void SetUp () + { + super::SetUp (); + + // pull up a test dbus + test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE); + for (const auto& dir : service_dirs) + g_test_dbus_add_service_dir (test_dbus, dir.c_str()); + g_test_dbus_up (test_dbus); + const char * address = g_test_dbus_get_bus_address (test_dbus); + g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, true); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", address, true); + g_debug ("test_dbus's address is %s", address); + + // wait for the GDBusConnection before returning + g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); + g_main_loop_run (loop); + } + + virtual void TearDown () + { + wait_msec(); + + // close the system bus + g_dbus_connection_close(bus, nullptr, on_bus_closed, this); + g_main_loop_run(loop); + g_clear_object(&bus); + + // tear down the test dbus + g_test_dbus_down(test_dbus); + g_clear_object(&test_dbus); + + super::TearDown(); + } +}; diff --git a/tests/manual b/tests/manual index fc81e6e..a0e5264 100644 --- a/tests/manual +++ b/tests/manual @@ -1,24 +1,14 @@ -Test-case indicator-transfer/simple-download-check +Test-case indicator-datetime/rotation-indicator <dl> - <dt>Ensure indicator-transfer-service is running<dt> - <dd>The indicator should be visible in the panel</dd> + <dt>On the phone, enable the orientation lock in ubuntu-system-settings.</dt> + <dd>The rotation lock indicator should appear, and its switch menuitem should be set to 'true'.</dd> - <dt>Run the script tests/simple-download.py and, immediately afterwards, - click or pull down on the indicator so that its menu is visible - and its menuitems can be observed while the downloads run.</dt> - <dd>In the 'Ongoing Transfers' section, the transfers should appear</dd> - <dd>In the 'Ongoing Transfers' section, a 'Pause all' button should appear</dd> - <dd>While transfers are active, the indicator's icon should indicate activity</dd> - <dd>As each transfer finishes, its menuitem should move from the 'Ongoing' to 'Successful' section</dd> - <dd>As the first transfer is moved to the 'Successful' section, a 'Clear all' button should appear</dd> - <dd>As the last transfer finishes, the indicator's icon should change to indicate an idle state</dd> - <dd>As the last transfer finishes, the 'Pause all' button should disappear</dd> + <dt>With the orientation locked, click on the indicator's switch menuitem to toggle from locked to unlocked.</dd> + <dd>The rotation lock indicator should disappear</dd> + <dd>In ubuntu-system-settings, the orientation lock control should change to 'none'.</dd> +</dl> - <dt>After all the transfers finish, press the 'Clear all' button.</dt> - <dd>All three transfers should be cleared from the menu.</dd> - <dd>The 'Clear all' button should disappear</dd> -</dt> <strong> If all actions produce the expected results listed, please <a href="results#add_result">submit</a> a 'passed' result. diff --git a/tests/test-rotation-lock.cpp b/tests/test-rotation-lock.cpp new file mode 100644 index 0000000..946b1dd --- /dev/null +++ b/tests/test-rotation-lock.cpp @@ -0,0 +1,61 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#include "gtestdbus-fixture.h" + +#include <src/rotation-lock.h> + +class RotationLockFixture: public GTestDBusFixture +{ +private: + typedef GTestDBusFixture super; + +protected: + + void SetUp() + { + super::SetUp(); + } + + void TearDown() + { + super::TearDown(); + } +}; + +/*** +**** +***/ + +TEST_F(RotationLockFixture, CheckIndicator) +{ + RotationLockIndicator indicator; + + ASSERT_STREQ("rotation_lock", indicator.name()); + auto actions = indicator.action_group(); + ASSERT_TRUE(actions != nullptr); + ASSERT_TRUE(g_action_group_has_action(G_ACTION_GROUP(actions), "rotation-lock")); + + std::vector<std::shared_ptr<Profile>> profiles = indicator.profiles(); + ASSERT_EQ(1, profiles.size()); + std::shared_ptr<Profile> phone = profiles[0]; + ASSERT_EQ(std::string("phone"), phone->name()); + ASSERT_FALSE(phone->header()->is_visible); +} + |