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_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