/*
* 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 as published by
* the Free Software Foundation; version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Authors:
* Ted Gould
*/
#include
#include
#include
#include
#include
#include
class IndicatorFixture : public ::testing::Test
{
private:
std::string _indicatorPath;
std::string _indicatorAddress;
class PerRunData {
public: /* We're private in the fixture but other than that we don't care, we don't leak out */
std::shared_ptr _menu;
std::shared_ptr _actions;
DbusTestService * _test_service;
DbusTestTask * _test_indicator;
DbusTestTask * _test_dummy;
GDBusConnection * _session;
PerRunData (const std::string& indicatorPath, const std::string& indicatorAddress)
: _menu(nullptr)
, _session(nullptr)
{
_test_service = dbus_test_service_new(nullptr);
_test_indicator = DBUS_TEST_TASK(dbus_test_process_new(indicatorPath.c_str()));
dbus_test_task_set_name(_test_indicator, "Indicator");
dbus_test_service_add_task(_test_service, _test_indicator);
_test_dummy = dbus_test_task_new();
dbus_test_task_set_wait_for(_test_dummy, indicatorAddress.c_str());
dbus_test_task_set_name(_test_dummy, "Dummy");
dbus_test_service_add_task(_test_service, _test_dummy);
if (true) {
DbusTestBustle * bustle = dbus_test_bustle_new("indicator-test.bustle");
dbus_test_task_set_name(DBUS_TEST_TASK(bustle), "Bustle");
dbus_test_service_add_task(_test_service, DBUS_TEST_TASK(bustle));
g_object_unref(bustle);
}
dbus_test_service_start_tasks(_test_service);
_session = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr);
}
virtual ~PerRunData (void) {
_menu.reset();
_actions.reset();
/* D-Bus Test Stuff */
g_clear_object(&_test_dummy);
g_clear_object(&_test_indicator);
g_clear_object(&_test_service);
/* Wait for D-Bus session bus to go */
if (!g_dbus_connection_is_closed(_session)) {
g_dbus_connection_close_sync(_session, nullptr, nullptr);
}
g_clear_object(&_session);
}
};
std::shared_ptr run;
public:
virtual ~IndicatorFixture() = default;
IndicatorFixture (const std::string& path,
const std::string& addr)
: _indicatorPath(path)
, _indicatorAddress(addr)
{
};
protected:
virtual void SetUp() override
{
run = std::make_shared(_indicatorPath, _indicatorAddress);
}
virtual void TearDown() override
{
run.reset();
}
private:
void waitForCore (GObject * obj, const gchar * signalname) {
auto loop = g_main_loop_new(nullptr, FALSE);
/* Our two exit criteria */
gulong signal = g_signal_connect_swapped(obj, signalname, G_CALLBACK(g_main_loop_quit), loop);
guint timer = g_timeout_add_seconds(5, [](gpointer user_data) -> gboolean {
g_warning("Menu Timeout");
g_main_loop_quit((GMainLoop *)user_data);
return G_SOURCE_CONTINUE;
}, loop);
/* Wait for sync */
g_main_loop_run(loop);
/* Clean up */
g_source_remove(timer);
g_signal_handler_disconnect(obj, signal);
g_main_loop_unref(loop);
}
void menuWaitForItems (const std::shared_ptr& menu) {
auto count = g_menu_model_get_n_items(menu.get());
if (count != 0)
return;
waitForCore(G_OBJECT(menu.get()), "items-changed");
}
void agWaitForActions (const std::shared_ptr& group) {
auto list = std::shared_ptr(
g_action_group_list_actions(group.get()),
[](gchar ** list) {
g_strfreev(list);
});
if (g_strv_length(list.get()) != 0) {
return;
}
waitForCore(G_OBJECT(group.get()), "action-added");
}
protected:
void setMenu (const std::string& path) {
run->_menu.reset();
g_debug("Getting Menu: %s:%s", _indicatorAddress.c_str(), path.c_str());
run->_menu = std::shared_ptr(G_MENU_MODEL(g_dbus_menu_model_get(run->_session, _indicatorAddress.c_str(), path.c_str())), [](GMenuModel * modelptr) {
g_clear_object(&modelptr);
});
menuWaitForItems(run->_menu);
}
void setActions (const std::string& path) {
run->_actions.reset();
run->_actions = std::shared_ptr(G_ACTION_GROUP(g_dbus_action_group_get(run->_session, _indicatorAddress.c_str(), path.c_str())), [](GActionGroup * groupptr) {
g_clear_object(&groupptr);
});
agWaitForActions(run->_actions);
}
bool expectActionExists (const std::string& name) {
bool hasit = g_action_group_has_action(run->_actions.get(), name.c_str());
if (!hasit) {
std::cout <<
" Action: " << name << std::endl <<
" Expected: " << "Exists" << std::endl <<
" Actual: " << "No action found" << std::endl;
}
return hasit;
}
bool expectActionStateType (const std::string& name, const GVariantType * type) {
auto atype = g_action_group_get_action_state_type(run->_actions.get(), name.c_str());
bool same = false;
if (atype != nullptr) {
same = g_variant_type_equal(atype, type);
}
if (!same) {
std::cout <<
" Action: " << name << std::endl <<
" Expected: " << g_variant_type_peek_string(type) << std::endl <<
" Actual: " << g_variant_type_peek_string(atype) << std::endl;
}
return same;
}
bool expectActionStateIs (const std::string& name, GVariant * value) {
auto varref = std::shared_ptr(g_variant_ref_sink(value), [](GVariant * varptr) {
if (varptr != nullptr)
g_variant_unref(varptr);
});
auto aval = std::shared_ptr(g_action_group_get_action_state(run->_actions.get(), name.c_str()), [] (GVariant * varptr) {
if (varptr != nullptr)
g_variant_unref(varptr);
});
bool match = false;
if (aval != nullptr) {
match = g_variant_equal(aval.get(), varref.get());
}
if (!match) {
gchar * valstr = nullptr;
gchar * attstr = nullptr;
if (aval != nullptr) {
attstr = g_variant_print(aval.get(), TRUE);
} else {
attstr = g_strdup("nullptr");
}
if (varref != nullptr) {
valstr = g_variant_print(varref.get(), TRUE);
} else {
valstr = g_strdup("nullptr");
}
std::cout <<
" Action: " << name << std::endl <<
" Expected: " << valstr << std::endl <<
" Actual: " << attstr << std::endl;
g_free(valstr);
g_free(attstr);
}
return match;
}
bool expectActionStateIs (const std::string& name, bool value) {
GVariant * var = g_variant_new_boolean(value);
return expectActionStateIs(name, var);
}
bool expectActionStateIs (const std::string& name, std::string value) {
GVariant * var = g_variant_new_string(value.c_str());
return expectActionStateIs(name, var);
}
bool expectActionStateIs (const std::string& name, const char * value) {
GVariant * var = g_variant_new_string(value);
return expectActionStateIs(name, var);
}
bool expectActionStateIs (const std::string& name, double value) {
GVariant * var = g_variant_new_double(value);
return expectActionStateIs(name, var);
}
bool expectActionStateIs (const std::string& name, float value) {
GVariant * var = g_variant_new_double(value);
return expectActionStateIs(name, var);
}
private:
std::shared_ptr getMenuAttributeVal (int location, std::shared_ptr& menu, const std::string& attribute, std::shared_ptr& value) {
if (!(location < g_menu_model_get_n_items(menu.get()))) {
return nullptr;
}
if (location >= g_menu_model_get_n_items(menu.get()))
return nullptr;
auto menuval = std::shared_ptr(g_menu_model_get_item_attribute_value(menu.get(), location, attribute.c_str(), g_variant_get_type(value.get())), [](GVariant * varptr) {
if (varptr != nullptr)
g_variant_unref(varptr);
});
return menuval;
}
std::shared_ptr getMenuAttributeRecurse (std::vector::const_iterator menuLocation, std::vector::const_iterator menuEnd, const std::string& attribute, std::shared_ptr& value, std::shared_ptr& menu) {
if (menuLocation == menuEnd)
return nullptr;
if (menuLocation + 1 == menuEnd)
return getMenuAttributeVal(*menuLocation, menu, attribute, value);
auto clearfunc = [](GMenuModel * modelptr) {
g_clear_object(&modelptr);
};
auto submenu = std::shared_ptr(g_menu_model_get_item_link(menu.get(), *menuLocation, G_MENU_LINK_SUBMENU), clearfunc);
if (submenu == nullptr)
submenu = std::shared_ptr(g_menu_model_get_item_link(menu.get(), *menuLocation, G_MENU_LINK_SECTION), clearfunc);
if (submenu == nullptr)
return nullptr;
menuWaitForItems(submenu);
return getMenuAttributeRecurse(menuLocation + 1, menuEnd, attribute, value, submenu);
}
protected:
bool expectMenuAttribute (const std::vector menuLocation, const std::string& attribute, GVariant * value) {
auto varref = std::shared_ptr(g_variant_ref_sink(value), [](GVariant * varptr) {
if (varptr != nullptr)
g_variant_unref(varptr);
});
auto attrib = getMenuAttributeRecurse(menuLocation.cbegin(), menuLocation.cend(), attribute, varref, run->_menu);
bool same = false;
if (attrib != nullptr && varref != nullptr) {
same = g_variant_equal(attrib.get(), varref.get());
}
if (!same) {
gchar * valstr = nullptr;
gchar * attstr = nullptr;
if (attrib != nullptr) {
attstr = g_variant_print(attrib.get(), TRUE);
} else {
attstr = g_strdup("nullptr");
}
if (varref != nullptr) {
valstr = g_variant_print(varref.get(), TRUE);
} else {
valstr = g_strdup("nullptr");
}
std::string menuprint("{ ");
std::for_each(menuLocation.begin(), menuLocation.end(), [&menuprint](int i) {
menuprint.append(std::to_string(i));
menuprint.append(", ");
});
menuprint += "}";
std::cout <<
" Menu: " << menuprint << std::endl <<
" Attribute: " << attribute << std::endl <<
" Expected: " << valstr << std::endl <<
" Actual: " << attstr << std::endl;
g_free(valstr);
g_free(attstr);
}
return same;
}
bool expectMenuAttribute (const std::vector menuLocation, const std::string& attribute, bool value) {
GVariant * var = g_variant_new_boolean(value);
return expectMenuAttribute(menuLocation, attribute, var);
}
bool expectMenuAttribute (const std::vector menuLocation, const std::string& attribute, std::string value) {
GVariant * var = g_variant_new_string(value.c_str());
return expectMenuAttribute(menuLocation, attribute, var);
}
bool expectMenuAttribute (const std::vector menuLocation, const std::string& attribute, const char * value) {
GVariant * var = g_variant_new_string(value);
return expectMenuAttribute(menuLocation, attribute, var);
}
};
#define EXPECT_MENU_ATTRIB(menu, attrib, value) expectMenuAttribute(menu, attrib, value)
#define ASSERT_MENU_ATTRIB(menu, attrib, value) \
if (!expectMenuAttribute(menu, attrib, value)) \
return;