File: resizing_host_observer_unittest.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (423 lines) | stat: -rw-r--r-- 17,219 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/resizing_host_observer.h"

#include <list>
#include <utility>

#include "base/containers/contains.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/notimplemented.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/test/task_environment.h"
#include "remoting/host/base/screen_resolution.h"
#include "remoting/host/desktop_display_info.h"
#include "remoting/host/desktop_resizer.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"

namespace remoting {

using Monitors = std::map<webrtc::ScreenId, ScreenResolution>;

std::ostream& operator<<(std::ostream& os, const ScreenResolution& resolution) {
  return os << resolution.dimensions().width() << "x"
            << resolution.dimensions().height() << " @ " << resolution.dpi().x()
            << "x" << resolution.dpi().y();
}

bool operator==(const ScreenResolution& a, const ScreenResolution& b) {
  return a.Equals(b);
}

const int kDefaultDPI = 96;

ScreenResolution MakeResolution(int width, int height) {
  return ScreenResolution(webrtc::DesktopSize(width, height),
                          webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
}

// Converts a monitor-list to an object suitable for passing to
// ResizingHostObserver::OnDisplayInfoChanged().
DesktopDisplayInfo ToDisplayInfo(const Monitors& monitors) {
  DesktopDisplayInfo result;
  for (const auto& [id, resolution] : monitors) {
    result.AddDisplay({id, /* x */ 0, /* y */ 0,
                       static_cast<uint32_t>(resolution.dimensions().width()),
                       static_cast<uint32_t>(resolution.dimensions().height()),
                       /* dpi */ static_cast<uint32_t>(resolution.dpi().x()),
                       /* bpp */ 32,
                       /* is_default */ false,
                       /* display_name */ "test display"});
  }
  return result;
}

class FakeDesktopResizer : public DesktopResizer {
 public:
  struct CallCounts {
    int set_resolution = 0;
    int restore_resolution = 0;
  };

  FakeDesktopResizer(bool exact_size_supported,
                     std::vector<ScreenResolution> supported_resolutions,
                     Monitors* monitors,
                     CallCounts* call_counts,
                     bool check_final_resolution)
      : exact_size_supported_(exact_size_supported),
        initial_resolutions_(*monitors),
        monitors_(monitors),
        supported_resolutions_(std::move(supported_resolutions)),
        call_counts_(call_counts),
        check_final_resolution_(check_final_resolution) {}

  ~FakeDesktopResizer() override {
    if (check_final_resolution_) {
      EXPECT_EQ(initial_resolutions_, *monitors_);
    }
  }

  // remoting::DesktopResizer interface
  ScreenResolution GetCurrentResolution(webrtc::ScreenId screen_id) override {
    ExpectValidId(screen_id);
    return (*monitors_)[screen_id];
  }
  std::list<ScreenResolution> GetSupportedResolutions(
      const ScreenResolution& preferred,
      webrtc::ScreenId screen_id) override {
    ExpectValidId(screen_id);
    std::list<ScreenResolution> result(supported_resolutions_.begin(),
                                       supported_resolutions_.end());
    if (exact_size_supported_) {
      result.push_back(preferred);
    }
    return result;
  }
  void SetResolution(const ScreenResolution& resolution,
                     webrtc::ScreenId screen_id) override {
    ExpectValidId(screen_id);
    (*monitors_)[screen_id] = resolution;
    ++call_counts_->set_resolution;
  }
  void RestoreResolution(const ScreenResolution& resolution,
                         webrtc::ScreenId screen_id) override {
    ExpectValidId(screen_id);
    (*monitors_)[screen_id] = resolution;
    ++call_counts_->restore_resolution;
  }
  void SetVideoLayout(const protocol::VideoLayout& layout) override {
    NOTIMPLEMENTED();
  }

 private:
  // Fails the unittest if |screen_id| is not a valid monitor ID.
  void ExpectValidId(webrtc::ScreenId screen_id) {
    EXPECT_TRUE(base::Contains(*monitors_, screen_id));
  }

  bool exact_size_supported_;

  // Used to verify that |monitors_| was restored to the initial resolutions.
  Monitors initial_resolutions_;

  // List of monitors to be resized.
  raw_ptr<Monitors> monitors_;

  std::vector<ScreenResolution> supported_resolutions_;
  raw_ptr<CallCounts> call_counts_;
  bool check_final_resolution_;
};

class ResizingHostObserverTest : public testing::Test {
 public:
  ResizingHostObserverTest() { clock_.SetNowTicks(base::TimeTicks::Now()); }

 protected:
  void InitDesktopResizer(const Monitors& initial_resolutions,
                          bool exact_size_supported,
                          std::vector<ScreenResolution> supported_resolutions,
                          bool restore_resolution) {
    monitors_ = initial_resolutions;
    call_counts_ = FakeDesktopResizer::CallCounts();
    resizing_host_observer_ = std::make_unique<ResizingHostObserver>(
        std::make_unique<FakeDesktopResizer>(
            exact_size_supported, std::move(supported_resolutions), &monitors_,
            &call_counts_, restore_resolution),
        restore_resolution);
    resizing_host_observer_->SetClockForTesting(&clock_);
  }

  void SetScreenResolution(const ScreenResolution& client_size) {
    resizing_host_observer_->SetScreenResolution(client_size, std::nullopt);
    if (auto_advance_clock_) {
      clock_.Advance(base::Seconds(1));
    }
  }

  void SetScreenResolution(const ScreenResolution& client_size,
                           webrtc::ScreenId id) {
    resizing_host_observer_->SetScreenResolution(client_size, id);
    if (auto_advance_clock_) {
      clock_.Advance(base::Seconds(1));
    }
  }

  // Should be used only for single-monitor tests.
  ScreenResolution GetBestResolution(const ScreenResolution& client_size) {
    SetScreenResolution(client_size);
    return monitors_.begin()->second;
  }

  // Should be used only for single-monitor tests.
  void VerifySizes(const std::vector<ScreenResolution>& client_sizes,
                   const std::vector<ScreenResolution>& expected_sizes) {
    ASSERT_EQ(client_sizes.size(), expected_sizes.size())
        << "Client and expected vectors must have the same length";
    for (auto client = client_sizes.begin(), expected = expected_sizes.begin();
         client != client_sizes.end() && expected != expected_sizes.end();
         ++client, ++expected) {
      ScreenResolution best_size = GetBestResolution(*client);
      EXPECT_EQ(*expected, best_size) << "Input resolution = " << *client;
    }
  }

  // Sends the current display-info to the ResizingHostObserver.
  void NotifyDisplayInfo() {
    resizing_host_observer_->SetDisplayInfoForTesting(ToDisplayInfo(monitors_));
  }

  Monitors monitors_;
  FakeDesktopResizer::CallCounts call_counts_;
  std::unique_ptr<ResizingHostObserver> resizing_host_observer_;
  base::SimpleTestTickClock clock_;
  bool auto_advance_clock_ = true;
};

// Check that the resolution isn't restored if it wasn't changed by this class.
TEST_F(ResizingHostObserverTest, NoRestoreResolution) {
  InitDesktopResizer({{123, MakeResolution(640, 480)}}, false,
                     std::vector<ScreenResolution>(), true);
  NotifyDisplayInfo();
  resizing_host_observer_.reset();
  EXPECT_EQ(0, call_counts_.restore_resolution);
}

// Check that the host is not resized if GetSupportedSizes returns an empty
// list (even if GetCurrentSize is supported).
TEST_F(ResizingHostObserverTest, EmptyGetSupportedSizes) {
  ScreenResolution initial = MakeResolution(640, 480);
  InitDesktopResizer({{123, initial}}, false, std::vector<ScreenResolution>(),
                     true);
  NotifyDisplayInfo();
  VerifySizes({MakeResolution(200, 100), MakeResolution(100, 200)},
              {initial, initial});
  resizing_host_observer_.reset();
  EXPECT_EQ(0, call_counts_.set_resolution);
  EXPECT_EQ(0, call_counts_.restore_resolution);
}

// Check that the restore flag is respected.
TEST_F(ResizingHostObserverTest, RestoreFlag) {
  ScreenResolution initial = MakeResolution(640, 480);
  std::vector<ScreenResolution> supported_sizes = {MakeResolution(640, 480),
                                                   MakeResolution(1024, 768)};
  std::vector<ScreenResolution> client_sizes = {MakeResolution(1024, 768)};

  // Flag false
  InitDesktopResizer({{123, initial}}, false, supported_sizes, false);
  NotifyDisplayInfo();
  VerifySizes(client_sizes, client_sizes);
  resizing_host_observer_.reset();
  EXPECT_EQ(1, call_counts_.set_resolution);
  EXPECT_EQ(0, call_counts_.restore_resolution);
  EXPECT_EQ(MakeResolution(1024, 768), monitors_[123]);

  // Flag true
  InitDesktopResizer({{123, initial}}, false, supported_sizes, true);
  NotifyDisplayInfo();
  VerifySizes(client_sizes, client_sizes);
  resizing_host_observer_.reset();
  EXPECT_EQ(1, call_counts_.set_resolution);
  EXPECT_EQ(1, call_counts_.restore_resolution);
  EXPECT_EQ(MakeResolution(640, 480), monitors_[123]);
}

// Check that the size is restored if an empty ClientResolution is received.
TEST_F(ResizingHostObserverTest, RestoreOnEmptyClientResolution) {
  InitDesktopResizer({{123, MakeResolution(640, 480)}}, true,
                     std::vector<ScreenResolution>(), true);
  NotifyDisplayInfo();
  SetScreenResolution(MakeResolution(200, 100), 123);
  EXPECT_EQ(1, call_counts_.set_resolution);
  EXPECT_EQ(0, call_counts_.restore_resolution);
  EXPECT_EQ(MakeResolution(200, 100), monitors_[123]);
  SetScreenResolution(MakeResolution(0, 0), 123);
  EXPECT_EQ(1, call_counts_.set_resolution);
  EXPECT_EQ(1, call_counts_.restore_resolution);
  EXPECT_EQ(MakeResolution(640, 480), monitors_[123]);
}

// Check that if the implementation supports exact size matching, it is used.
TEST_F(ResizingHostObserverTest, SelectExactSize) {
  InitDesktopResizer({{123, MakeResolution(640, 480)}}, true,
                     std::vector<ScreenResolution>(), true);
  NotifyDisplayInfo();
  std::vector<ScreenResolution> client_sizes = {
      MakeResolution(200, 100), MakeResolution(100, 200),
      MakeResolution(640, 480), MakeResolution(480, 640),
      MakeResolution(1280, 1024)};
  VerifySizes(client_sizes, client_sizes);
  resizing_host_observer_.reset();
  EXPECT_EQ(1, call_counts_.restore_resolution);
}

// Check that if the implementation supports a size that is no larger than
// the requested size, then the largest such size is used.
TEST_F(ResizingHostObserverTest, SelectBestSmallerSize) {
  std::vector<ScreenResolution> supported_sizes = {MakeResolution(639, 479),
                                                   MakeResolution(640, 480)};
  InitDesktopResizer({{123, MakeResolution(640, 480)}}, false, supported_sizes,
                     true);
  NotifyDisplayInfo();
  VerifySizes({MakeResolution(639, 479), MakeResolution(640, 480),
               MakeResolution(641, 481), MakeResolution(999, 999)},
              {supported_sizes[0], supported_sizes[1], supported_sizes[1],
               supported_sizes[1]});
}

// Check that if the implementation supports only sizes that are larger than
// the requested size, then the one that requires the least down-scaling.
TEST_F(ResizingHostObserverTest, SelectBestScaleFactor) {
  std::vector<ScreenResolution> supported_sizes = {MakeResolution(100, 100),
                                                   MakeResolution(200, 100)};
  InitDesktopResizer({{123, MakeResolution(200, 100)}}, false, supported_sizes,
                     true);
  NotifyDisplayInfo();
  VerifySizes(
      {MakeResolution(1, 1), MakeResolution(99, 99), MakeResolution(199, 99)},
      {supported_sizes[0], supported_sizes[0], supported_sizes[1]});
}

// Check that if the implementation supports two sizes that have the same
// resultant scale factor, then the widest one is selected.
TEST_F(ResizingHostObserverTest, SelectWidest) {
  std::vector<ScreenResolution> supported_sizes = {MakeResolution(640, 480),
                                                   MakeResolution(480, 640)};
  InitDesktopResizer({{123, MakeResolution(480, 640)}}, false, supported_sizes,
                     true);
  NotifyDisplayInfo();
  VerifySizes({MakeResolution(100, 100), MakeResolution(480, 480),
               MakeResolution(500, 500), MakeResolution(640, 640),
               MakeResolution(1000, 1000)},
              {supported_sizes[0], supported_sizes[0], supported_sizes[0],
               supported_sizes[0], supported_sizes[0]});
}

// Check that if the best match for the client size doesn't change, then we
// don't call SetSize.
TEST_F(ResizingHostObserverTest, NoSetSizeForSameSize) {
  std::vector<ScreenResolution> supported_sizes = {MakeResolution(640, 480),
                                                   MakeResolution(480, 640)};
  InitDesktopResizer({{123, MakeResolution(480, 640)}}, false, supported_sizes,
                     true);
  NotifyDisplayInfo();
  VerifySizes({MakeResolution(640, 640), MakeResolution(1024, 768),
               MakeResolution(640, 480)},
              {supported_sizes[0], supported_sizes[0], supported_sizes[0]});
  EXPECT_EQ(1, call_counts_.set_resolution);
}

// Check that desktop resizes are rate-limited, and that if multiple resize
// requests are received in the time-out period, the most recent is respected.
TEST_F(ResizingHostObserverTest, RateLimited) {
  InitDesktopResizer({{123, MakeResolution(640, 480)}}, true,
                     std::vector<ScreenResolution>(), true);
  NotifyDisplayInfo();
  auto_advance_clock_ = false;

  base::test::SingleThreadTaskEnvironment task_environment;
  base::RunLoop run_loop;

  EXPECT_EQ(MakeResolution(100, 100),
            GetBestResolution(MakeResolution(100, 100)));
  clock_.Advance(base::Milliseconds(900));
  EXPECT_EQ(MakeResolution(100, 100),
            GetBestResolution(MakeResolution(200, 200)));
  clock_.Advance(base::Milliseconds(99));
  EXPECT_EQ(MakeResolution(100, 100),
            GetBestResolution(MakeResolution(300, 300)));
  clock_.Advance(base::Milliseconds(1));

  // Due to the kMinimumResizeIntervalMs constant in resizing_host_observer.cc,
  // We need to wait a total of 1000ms for the final resize to be processed.
  // Since it was queued 900 + 99 ms after the first, we need to wait an
  // additional 1ms. However, since RunLoop is not guaranteed to process tasks
  // with the same due time in FIFO order, wait an additional 1ms for safety.
  task_environment.GetMainThreadTaskRunner()->PostDelayedTask(
      FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(2));
  run_loop.Run();

  // If the QuitClosure fired before the final resize, it's a test failure.
  EXPECT_EQ(MakeResolution(300, 300), monitors_[123]);
}

TEST_F(ResizingHostObserverTest, PendingResolutionAppliedToFirstMonitor) {
  // An anonymous resolution request should be remembered and applied as soon
  // as the first display-info is provided.
  InitDesktopResizer({{123, MakeResolution(640, 480)}}, true,
                     std::vector<ScreenResolution>(), false);
  SetScreenResolution(MakeResolution(200, 100));
  EXPECT_EQ(0, call_counts_.set_resolution);
  NotifyDisplayInfo();
  EXPECT_EQ(1, call_counts_.set_resolution);
  Monitors expected = {{123, MakeResolution(200, 100)}};
  EXPECT_EQ(monitors_, expected);
}

TEST_F(ResizingHostObserverTest, AnonymousRequestDroppedIfMultipleMonitors) {
  InitDesktopResizer(
      {{123, MakeResolution(640, 480)}, {234, MakeResolution(800, 600)}}, true,
      std::vector<ScreenResolution>(), false);
  NotifyDisplayInfo();
  SetScreenResolution(MakeResolution(200, 100));
  EXPECT_EQ(0, call_counts_.set_resolution);
}

TEST_F(ResizingHostObserverTest, RequestDroppedForUnknownMonitor) {
  InitDesktopResizer({{123, MakeResolution(640, 480)}}, true,
                     std::vector<ScreenResolution>(), false);
  NotifyDisplayInfo();
  SetScreenResolution(MakeResolution(200, 100), 234);
  EXPECT_EQ(0, call_counts_.set_resolution);
  SetScreenResolution(MakeResolution(200, 100), 123);
  EXPECT_EQ(1, call_counts_.set_resolution);
}

TEST_F(ResizingHostObserverTest, MultipleMonitorSizesRestored) {
  InitDesktopResizer({{123, MakeResolution(1230, 1230)},
                      {234, MakeResolution(2340, 2340)},
                      {345, MakeResolution(3450, 3450)}},
                     true, std::vector<ScreenResolution>(), false);
  NotifyDisplayInfo();

  SetScreenResolution(MakeResolution(999, 999), 123);
  SetScreenResolution(MakeResolution(999, 999), 234);
  SetScreenResolution(MakeResolution(999, 999), 345);
  EXPECT_EQ(3, call_counts_.set_resolution);

  SetScreenResolution({}, 123);
  SetScreenResolution({}, 345);
  EXPECT_EQ(2, call_counts_.restore_resolution);
  Monitors expected = {{123, MakeResolution(1230, 1230)},
                       {234, MakeResolution(999, 999)},
                       {345, MakeResolution(3450, 3450)}};
  EXPECT_EQ(monitors_, expected);
}

}  // namespace remoting