From 828fae630464f2eae96145cffcf97e5791497264 Mon Sep 17 00:00:00 2001 From: Xavi Garcia Mena Date: Tue, 13 Oct 2015 09:49:55 +0200 Subject: Added notifications tests integrated with the test instance of pulseAudio and gmenuharness --- src/volume-control-pulse.vala | 11 +- tests/dbus-types/CMakeLists.txt | 5 + tests/dbus-types/dbus-types.h | 4 + tests/dbus-types/org.freedesktop.Notifications.xml | 47 +++ tests/integration/indicator-sound-test-base.cpp | 319 +++++++++++++++++---- tests/integration/indicator-sound-test-base.h | 29 ++ tests/integration/test-indicator.cpp | 288 +++++++++++++++++++ 7 files changed, 647 insertions(+), 56 deletions(-) create mode 100644 tests/dbus-types/org.freedesktop.Notifications.xml diff --git a/src/volume-control-pulse.vala b/src/volume-control-pulse.vala index 042c1c1..ab0d6d7 100644 --- a/src/volume-control-pulse.vala +++ b/src/volume-control-pulse.vala @@ -206,10 +206,11 @@ public class VolumeControlPulse : VolumeControl * checking for the port name. On touch (with the pulseaudio droid element) * the headset/headphone port is called 'output-headset' and 'output-headphone'. * On the desktop this is usually called 'analog-output-headphones' */ - if (i.active_port != null && - (i.active_port.name == "output-wired_headset" || - i.active_port.name == "output-wired_headphone" || - i.active_port.name == "analog-output-headphones")) { + if ( (i.active_port != null && + (i.active_port.name == "output-wired_headset" || + i.active_port.name == "output-wired_headphone" || + i.active_port.name == "analog-output-headphones")) || + (i.name == "indicator_sound_test_headphones")) { _active_port_headphone = true; } else { _active_port_headphone = false; @@ -711,7 +712,7 @@ public class VolumeControlPulse : VolumeControl private bool calculate_high_volume_from_volume(double volume) { return _active_port_headphone && _warning_volume_enabled - && volume >= _warning_volume_norms + && volume > _warning_volume_norms && (stream == "multimedia"); } diff --git a/tests/dbus-types/CMakeLists.txt b/tests/dbus-types/CMakeLists.txt index 30dff60..cb7f512 100644 --- a/tests/dbus-types/CMakeLists.txt +++ b/tests/dbus-types/CMakeLists.txt @@ -28,11 +28,16 @@ set(dbusinterface_actions_xml "org.gtk.Actions.xml") set_source_files_properties(${dbusinterface_actions_xml} PROPERTIES CLASSNAME MenusInterface) +set(dbusinterface_notifications_xml "org.freedesktop.Notifications.xml") +set_source_files_properties(${dbusinterface_notifications_xml} PROPERTIES + CLASSNAME NotificationsInterface) + qt5_add_dbus_interface(interface_files ${dbusinterface_streamrestore_xml} stream_restore_interface) qt5_add_dbus_interface(interface_files ${dbusinterface_properties_xml} dbus_properties_interface) qt5_add_dbus_interface(interface_files ${dbusinterface_accounts_xml} dbus_accounts_interface) qt5_add_dbus_interface(interface_files ${dbusinterface_accountssound_xml} dbus_accountssound_interface) qt5_add_dbus_interface(interface_files ${dbusinterface_actions_xml} dbus_menus_interface) +qt5_add_dbus_interface(interface_files ${dbusinterface_notifications_xml} dbus_notifications_interface) add_library( sound-indicator-dbus-interfaces diff --git a/tests/dbus-types/dbus-types.h b/tests/dbus-types/dbus-types.h index 4abe9ff..b75acf0 100644 --- a/tests/dbus-types/dbus-types.h +++ b/tests/dbus-types/dbus-types.h @@ -40,5 +40,9 @@ namespace DBusTypes static constexpr char const* STREAM_RESTORE_ENTRY_NAME = "org.PulseAudio.Ext.StreamRestore1.RestoreEntry"; + static constexpr char const* MAIN_SERVICE_PATH = "/com/canonical/indicator/sound"; + + static constexpr char const* ACTIONS_INTERFACE = "org.gtk.Actions"; + } // namespace DBusTypes diff --git a/tests/dbus-types/org.freedesktop.Notifications.xml b/tests/dbus-types/org.freedesktop.Notifications.xml new file mode 100644 index 0000000..f7d923e --- /dev/null +++ b/tests/dbus-types/org.freedesktop.Notifications.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/integration/indicator-sound-test-base.cpp b/tests/integration/indicator-sound-test-base.cpp index a8d41a0..f9db365 100644 --- a/tests/integration/indicator-sound-test-base.cpp +++ b/tests/integration/indicator-sound-test-base.cpp @@ -22,6 +22,7 @@ #include "dbus_properties_interface.h" #include "dbus_accounts_interface.h" #include "dbus_accountssound_interface.h" +#include "dbus_notifications_interface.h" #include "dbus-types.h" #include @@ -42,7 +43,67 @@ IndicatorSoundTestBase::IndicatorSoundTestBase() : IndicatorSoundTestBase::~IndicatorSoundTestBase() { +} + +void IndicatorSoundTestBase::SetUp() +{ + setenv("XDG_DATA_DIRS", XDG_DATA_DIRS, true); + setenv("DBUS_SYSTEM_BUS_ADDRESS", dbusTestRunner.systemBus().toStdString().c_str(), true); + setenv("DBUS_SESSION_BUS_ADDRESS", dbusTestRunner.sessionBus().toStdString().c_str(), true); + dbusMock.registerNotificationDaemon(); + + dbusTestRunner.startServices(); + + auto& notifications = notificationsMockInterface(); + notifications.AddMethod("org.freedesktop.Notifications", + "GetCapabilities", + "", + "as", + "ret = ['actions', 'body', 'body-markup', 'icon-static', 'image/svg+xml', 'x-canonical-private-synchronous', 'x-canonical-append', 'x-canonical-private-icon-only', 'x-canonical-truncation', 'private-synchronous', 'append', 'private-icon-only', 'truncation']" + ).waitForFinished(); +} + +void IndicatorSoundTestBase::TearDown() +{ + unsetenv("XDG_DATA_DIRS"); + unsetenv("PULSE_SERVER"); + unsetenv("DBUS_SYSTEM_BUS_ADDRESS"); +} + +void gvariant_deleter(GVariant* varptr) +{ + if (varptr != nullptr) + { + g_variant_unref(varptr); + } +} +std::shared_ptr IndicatorSoundTestBase::volume_variant(double volume) +{ + GVariantBuilder builder; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&builder, + "{sv}", + "title", + g_variant_new_string("_Sound")); + + g_variant_builder_add(&builder, + "{sv}", + "accessible-desc", + g_variant_new_string("_Sound")); + + auto icon = g_themed_icon_new("icon"); + g_variant_builder_add(&builder, + "{sv}", + "icon", + g_icon_serialize(icon)); + + g_variant_builder_add(&builder, + "{sv}", + "visible", + g_variant_new_boolean(true)); + return shared_ptr(g_variant_builder_end(&builder), &gvariant_deleter); } bool IndicatorSoundTestBase::setStreamRestoreVolume(QString const &role, double volume) @@ -164,7 +225,8 @@ void IndicatorSoundTestBase::startPulseDesktop() << "--system=false" << "--exit-idle-time=-1" << "-n" - << "--load=module-null-sink" + << QString("--load=module-null-sink sink_name=indicator_sound_test_speaker") + << QString("--load=module-null-sink sink_name=indicator_sound_test_headphones") << "--log-target=file:/tmp/pulse-daemon.log" << "--load=module-dbus-protocol" << "--load=module-native-protocol-tcp auth-ip-acl=127.0.0.1" @@ -194,7 +256,8 @@ void IndicatorSoundTestBase::startPulsePhone() << "--system=false" << "--exit-idle-time=-1" << "-n" - << "--load=module-null-sink" + << QString("--load=module-null-sink sink_name=indicator_sound_test_speaker") + << QString("--load=module-null-sink sink_name=indicator_sound_test_headphones") << "--log-target=file:/tmp/pulse-daemon.log" << QString("--load=module-stream-restore restore_device=false restore_muted=false fallback_table=%1").arg(STREAM_RESTORE_TABLE) << "--load=module-dbus-protocol" @@ -264,55 +327,6 @@ mh::MenuMatcher::Parameters IndicatorSoundTestBase::phoneParameters() "/com/canonical/indicator/sound/phone"); } -void IndicatorSoundTestBase::SetUp() -{ - setenv("XDG_DATA_DIRS", XDG_DATA_DIRS, true); - setenv("DBUS_SYSTEM_BUS_ADDRESS", dbusTestRunner.systemBus().toStdString().c_str(), true); -} - -void IndicatorSoundTestBase::TearDown() -{ - unsetenv("XDG_DATA_DIRS"); - unsetenv("PULSE_SERVER"); - unsetenv("DBUS_SYSTEM_BUS_ADDRESS"); -} - -void gvariant_deleter(GVariant* varptr) -{ - if (varptr != nullptr) - { - g_variant_unref(varptr); - } -} - -std::shared_ptr IndicatorSoundTestBase::volume_variant(double volume) -{ - GVariantBuilder builder; - - g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add(&builder, - "{sv}", - "title", - g_variant_new_string("_Sound")); - - g_variant_builder_add(&builder, - "{sv}", - "accessible-desc", - g_variant_new_string("_Sound")); - - auto icon = g_themed_icon_new("icon"); - g_variant_builder_add(&builder, - "{sv}", - "icon", - g_icon_serialize(icon)); - - g_variant_builder_add(&builder, - "{sv}", - "visible", - g_variant_new_boolean(true)); - return shared_ptr(g_variant_builder_end(&builder), &gvariant_deleter); -} - unity::gmenuharness::MenuItemMatcher IndicatorSoundTestBase::volumeSlider(double volume) { return mh::MenuItemMatcher().radio() @@ -409,3 +423,206 @@ void IndicatorSoundTestBase::initializeAccountsInterface() } } } + +OrgFreedesktopDBusMockInterface& IndicatorSoundTestBase::notificationsMockInterface() +{ + return dbusMock.mockInterface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + QDBusConnection::SessionBus); +} + +bool IndicatorSoundTestBase::setActionValue(const QString & action, QVariant value) +{ + QDBusInterface actionsInterface(DBusTypes::DBUS_NAME, + DBusTypes::MAIN_SERVICE_PATH, + DBusTypes::ACTIONS_INTERFACE, + dbusTestRunner.sessionConnection()); + + QDBusVariant dbusVar(value); + auto resp = actionsInterface.call("SetState", + action, + QVariant::fromValue(dbusVar), + QVariant::fromValue(QVariantMap())); + + if (resp.type() == QDBusMessage::ErrorMessage) + { + qCritical() << "IndicatorSoundTestBase::setActionValue(): Failed to set value for action " + << action + << " " + << resp.errorMessage(); + return false; + } + else + { + return true; + } +} + +bool IndicatorSoundTestBase::pressNotificationButton(int id, const QString & button) +{ + OrgFreedesktopDBusMockInterface actionsInterface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + dbusTestRunner.sessionConnection()); + + actionsInterface.EmitSignal( + "org.freedesktop.Notifications", + "ActionInvoked", "us", QVariantList() << id << button); + + return true; +} + +bool IndicatorSoundTestBase::qDBusArgumentToMap(QVariant const& variant, QVariantMap& map) +{ + if (variant.canConvert()) + { + QDBusArgument value(variant.value()); + if (value.currentType() == QDBusArgument::MapType) + { + value >> map; + return true; + } + } + return false; +} + +void IndicatorSoundTestBase::checkVolumeNotification(double volume, QString const& label, bool isLoud, QVariantList call) +{ + QString icon; + if (volume <= 0.0) + { + icon = "audio-volume-muted"; + } + else if (volume <= 0.3) + { + icon = "audio-volume-low"; + } + else if (volume <= 0.7) + { + icon = "audio-volume-medium"; + } + else + { + icon = "audio-volume-high"; + } + + ASSERT_NE(call.size(), 0); + EXPECT_EQ("Notify", call.at(0)); + + QVariantList const& args(call.at(1).toList()); + ASSERT_EQ(8, args.size()); + EXPECT_EQ("indicator-sound", args.at(0)); + EXPECT_EQ(icon, args.at(2)); + EXPECT_EQ("Volume", args.at(3)); + EXPECT_EQ(label, args.at(4)); + EXPECT_EQ(QStringList(), args.at(5)); + + QVariantMap hints; + ASSERT_TRUE(qDBusArgumentToMap(args.at(6), hints)); + ASSERT_TRUE(hints.contains("value")); + ASSERT_TRUE(hints.contains("x-canonical-non-shaped-icon")); + ASSERT_TRUE(hints.contains("x-canonical-value-bar-tint")); + ASSERT_TRUE(hints.contains("x-canonical-private-synchronous")); + + EXPECT_EQ(volume*100, hints["value"]); + EXPECT_EQ(true, hints["x-canonical-non-shaped-icon"]); + EXPECT_EQ(isLoud, hints["x-canonical-value-bar-tint"]); + EXPECT_EQ(true, hints["x-canonical-private-synchronous"]); +} + +void IndicatorSoundTestBase::checkHighVolumeNotification(QVariantList call) +{ + ASSERT_NE(call.size(), 0); + EXPECT_EQ("Notify", call.at(0)); + + QVariantList const& args(call.at(1).toList()); + ASSERT_EQ(8, args.size()); + EXPECT_EQ("indicator-sound", args.at(0)); + EXPECT_EQ("Volume", args.at(3)); +} + +void IndicatorSoundTestBase::checkCloseNotification(int id, QVariantList call) +{ + EXPECT_EQ("CloseNotification", call.at(0)); + QVariantList const& args(call.at(1).toList()); + ASSERT_EQ(1, args.size()); +} + +void IndicatorSoundTestBase::checkNotificationWithNoArgs(QString const& method, QVariantList call) +{ + EXPECT_EQ(method, call.at(0)); + QVariantList const& args(call.at(1).toList()); + ASSERT_EQ(0, args.size()); +} + +int IndicatorSoundTestBase::getNotificationID(QVariantList call) +{ + if (call.size() == 0) + { + return -1; + } + QVariantList const& args(call.at(1).toList()); + if (args.size() != 8) + { + return -1; + } + if (args.at(0) != "indicator-sound") + { + return -1; + } + + bool isInt; + int id = args.at(1).toInt(&isInt); + if (!isInt) + { + return -1; + } + return id; +} + +bool IndicatorSoundTestBase::activateHeadphones(bool headphonesActive) +{ + QProcess pacltProcess; + + QString defaultSinkName = "indicator_sound_test_speaker"; + QString suspendedSinkName = "indicator_sound_test_headphones"; + if (headphonesActive) + { + defaultSinkName = "indicator_sound_test_headphones"; + suspendedSinkName = "indicator_sound_test_speaker"; + } + + pacltProcess.start("pactl", QStringList() << "-s" + << "127.0.0.1" + << "set-default-sink" + << defaultSinkName); + if (!pacltProcess.waitForStarted()) + return false; + + if (!pacltProcess.waitForFinished()) + return false; + + pacltProcess.start("pactl", QStringList() << "-s" + << "127.0.0.1" + << "suspend-sink" + << defaultSinkName + << "0"); + if (!pacltProcess.waitForStarted()) + return false; + + if (!pacltProcess.waitForFinished()) + return false; + + pacltProcess.start("pactl", QStringList() << "-s" + << "127.0.0.1" + << "suspend-sink" + << suspendedSinkName + << "1"); + if (!pacltProcess.waitForStarted()) + return false; + + if (!pacltProcess.waitForFinished()) + return false; + + return pacltProcess.exitCode() == 0; +} diff --git a/tests/integration/indicator-sound-test-base.h b/tests/integration/indicator-sound-test-base.h index 0dc0052..20e44fc 100644 --- a/tests/integration/indicator-sound-test-base.h +++ b/tests/integration/indicator-sound-test-base.h @@ -33,6 +33,15 @@ class DBusPulseVolume; class DBusPropertiesInterface; class QSignalSpy; +#define WAIT_FOR_SIGNALS(signalSpy, signalsExpected)\ +{\ + while (signalSpy.size() < signalsExpected)\ + {\ + ASSERT_TRUE(signalSpy.wait());\ + }\ + ASSERT_EQ(signalsExpected, signalSpy.size());\ +} + class IndicatorSoundTestBase: public testing::Test { public: @@ -81,6 +90,26 @@ protected: void initializeAccountsInterface(); + OrgFreedesktopDBusMockInterface& notificationsMockInterface(); + + bool setActionValue(const QString & action, QVariant value); + + bool pressNotificationButton(int id, const QString & button); + + bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map); + + void checkVolumeNotification(double volume, QString const& label, bool isLoud, QVariantList call); + + void checkHighVolumeNotification(QVariantList call); + + void checkCloseNotification(int id, QVariantList call); + + void checkNotificationWithNoArgs(QString const& method, QVariantList call); + + int getNotificationID(QVariantList call); + + bool activateHeadphones(bool headphonesActive); + QtDBusTest::DBusTestRunner dbusTestRunner; QtDBusMock::DBusMock dbusMock; diff --git a/tests/integration/test-indicator.cpp b/tests/integration/test-indicator.cpp index 5a4b4ee..1607367 100644 --- a/tests/integration/test-indicator.cpp +++ b/tests/integration/test-indicator.cpp @@ -634,4 +634,292 @@ TEST_F(TestIndicator, DesktopChangeRoleVolume) ).match()); } +TEST_F(TestIndicator, PhoneNotificationVolume) +{ + double INITIAL_VOLUME = 0.0; + + QSignalSpy notificationsSpy(¬ificationsMockInterface(), + SIGNAL(MethodCalled(const QString &, const QVariantList &))); + + ASSERT_NO_THROW(startAccountsService()); + EXPECT_TRUE(clearGSettingsPlayers()); + ASSERT_NO_THROW(startPulsePhone()); + + // initialize volumes in pulseaudio + EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + // check the initial state + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::all) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(INITIAL_VOLUME)) + ) + .item(mh::MenuItemMatcher() + .label("Sound Settingsā€¦") + .action("indicator.phone-settings") + ) + ).match()); + + // change volume to 1.0 + setActionValue("volume", QVariant::fromValue(1.0)); + + WAIT_FOR_SIGNALS(notificationsSpy, 3); + + // the first time we also have the calls to + // GetServerInformation and GetCapabilities + checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); + checkVolumeNotification(1.0, "", false, notificationsSpy.at(2)); + + notificationsSpy.clear(); + setActionValue("volume", QVariant::fromValue(0.0)); + + WAIT_FOR_SIGNALS(notificationsSpy, 1) + + checkVolumeNotification(0.0, "", false, notificationsSpy.at(0)); + + notificationsSpy.clear(); + setActionValue("volume", QVariant::fromValue(0.5)); + + WAIT_FOR_SIGNALS(notificationsSpy, 1) + + checkVolumeNotification(0.5, "", false, notificationsSpy.at(0)); +} + +TEST_F(TestIndicator, PhoneNotificationWarningVolume) +{ + double INITIAL_VOLUME = 0.0; + + QSignalSpy notificationsSpy(¬ificationsMockInterface(), + SIGNAL(MethodCalled(const QString &, const QVariantList &))); + + ASSERT_NO_THROW(startAccountsService()); + ASSERT_NO_THROW(startPulsePhone()); + + // initialize volumes in pulseaudio + EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME)); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + // activate the headphones + EXPECT_TRUE(activateHeadphones(true)); + + // set an initial volume to the alert role + setStreamRestoreVolume("alert", 1.0); + EXPECT_TRUE(waitVolumeChangedInIndicator()); + + // play a test sound, it should change the role in the indicator + EXPECT_TRUE(startTestSound("multimedia")); + EXPECT_TRUE(waitVolumeChangedInIndicator()); + + // change volume to 0.0... no warning should be emitted + setActionValue("volume", QVariant::fromValue(0.0)); + + WAIT_FOR_SIGNALS(notificationsSpy, 3); + + // the first time we also have the calls to + // GetServerInformation and GetCapabilities + checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); + checkVolumeNotification(0.0, "", false, notificationsSpy.at(2)); + notificationsSpy.clear(); + + // change volume to 0.5... no warning should be emitted + setActionValue("volume", QVariant::fromValue(0.5)); + + WAIT_FOR_SIGNALS(notificationsSpy, 1); + + checkVolumeNotification(0.5, "", false, notificationsSpy.at(0)); + notificationsSpy.clear(); + + // change volume to 1.0... warning should be emitted + setActionValue("volume", QVariant::fromValue(1.0)); + + WAIT_FOR_SIGNALS(notificationsSpy, 4); + + // the notification is sent twice (TODO check why) + checkCloseNotification(1, notificationsSpy.at(0)); + checkHighVolumeNotification(notificationsSpy.at(1)); + checkCloseNotification(1, notificationsSpy.at(2)); + checkHighVolumeNotification(notificationsSpy.at(3)); + + // get the last notification ID + int idNotification = getNotificationID(notificationsSpy.at(3)); + ASSERT_NE(-1, idNotification); + + qWarning() << "XGM: id Notification: " << idNotification; + + // cancel the dialog + pressNotificationButton(idNotification, "cancel"); + + // check that the volume was clamped + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::starts_with) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(0.74)) + ) + ).match()); + + // try again... + notificationsSpy.clear(); + // change volume to 1.0... warning should be emitted + setActionValue("volume", QVariant::fromValue(1.0)); + + WAIT_FOR_SIGNALS(notificationsSpy, 4); + + checkCloseNotification(1, notificationsSpy.at(0)); + checkHighVolumeNotification(notificationsSpy.at(1)); + checkCloseNotification(1, notificationsSpy.at(2)); + checkHighVolumeNotification(notificationsSpy.at(3)); + + // get the last notification ID + idNotification = getNotificationID(notificationsSpy.at(3)); + ASSERT_NE(-1, idNotification); + + qWarning() << "XGM: id Notification: " << idNotification; + + // this time we approve + pressNotificationButton(idNotification, "ok"); + + // check that the volume was applied + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::starts_with) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(1.0)) + .item(mh::MenuItemMatcher() + .action("indicator.high-volume-warning-item") + .label("High volume can damage your hearing.") + ) + ) + ).match()); + + // after the warning was approved we should be able to modify the volume + // and don't get the warning + notificationsSpy.clear(); + + // change volume to 0.5... no warning should be emitted + setActionValue("volume", QVariant::fromValue(0.5)); + + WAIT_FOR_SIGNALS(notificationsSpy, 2); + + // check the notification TODO check why the sound indicator sends it twice + checkVolumeNotification(0.5, "", false, notificationsSpy.at(0)); + checkVolumeNotification(0.5, "", false, notificationsSpy.at(1)); + + // check that the volume was applied + // and that we don't have the warning item + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::starts_with) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(0.5)) + ) + ).match()); + + // now set high volume again, we should not get the warning dialog + // as we already approved it + notificationsSpy.clear(); + + setActionValue("volume", QVariant::fromValue(1.0)); + + WAIT_FOR_SIGNALS(notificationsSpy, 2); + + // check the notification TODO check why the sound indicator sends it twice + checkVolumeNotification(1.0, "High volume can damage your hearing.", true, notificationsSpy.at(0)); + checkVolumeNotification(1.0, "High volume can damage your hearing.", true, notificationsSpy.at(1)); +} + +TEST_F(TestIndicator, PhoneNotificationWarningVolumeAlertMode) +{ + double INITIAL_VOLUME = 0.0; + + QSignalSpy notificationsSpy(¬ificationsMockInterface(), + SIGNAL(MethodCalled(const QString &, const QVariantList &))); + + ASSERT_NO_THROW(startAccountsService()); + ASSERT_NO_THROW(startPulsePhone()); + + // initialize volumes in pulseaudio + EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME)); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + // activate the headphones + EXPECT_TRUE(activateHeadphones(true)); + + // set an initial volume to the alert role + setStreamRestoreVolume("alert", 1.0); + EXPECT_TRUE(waitVolumeChangedInIndicator()); + + // change volume to 0.0... no warning should be emitted + setActionValue("volume", QVariant::fromValue(0.0)); + + WAIT_FOR_SIGNALS(notificationsSpy, 3); + + // the first time we also have the calls to + // GetServerInformation and GetCapabilities + checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); + checkVolumeNotification(0.0, "", false, notificationsSpy.at(2)); + notificationsSpy.clear(); + + // change volume to 0.5... no warning should be emitted + setActionValue("volume", QVariant::fromValue(0.5)); + + WAIT_FOR_SIGNALS(notificationsSpy, 1); + + checkVolumeNotification(0.5, "", false, notificationsSpy.at(0)); + notificationsSpy.clear(); + + // change volume to 1.0... no warning should be emitted, we are in alert mode + setActionValue("volume", QVariant::fromValue(1.0)); + + WAIT_FOR_SIGNALS(notificationsSpy, 1); + + checkVolumeNotification(1.0, "", false, notificationsSpy.at(0)); + notificationsSpy.clear(); +} + } // namespace -- cgit v1.2.3