| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <tuple> | |
| 6 | |
| 7 #include "base/command_line.h" | |
| 8 #include "base/location.h" | |
| 9 #include "base/logging.h" | |
| 10 #include "base/single_thread_task_runner.h" | |
| 11 #include "base/threading/thread_task_runner_handle.h" | |
| 12 #include "content/browser/media/session/media_session.h" | |
| 13 #include "content/public/browser/web_contents.h" | |
| 14 #include "content/public/common/content_switches.h" | |
| 15 #include "content/public/test/browser_test_utils.h" | |
| 16 #include "content/public/test/content_browser_test.h" | |
| 17 #include "content/public/test/content_browser_test_utils.h" | |
| 18 #include "content/public/test/test_navigation_observer.h" | |
| 19 #include "content/shell/browser/shell.h" | |
| 20 #include "media/base/media_switches.h" | |
| 21 #include "testing/gtest/include/gtest/gtest.h" | |
| 22 | |
| 23 namespace content { | |
| 24 namespace { | |
| 25 static const char kStartPlayerScript[] = | |
| 26 "document.getElementById('long-video').play()"; | |
| 27 static const char kPausePlayerScript[] = | |
| 28 "document.getElementById('long-video').pause()"; | |
| 29 | |
| 30 enum class MediaSuspend { | |
| 31 ENABLED, | |
| 32 DISABLED, | |
| 33 }; | |
| 34 | |
| 35 enum class Pipeline { | |
| 36 WMPI, | |
| 37 WMPA, | |
| 38 }; | |
| 39 | |
| 40 enum class BackgroundResuming { | |
| 41 ENABLED, | |
| 42 DISABLED, | |
| 43 }; | |
| 44 | |
| 45 enum class SessionState { | |
| 46 ACTIVE, | |
| 47 SUSPENDED, | |
| 48 INACTIVE, | |
| 49 }; | |
| 50 | |
| 51 struct VisibilityTestData { | |
| 52 MediaSuspend media_suspend; | |
| 53 BackgroundResuming background_resuming; | |
| 54 SessionState session_state_before_hide; | |
| 55 SessionState session_state_after_hide; | |
| 56 }; | |
| 57 | |
| 58 } | |
| 59 | |
| 60 | |
| 61 // Base class of MediaSession visibility tests. The class is intended | |
| 62 // to be used to run tests under different configurations. Tests | |
| 63 // should inheret from this class, set up their own command line per | |
| 64 // their configuration, and use macro INCLUDE_TEST_FROM_BASE_CLASS to | |
| 65 // include required tests. See | |
| 66 // media_session_visibility_browsertest_instances.cc for examples. | |
| 67 class MediaSessionVisibilityBrowserTest | |
| 68 : public ContentBrowserTest, | |
| 69 public ::testing::WithParamInterface< | |
| 70 std::tr1::tuple<VisibilityTestData, Pipeline>> { | |
| 71 public: | |
| 72 MediaSessionVisibilityBrowserTest() = default; | |
| 73 ~MediaSessionVisibilityBrowserTest() override = default; | |
| 74 | |
| 75 void SetUpOnMainThread() override { | |
| 76 ContentBrowserTest::SetUpOnMainThread(); | |
| 77 web_contents_ = shell()->web_contents(); | |
| 78 media_session_ = MediaSession::Get(web_contents_); | |
| 79 | |
| 80 media_session_state_loop_runners_[MediaSession::State::ACTIVE] = | |
| 81 new MessageLoopRunner(); | |
| 82 media_session_state_loop_runners_[MediaSession::State::SUSPENDED] = | |
| 83 new MessageLoopRunner(); | |
| 84 media_session_state_loop_runners_[MediaSession::State::INACTIVE] = | |
| 85 new MessageLoopRunner(); | |
| 86 media_session_state_callback_subscription_ = | |
| 87 media_session_->RegisterMediaSessionStateChangedCallbackForTest( | |
| 88 base::Bind(&MediaSessionVisibilityBrowserTest:: | |
| 89 OnMediaSessionStateChanged, | |
| 90 base::Unretained(this))); | |
| 91 } | |
| 92 | |
| 93 void TearDownOnMainThread() override { | |
| 94 // Unsubscribe the callback subscription before tearing down, so that the | |
| 95 // CallbackList in MediaSession will be empty when it is destroyed. | |
| 96 media_session_state_callback_subscription_.reset(); | |
| 97 } | |
| 98 | |
| 99 void EnableDisableResumingBackgroundVideos(bool enable) { | |
| 100 std::string enabled_features; | |
| 101 std::string disabled_features; | |
| 102 if (enable) | |
| 103 enabled_features = media::kResumeBackgroundVideo.name; | |
| 104 else | |
| 105 disabled_features = media::kResumeBackgroundVideo.name; | |
| 106 | |
| 107 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); | |
| 108 feature_list->InitializeFromCommandLine( | |
| 109 enabled_features, disabled_features); | |
| 110 base::FeatureList::ClearInstanceForTesting(); | |
| 111 base::FeatureList::SetInstance(std::move(feature_list)); | |
| 112 } | |
| 113 | |
| 114 void SetUpCommandLine(base::CommandLine* command_line) override { | |
| 115 command_line->AppendSwitch( | |
| 116 switches::kDisableGestureRequirementForMediaPlayback); | |
| 117 #if !defined(OS_ANDROID) | |
| 118 command_line->AppendSwitch( | |
| 119 switches::kEnableDefaultMediaSession); | |
| 120 #endif // !defined(OS_ANDROID) | |
| 121 | |
| 122 VisibilityTestData params = GetVisibilityTestData(); | |
| 123 | |
| 124 if (params.media_suspend == MediaSuspend::ENABLED) | |
| 125 command_line->AppendSwitch(switches::kEnableMediaSuspend); | |
| 126 else | |
| 127 command_line->AppendSwitch(switches::kDisableMediaSuspend); | |
| 128 | |
| 129 #if defined(OS_ANDROID) | |
| 130 Pipeline pipeline = std::tr1::get<1>(GetParam()); | |
| 131 if (pipeline == Pipeline::WMPA) | |
| 132 command_line->AppendSwitch(switches::kDisableUnifiedMediaPipeline); | |
| 133 #endif // defined(OS_ANDROID) | |
| 134 | |
| 135 if (params.background_resuming == BackgroundResuming::ENABLED) { | |
| 136 command_line->AppendSwitchASCII(switches::kEnableFeatures, | |
| 137 media::kResumeBackgroundVideo.name); | |
| 138 } else { | |
| 139 command_line->AppendSwitchASCII(switches::kDisableFeatures, | |
| 140 media::kResumeBackgroundVideo.name); | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 const VisibilityTestData& GetVisibilityTestData() { | |
| 145 return std::tr1::get<0>(GetParam()); | |
| 146 } | |
| 147 | |
| 148 void StartPlayer() { | |
| 149 LoadTestPage(); | |
| 150 | |
| 151 LOG(INFO) << "Starting player"; | |
| 152 ClearMediaSessionStateLoopRunners(); | |
| 153 RunScript(kStartPlayerScript); | |
| 154 LOG(INFO) << "Waiting for session to be active"; | |
| 155 WaitForMediaSessionState(MediaSession::State::ACTIVE); | |
| 156 } | |
| 157 | |
| 158 // Maybe pause the player depending on whether the session state before hide | |
| 159 // is SUSPENDED. | |
| 160 void MaybePausePlayer() { | |
| 161 ASSERT_TRUE(GetVisibilityTestData().session_state_before_hide | |
| 162 != SessionState::INACTIVE); | |
| 163 if (GetVisibilityTestData().session_state_before_hide | |
| 164 == SessionState::ACTIVE) | |
| 165 return; | |
| 166 | |
| 167 LOG(INFO) << "Pausing player"; | |
| 168 ClearMediaSessionStateLoopRunners(); | |
| 169 RunScript(kPausePlayerScript); | |
| 170 LOG(INFO) << "Waiting for session to be suspended"; | |
| 171 WaitForMediaSessionState(MediaSession::State::SUSPENDED); | |
| 172 } | |
| 173 | |
| 174 void HideTab() { | |
| 175 LOG(INFO) << "Hiding the tab"; | |
| 176 ClearMediaSessionStateLoopRunners(); | |
| 177 web_contents_->WasHidden(); | |
| 178 } | |
| 179 | |
| 180 void CheckSessionStateAfterHide() { | |
| 181 MediaSession::State state_before_hide = | |
| 182 ToMediaSessionState(GetVisibilityTestData().session_state_before_hide); | |
| 183 MediaSession::State state_after_hide = | |
| 184 ToMediaSessionState(GetVisibilityTestData().session_state_after_hide); | |
| 185 | |
| 186 if (state_before_hide == state_after_hide) { | |
| 187 LOG(INFO) << "Waiting for 1 second and check session state is unchanged"; | |
| 188 Wait(base::TimeDelta::FromSeconds(1)); | |
| 189 ASSERT_EQ(media_session_->audio_focus_state_, state_after_hide); | |
| 190 } else { | |
| 191 LOG(INFO) << "Waiting for Session to change"; | |
| 192 WaitForMediaSessionState(state_after_hide); | |
| 193 } | |
| 194 | |
| 195 LOG(INFO) << "Test succeeded"; | |
| 196 } | |
| 197 | |
| 198 private: | |
| 199 void LoadTestPage() { | |
| 200 TestNavigationObserver navigation_observer(shell()->web_contents(), 1); | |
| 201 shell()->LoadURL(GetTestUrl("media/session", "media-session.html")); | |
| 202 navigation_observer.Wait(); | |
| 203 } | |
| 204 | |
| 205 void RunScript(const std::string& script) { | |
| 206 ASSERT_TRUE(ExecuteScript(web_contents_->GetMainFrame(), script)); | |
| 207 } | |
| 208 | |
| 209 void ClearMediaSessionStateLoopRunners() { | |
| 210 for (auto& state_loop_runner : media_session_state_loop_runners_) | |
| 211 state_loop_runner.second = new MessageLoopRunner(); | |
| 212 } | |
| 213 | |
| 214 void OnMediaSessionStateChanged(MediaSession::State state) { | |
| 215 ASSERT_TRUE(media_session_state_loop_runners_.count(state)); | |
| 216 media_session_state_loop_runners_[state]->Quit(); | |
| 217 } | |
| 218 | |
| 219 // TODO(zqzhang): This method is shared with | |
| 220 // MediaRouterIntegrationTests. Move it into a general place. | |
| 221 void Wait(base::TimeDelta timeout) { | |
| 222 base::RunLoop run_loop; | |
| 223 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
| 224 FROM_HERE, run_loop.QuitClosure(), timeout); | |
| 225 run_loop.Run(); | |
| 226 } | |
| 227 | |
| 228 void WaitForMediaSessionState(MediaSession::State state) { | |
| 229 ASSERT_TRUE(media_session_state_loop_runners_.count(state)); | |
| 230 media_session_state_loop_runners_[state]->Run(); | |
| 231 } | |
| 232 | |
| 233 MediaSession::State ToMediaSessionState(SessionState state) { | |
| 234 switch (state) { | |
| 235 case SessionState::ACTIVE: | |
| 236 return MediaSession::State::ACTIVE; | |
| 237 break; | |
| 238 case SessionState::SUSPENDED: | |
| 239 return MediaSession::State::SUSPENDED; | |
| 240 break; | |
| 241 case SessionState::INACTIVE: | |
| 242 return MediaSession::State::INACTIVE; | |
| 243 break; | |
| 244 default: | |
| 245 ADD_FAILURE() << "invalid SessionState to convert"; | |
| 246 return MediaSession::State::INACTIVE; | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 WebContents* web_contents_; | |
| 251 MediaSession* media_session_; | |
| 252 // MessageLoopRunners for waiting MediaSession state to change. Note that the | |
| 253 // MessageLoopRunners can accept Quit() before calling Run(), thus the state | |
| 254 // change can still be captured before waiting. For example, the MediaSession | |
| 255 // might go active immediately after calling HTMLMediaElement.play(). A test | |
| 256 // can listen to the state change before calling play(), and then wait for the | |
| 257 // state change after play(). | |
| 258 std::map<MediaSession::State, scoped_refptr<MessageLoopRunner> > | |
| 259 media_session_state_loop_runners_; | |
| 260 std::unique_ptr<base::CallbackList<void(MediaSession::State)>::Subscription> | |
| 261 media_session_state_callback_subscription_; | |
| 262 | |
| 263 DISALLOW_COPY_AND_ASSIGN(MediaSessionVisibilityBrowserTest); | |
| 264 }; | |
| 265 | |
| 266 namespace { | |
| 267 | |
| 268 VisibilityTestData kTestParams[] = { | |
| 269 { MediaSuspend::ENABLED, BackgroundResuming::DISABLED, | |
| 270 SessionState::SUSPENDED, SessionState::INACTIVE }, | |
| 271 { MediaSuspend::ENABLED, BackgroundResuming::DISABLED, | |
| 272 SessionState::ACTIVE, SessionState::INACTIVE }, | |
| 273 { MediaSuspend::ENABLED, BackgroundResuming::ENABLED, | |
| 274 SessionState::ACTIVE, SessionState::SUSPENDED }, | |
| 275 { MediaSuspend::ENABLED, BackgroundResuming::ENABLED, | |
| 276 SessionState::SUSPENDED, SessionState::SUSPENDED }, | |
| 277 { MediaSuspend::DISABLED, BackgroundResuming::DISABLED, | |
| 278 SessionState::SUSPENDED, SessionState::SUSPENDED }, | |
| 279 { MediaSuspend::DISABLED, BackgroundResuming::DISABLED, | |
| 280 SessionState::ACTIVE, SessionState::ACTIVE }, | |
| 281 { MediaSuspend::DISABLED, BackgroundResuming::ENABLED, | |
| 282 SessionState::ACTIVE, SessionState::ACTIVE }, | |
| 283 { MediaSuspend::DISABLED, BackgroundResuming::ENABLED, | |
| 284 SessionState::SUSPENDED, SessionState::SUSPENDED }, | |
| 285 }; | |
| 286 | |
| 287 Pipeline kPipelines[] = { | |
| 288 Pipeline::WMPI, | |
| 289 #if defined(OS_ANDROID) | |
| 290 // Disabling WMPA tests because of https://crbug.com/646312 | |
| 291 // Pipeline::WMPA, | |
| 292 #endif // defined(OS_ANDROID) | |
| 293 }; | |
| 294 | |
| 295 } // anonymous namespace | |
| 296 | |
| 297 IN_PROC_BROWSER_TEST_P(MediaSessionVisibilityBrowserTest, | |
| 298 TestEntryPoint) { | |
| 299 StartPlayer(); | |
| 300 MaybePausePlayer(); | |
| 301 HideTab(); | |
| 302 CheckSessionStateAfterHide(); | |
| 303 } | |
| 304 | |
| 305 INSTANTIATE_TEST_CASE_P(MediaSessionVisibilityBrowserTestInstances, | |
| 306 MediaSessionVisibilityBrowserTest, | |
| 307 ::testing::Combine(::testing::ValuesIn(kTestParams), | |
| 308 ::testing::ValuesIn(kPipelines))); | |
| 309 | |
| 310 } // namespace content | |
| OLD | NEW |