/*
* Copyright (C) 2015 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 .
*
* Author: Xavi Garcia
*/
#include
#include
#include
#include
using namespace std;
using namespace testing;
namespace mh = lomiri::gmenuharness;
namespace
{
class TestIndicator: public IndicatorSoundTestBase
{
};
TEST_F(TestIndicator, PhoneCheckRootIcon)
{
double INITIAL_VOLUME = 0.0;
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 that the volume is set and give
// time to the indicator to start
EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-scroll-action", "indicator.scroll")
.string_attribute("x-ayatana-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, "Volume"))
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
.action("indicator.phone-settings")
)
).match());
QStringList mutedIcon = {"audio-volume-muted-panel", "audio-volume-muted", "audio-volume", "audio"};
EXPECT_EQ(getRootIconValue(), mutedIcon);
QStringList lowVolumeIcon = {"audio-volume-low-panel", "audio-volume-low", "audio-volume", "audio"};
for( double volume = 0.1; volume <= 0.3; volume+=0.1)
{
EXPECT_TRUE(setStreamRestoreVolume("alert", volume));
EXPECT_EQ(getRootIconValue(), lowVolumeIcon);
}
EXPECT_TRUE(setStreamRestoreVolume("alert", 0.4));
QStringList mediumVolumeIcon = {"audio-volume-medium-panel", "audio-volume-medium", "audio-volume", "audio"};
for( double volume = 0.4; volume <= 0.7; volume+=0.1)
{
EXPECT_TRUE(setStreamRestoreVolume("alert", volume));
EXPECT_EQ(getRootIconValue(), mediumVolumeIcon);
}
QStringList highVolumeIcon = {"audio-volume-high-panel", "audio-volume-high", "audio-volume", "audio"};
for( double volume = 0.8; volume <= 1.0; volume+=0.1)
{
EXPECT_TRUE(setStreamRestoreVolume("alert", volume));
EXPECT_EQ(getRootIconValue(), highVolumeIcon);
}
}
TEST_F(TestIndicator, PhoneTestExternalMicInOut)
{
double INITIAL_VOLUME = 0.0;
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());
EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-scroll-action", "indicator.scroll")
.string_attribute("x-ayatana-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, "Volume"))
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
.action("indicator.phone-settings")
)
).match());
EXPECT_TRUE(plugExternalMic(true));
// check that we have the mic slider now.
EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-scroll-action", "indicator.scroll")
.string_attribute("x-ayatana-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, "Volume"))
.item(micSlider(1.0, "Microphone Volume"))
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
.action("indicator.phone-settings")
)
).match());
// unplug the external mic
EXPECT_TRUE(plugExternalMic(false));
// and check that there's no mic slider
EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-scroll-action", "indicator.scroll")
.string_attribute("x-ayatana-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, "Volume"))
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
.action("indicator.phone-settings")
)
).match());
}
TEST_F(TestIndicator, DesktopTestExternalMicInOut)
{
double INITIAL_VOLUME = 0.0;
ASSERT_NO_THROW(startAccountsService());
EXPECT_TRUE(clearGSettingsPlayers());
ASSERT_NO_THROW(startPulseDesktop());
// initialize volumes in pulseaudio
EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME));
EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME));
// start now the indicator, so it picks the new volumes
ASSERT_NO_THROW(startIndicator());
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
EXPECT_TRUE(plugExternalMic(true));
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
.item(micSlider(1.0, "Microphone Volume"))
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
EXPECT_TRUE(plugExternalMic(false));
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
}
TEST_F(TestIndicator, PhoneChangeRoleVolume)
{
double INITIAL_VOLUME = 0.0;
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());
// Generate a random volume in the range [0...0.33]
QTime now = QTime::currentTime();
qsrand(now.msec());
int randInt = qrand() % 33;
const double randomVolume = randInt / 100.0;
QSignalSpy &userAccountsSpy = *signal_spy_volume_changed_;
// set an initial volume to the alert role
userAccountsSpy.clear();
EXPECT_TRUE(setVolumeUntilAccountsIsConnected(1.0));
userAccountsSpy.clear();
// play a test sound, it should change the role in the indicator
EXPECT_TRUE(startTestSound("multimedia"));
// this time we only expect 1 signal as it's only the indicator
// updating the value
WAIT_FOR_SIGNALS(userAccountsSpy, 1);
//EXPECT_TRUE(waitVolumeChangedInIndicator());
userAccountsSpy.clear();
// set the random volume to the multimedia role
setActionValue("volume", QVariant::fromValue(randomVolume));
if (randomVolume != INITIAL_VOLUME)
{
WAIT_FOR_SIGNALS(userAccountsSpy, 1);
}
// check the indicator
EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-scroll-action", "indicator.scroll")
.string_attribute("x-ayatana-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(randomVolume, "Volume"))
)
).match());
// check that the last item is Sound Settings
EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::ends_with)
.submenu()
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
.action("indicator.phone-settings")
)
).match());
// initialize the signal spy
EXPECT_TRUE(initializeMenuChangedSignal());
userAccountsSpy.clear();
// stop the test sound, the role should change again to alert
stopTestSound();
if (randomVolume != 1.0)
{
// wait for the menu change
EXPECT_TRUE(waitMenuChange());
}
// check the initial volume for the alert role
EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-scroll-action", "indicator.scroll")
.string_attribute("x-ayatana-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, "Volume"))
)
).match());
// check that the last item is Sound Settings
EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::ends_with)
.submenu()
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
.action("indicator.phone-settings")
)
).match());
}
TEST_F(TestIndicator, PhoneBasicInitialVolume)
{
double INITIAL_VOLUME = 0.0;
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());
EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-scroll-action", "indicator.scroll")
.string_attribute("x-ayatana-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, "Volume"))
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
.action("indicator.phone-settings")
)
).match());
}
TEST_F(TestIndicator, PhoneAddMprisPlayer)
{
double INITIAL_VOLUME = 0.0;
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());
EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-scroll-action", "indicator.scroll")
.string_attribute("x-ayatana-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, "Volume"))
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
.action("indicator.phone-settings")
)
).match());
// initialize the signal spy
EXPECT_TRUE(initializeMenuChangedSignal());
// start the test player
EXPECT_TRUE(startTestMprisPlayer("testplayer1"));
// wait for the menu change
EXPECT_TRUE(waitMenuChange());
// finally verify that the player is added
EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-scroll-action", "indicator.scroll")
.string_attribute("x-ayatana-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, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer1.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer1.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
.action("indicator.phone-settings")
)
).match());
}
TEST_F(TestIndicator, DesktopBasicInitialVolume)
{
double INITIAL_VOLUME = 0.0;
ASSERT_NO_THROW(startAccountsService());
EXPECT_TRUE(clearGSettingsPlayers());
ASSERT_NO_THROW(startPulseDesktop());
// initialize volumes in pulseaudio
EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME));
EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME));
// start the test player
EXPECT_TRUE(startTestMprisPlayer("testplayer1"));
// start now the indicator, so it picks the new volumes
ASSERT_NO_THROW(startIndicator());
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer1.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer1.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
}
TEST_F(TestIndicator, DesktopAddMprisPlayer)
{
double INITIAL_VOLUME = 0.0;
ASSERT_NO_THROW(startAccountsService());
EXPECT_TRUE(clearGSettingsPlayers());
ASSERT_NO_THROW(startPulseDesktop());
// initialize volumes in pulseaudio
EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME));
EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME));
// start the test player
EXPECT_TRUE(startTestMprisPlayer("testplayer1"));
// start now the indicator, so it picks the new volumes
ASSERT_NO_THROW(startIndicator());
// check that the player is added
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer1.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer1.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
// stop the test player
EXPECT_TRUE(stopTestMprisPlayer("testplayer1"));
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
}
TEST_F(TestIndicator, DesktopMprisPlayersPlaybackControls)
{
double INITIAL_VOLUME = 0.0;
ASSERT_NO_THROW(startAccountsService());
EXPECT_TRUE(clearGSettingsPlayers());
ASSERT_NO_THROW(startPulseDesktop());
// initialize volumes in pulseaudio
EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME));
EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME));
// start the test player
EXPECT_TRUE(startTestMprisPlayer("testplayer1"));
// start now the indicator, so it picks the new volumes
ASSERT_NO_THROW(startIndicator());
// check that the player is added
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer1.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer1.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
// start the second test player
EXPECT_TRUE(startTestMprisPlayer("testplayer2"));
// check that the player is added
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer1.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer1.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer2.desktop")
.label("TestPlayer2")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer2.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer2.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer2.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
// start the third test player
EXPECT_TRUE(startTestMprisPlayer("testplayer3"));
// check that the player is added
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer1.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer1.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer2.desktop")
.label("TestPlayer2")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer2.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer2.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer2.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer3.desktop")
.label("TestPlayer3")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer3.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer3.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer3.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
// stop the test player
EXPECT_TRUE(stopTestMprisPlayer("testplayer3"));
// check that player 3 is present, but it has no playback controls
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer1.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer1.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer2.desktop")
.label("TestPlayer2")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer2.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer2.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer2.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer3.desktop")
.label("TestPlayer3")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
EXPECT_TRUE(stopTestMprisPlayer("testplayer2"));
// check that player 2 is present, but it has no playback controls
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer1.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer1.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer2.desktop")
.label("TestPlayer2")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer3.desktop")
.label("TestPlayer3")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
EXPECT_TRUE(stopTestMprisPlayer("testplayer1"));
// check that player 1 is present, and it still has the playback controls
// as it was the last one being executed
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer1.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer1.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer2.desktop")
.label("TestPlayer2")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer3.desktop")
.label("TestPlayer3")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
// start the third test player
EXPECT_TRUE(startTestMprisPlayer("testplayer3"));
// check that player 3 is the only one with playback controls
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer2.desktop")
.label("TestPlayer2")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer3.desktop")
.label("TestPlayer3")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer3.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer3.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer3.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
// start the rest of players
EXPECT_TRUE(startTestMprisPlayer("testplayer1"));
EXPECT_TRUE(startTestMprisPlayer("testplayer2"));
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer1.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer1.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer2.desktop")
.label("TestPlayer2")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer2.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer2.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer2.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer3.desktop")
.label("TestPlayer3")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer3.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer3.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer3.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
// stop all players
EXPECT_TRUE(stopTestMprisPlayer("testplayer1"));
EXPECT_TRUE(stopTestMprisPlayer("testplayer2"));
EXPECT_TRUE(stopTestMprisPlayer("testplayer3"));
// check that player 3 is the only one with playback controls
// as it was the last one being stopped
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer2.desktop")
.label("TestPlayer2")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer3.desktop")
.label("TestPlayer3")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-play-action","indicator.play.testplayer3.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
// check that the last running player is the one we expect
QVariant lastPlayerRunning = waitLastRunningPlayerChanged();
EXPECT_TRUE(lastPlayerRunning.type() == QVariant::String);
EXPECT_EQ(lastPlayerRunning.toString(), "testplayer3.desktop");
// restart the indicator to simulate a new user session
ASSERT_NO_THROW(startIndicator());
// check that player 3 is the only one with playback controls
// as it was the last one being stopped
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer3.desktop")
.label("TestPlayer3")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-play-action","indicator.play.testplayer3.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer2.desktop")
.label("TestPlayer2")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
}
TEST_F(TestIndicator, DesktopMprisPlayerButtonsState)
{
double INITIAL_VOLUME = 0.0;
ASSERT_NO_THROW(startAccountsService());
EXPECT_TRUE(clearGSettingsPlayers());
ASSERT_NO_THROW(startPulseDesktop());
// initialize volumes in pulseaudio
EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME));
EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME));
// start the test player
EXPECT_TRUE(startTestMprisPlayer("testplayer1"));
// start now the indicator, so it picks the new volumes
ASSERT_NO_THROW(startIndicator());
// check that the player is added
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer1.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer1.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
// change the state of CanGoNext
EXPECT_TRUE(setTestMprisPlayerProperty("testplayer1", "CanGoNext", false));
// verify that the action changes
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer1.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.attribute_not_set("x-ayatana-next-action")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
// change the state of CanGoPrevious
EXPECT_TRUE(setTestMprisPlayerProperty("testplayer1", "CanGoPrevious", false));
// verify that the action changes
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.attribute_not_set("x-ayatana-previous-action")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.attribute_not_set("x-ayatana-next-action")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
// set back both to true
EXPECT_TRUE(setTestMprisPlayerProperty("testplayer1", "CanGoNext", true));
EXPECT_TRUE(setTestMprisPlayerProperty("testplayer1", "CanGoPrevious", true));
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::all)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(INITIAL_VOLUME, "Volume"))
)
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher()
.action("indicator.testplayer1.desktop")
.label("TestPlayer1")
.themed_icon("icon", {"testplayer"})
.string_attribute("x-ayatana-type", "org.ayatana.unity.media-player")
)
.item(mh::MenuItemMatcher()
.string_attribute("x-ayatana-previous-action","indicator.previous.testplayer1.desktop")
.string_attribute("x-ayatana-play-action","indicator.play.testplayer1.desktop")
.string_attribute("x-ayatana-next-action","indicator.next.testplayer1.desktop")
.string_attribute("x-ayatana-type","org.ayatana.unity.playback-item")
)
)
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
}
TEST_F(TestIndicator, DesktopChangeRoleVolume)
{
double INITIAL_VOLUME = 0.0;
EXPECT_TRUE(resetAllowAmplifiedVolume());
ASSERT_NO_THROW(startAccountsService());
ASSERT_NO_THROW(startPulseDesktop());
// initialize volumes in pulseaudio
// expect false for stream restore, because the module
// is not loaded in the desktop pulseaudio instance
EXPECT_FALSE(setStreamRestoreVolume("mutimedia", INITIAL_VOLUME));
EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME));
EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME));
// start now the indicator, so it picks the new volumes
ASSERT_NO_THROW(startIndicator());
// Generate a random volume in the range [0...0.33]
QTime now = QTime::currentTime();
qsrand(now.msec());
int randInt = qrand() % 33;
const double randomVolume = randInt / 100.0;
// play a test sound, it should NOT change the role in the indicator
EXPECT_TRUE(startTestSound("multimedia"));
EXPECT_FALSE(waitVolumeChangedInIndicator());
// set the random volume
EXPECT_TRUE(setSinkVolume(randomVolume));
if (randomVolume != INITIAL_VOLUME)
{
EXPECT_TRUE(waitVolumeChangedInIndicator());
}
// check the indicator
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::starts_with)
.submenu()
.item(mh::MenuItemMatcher()
.section()
.item(mh::MenuItemMatcher().checkbox()
.label("Mute")
)
.item(volumeSlider(randomVolume, "Volume"))
)
).match());
// check that the last item is Sound Settings
EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::ends_with)
.submenu()
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
)
).match());
// stop the test sound, the role should change again to alert
stopTestSound();
// although we were playing something in the multimedia role
// the server does not have the streamrestore module, so
// the volume does not change (the role does not change)
EXPECT_FALSE(waitVolumeChangedInIndicator());
// check the initial volume for the alert role
EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-scroll-action", "indicator.scroll")
.string_attribute("x-ayatana-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(randomVolume, "Volume"))
)
).match());
// check that the last item is Sound Settings
EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
.item(mh::MenuItemMatcher()
.action("indicator.root")
.string_attribute("x-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-secondary-action", "indicator.mute")
.mode(mh::MenuItemMatcher::Mode::ends_with)
.submenu()
.item(mh::MenuItemMatcher()
.label("Sound Settings…")
.action("indicator.phone-settings")
)
).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-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-scroll-action", "indicator.scroll")
.string_attribute("x-ayatana-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, "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, "Speakers", false, notificationsSpy.at(2));
notificationsSpy.clear();
setActionValue("volume", QVariant::fromValue(0.0));
WAIT_FOR_SIGNALS(notificationsSpy, 2)
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0));
checkVolumeNotification(0.0, "Speakers", false, notificationsSpy.at(1));
notificationsSpy.clear();
setActionValue("volume", QVariant::fromValue(0.5));
WAIT_FOR_SIGNALS(notificationsSpy, 2)
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0));
checkVolumeNotification(0.5, "Speakers", false, notificationsSpy.at(1));
}
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());
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, "Headphones", false, notificationsSpy.at(2));
notificationsSpy.clear();
// change volume to 0.3... no warning should be emitted
setActionValue("volume", QVariant::fromValue(0.3));
EXPECT_TRUE(waitVolumeChangedInIndicator());
WAIT_FOR_SIGNALS(notificationsSpy, 2);
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0));
checkVolumeNotification(0.3, "Headphones", false, notificationsSpy.at(1));
notificationsSpy.clear();
// change volume to 0.5... no warning should be emitted
setActionValue("volume", QVariant::fromValue(0.5));
EXPECT_TRUE(waitVolumeChangedInIndicator());
WAIT_FOR_SIGNALS(notificationsSpy, 2);
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0));
checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(1));
notificationsSpy.clear();
// change volume to 1.0... warning should be emitted
setActionValue("volume", QVariant::fromValue(1.0));
EXPECT_TRUE(waitVolumeChangedInIndicator());
WAIT_FOR_SIGNALS(notificationsSpy, 8);
// the notification is sent twice (TODO check why)
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0));
checkCloseNotification(1, notificationsSpy.at(1));
checkHighVolumeNotification(notificationsSpy.at(2));
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(3));
checkCloseNotification(1, notificationsSpy.at(4));
checkHighVolumeNotification(notificationsSpy.at(5));
// get the last notification ID
int idNotification = getNotificationID(notificationsSpy.at(5));
ASSERT_NE(-1, idNotification);
// check the sync value before cancelling the dialog
bool isValid;
qlonglong syncValueBeforeCancel = getVolumeSyncValue(&isValid);
EXPECT_TRUE(isValid);
// 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-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-scroll-action", "indicator.scroll")
.string_attribute("x-ayatana-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, "Volume (Headphones)"))
)
).match());
// verify that the sync value is increased
qlonglong syncValueAfterCancel = getVolumeSyncValue(&isValid);
EXPECT_TRUE(isValid);
EXPECT_NE(syncValueBeforeCancel, syncValueAfterCancel);
// try again...
notificationsSpy.clear();
// change volume to 1.0... warning should be emitted
setActionValue("volume", QVariant::fromValue(1.0));
EXPECT_TRUE(waitVolumeChangedInIndicator());
WAIT_FOR_SIGNALS(notificationsSpy, 6);
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0));
checkHighVolumeNotification(notificationsSpy.at(1));
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(2));
checkHighVolumeNotification(notificationsSpy.at(3));
// get the last notification ID
idNotification = getNotificationID(notificationsSpy.at(1));
ASSERT_NE(-1, 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-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-scroll-action", "indicator.scroll")
.string_attribute("x-ayatana-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, "Volume (Headphones)"))
.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));
EXPECT_TRUE(waitVolumeChangedInIndicator());
WAIT_FOR_SIGNALS(notificationsSpy, 6);
// check the notification TODO check why the sound indicator sends it twice
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0));
checkCloseNotification(idNotification, notificationsSpy.at(1));
checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(2));
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(3));
checkCloseNotification(idNotification, notificationsSpy.at(4));
checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(5));
// 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-ayatana-type", "org.ayatana.indicator.root")
.string_attribute("x-ayatana-scroll-action", "indicator.scroll")
.string_attribute("x-ayatana-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, "Volume (Headphones)"))
)
).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));
EXPECT_TRUE(waitVolumeChangedInIndicator());
WAIT_FOR_SIGNALS(notificationsSpy, 4);
// check the notification TODO check why the sound indicator sends it twice
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0));
checkVolumeNotification(1.0, "Headphones", true, notificationsSpy.at(1));
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(2));
checkVolumeNotification(1.0, "Headphones", true, notificationsSpy.at(3));
}
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, 5);
// 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, "Headphones", false, notificationsSpy.at(2));
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(3));
checkVolumeNotification(0.0, "Headphones", false, notificationsSpy.at(4));
notificationsSpy.clear();
// change volume to 0.5... no warning should be emitted
setActionValue("volume", QVariant::fromValue(0.5));
WAIT_FOR_SIGNALS(notificationsSpy, 2);
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0));
checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(1));
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, 2);
checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0));
checkVolumeNotification(1.0, "Headphones", false, notificationsSpy.at(1));
notificationsSpy.clear();
}
TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerWiredLabels)
{
checkPortDevicesLabels(WIRED, WIRED);
}
TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerBluetoothLabels)
{
checkPortDevicesLabels(BLUETOOTH, BLUETOOTH);
}
TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerUSBLabels)
{
checkPortDevicesLabels(USB, USB);
}
TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerHDMILabels)
{
checkPortDevicesLabels(HDMI, HDMI);
}
} // namespace