/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifdef XP_WIN #include #else #include #endif #include "TestHarness.h" #include "nsWeakReference.h" #include "AudioChannelService.h" #include "AudioChannelAgent.h" #include "nsThreadUtils.h" #define TEST_ENSURE_BASE(_test, _msg) \ PR_BEGIN_MACRO \ if (!(_test)) { \ fail(_msg); \ return NS_ERROR_FAILURE; \ } else { \ passed(_msg); \ } \ PR_END_MACRO using namespace mozilla::dom; void spin_events_loop_until_false(const bool* const aCondition) { nsCOMPtr thread(::do_GetCurrentThread()); nsresult rv = NS_OK; bool processed = true; while (*aCondition && NS_SUCCEEDED(rv)) { rv = thread->ProcessNextEvent(true, &processed); } } class Agent : public nsIAudioChannelAgentCallback, public nsSupportsWeakReference { protected: virtual ~Agent() { if (mRegistered) { StopPlaying(); } } public: NS_DECL_ISUPPORTS explicit Agent(AudioChannel aChannel) : mChannel(aChannel) , mWaitCallback(false) , mRegistered(false) , mCanPlay(AUDIO_CHANNEL_STATE_MUTED) { mAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1"); } nsresult Init(bool video=false) { nsresult rv = NS_OK; if (video) { rv = mAgent->InitWithVideo(nullptr, static_cast(mChannel), this, true); } else { rv = mAgent->InitWithWeakCallback(nullptr, static_cast(mChannel), this); } NS_ENSURE_SUCCESS(rv, rv); return mAgent->SetVisibilityState(false); } nsresult StartPlaying(AudioChannelState *_ret) { if (mRegistered) { StopPlaying(); } nsresult rv = mAgent->StartPlaying((int32_t *)_ret); mRegistered = true; return rv; } nsresult StopPlaying() { mRegistered = false; spin_events_loop_until_false(&mWaitCallback); return mAgent->StopPlaying(); } nsresult SetVisibilityState(bool visible) { if (mRegistered) { mWaitCallback = true; } return mAgent->SetVisibilityState(visible); } NS_IMETHODIMP CanPlayChanged(int32_t canPlay) override { mCanPlay = static_cast(canPlay); mWaitCallback = false; return NS_OK; } NS_IMETHODIMP WindowVolumeChanged() override { return NS_OK; } nsresult GetCanPlay(AudioChannelState *_ret, bool aWaitCallback = false) { if (aWaitCallback) { mWaitCallback = true; } spin_events_loop_until_false(&mWaitCallback); *_ret = mCanPlay; return NS_OK; } nsCOMPtr mAgent; AudioChannel mChannel; bool mWaitCallback; bool mRegistered; AudioChannelState mCanPlay; }; NS_IMPL_ISUPPORTS(Agent, nsIAudioChannelAgentCallback, nsISupportsWeakReference) nsresult TestDoubleStartPlaying() { nsRefPtr agent = new Agent(AudioChannel::Normal); nsresult rv = agent->Init(); NS_ENSURE_SUCCESS(rv, rv); AudioChannelState playable; rv = agent->mAgent->StartPlaying((int32_t *)&playable); NS_ENSURE_SUCCESS(rv, rv); rv = agent->mAgent->StartPlaying((int32_t *)&playable); TEST_ENSURE_BASE(NS_FAILED(rv), "Test0: StartPlaying calling twice must return error"); return NS_OK; } nsresult TestOneNormalChannel() { nsRefPtr agent = new Agent(AudioChannel::Normal); nsresult rv = agent->Init(); NS_ENSURE_SUCCESS(rv, rv); AudioChannelState playable; rv = agent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, "Test1: A normal channel unvisible agent must be muted"); rv = agent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = agent->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test1: A normal channel visible agent must be playable"); return rv; } nsresult TestTwoNormalChannels() { nsRefPtr agent1 = new Agent(AudioChannel::Normal); nsresult rv = agent1->Init(); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr agent2 = new Agent(AudioChannel::Normal); rv = agent2->Init(); NS_ENSURE_SUCCESS(rv, rv); AudioChannelState playable; rv = agent1->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, "Test2: A normal channel unvisible agent1 must be muted"); rv = agent2->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, "Test2: A normal channel unvisible agent2 must be muted"); rv = agent1->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = agent2->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = agent1->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test2: A normal channel visible agent1 must be playable"); rv = agent2->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test2: A normal channel visible agent2 must be playable"); return rv; } nsresult TestContentChannels() { nsRefPtr agent1 = new Agent(AudioChannel::Content); nsresult rv = agent1->Init(); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr agent2 = new Agent(AudioChannel::Content); rv = agent2->Init(); NS_ENSURE_SUCCESS(rv, rv); // All content channels in the foreground can be allowed to play rv = agent1->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = agent2->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); AudioChannelState playable; rv = agent1->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test3: A content channel visible agent1 must be playable"); rv = agent2->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test3: A content channel visible agent2 must be playable"); // Test the transition state of one content channel tried to set non-visible // state first when app is going to background. rv = agent1->SetVisibilityState(false); NS_ENSURE_SUCCESS(rv, rv); rv = agent1->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test3: A content channel unvisible agent1 must be playable from " "foreground to background"); // Test all content channels set non-visible already rv = agent2->SetVisibilityState(false); NS_ENSURE_SUCCESS(rv, rv); rv = agent2->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test3: A content channel unvisible agent2 must be playable from " "foreground to background"); // Clear the content channels & mActiveContentChildIDs in AudioChannelService. // If agent stop playable in the background, we will reserve it's childID in // mActiveContentChildIDs, then it can allow to play next song. So we set agents // to foreground first then stopping to play rv = agent1->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = agent2->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = agent1->StopPlaying(); NS_ENSURE_SUCCESS(rv, rv); rv = agent2->StopPlaying(); NS_ENSURE_SUCCESS(rv, rv); // Test that content channels can be allow to play when they starts from // the background state rv = agent1->SetVisibilityState(false); NS_ENSURE_SUCCESS(rv, rv); rv = agent2->SetVisibilityState(false); NS_ENSURE_SUCCESS(rv, rv); rv = agent1->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test3: A content channel unvisible agent1 must be playable " "from background state"); rv = agent2->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test3: A content channel unvisible agent2 must be playable " "from background state"); return rv; } nsresult TestFadedState() { nsRefPtr normalAgent = new Agent(AudioChannel::Normal); nsresult rv = normalAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr contentAgent = new Agent(AudioChannel::Content); rv = contentAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr notificationAgent = new Agent(AudioChannel::Notification); rv = notificationAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); rv = normalAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = contentAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = notificationAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); AudioChannelState playable; rv = normalAgent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test4: A normal channel visible agent must be playable"); rv = contentAgent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test4: A content channel visible agent must be playable"); rv = notificationAgent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test4: A notification channel visible agent must be playable"); rv = contentAgent->GetCanPlay(&playable, true); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_FADED, "Test4: A content channel unvisible agent must be faded because of " "notification channel is playing"); rv = contentAgent->SetVisibilityState(false); NS_ENSURE_SUCCESS(rv, rv); rv = contentAgent->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_FADED, "Test4: A content channel unvisible agent must be faded because of " "notification channel is playing"); rv = notificationAgent->SetVisibilityState(false); NS_ENSURE_SUCCESS(rv, rv); rv = notificationAgent->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test4: A notification channel unvisible agent must be playable from " "foreground to background"); rv = notificationAgent->StopPlaying(); NS_ENSURE_SUCCESS(rv, rv); rv = contentAgent->GetCanPlay(&playable, true); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test4: A content channel unvisible agent must be playable " "because of notification channel is stopped"); rv = contentAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); return rv; } nsresult TestPriorities() { nsRefPtr normalAgent = new Agent(AudioChannel::Normal); nsresult rv = normalAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr contentAgent = new Agent(AudioChannel::Content); rv = contentAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr notificationAgent = new Agent(AudioChannel::Notification); rv = notificationAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr alarmAgent = new Agent(AudioChannel::Alarm); rv = alarmAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr telephonyAgent = new Agent(AudioChannel::Telephony); rv = telephonyAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr ringerAgent = new Agent(AudioChannel::Ringer); rv = ringerAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr pNotificationAgent = new Agent(AudioChannel::Publicnotification); rv = pNotificationAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); AudioChannelState playable; rv = normalAgent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, "Test5: A normal channel unvisible agent must be muted"); rv = contentAgent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test5: A content channel unvisible agent must be playable while " "playing from background state"); rv = notificationAgent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test5: A notification channel unvisible agent must be playable"); rv = alarmAgent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test5: An alarm channel unvisible agent must be playable"); rv = notificationAgent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, "Test5: A notification channel unvisible agent must be muted when an " "alarm is playing"); rv = telephonyAgent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test5: A telephony channel unvisible agent must be playable"); rv = alarmAgent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, "Test5: An alarm channel unvisible agent must be muted when a telephony " "is playing"); rv = ringerAgent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test5: A ringer channel unvisible agent must be playable"); rv = telephonyAgent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, "Test5: A telephony channel unvisible agent must be muted when a ringer " "is playing"); rv = pNotificationAgent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test5: A pNotification channel unvisible agent must be playable"); rv = ringerAgent->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, "Test5: A ringer channel unvisible agent must be muted when a public " "notification is playing"); // Stop to play notification channel or normal/content will be faded. // Which already be tested on Test 4. rv = notificationAgent->StopPlaying(); NS_ENSURE_SUCCESS(rv, rv); // Settings visible the normal channel. rv = normalAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = normalAgent->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test5: A normal channel visible agent must be playable"); // Set the content channel as visible . rv = contentAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); // Content must be playable because visible. rv = contentAgent->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test5: A content channel visible agent must be playable"); // Set the alarm channel as visible. rv = alarmAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = alarmAgent->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test5: An alarm channel visible agent must be playable"); // Set the telephony channel as visible. rv = telephonyAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = telephonyAgent->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test5: A telephony channel visible agent must be playable"); // Set the ringer channel as visible. rv = ringerAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = ringerAgent->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test5: A ringer channel visible agent must be playable"); // Set the public notification channel as visible. rv = pNotificationAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = pNotificationAgent->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test5: A pNotification channel visible agent must be playable"); return rv; } nsresult TestOneVideoNormalChannel() { nsRefPtr agent1 = new Agent(AudioChannel::Normal); nsresult rv = agent1->Init(true); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr agent2 = new Agent(AudioChannel::Content); rv = agent2->Init(false); NS_ENSURE_SUCCESS(rv, rv); AudioChannelState playable; rv = agent1->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, "Test6: A video normal channel invisible agent1 must be muted"); rv = agent2->StartPlaying(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test6: A content channel invisible agent2 must be playable"); // one video normal channel in foreground and one content channel in background rv = agent1->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = agent1->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test6: A video normal channel visible agent1 must be playable"); rv = agent2->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, "Test6: A content channel invisible agent2 must be muted"); // both one video normal channel and one content channel in foreground rv = agent2->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); rv = agent1->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test6: A video normal channel visible agent1 must be playable"); rv = agent2->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test6: A content channel visible agent2 must be playable"); // one video normal channel in background and one content channel in foreground rv = agent1->SetVisibilityState(false); NS_ENSURE_SUCCESS(rv, rv); rv = agent1->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, "Test6: A video normal channel invisible agent1 must be muted"); rv = agent2->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test6: A content channel visible agent2 must be playable"); // both one video normal channel and one content channel in background rv = agent2->SetVisibilityState(false); NS_ENSURE_SUCCESS(rv, rv); rv = agent1->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, "Test6: A video normal channel invisible agent1 must be muted"); rv = agent2->GetCanPlay(&playable); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, "Test6: A content channel invisible agent2 must be playable"); return rv; } int main(int argc, char** argv) { ScopedXPCOM xpcom("AudioChannelService"); if (xpcom.failed()) { return 1; } if (NS_FAILED(TestDoubleStartPlaying())) { return 1; } if (NS_FAILED(TestOneNormalChannel())) { return 1; } if (NS_FAILED(TestTwoNormalChannels())) { return 1; } if (NS_FAILED(TestContentChannels())) { return 1; } if (NS_FAILED(TestFadedState())) { return 1; } // Channel type with AudioChannel::Telephony cannot be unregistered until the // main thread has chances to process 1500 millisecond timer. In order to // skip ambiguous return value of ChannelsActiveWithHigherPriorityThan(), new // test cases are added before any test case that registers the channel type // with AudioChannel::Telephony channel. if (NS_FAILED(TestOneVideoNormalChannel())) { return 1; } if (NS_FAILED(TestPriorities())) { return 1; } return 0; }