1 // Copyright (c) 2012 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 "ash/wm/video_detector.h" 6 7 #include "ash/shell.h" 8 #include "ash/test/ash_test_base.h" 9 #include "ash/wm/window_util.h" 10 #include "base/compiler_specific.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/time/time.h" 13 #include "third_party/skia/include/core/SkColor.h" 14 #include "ui/aura/client/aura_constants.h" 15 #include "ui/aura/client/window_types.h" 16 #include "ui/aura/root_window.h" 17 #include "ui/aura/test/test_windows.h" 18 #include "ui/aura/window.h" 19 #include "ui/gfx/rect.h" 20 21 namespace ash { 22 namespace test { 23 24 // Implementation that just counts the number of times we've been told that a 25 // video is playing. 26 class TestVideoDetectorObserver : public VideoDetectorObserver { 27 public: 28 TestVideoDetectorObserver() : num_invocations_(0), 29 num_fullscreens_(0), 30 num_not_fullscreens_(0) {} 31 32 int num_invocations() const { return num_invocations_; } 33 int num_fullscreens() const { return num_fullscreens_; } 34 int num_not_fullscreens() const { return num_not_fullscreens_; } 35 void reset_stats() { 36 num_invocations_ = 0; 37 num_fullscreens_ = 0; 38 num_not_fullscreens_ = 0; 39 } 40 41 // VideoDetectorObserver implementation. 42 virtual void OnVideoDetected(bool is_fullscreen) OVERRIDE { 43 num_invocations_++; 44 if (is_fullscreen) 45 num_fullscreens_++; 46 else 47 num_not_fullscreens_++; 48 } 49 50 private: 51 // Number of times that OnVideoDetected() has been called. 52 int num_invocations_; 53 // Number of times that OnVideoDetected() has been called with is_fullscreen 54 // == true. 55 int num_fullscreens_; 56 // Number of times that OnVideoDetected() has been called with is_fullscreen 57 // == false. 58 int num_not_fullscreens_; 59 60 DISALLOW_COPY_AND_ASSIGN(TestVideoDetectorObserver); 61 }; 62 63 class VideoDetectorTest : public AshTestBase { 64 public: 65 VideoDetectorTest() {} 66 virtual ~VideoDetectorTest() {} 67 68 virtual void SetUp() OVERRIDE { 69 AshTestBase::SetUp(); 70 observer_.reset(new TestVideoDetectorObserver); 71 detector_ = Shell::GetInstance()->video_detector(); 72 detector_->AddObserver(observer_.get()); 73 74 now_ = base::TimeTicks::Now(); 75 detector_->set_now_for_test(now_); 76 } 77 78 virtual void TearDown() OVERRIDE { 79 detector_->RemoveObserver(observer_.get()); 80 AshTestBase::TearDown(); 81 } 82 83 protected: 84 // Move |detector_|'s idea of the current time forward by |delta|. 85 void AdvanceTime(base::TimeDelta delta) { 86 now_ += delta; 87 detector_->set_now_for_test(now_); 88 } 89 90 VideoDetector* detector_; // not owned 91 92 scoped_ptr<TestVideoDetectorObserver> observer_; 93 94 base::TimeTicks now_; 95 96 private: 97 DISALLOW_COPY_AND_ASSIGN(VideoDetectorTest); 98 }; 99 100 TEST_F(VideoDetectorTest, Basic) { 101 gfx::Rect window_bounds(gfx::Point(), gfx::Size(1024, 768)); 102 scoped_ptr<aura::Window> window( 103 CreateTestWindowInShell(SK_ColorRED, 12345, window_bounds)); 104 105 // Send enough updates, but make them be too small to trigger detection. 106 gfx::Rect update_region( 107 gfx::Point(), 108 gfx::Size(VideoDetector::kMinUpdateWidth - 1, 109 VideoDetector::kMinUpdateHeight)); 110 for (int i = 0; i < VideoDetector::kMinFramesPerSecond; ++i) 111 detector_->OnWindowPaintScheduled(window.get(), update_region); 112 EXPECT_EQ(0, observer_->num_invocations()); 113 114 // Send not-quite-enough adaquately-sized updates. 115 observer_->reset_stats(); 116 AdvanceTime(base::TimeDelta::FromSeconds(2)); 117 update_region.set_size( 118 gfx::Size(VideoDetector::kMinUpdateWidth, 119 VideoDetector::kMinUpdateHeight)); 120 for (int i = 0; i < VideoDetector::kMinFramesPerSecond - 1; ++i) 121 detector_->OnWindowPaintScheduled(window.get(), update_region); 122 EXPECT_EQ(0, observer_->num_invocations()); 123 124 // We should get notified after the next update, but not in response to 125 // additional updates. 126 detector_->OnWindowPaintScheduled(window.get(), update_region); 127 EXPECT_EQ(1, observer_->num_invocations()); 128 EXPECT_EQ(0, observer_->num_fullscreens()); 129 EXPECT_EQ(1, observer_->num_not_fullscreens()); 130 detector_->OnWindowPaintScheduled(window.get(), update_region); 131 EXPECT_EQ(1, observer_->num_invocations()); 132 EXPECT_EQ(0, observer_->num_fullscreens()); 133 EXPECT_EQ(1, observer_->num_not_fullscreens()); 134 135 // Spread out the frames over a longer period of time, but send enough 136 // over a one-second window that the observer should be notified. 137 observer_->reset_stats(); 138 AdvanceTime(base::TimeDelta::FromSeconds(2)); 139 detector_->OnWindowPaintScheduled(window.get(), update_region); 140 EXPECT_EQ(0, observer_->num_invocations()); 141 142 AdvanceTime(base::TimeDelta::FromMilliseconds(500)); 143 const int kNumFrames = VideoDetector::kMinFramesPerSecond + 1; 144 base::TimeDelta kInterval = 145 base::TimeDelta::FromMilliseconds(1000 / kNumFrames); 146 for (int i = 0; i < kNumFrames; ++i) { 147 AdvanceTime(kInterval); 148 detector_->OnWindowPaintScheduled(window.get(), update_region); 149 } 150 EXPECT_EQ(1, observer_->num_invocations()); 151 152 // Keep going and check that the observer is notified again. 153 for (int i = 0; i < kNumFrames; ++i) { 154 AdvanceTime(kInterval); 155 detector_->OnWindowPaintScheduled(window.get(), update_region); 156 } 157 EXPECT_EQ(2, observer_->num_invocations()); 158 159 // Send updates at a slower rate and check that the observer isn't notified. 160 base::TimeDelta kSlowInterval = base::TimeDelta::FromMilliseconds( 161 1000 / (VideoDetector::kMinFramesPerSecond - 2)); 162 for (int i = 0; i < kNumFrames; ++i) { 163 AdvanceTime(kSlowInterval); 164 detector_->OnWindowPaintScheduled(window.get(), update_region); 165 } 166 EXPECT_EQ(2, observer_->num_invocations()); 167 } 168 169 TEST_F(VideoDetectorTest, Shutdown) { 170 gfx::Rect window_bounds(gfx::Point(), gfx::Size(1024, 768)); 171 scoped_ptr<aura::Window> window( 172 CreateTestWindowInShell(SK_ColorRED, 12345, window_bounds)); 173 gfx::Rect update_region( 174 gfx::Point(), 175 gfx::Size(VideoDetector::kMinUpdateWidth, 176 VideoDetector::kMinUpdateHeight)); 177 178 // It should not detect video during the shutdown. 179 Shell::GetInstance()->OnAppTerminating(); 180 for (int i = 0; i < VideoDetector::kMinFramesPerSecond; ++i) 181 detector_->OnWindowPaintScheduled(window.get(), update_region); 182 EXPECT_EQ(0, observer_->num_invocations()); 183 } 184 185 TEST_F(VideoDetectorTest, WindowNotVisible) { 186 gfx::Rect window_bounds(gfx::Point(), gfx::Size(1024, 768)); 187 scoped_ptr<aura::Window> window( 188 CreateTestWindowInShell(SK_ColorRED, 12345, window_bounds)); 189 190 // Reparent the window to the root to make sure that visibility changes aren't 191 // animated. 192 Shell::GetPrimaryRootWindow()->AddChild(window.get()); 193 194 // We shouldn't report video that's played in a hidden window. 195 window->Hide(); 196 gfx::Rect update_region( 197 gfx::Point(), 198 gfx::Size(VideoDetector::kMinUpdateWidth, 199 VideoDetector::kMinUpdateHeight)); 200 for (int i = 0; i < VideoDetector::kMinFramesPerSecond; ++i) 201 detector_->OnWindowPaintScheduled(window.get(), update_region); 202 EXPECT_EQ(0, observer_->num_invocations()); 203 204 // Make the window visible and send more updates. 205 observer_->reset_stats(); 206 AdvanceTime(base::TimeDelta::FromSeconds(2)); 207 window->Show(); 208 for (int i = 0; i < VideoDetector::kMinFramesPerSecond; ++i) 209 detector_->OnWindowPaintScheduled(window.get(), update_region); 210 EXPECT_EQ(1, observer_->num_invocations()); 211 EXPECT_EQ(0, observer_->num_fullscreens()); 212 EXPECT_EQ(1, observer_->num_not_fullscreens()); 213 214 // We also shouldn't report video in a window that's fully offscreen. 215 observer_->reset_stats(); 216 AdvanceTime(base::TimeDelta::FromSeconds(2)); 217 gfx::Rect offscreen_bounds( 218 gfx::Point(Shell::GetPrimaryRootWindow()->bounds().width(), 0), 219 window_bounds.size()); 220 window->SetBounds(offscreen_bounds); 221 ASSERT_EQ(offscreen_bounds, window->bounds()); 222 for (int i = 0; i < VideoDetector::kMinFramesPerSecond; ++i) 223 detector_->OnWindowPaintScheduled(window.get(), update_region); 224 EXPECT_EQ(0, observer_->num_invocations()); 225 } 226 227 TEST_F(VideoDetectorTest, MultipleWindows) { 228 // Create two windows. 229 gfx::Rect window_bounds(gfx::Point(), gfx::Size(1024, 768)); 230 scoped_ptr<aura::Window> window1( 231 CreateTestWindowInShell(SK_ColorRED, 12345, window_bounds)); 232 scoped_ptr<aura::Window> window2( 233 CreateTestWindowInShell(SK_ColorBLUE, 23456, window_bounds)); 234 235 // Even if there's video playing in both, the observer should only receive a 236 // single notification. 237 gfx::Rect update_region( 238 gfx::Point(), 239 gfx::Size(VideoDetector::kMinUpdateWidth, 240 VideoDetector::kMinUpdateHeight)); 241 for (int i = 0; i < VideoDetector::kMinFramesPerSecond; ++i) 242 detector_->OnWindowPaintScheduled(window1.get(), update_region); 243 for (int i = 0; i < VideoDetector::kMinFramesPerSecond; ++i) 244 detector_->OnWindowPaintScheduled(window2.get(), update_region); 245 EXPECT_EQ(1, observer_->num_invocations()); 246 EXPECT_EQ(0, observer_->num_fullscreens()); 247 EXPECT_EQ(1, observer_->num_not_fullscreens()); 248 } 249 250 // Test that the observer receives repeated notifications. 251 TEST_F(VideoDetectorTest, RepeatedNotifications) { 252 gfx::Rect window_bounds(gfx::Point(), gfx::Size(1024, 768)); 253 scoped_ptr<aura::Window> window( 254 CreateTestWindowInShell(SK_ColorRED, 12345, window_bounds)); 255 256 gfx::Rect update_region( 257 gfx::Point(), 258 gfx::Size(VideoDetector::kMinUpdateWidth, 259 VideoDetector::kMinUpdateHeight)); 260 for (int i = 0; i < VideoDetector::kMinFramesPerSecond; ++i) 261 detector_->OnWindowPaintScheduled(window.get(), update_region); 262 EXPECT_EQ(1, observer_->num_invocations()); 263 EXPECT_EQ(0, observer_->num_fullscreens()); 264 EXPECT_EQ(1, observer_->num_not_fullscreens()); 265 // Let enough time pass that a second notification should be sent. 266 observer_->reset_stats(); 267 AdvanceTime(base::TimeDelta::FromSeconds( 268 static_cast<int64>(VideoDetector::kNotifyIntervalSec + 1))); 269 for (int i = 0; i < VideoDetector::kMinFramesPerSecond; ++i) 270 detector_->OnWindowPaintScheduled(window.get(), update_region); 271 EXPECT_EQ(1, observer_->num_invocations()); 272 EXPECT_EQ(0, observer_->num_fullscreens()); 273 EXPECT_EQ(1, observer_->num_not_fullscreens()); 274 } 275 276 // Test that the observer receives a true value when the window is fullscreen. 277 TEST_F(VideoDetectorTest, FullscreenWindow) { 278 gfx::Rect window_bounds(gfx::Point(), gfx::Size(1024, 768)); 279 scoped_ptr<aura::Window> window( 280 CreateTestWindowInShell(SK_ColorRED, 12345, window_bounds)); 281 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 282 window->Focus(); 283 gfx::Rect update_region( 284 gfx::Point(), 285 gfx::Size(VideoDetector::kMinUpdateWidth, 286 VideoDetector::kMinUpdateHeight)); 287 for (int i = 0; i < VideoDetector::kMinFramesPerSecond; ++i) 288 detector_->OnWindowPaintScheduled(window.get(), update_region); 289 EXPECT_EQ(1, observer_->num_invocations()); 290 EXPECT_EQ(1, observer_->num_fullscreens()); 291 EXPECT_EQ(0, observer_->num_not_fullscreens()); 292 } 293 294 } // namespace test 295 } // namespace ash 296