/* * 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 . * * Authors: * Charles Kerr */ #include "actions-mock.h" #include "state-fixture.h" #include #include #include #include #include using namespace unity::indicator::datetime; class MenuFixture: public StateFixture { private: typedef StateFixture super; protected: std::shared_ptr m_menu_factory; std::vector> m_menus; virtual void SetUp() { super::SetUp(); // build the menus on top of the actions and state m_menu_factory.reset(new MenuFactory(m_actions, m_state)); for(int i=0; ibuildMenu(Menu::Profile(i))); } virtual void TearDown() { m_menus.clear(); m_menu_factory.reset(); super::TearDown(); } void InspectHeader(GMenuModel* menu_model, const std::string& name) { // check that there's a header menuitem EXPECT_EQ(1,g_menu_model_get_n_items(menu_model)); gchar* str = nullptr; g_menu_model_get_item_attribute(menu_model, 0, "x-canonical-type", "s", &str); EXPECT_STREQ("com.canonical.indicator.root", str); g_clear_pointer(&str, g_free); g_menu_model_get_item_attribute(menu_model, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); const auto action_name = name + "-header"; EXPECT_EQ(std::string("indicator.")+action_name, str); g_clear_pointer(&str, g_free); // check the header auto dict = g_action_group_get_action_state(m_actions->action_group(), action_name.c_str()); EXPECT_TRUE(dict != nullptr); EXPECT_TRUE(g_variant_is_of_type(dict, G_VARIANT_TYPE_VARDICT)); auto v = g_variant_lookup_value(dict, "accessible-desc", G_VARIANT_TYPE_STRING); EXPECT_TRUE(v != nullptr); g_variant_unref(v); v = g_variant_lookup_value(dict, "label", G_VARIANT_TYPE_STRING); EXPECT_TRUE(v != nullptr); g_variant_unref(v); v = g_variant_lookup_value(dict, "title", G_VARIANT_TYPE_STRING); EXPECT_TRUE(v != nullptr); g_variant_unref(v); v = g_variant_lookup_value(dict, "visible", G_VARIANT_TYPE_BOOLEAN); EXPECT_TRUE(v != nullptr); g_variant_unref(v); g_variant_unref(dict); } void InspectCalendar(GMenuModel* menu_model, Menu::Profile profile) { gchar* str = nullptr; const auto actions_expected = (profile == Menu::Desktop) || (profile == Menu::Phone); const auto calendar_expected = (profile == Menu::Desktop) || (profile == Menu::DesktopGreeter); // get the calendar section auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); auto section = g_menu_model_get_item_link(submenu, Menu::Calendar, G_MENU_LINK_SECTION); // should be one or two items: a date label and maybe a calendar ASSERT_TRUE(section != nullptr); auto n_expected = calendar_expected ? 2 : 1; EXPECT_EQ(n_expected, g_menu_model_get_n_items(section)); // look at the date menuitem g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_LABEL, "s", &str); const auto now = m_state->clock->localtime(); EXPECT_EQ(now.format("%A, %e %B %Y"), str); g_clear_pointer(&str, g_free); g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); if (actions_expected) EXPECT_STREQ("indicator.activate-planner", str); else EXPECT_TRUE(str == nullptr); g_clear_pointer(&str, g_free); // look at the calendar menuitem if (calendar_expected) { g_menu_model_get_item_attribute(section, 1, "x-canonical-type", "s", &str); EXPECT_STREQ("com.canonical.indicator.calendar", str); g_clear_pointer(&str, g_free); g_menu_model_get_item_attribute(section, 1, G_MENU_ATTRIBUTE_ACTION, "s", &str); EXPECT_STREQ("indicator.calendar", str); g_clear_pointer(&str, g_free); g_menu_model_get_item_attribute(section, 1, "activation-action", "s", &str); if (actions_expected) EXPECT_STREQ("indicator.activate-planner", str); else EXPECT_TRUE(str == nullptr); g_clear_pointer(&str, g_free); } g_clear_object(§ion); // now change the clock and see if the date label changes appropriately auto gdt_tomorrow = g_date_time_add_days(now.get(), 1); auto tomorrow = DateTime(gdt_tomorrow); g_date_time_unref(gdt_tomorrow); m_mock_state->mock_clock->set_localtime(tomorrow); wait_msec(); section = g_menu_model_get_item_link(submenu, Menu::Calendar, G_MENU_LINK_SECTION); g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_LABEL, "s", &str); EXPECT_EQ(tomorrow.format("%A, %e %B %Y"), str); g_clear_pointer(&str, g_free); g_clear_object(§ion); // cleanup g_object_unref(submenu); } void InspectAppointments(GMenuModel* menu_model, Menu::Profile profile) { const bool appointments_expected = (profile == Menu::Desktop) || (profile == Menu::Phone); // get the Appointments section auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); // there shouldn't be any menuitems when "show events" is false m_state->settings->show_events.set(false); wait_msec(); auto section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); EXPECT_EQ(0, g_menu_model_get_n_items(section)); g_clear_object(§ion); // when "show_events" is true, // there should be an "add event" button even if there aren't any appointments std::vector appointments; m_state->settings->show_events.set(true); m_state->planner->upcoming.set(appointments); wait_msec(); section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); int expected_n = appointments_expected ? 1 : 0; EXPECT_EQ(expected_n, g_menu_model_get_n_items(section)); g_clear_object(§ion); // try adding a few appointments and see if the menu updates itself const auto now = m_state->clock->localtime(); auto gdt_tomorrow = g_date_time_add_days(now.get(), 1); const auto tomorrow = DateTime(gdt_tomorrow); g_date_time_unref(gdt_tomorrow); Appointment a1; // an alarm clock appointment a1.color = "red"; a1.summary = "Alarm"; a1.summary = "http://www.example.com/"; a1.uid = "example"; a1.has_alarms = true; a1.begin = a1.end = tomorrow; appointments.push_back(a1); Appointment a2; // a non-alarm appointment a2.color = "green"; a2.summary = "Other Text"; a2.summary = "http://www.monkey.com/"; a2.uid = "monkey"; a2.has_alarms = false; a2.begin = a2.end = tomorrow; appointments.push_back(a2); m_state->planner->upcoming.set(appointments); wait_msec(); // wait a moment for the menu to update section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); expected_n = appointments_expected ? 3 : 0; EXPECT_EQ(expected_n, g_menu_model_get_n_items(section)); if (appointments_expected) { gchar * str = nullptr; // test the alarm // - confirm it has an x-canonical-type of "alarm" g_menu_model_get_item_attribute(section, 0, "x-canonical-type", "s", &str); EXPECT_STREQ("com.canonical.indicator.alarm", str); g_clear_pointer(&str, g_free); // - confirm it has a nonempty x-canonical-time-format g_menu_model_get_item_attribute(section, 0, "x-canonical-time-format", "s", &str); EXPECT_TRUE(str && *str); g_clear_pointer(&str, g_free); // - confirm it has a serialized icon attribute auto v = g_menu_model_get_item_attribute_value(section, 0, G_MENU_ATTRIBUTE_ICON, nullptr); EXPECT_TRUE(v != nullptr); auto icon = g_icon_deserialize(v); EXPECT_TRUE(icon != nullptr); g_clear_object(&icon); g_clear_pointer(&v, g_variant_unref); // test the appointment // - confirm it has an x-canonical-type of "appointment" g_menu_model_get_item_attribute(section, 1, "x-canonical-type", "s", &str); EXPECT_STREQ("com.canonical.indicator.appointment", str); g_clear_pointer(&str, g_free); // - confirm it has a nonempty x-canonical-time-format g_menu_model_get_item_attribute(section, 0, "x-canonical-time-format", "s", &str); EXPECT_TRUE(str && *str); g_clear_pointer(&str, g_free); // - confirm its color matches the one we fed the appointments vector g_menu_model_get_item_attribute(section, 1, "x-canonical-color", "s", &str); EXPECT_EQ(a2.color, str); g_clear_pointer(&str, g_free); } g_clear_object(§ion); g_object_unref(submenu); } void CompareLocationsTo(GMenuModel* menu_model, const std::vector& locations) { // get the Locations section auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); auto section = g_menu_model_get_item_link(submenu, Menu::Locations, G_MENU_LINK_SECTION); // confirm that section's menuitems mirror the "locations" vector const auto n = locations.size(); ASSERT_EQ(n, g_menu_model_get_n_items(section)); for (guint i=0; i empty; m_state->locations->locations.set(empty); wait_msec(); CompareLocationsTo(menu_model, empty); // add some locations and confirm the menu picked up our changes Location l1 ("America/Chicago", "Dallas"); Location l2 ("America/Arizona", "Phoenix"); std::vector locations({l1, l2}); m_state->locations->locations.set(locations); wait_msec(); CompareLocationsTo(menu_model, locations_expected ? locations : empty); // now remove one of the locations... locations.pop_back(); m_state->locations->locations.set(locations); wait_msec(); CompareLocationsTo(menu_model, locations_expected ? locations : empty); } void InspectSettings(GMenuModel* menu_model, Menu::Profile profile) { std::string expected_action; if (profile == Menu::Desktop) expected_action = "indicator.activate-desktop-settings"; else if (profile == Menu::Phone) expected_action = "indicator.activate-phone-settings"; // get the Settings section auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); auto section = g_menu_model_get_item_link(submenu, Menu::Settings, G_MENU_LINK_SECTION); if (expected_action.empty()) { EXPECT_EQ(0, g_menu_model_get_n_items(section)); } else { EXPECT_EQ(1, g_menu_model_get_n_items(section)); gchar* str = nullptr; g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); EXPECT_EQ(expected_action, str); g_clear_pointer(&str, g_free); } g_clear_object(§ion); g_object_unref(submenu); } }; TEST_F(MenuFixture, HelloWorld) { EXPECT_EQ(Menu::NUM_PROFILES, m_menus.size()); for (int i=0; imenu_model() != nullptr); EXPECT_EQ(i, m_menus[i]->profile()); } EXPECT_EQ(m_menus[Menu::Desktop]->name(), "desktop"); } TEST_F(MenuFixture, Header) { for(auto& menu : m_menus) InspectHeader(menu->menu_model(), menu->name()); } TEST_F(MenuFixture, Sections) { for(auto& menu : m_menus) { // check that the header has a submenu auto menu_model = menu->menu_model(); auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); EXPECT_TRUE(submenu != nullptr); EXPECT_EQ(Menu::NUM_SECTIONS, g_menu_model_get_n_items(submenu)); g_object_unref(submenu); } } TEST_F(MenuFixture, Calendar) { for(auto& menu : m_menus) InspectCalendar(menu->menu_model(), menu->profile()); } TEST_F(MenuFixture, Appointments) { for(auto& menu : m_menus) InspectAppointments(menu->menu_model(), menu->profile()); } TEST_F(MenuFixture, Locations) { for(auto& menu : m_menus) InspectLocations(menu->menu_model(), menu->profile()); } TEST_F(MenuFixture, Settings) { for(auto& menu : m_menus) InspectSettings(menu->menu_model(), menu->profile()); }