Home | History | Annotate | Download | only in host
      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 <list>
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/logging.h"
      9 #include "base/memory/ref_counted.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/run_loop.h"
     12 #include "remoting/host/desktop_resizer.h"
     13 #include "remoting/host/resizing_host_observer.h"
     14 #include "remoting/host/screen_resolution.h"
     15 #include "testing/gtest/include/gtest/gtest.h"
     16 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
     17 
     18 namespace remoting {
     19 
     20 std::ostream& operator<<(std::ostream& os, const ScreenResolution& resolution) {
     21   return os << resolution.dimensions().width() << "x"
     22             << resolution.dimensions().height() << " @ "
     23             << resolution.dpi().x() << "x" << resolution.dpi().y();
     24 }
     25 
     26 bool operator==(const ScreenResolution& a, const ScreenResolution& b) {
     27   return a.Equals(b);
     28 }
     29 
     30 const int kDefaultDPI = 96;
     31 
     32 ScreenResolution MakeResolution(int width, int height) {
     33   return ScreenResolution(webrtc::DesktopSize(width, height),
     34                           webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
     35 }
     36 
     37 class FakeDesktopResizer : public DesktopResizer {
     38  public:
     39   FakeDesktopResizer(const ScreenResolution& initial_resolution,
     40                      bool exact_size_supported,
     41                      const ScreenResolution* supported_resolutions,
     42                      int num_supported_resolutions,
     43                      int* restore_resolution_call_count)
     44       : initial_resolution_(initial_resolution),
     45         current_resolution_(initial_resolution),
     46         exact_size_supported_(exact_size_supported),
     47         set_resolution_call_count_(0),
     48         restore_resolution_call_count_(restore_resolution_call_count) {
     49     for (int i = 0; i < num_supported_resolutions; ++i) {
     50       supported_resolutions_.push_back(supported_resolutions[i]);
     51     }
     52   }
     53 
     54   virtual ~FakeDesktopResizer() {
     55     EXPECT_EQ(initial_resolution_, GetCurrentResolution());
     56   }
     57 
     58   int set_resolution_call_count() { return set_resolution_call_count_; }
     59 
     60   // remoting::DesktopResizer interface
     61   virtual ScreenResolution GetCurrentResolution() OVERRIDE {
     62     return current_resolution_;
     63   }
     64   virtual std::list<ScreenResolution> GetSupportedResolutions(
     65       const ScreenResolution& preferred) OVERRIDE {
     66     std::list<ScreenResolution> result = supported_resolutions_;
     67     if (exact_size_supported_) {
     68       result.push_back(preferred);
     69     }
     70     return result;
     71   }
     72   virtual void SetResolution(const ScreenResolution& resolution) OVERRIDE {
     73     current_resolution_ = resolution;
     74     ++set_resolution_call_count_;
     75   }
     76   virtual void RestoreResolution(const ScreenResolution& resolution) OVERRIDE {
     77     current_resolution_ = resolution;
     78     if (restore_resolution_call_count_)
     79       ++(*restore_resolution_call_count_);
     80   }
     81 
     82  private:
     83   ScreenResolution initial_resolution_;
     84   ScreenResolution current_resolution_;
     85   bool exact_size_supported_;
     86   std::list<ScreenResolution> supported_resolutions_;
     87 
     88   int set_resolution_call_count_;
     89   int* restore_resolution_call_count_;
     90 };
     91 
     92 class ResizingHostObserverTest : public testing::Test {
     93  public:
     94   ResizingHostObserverTest()
     95       : desktop_resizer_(NULL),
     96         now_(base::Time::Now()) {
     97   }
     98 
     99   // This needs to be public because the derived test-case class needs to
    100   // pass it to Bind, which fails if it's protected.
    101   base::Time GetTime() {
    102     return now_;
    103   }
    104 
    105  protected:
    106   void SetDesktopResizer(scoped_ptr<FakeDesktopResizer> desktop_resizer) {
    107     CHECK(!desktop_resizer_) << "Call SetDeskopResizer once per test";
    108     desktop_resizer_ = desktop_resizer.get();
    109 
    110     resizing_host_observer_.reset(
    111         new ResizingHostObserver(desktop_resizer.PassAs<DesktopResizer>()));
    112     resizing_host_observer_->SetNowFunctionForTesting(
    113         base::Bind(&ResizingHostObserverTest::GetTimeAndIncrement,
    114                    base::Unretained(this)));
    115   }
    116 
    117   ScreenResolution GetBestResolution(const ScreenResolution& client_size) {
    118     resizing_host_observer_->SetScreenResolution(client_size);
    119     return desktop_resizer_->GetCurrentResolution();
    120   }
    121 
    122   void VerifySizes(const ScreenResolution* client_sizes,
    123                    const ScreenResolution* expected_sizes,
    124                    int number_of_sizes) {
    125     for (int i = 0; i < number_of_sizes; ++i) {
    126       ScreenResolution best_size = GetBestResolution(client_sizes[i]);
    127       EXPECT_EQ(expected_sizes[i], best_size)
    128           << "Input resolution = " << client_sizes[i];
    129     }
    130   }
    131 
    132   base::Time GetTimeAndIncrement() {
    133     base::Time result = now_;
    134     now_ += base::TimeDelta::FromSeconds(1);
    135     return result;
    136   }
    137 
    138   scoped_ptr<ResizingHostObserver> resizing_host_observer_;
    139   FakeDesktopResizer* desktop_resizer_;
    140   base::Time now_;
    141 };
    142 
    143 // Check that the resolution isn't restored if it wasn't changed by this class.
    144 TEST_F(ResizingHostObserverTest, NoRestoreResolution) {
    145   int restore_resolution_call_count = 0;
    146   ScreenResolution initial = MakeResolution(640, 480);
    147   scoped_ptr<FakeDesktopResizer> desktop_resizer(
    148       new FakeDesktopResizer(initial, false, NULL, 0,
    149                              &restore_resolution_call_count));
    150   SetDesktopResizer(desktop_resizer.Pass());
    151   VerifySizes(NULL, NULL, 0);
    152   resizing_host_observer_.reset();
    153   EXPECT_EQ(0, restore_resolution_call_count);
    154 }
    155 
    156 // Check that the host is not resized if GetSupportedSizes returns an empty
    157 // list (even if GetCurrentSize is supported).
    158 TEST_F(ResizingHostObserverTest, EmptyGetSupportedSizes) {
    159   int restore_resolution_call_count = 0;
    160   ScreenResolution initial = MakeResolution(640, 480);
    161   scoped_ptr<FakeDesktopResizer> desktop_resizer(
    162       new FakeDesktopResizer(initial, false, NULL, 0,
    163                              &restore_resolution_call_count));
    164   SetDesktopResizer(desktop_resizer.Pass());
    165 
    166   ScreenResolution client_sizes[] = { MakeResolution(200, 100),
    167                                       MakeResolution(100, 200) };
    168   ScreenResolution expected_sizes[] = { initial, initial };
    169   VerifySizes(client_sizes, expected_sizes, arraysize(client_sizes));
    170 
    171   resizing_host_observer_.reset();
    172   EXPECT_EQ(0, restore_resolution_call_count);
    173 }
    174 
    175 // Check that if the implementation supports exact size matching, it is used.
    176 TEST_F(ResizingHostObserverTest, SelectExactSize) {
    177   int restore_resolution_call_count = 0;
    178   scoped_ptr<FakeDesktopResizer> desktop_resizer(
    179         new FakeDesktopResizer(MakeResolution(640, 480), true, NULL, 0,
    180                                &restore_resolution_call_count));
    181   SetDesktopResizer(desktop_resizer.Pass());
    182 
    183   ScreenResolution client_sizes[] = { MakeResolution(200, 100),
    184                                       MakeResolution(100, 200),
    185                                       MakeResolution(640, 480),
    186                                       MakeResolution(480, 640),
    187                                       MakeResolution(1280, 1024) };
    188   VerifySizes(client_sizes, client_sizes, arraysize(client_sizes));
    189   resizing_host_observer_.reset();
    190   EXPECT_EQ(1, restore_resolution_call_count);
    191 }
    192 
    193 // Check that if the implementation supports a size that is no larger than
    194 // the requested size, then the largest such size is used.
    195 TEST_F(ResizingHostObserverTest, SelectBestSmallerSize) {
    196   ScreenResolution supported_sizes[] = { MakeResolution(639, 479),
    197                                          MakeResolution(640, 480) };
    198   scoped_ptr<FakeDesktopResizer> desktop_resizer(
    199       new FakeDesktopResizer(MakeResolution(640, 480), false,
    200                              supported_sizes, arraysize(supported_sizes),
    201                              NULL));
    202   SetDesktopResizer(desktop_resizer.Pass());
    203 
    204   ScreenResolution client_sizes[] = { MakeResolution(639, 479),
    205                                       MakeResolution(640, 480),
    206                                       MakeResolution(641, 481),
    207                                       MakeResolution(999, 999) };
    208   ScreenResolution expected_sizes[] = { supported_sizes[0], supported_sizes[1],
    209                                supported_sizes[1], supported_sizes[1] };
    210   VerifySizes(client_sizes, expected_sizes, arraysize(client_sizes));
    211 }
    212 
    213 // Check that if the implementation supports only sizes that are larger than
    214 // the requested size, then the one that requires the least down-scaling.
    215 TEST_F(ResizingHostObserverTest, SelectBestScaleFactor) {
    216   ScreenResolution supported_sizes[] = { MakeResolution(100, 100),
    217                                          MakeResolution(200, 100) };
    218   scoped_ptr<FakeDesktopResizer> desktop_resizer(
    219       new FakeDesktopResizer(MakeResolution(200, 100), false,
    220                              supported_sizes, arraysize(supported_sizes),
    221                              NULL));
    222   SetDesktopResizer(desktop_resizer.Pass());
    223 
    224   ScreenResolution client_sizes[] = { MakeResolution(1, 1),
    225                                       MakeResolution(99, 99),
    226                                       MakeResolution(199, 99) };
    227   ScreenResolution expected_sizes[] = { supported_sizes[0], supported_sizes[0],
    228                                supported_sizes[1] };
    229   VerifySizes(client_sizes, expected_sizes, arraysize(client_sizes));
    230 }
    231 
    232 // Check that if the implementation supports two sizes that have the same
    233 // resultant scale factor, then the widest one is selected.
    234 TEST_F(ResizingHostObserverTest, SelectWidest) {
    235   ScreenResolution supported_sizes[] = { MakeResolution(640, 480),
    236                                          MakeResolution(480, 640) };
    237   scoped_ptr<FakeDesktopResizer> desktop_resizer(
    238       new FakeDesktopResizer(MakeResolution(480, 640), false,
    239                              supported_sizes, arraysize(supported_sizes),
    240                              NULL));
    241   SetDesktopResizer(desktop_resizer.Pass());
    242 
    243   ScreenResolution client_sizes[] = { MakeResolution(100, 100),
    244                                       MakeResolution(480, 480),
    245                                       MakeResolution(500, 500),
    246                                       MakeResolution(640, 640),
    247                                       MakeResolution(1000, 1000) };
    248   ScreenResolution expected_sizes[] = { supported_sizes[0], supported_sizes[0],
    249                                supported_sizes[0], supported_sizes[0],
    250                                supported_sizes[0] };
    251   VerifySizes(client_sizes, expected_sizes, arraysize(client_sizes));
    252 }
    253 
    254 // Check that if the best match for the client size doesn't change, then we
    255 // don't call SetSize.
    256 TEST_F(ResizingHostObserverTest, NoSetSizeForSameSize) {
    257   ScreenResolution supported_sizes[] = { MakeResolution(640, 480),
    258                                          MakeResolution(480, 640) };
    259   FakeDesktopResizer* desktop_resizer =
    260       new FakeDesktopResizer(MakeResolution(480, 640), false,
    261                              supported_sizes, arraysize(supported_sizes), NULL);
    262   SetDesktopResizer(scoped_ptr<FakeDesktopResizer>(desktop_resizer));
    263 
    264   ScreenResolution client_sizes[] = { MakeResolution(640, 640),
    265                                       MakeResolution(1024, 768),
    266                                       MakeResolution(640, 480) };
    267   ScreenResolution expected_sizes[] = { MakeResolution(640, 480),
    268                                         MakeResolution(640, 480),
    269                                         MakeResolution(640, 480) };
    270   VerifySizes(client_sizes, expected_sizes, arraysize(client_sizes));
    271   EXPECT_EQ(desktop_resizer->set_resolution_call_count(), 1);
    272 }
    273 
    274 // Check that desktop resizes are rate-limited, and that if multiple resize
    275 // requests are received in the time-out period, the most recent is respected.
    276 TEST_F(ResizingHostObserverTest, RateLimited) {
    277   FakeDesktopResizer* desktop_resizer =
    278       new FakeDesktopResizer(MakeResolution(640, 480), true, NULL, 0, NULL);
    279   SetDesktopResizer(scoped_ptr<FakeDesktopResizer>(desktop_resizer));
    280   resizing_host_observer_->SetNowFunctionForTesting(
    281       base::Bind(&ResizingHostObserverTest::GetTime, base::Unretained(this)));
    282 
    283   base::MessageLoop message_loop;
    284   base::RunLoop run_loop;
    285 
    286   EXPECT_EQ(GetBestResolution(MakeResolution(100, 100)),
    287             MakeResolution(100, 100));
    288   now_ += base::TimeDelta::FromMilliseconds(900);
    289   EXPECT_EQ(GetBestResolution(MakeResolution(200, 200)),
    290             MakeResolution(100, 100));
    291   now_ += base::TimeDelta::FromMilliseconds(99);
    292   EXPECT_EQ(GetBestResolution(MakeResolution(300, 300)),
    293             MakeResolution(100, 100));
    294   now_ += base::TimeDelta::FromMilliseconds(1);
    295 
    296   // Due to the kMinimumResizeIntervalMs constant in resizing_host_observer.cc,
    297   // We need to wait a total of 1000ms for the final resize to be processed.
    298   // Since it was queued 900 + 99 ms after the first, we need to wait an
    299   // additional 1ms. However, since RunLoop is not guaranteed to process tasks
    300   // with the same due time in FIFO order, wait an additional 1ms for safety.
    301   message_loop.PostDelayedTask(
    302       FROM_HERE,
    303       run_loop.QuitClosure(),
    304       base::TimeDelta::FromMilliseconds(2));
    305   run_loop.Run();
    306 
    307   // If the QuitClosure fired before the final resize, it's a test failure.
    308   EXPECT_EQ(desktop_resizer_->GetCurrentResolution(), MakeResolution(300, 300));
    309 }
    310 
    311 }  // namespace remoting
    312