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