video_detector_unittest.cc revision a36e5920737c6adbddd3e43b760e5de8431db6e0
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
21namespace ash {
22namespace test {
23
24// Implementation that just counts the number of times we've been told that a
25// video is playing.
26class 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
63class 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
100TEST_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
169TEST_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
185TEST_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
227TEST_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.
251TEST_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.
277TEST_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