tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

screen_capturer_integration_test.cc (12749B)


      1 /*
      2 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include <algorithm>
     12 #include <cstdint>
     13 #include <cstring>
     14 #include <initializer_list>
     15 #include <iostream>  // TODO(zijiehe): Remove once flaky has been resolved.
     16 #include <memory>
     17 #include <string>
     18 #include <utility>
     19 #include <vector>
     20 
     21 #include "api/array_view.h"
     22 #include "modules/desktop_capture/desktop_capture_options.h"
     23 #include "modules/desktop_capture/desktop_capturer.h"
     24 #include "modules/desktop_capture/desktop_frame.h"
     25 #include "modules/desktop_capture/desktop_geometry.h"
     26 #include "modules/desktop_capture/desktop_region.h"
     27 #include "modules/desktop_capture/mock_desktop_capturer_callback.h"
     28 #include "modules/desktop_capture/rgba_color.h"
     29 #include "modules/desktop_capture/screen_drawer.h"
     30 #include "rtc_base/base64.h"
     31 #include "rtc_base/checks.h"
     32 #include "rtc_base/logging.h"
     33 #include "test/gmock.h"
     34 #include "test/gtest.h"
     35 
     36 #if defined(WEBRTC_WIN)
     37 #include "modules/desktop_capture/win/screen_capturer_win_directx.h"
     38 #include "rtc_base/win/windows_version.h"
     39 #endif  // defined(WEBRTC_WIN)
     40 
     41 using ::testing::_;
     42 
     43 namespace webrtc {
     44 
     45 namespace {
     46 
     47 ACTION_P2(SaveCaptureResult, result, dest) {
     48  *result = arg0;
     49  *dest = std::move(*arg1);
     50 }
     51 
     52 // Returns true if color in `rect` of `frame` is `color`.
     53 bool ArePixelsColoredBy(const DesktopFrame& frame,
     54                        DesktopRect rect,
     55                        RgbaColor color,
     56                        bool may_partially_draw) {
     57  if (!may_partially_draw) {
     58    // updated_region() should cover the painted area.
     59    DesktopRegion updated_region(frame.updated_region());
     60    updated_region.IntersectWith(rect);
     61    if (!updated_region.Equals(DesktopRegion(rect))) {
     62      return false;
     63    }
     64  }
     65 
     66  // Color in the `rect` should be `color`.
     67  uint8_t* row = frame.GetFrameDataAtPos(rect.top_left());
     68  for (int i = 0; i < rect.height(); i++) {
     69    uint8_t* column = row;
     70    for (int j = 0; j < rect.width(); j++) {
     71      if (color != RgbaColor(column)) {
     72        return false;
     73      }
     74      column += DesktopFrame::kBytesPerPixel;
     75    }
     76    row += frame.stride();
     77  }
     78  return true;
     79 }
     80 
     81 }  // namespace
     82 
     83 class ScreenCapturerIntegrationTest : public ::testing::Test {
     84 public:
     85  void SetUp() override {
     86    capturer_ = DesktopCapturer::CreateScreenCapturer(
     87        DesktopCaptureOptions::CreateDefault());
     88  }
     89 
     90 protected:
     91  void TestCaptureUpdatedRegion(
     92      std::initializer_list<DesktopCapturer*> capturers) {
     93    RTC_DCHECK(capturers.size() > 0);
     94 // A large enough area for the tests, which should be able to be fulfilled
     95 // by most systems.
     96 #if defined(WEBRTC_WIN)
     97    // On Windows, an interesting warning window may pop up randomly. The root
     98    // cause is still under investigation, so reduce the test area to work
     99    // around. Bug https://bugs.chromium.org/p/webrtc/issues/detail?id=6666.
    100    const int kTestArea = 416;
    101 #else
    102    const int kTestArea = 512;
    103 #endif
    104    const int kRectSize = 32;
    105    std::unique_ptr<ScreenDrawer> drawer = ScreenDrawer::Create();
    106    if (!drawer || drawer->DrawableRegion().is_empty()) {
    107      RTC_LOG(LS_WARNING)
    108          << "No ScreenDrawer implementation for current platform.";
    109      return;
    110    }
    111    if (drawer->DrawableRegion().width() < kTestArea ||
    112        drawer->DrawableRegion().height() < kTestArea) {
    113      RTC_LOG(LS_WARNING)
    114          << "ScreenDrawer::DrawableRegion() is too small for the "
    115             "CaptureUpdatedRegion tests.";
    116      return;
    117    }
    118 
    119    for (DesktopCapturer* capturer : capturers) {
    120      capturer->Start(&callback_);
    121    }
    122 
    123    // Draw a set of `kRectSize` by `kRectSize` rectangles at (`i`, `i`), or
    124    // `i` by `i` rectangles at (`kRectSize`, `kRectSize`). One of (controlled
    125    // by `c`) its primary colors is `i`, and the other two are 0x7f. So we
    126    // won't draw a black or white rectangle.
    127    for (int c = 0; c < 3; c++) {
    128      // A fixed size rectangle.
    129      for (int i = 0; i < kTestArea - kRectSize; i += 16) {
    130        DesktopRect rect = DesktopRect::MakeXYWH(i, i, kRectSize, kRectSize);
    131        rect.Translate(drawer->DrawableRegion().top_left());
    132        RgbaColor color((c == 0 ? (i & 0xff) : 0x7f),
    133                        (c == 1 ? (i & 0xff) : 0x7f),
    134                        (c == 2 ? (i & 0xff) : 0x7f));
    135        // Fail fast.
    136        ASSERT_NO_FATAL_FAILURE(
    137            TestCaptureOneFrame(capturers, drawer.get(), rect, color));
    138      }
    139 
    140      // A variable-size rectangle.
    141      for (int i = 0; i < kTestArea - kRectSize; i += 16) {
    142        DesktopRect rect = DesktopRect::MakeXYWH(kRectSize, kRectSize, i, i);
    143        rect.Translate(drawer->DrawableRegion().top_left());
    144        RgbaColor color((c == 0 ? (i & 0xff) : 0x7f),
    145                        (c == 1 ? (i & 0xff) : 0x7f),
    146                        (c == 2 ? (i & 0xff) : 0x7f));
    147        // Fail fast.
    148        ASSERT_NO_FATAL_FAILURE(
    149            TestCaptureOneFrame(capturers, drawer.get(), rect, color));
    150      }
    151    }
    152  }
    153 
    154  void TestCaptureUpdatedRegion() {
    155    TestCaptureUpdatedRegion({capturer_.get()});
    156  }
    157 
    158 #if defined(WEBRTC_WIN)
    159  // Enable allow_directx_capturer in DesktopCaptureOptions, but let
    160  // DesktopCapturer::CreateScreenCapturer() to decide whether a DirectX
    161  // capturer should be used.
    162  void MaybeCreateDirectxCapturer() {
    163    DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault());
    164    options.set_allow_directx_capturer(true);
    165    capturer_ = DesktopCapturer::CreateScreenCapturer(options);
    166  }
    167 
    168  bool CreateDirectxCapturer() {
    169    if (!ScreenCapturerWinDirectx::IsSupported()) {
    170      RTC_LOG(LS_WARNING) << "Directx capturer is not supported";
    171      return false;
    172    }
    173 
    174    MaybeCreateDirectxCapturer();
    175    return true;
    176  }
    177 #endif  // defined(WEBRTC_WIN)
    178 
    179  std::unique_ptr<DesktopCapturer> capturer_;
    180  MockDesktopCapturerCallback callback_;
    181 
    182 private:
    183  // Repeats capturing the frame by using `capturers` one-by-one for 600 times,
    184  // typically 30 seconds, until they succeeded captured a `color` rectangle at
    185  // `rect`. This function uses `drawer`->WaitForPendingDraws() between two
    186  // attempts to wait for the screen to update.
    187  void TestCaptureOneFrame(std::vector<DesktopCapturer*> capturers,
    188                           ScreenDrawer* drawer,
    189                           DesktopRect rect,
    190                           RgbaColor color) {
    191    const int wait_capture_round = 600;
    192    drawer->Clear();
    193    size_t succeeded_capturers = 0;
    194    for (int i = 0; i < wait_capture_round; i++) {
    195      drawer->DrawRectangle(rect, color);
    196      drawer->WaitForPendingDraws();
    197      for (size_t j = 0; j < capturers.size(); j++) {
    198        if (capturers[j] == nullptr) {
    199          // DesktopCapturer should return an empty updated_region() if no
    200          // update detected. So we won't test it again if it has captured the
    201          // rectangle we drew.
    202          continue;
    203        }
    204        std::unique_ptr<DesktopFrame> frame = CaptureFrame(capturers[j]);
    205        if (!frame) {
    206          // CaptureFrame() has triggered an assertion failure already, we only
    207          // need to return here.
    208          return;
    209        }
    210 
    211        if (ArePixelsColoredBy(*frame, rect, color,
    212                               drawer->MayDrawIncompleteShapes())) {
    213          capturers[j] = nullptr;
    214          succeeded_capturers++;
    215        } else if (i == wait_capture_round - 1) {
    216          // The else if statement is for debugging purpose only,
    217          // which should be removed after flakiness of
    218          // ScreenCapturerIntegrationTest has been resolved.
    219          ArrayView<const uint8_t> frame_data(
    220              frame->data(), frame->size().height() * frame->stride());
    221          std::string result = Base64Encode(frame_data);
    222          std::cout << frame->size().width() << " x " << frame->size().height()
    223                    << std::endl;
    224          // Split the entire string (can be over 4M) into several lines to
    225          // avoid browser from sticking.
    226          static const size_t kLineLength = 32768;
    227          const char* result_end = result.c_str() + result.length();
    228          for (const char* it = result.c_str(); it < result_end;
    229               it += kLineLength) {
    230            const size_t max_length = result_end - it;
    231            std::cout << std::string(it, std::min(kLineLength, max_length))
    232                      << std::endl;
    233          }
    234          std::cout << "Failed to capture rectangle " << rect.left() << " x "
    235                    << rect.top() << " - " << rect.right() << " x "
    236                    << rect.bottom() << " with color ("
    237                    << static_cast<int>(color.red) << ", "
    238                    << static_cast<int>(color.green) << ", "
    239                    << static_cast<int>(color.blue) << ", "
    240                    << static_cast<int>(color.alpha) << ")" << std::endl;
    241          ASSERT_TRUE(false) << "ScreenCapturerIntegrationTest may be flaky. "
    242                                "Please kindly FYI the broken link to "
    243                                "zijiehe@chromium.org for investigation. If "
    244                                "the failure continually happens, but I have "
    245                                "not responded as quick as expected, disable "
    246                                "*all* tests in "
    247                                "screen_capturer_integration_test.cc to "
    248                                "unblock other developers.";
    249        }
    250      }
    251 
    252      if (succeeded_capturers == capturers.size()) {
    253        break;
    254      }
    255    }
    256 
    257    ASSERT_EQ(succeeded_capturers, capturers.size());
    258  }
    259 
    260  // Expects `capturer` to successfully capture a frame, and returns it.
    261  std::unique_ptr<DesktopFrame> CaptureFrame(DesktopCapturer* capturer) {
    262    for (int i = 0; i < 10; i++) {
    263      std::unique_ptr<DesktopFrame> frame;
    264      DesktopCapturer::Result result;
    265      EXPECT_CALL(callback_, OnCaptureResultPtr(_, _))
    266          .WillOnce(SaveCaptureResult(&result, &frame));
    267      capturer->CaptureFrame();
    268      ::testing::Mock::VerifyAndClearExpectations(&callback_);
    269      if (result == DesktopCapturer::Result::SUCCESS) {
    270        EXPECT_TRUE(frame);
    271        return frame;
    272      } else {
    273        EXPECT_FALSE(frame);
    274      }
    275    }
    276 
    277    EXPECT_TRUE(false);
    278    return nullptr;
    279  }
    280 };
    281 
    282 #if defined(WEBRTC_WIN)
    283 // ScreenCapturerWinGdi randomly returns blank screen, the root cause is still
    284 // unknown. Bug, https://bugs.chromium.org/p/webrtc/issues/detail?id=6843.
    285 #define MAYBE_CaptureUpdatedRegion DISABLED_CaptureUpdatedRegion
    286 #else
    287 #define MAYBE_CaptureUpdatedRegion CaptureUpdatedRegion
    288 #endif
    289 TEST_F(ScreenCapturerIntegrationTest, MAYBE_CaptureUpdatedRegion) {
    290  TestCaptureUpdatedRegion();
    291 }
    292 
    293 #if defined(WEBRTC_WIN)
    294 // ScreenCapturerWinGdi randomly returns blank screen, the root cause is still
    295 // unknown. Bug, https://bugs.chromium.org/p/webrtc/issues/detail?id=6843.
    296 #define MAYBE_TwoCapturers DISABLED_TwoCapturers
    297 #else
    298 #define MAYBE_TwoCapturers TwoCapturers
    299 #endif
    300 TEST_F(ScreenCapturerIntegrationTest, MAYBE_TwoCapturers) {
    301  std::unique_ptr<DesktopCapturer> capturer2 = std::move(capturer_);
    302  SetUp();
    303  TestCaptureUpdatedRegion({capturer_.get(), capturer2.get()});
    304 }
    305 
    306 #if defined(WEBRTC_WIN)
    307 
    308 // Windows cannot capture contents on VMs hosted in GCE. See bug
    309 // https://bugs.chromium.org/p/webrtc/issues/detail?id=8153.
    310 TEST_F(ScreenCapturerIntegrationTest,
    311       DISABLED_CaptureUpdatedRegionWithDirectxCapturer) {
    312  if (!CreateDirectxCapturer()) {
    313    return;
    314  }
    315 
    316  TestCaptureUpdatedRegion();
    317 }
    318 
    319 TEST_F(ScreenCapturerIntegrationTest, DISABLED_TwoDirectxCapturers) {
    320  if (!CreateDirectxCapturer()) {
    321    return;
    322  }
    323 
    324  std::unique_ptr<DesktopCapturer> capturer2 = std::move(capturer_);
    325  RTC_CHECK(CreateDirectxCapturer());
    326  TestCaptureUpdatedRegion({capturer_.get(), capturer2.get()});
    327 }
    328 
    329 TEST_F(ScreenCapturerIntegrationTest,
    330       DISABLED_MaybeCaptureUpdatedRegionWithDirectxCapturer) {
    331  if (rtc_win::GetVersion() < rtc_win::Version::VERSION_WIN8) {
    332    // ScreenCapturerWinGdi randomly returns blank screen, the root cause is
    333    // still unknown. Bug,
    334    // https://bugs.chromium.org/p/webrtc/issues/detail?id=6843.
    335    // On Windows 7 or early version, MaybeCreateDirectxCapturer() always
    336    // creates GDI capturer.
    337    return;
    338  }
    339  // Even DirectX capturer is not supported in current system, we should be able
    340  // to select a usable capturer.
    341  MaybeCreateDirectxCapturer();
    342  TestCaptureUpdatedRegion();
    343 }
    344 
    345 #endif  // defined(WEBRTC_WIN)
    346 
    347 }  // namespace webrtc