tor-browser

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

wgc_capturer_win_unittest.cc (27394B)


      1 /*
      2 *  Copyright (c) 2020 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 "modules/desktop_capture/win/wgc_capturer_win.h"
     12 
     13 #include <algorithm>
     14 #include <cstddef>
     15 #include <cstdint>
     16 #include <memory>
     17 #include <utility>
     18 #include <vector>
     19 
     20 #include "modules/desktop_capture/desktop_capture_options.h"
     21 #include "modules/desktop_capture/desktop_capture_types.h"
     22 #include "modules/desktop_capture/desktop_capturer.h"
     23 #include "modules/desktop_capture/win/screen_capture_utils.h"
     24 #include "modules/desktop_capture/win/test_support/test_window.h"
     25 #include "modules/desktop_capture/win/wgc_capture_session.h"
     26 #include "modules/desktop_capture/win/window_capture_utils.h"
     27 #include "rtc_base/logging.h"
     28 #include "rtc_base/task_queue_for_test.h"
     29 #include "rtc_base/thread.h"
     30 #include "rtc_base/time_utils.h"
     31 #include "rtc_base/win/scoped_com_initializer.h"
     32 #include "rtc_base/win/windows_version.h"
     33 #include "system_wrappers/include/metrics.h"
     34 #include "test/gtest.h"
     35 
     36 namespace webrtc {
     37 namespace {
     38 
     39 constexpr char kWindowThreadName[] = "wgc_capturer_test_window_thread";
     40 constexpr WCHAR kWindowTitle[] = L"WGC Capturer Test Window";
     41 
     42 constexpr char kCapturerImplHistogram[] =
     43    "WebRTC.DesktopCapture.Win.DesktopCapturerImpl";
     44 
     45 constexpr char kCapturerResultHistogram[] =
     46    "WebRTC.DesktopCapture.Win.WgcCapturerResult";
     47 constexpr int kSuccess = 0;
     48 constexpr int kSessionStartFailure = 4;
     49 
     50 constexpr char kCaptureSessionResultHistogram[] =
     51    "WebRTC.DesktopCapture.Win.WgcCaptureSessionStartResult";
     52 constexpr int kSourceClosed = 1;
     53 
     54 constexpr char kCaptureTimeHistogram[] =
     55    "WebRTC.DesktopCapture.Win.WgcCapturerFrameTime";
     56 
     57 constexpr char kFullScreenDetectorResult[] =
     58    "WebRTC.Screenshare.FullScreenDetectorResult";
     59 constexpr int detector_result_success = 1;
     60 constexpr int detector_result_failure_same_title_windows = 2;
     61 constexpr int detector_result_failure_slide_show_not_chosen = 3;
     62 
     63 constexpr char kCaptureFullscreenDetectorHistogram[] =
     64    "WebRTC.Screenshare.DesktopCapturerFullscreenDetector";
     65 
     66 // The capturer keeps `kNumBuffers` in its frame pool, so we need to request
     67 // that many frames to clear those out. The next frame will have the new size
     68 // (if the size has changed) so we will resize the frame pool at this point.
     69 // Then, we need to clear any frames that may have delivered to the frame pool
     70 // before the resize. Finally, the next frame will be guaranteed to be the new
     71 // size.
     72 constexpr int kNumCapturesToFlushBuffers =
     73    WgcCaptureSession::kNumBuffers * 2 + 1;
     74 
     75 constexpr int kSmallWindowWidth = 200;
     76 constexpr int kSmallWindowHeight = 100;
     77 constexpr int kMediumWindowWidth = 300;
     78 constexpr int kMediumWindowHeight = 200;
     79 constexpr int kLargeWindowWidth = 400;
     80 constexpr int kLargeWindowHeight = 500;
     81 
     82 // The size of the image we capture is slightly smaller than the actual size of
     83 // the window.
     84 constexpr int kWindowWidthSubtrahend = 14;
     85 constexpr int kWindowHeightSubtrahend = 7;
     86 
     87 // Custom message constants so we can direct our thread to close windows and
     88 // quit running.
     89 constexpr UINT kDestroyWindow = WM_APP;
     90 constexpr UINT kQuitRunning = WM_APP + 1;
     91 
     92 // When testing changes to real windows, sometimes the effects (close or resize)
     93 // don't happen immediately, we want to keep trying until we see the effect but
     94 // only for a reasonable amount of time.
     95 constexpr int kMaxTries = 50;
     96 
     97 }  // namespace
     98 
     99 class WgcCapturerWinTest : public ::testing::TestWithParam<CaptureType>,
    100                           public DesktopCapturer::Callback {
    101 public:
    102  void SetUp() override {
    103    com_initializer_ =
    104        std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
    105    EXPECT_TRUE(com_initializer_->Succeeded());
    106 
    107    if (!IsWgcSupported(GetParam())) {
    108      RTC_LOG(LS_INFO)
    109          << "Skipping WgcCapturerWinTests on unsupported platforms.";
    110      GTEST_SKIP();
    111    }
    112  }
    113 
    114  void SetUpForWindowCapture(int window_width = kMediumWindowWidth,
    115                             int window_height = kMediumWindowHeight) {
    116    capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
    117        DesktopCaptureOptions::CreateDefault());
    118    CreateWindowOnSeparateThread(window_width, window_height);
    119    StartWindowThreadMessageLoop();
    120    source_id_ = GetTestWindowIdFromSourceList();
    121  }
    122 
    123  void SetUpForScreenCapture() {
    124    capturer_ = WgcCapturerWin::CreateRawScreenCapturer(
    125        DesktopCaptureOptions::CreateDefault());
    126    source_id_ = GetScreenIdFromSourceList();
    127  }
    128 
    129  void TearDown() override {
    130    if (window_open_) {
    131      CloseTestWindow();
    132    }
    133  }
    134 
    135  // The window must live on a separate thread so that we can run a message pump
    136  // without blocking the test thread. This is necessary if we are interested in
    137  // having GraphicsCaptureItem events (i.e. the Closed event) fire, and it more
    138  // closely resembles how capture works in the wild.
    139  void CreateWindowOnSeparateThread(int window_width, int window_height) {
    140    window_thread_ = Thread::Create();
    141    window_thread_->SetName(kWindowThreadName, nullptr);
    142    window_thread_->Start();
    143    SendTask(window_thread_.get(), [this, window_width, window_height]() {
    144      window_thread_id_ = GetCurrentThreadId();
    145      window_info_ =
    146          CreateTestWindow(kWindowTitle, window_height, window_width);
    147      window_open_ = true;
    148 
    149      while (!IsWindowResponding(window_info_.hwnd)) {
    150        RTC_LOG(LS_INFO) << "Waiting for test window to become responsive in "
    151                            "WgcWindowCaptureTest.";
    152      }
    153 
    154      while (!IsWindowValidAndVisible(window_info_.hwnd)) {
    155        RTC_LOG(LS_INFO) << "Waiting for test window to be visible in "
    156                            "WgcWindowCaptureTest.";
    157      }
    158    });
    159 
    160    ASSERT_TRUE(window_thread_->RunningForTest());
    161    ASSERT_FALSE(window_thread_->IsCurrent());
    162  }
    163 
    164  void StartWindowThreadMessageLoop() {
    165    window_thread_->PostTask([this]() {
    166      MSG msg;
    167      BOOL gm;
    168      while ((gm = ::GetMessage(&msg, NULL, 0, 0)) != 0 && gm != -1) {
    169        ::DispatchMessage(&msg);
    170        if (msg.message == kDestroyWindow) {
    171          DestroyTestWindow(window_info_);
    172        }
    173        if (msg.message == kQuitRunning) {
    174          PostQuitMessage(0);
    175        }
    176      }
    177    });
    178  }
    179 
    180  void CloseTestWindow() {
    181    ::PostThreadMessage(window_thread_id_, kDestroyWindow, 0, 0);
    182    ::PostThreadMessage(window_thread_id_, kQuitRunning, 0, 0);
    183    window_thread_->Stop();
    184    window_open_ = false;
    185  }
    186 
    187  DesktopCapturer::SourceId GetTestWindowIdFromSourceList() {
    188    // Frequently, the test window will not show up in GetSourceList because it
    189    // was created too recently. Since we are confident the window will be found
    190    // eventually we loop here until we find it.
    191    intptr_t src_id = 0;
    192    do {
    193      DesktopCapturer::SourceList sources;
    194      EXPECT_TRUE(capturer_->GetSourceList(&sources));
    195      auto it = std::find_if(
    196          sources.begin(), sources.end(),
    197          [&](const DesktopCapturer::Source& src) {
    198            return src.id == reinterpret_cast<intptr_t>(window_info_.hwnd);
    199          });
    200 
    201      if (it != sources.end())
    202        src_id = it->id;
    203    } while (src_id != reinterpret_cast<intptr_t>(window_info_.hwnd));
    204 
    205    return src_id;
    206  }
    207 
    208  DesktopCapturer::SourceId GetScreenIdFromSourceList() {
    209    DesktopCapturer::SourceList sources;
    210    EXPECT_TRUE(capturer_->GetSourceList(&sources));
    211    EXPECT_GT(sources.size(), 0ULL);
    212    return sources[0].id;
    213  }
    214 
    215  void DoCapture(int num_captures = 1) {
    216    // Capture the requested number of frames. We expect the first capture to
    217    // always succeed. If we're asked for multiple frames, we do expect to see a
    218    // a couple dropped frames due to resizing the window.
    219    const int max_tries = num_captures == 1 ? 1 : kMaxTries;
    220    int success_count = 0;
    221    for (int i = 0; success_count < num_captures && i < max_tries; i++) {
    222      capturer_->CaptureFrame();
    223      if (result_ == DesktopCapturer::Result::ERROR_PERMANENT)
    224        break;
    225      if (result_ == DesktopCapturer::Result::SUCCESS)
    226        success_count++;
    227    }
    228 
    229    total_successful_captures_ += success_count;
    230    EXPECT_EQ(success_count, num_captures);
    231    EXPECT_EQ(result_, DesktopCapturer::Result::SUCCESS);
    232    EXPECT_TRUE(frame_);
    233    EXPECT_GE(metrics::NumEvents(kCapturerResultHistogram, kSuccess),
    234              total_successful_captures_);
    235  }
    236 
    237  void ValidateFrame(int expected_width, int expected_height) {
    238    EXPECT_EQ(frame_->size().width(), expected_width - kWindowWidthSubtrahend);
    239    EXPECT_EQ(frame_->size().height(),
    240              expected_height - kWindowHeightSubtrahend);
    241 
    242    // Verify the buffer contains as much data as it should.
    243    int data_length = frame_->stride() * frame_->size().height();
    244 
    245    // The first and last pixel should have the same color because they will be
    246    // from the border of the window.
    247    // Pixels have 4 bytes of data so the whole pixel needs a uint32_t to fit.
    248    uint32_t first_pixel = static_cast<uint32_t>(*frame_->data());
    249    uint32_t last_pixel = static_cast<uint32_t>(
    250        *(frame_->data() + data_length - DesktopFrame::kBytesPerPixel));
    251    EXPECT_EQ(first_pixel, last_pixel);
    252 
    253    // Let's also check a pixel from the middle of the content area, which the
    254    // test window will paint a consistent color for us to verify.
    255    uint8_t* middle_pixel = frame_->data() + (data_length / 2);
    256 
    257    int sub_pixel_offset = DesktopFrame::kBytesPerPixel / 4;
    258    EXPECT_EQ(*middle_pixel, kTestWindowBValue);
    259    middle_pixel += sub_pixel_offset;
    260    EXPECT_EQ(*middle_pixel, kTestWindowGValue);
    261    middle_pixel += sub_pixel_offset;
    262    EXPECT_EQ(*middle_pixel, kTestWindowRValue);
    263    middle_pixel += sub_pixel_offset;
    264 
    265    // The window is opaque so we expect 0xFF for the Alpha channel.
    266    EXPECT_EQ(*middle_pixel, 0xFF);
    267  }
    268 
    269  // DesktopCapturer::Callback interface
    270  // The capturer synchronously invokes this method before `CaptureFrame()`
    271  // returns.
    272  void OnCaptureResult(DesktopCapturer::Result result,
    273                       std::unique_ptr<DesktopFrame> frame) override {
    274    result_ = result;
    275    frame_ = std::move(frame);
    276  }
    277 
    278 protected:
    279  std::unique_ptr<ScopedCOMInitializer> com_initializer_;
    280  DWORD window_thread_id_;
    281  std::unique_ptr<Thread> window_thread_;
    282  WindowInfo window_info_;
    283  intptr_t source_id_;
    284  bool window_open_ = false;
    285  DesktopCapturer::Result result_;
    286  int total_successful_captures_ = 0;
    287  std::unique_ptr<DesktopFrame> frame_;
    288  std::unique_ptr<DesktopCapturer> capturer_;
    289 };
    290 
    291 TEST_P(WgcCapturerWinTest, SelectValidSource) {
    292  if (GetParam() == CaptureType::kWindow) {
    293    SetUpForWindowCapture();
    294  } else {
    295    SetUpForScreenCapture();
    296  }
    297 
    298  EXPECT_TRUE(capturer_->SelectSource(source_id_));
    299 }
    300 
    301 TEST_P(WgcCapturerWinTest, SelectInvalidSource) {
    302  if (GetParam() == CaptureType::kWindow) {
    303    capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
    304        DesktopCaptureOptions::CreateDefault());
    305    source_id_ = kNullWindowId;
    306  } else {
    307    capturer_ = WgcCapturerWin::CreateRawScreenCapturer(
    308        DesktopCaptureOptions::CreateDefault());
    309    source_id_ = kInvalidScreenId;
    310  }
    311 
    312  EXPECT_FALSE(capturer_->SelectSource(source_id_));
    313 }
    314 
    315 TEST_P(WgcCapturerWinTest, Capture) {
    316  if (GetParam() == CaptureType::kWindow) {
    317    SetUpForWindowCapture();
    318  } else {
    319    SetUpForScreenCapture();
    320  }
    321 
    322  EXPECT_TRUE(capturer_->SelectSource(source_id_));
    323 
    324  capturer_->Start(this);
    325  EXPECT_GE(metrics::NumEvents(kCapturerImplHistogram,
    326                               DesktopCapturerId::kWgcCapturerWin),
    327            1);
    328 
    329  DoCapture();
    330  EXPECT_GT(frame_->size().width(), 0);
    331  EXPECT_GT(frame_->size().height(), 0);
    332 }
    333 
    334 TEST_P(WgcCapturerWinTest, CaptureTime) {
    335  if (GetParam() == CaptureType::kWindow) {
    336    SetUpForWindowCapture();
    337  } else {
    338    SetUpForScreenCapture();
    339  }
    340 
    341  EXPECT_TRUE(capturer_->SelectSource(source_id_));
    342  capturer_->Start(this);
    343 
    344  int64_t start_time;
    345  start_time = TimeNanos();
    346  capturer_->CaptureFrame();
    347 
    348  int capture_time_ms = (TimeNanos() - start_time) / kNumNanosecsPerMillisec;
    349  EXPECT_EQ(result_, DesktopCapturer::Result::SUCCESS);
    350  EXPECT_TRUE(frame_);
    351 
    352  // The test may measure the time slightly differently than the capturer. So we
    353  // just check if it's within 5 ms.
    354  EXPECT_NEAR(frame_->capture_time_ms(), capture_time_ms, 5);
    355  EXPECT_GE(
    356      metrics::NumEvents(kCaptureTimeHistogram, frame_->capture_time_ms()), 1);
    357 }
    358 
    359 INSTANTIATE_TEST_SUITE_P(SourceAgnostic,
    360                         WgcCapturerWinTest,
    361                         ::testing::Values(CaptureType::kWindow,
    362                                           CaptureType::kScreen));
    363 
    364 TEST(WgcCapturerNoMonitorTest, NoMonitors) {
    365  ScopedCOMInitializer com_initializer(ScopedCOMInitializer::kMTA);
    366  EXPECT_TRUE(com_initializer.Succeeded());
    367  if (HasActiveDisplay()) {
    368    RTC_LOG(LS_INFO) << "Skip WgcCapturerWinTest designed specifically for "
    369                        "systems with no monitors";
    370    GTEST_SKIP();
    371  }
    372 
    373  // A bug in `CreateForMonitor` prevents screen capture when no displays are
    374  // attached.
    375  EXPECT_FALSE(IsWgcSupported(CaptureType::kScreen));
    376 
    377  // A bug in the DWM (Desktop Window Manager) prevents it from providing image
    378  // data if there are no displays attached. This was fixed in Windows 11.
    379  if (rtc_win::GetVersion() < rtc_win::Version::VERSION_WIN11)
    380    EXPECT_FALSE(IsWgcSupported(CaptureType::kWindow));
    381  else
    382    EXPECT_TRUE(IsWgcSupported(CaptureType::kWindow));
    383 }
    384 
    385 class WgcCapturerMonitorTest : public WgcCapturerWinTest {
    386 public:
    387  void SetUp() {
    388    com_initializer_ =
    389        std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
    390    EXPECT_TRUE(com_initializer_->Succeeded());
    391 
    392    if (!IsWgcSupported(CaptureType::kScreen)) {
    393      RTC_LOG(LS_INFO)
    394          << "Skipping WgcCapturerWinTests on unsupported platforms.";
    395      GTEST_SKIP();
    396    }
    397  }
    398 };
    399 
    400 TEST_F(WgcCapturerMonitorTest, FocusOnMonitor) {
    401  SetUpForScreenCapture();
    402  EXPECT_TRUE(capturer_->SelectSource(0));
    403 
    404  // You can't set focus on a monitor.
    405  EXPECT_FALSE(capturer_->FocusOnSelectedSource());
    406 }
    407 
    408 TEST_F(WgcCapturerMonitorTest, CaptureAllMonitors) {
    409  SetUpForScreenCapture();
    410  EXPECT_TRUE(capturer_->SelectSource(kFullDesktopScreenId));
    411 
    412  capturer_->Start(this);
    413  DoCapture();
    414  EXPECT_GT(frame_->size().width(), 0);
    415  EXPECT_GT(frame_->size().height(), 0);
    416 }
    417 
    418 class WgcCapturerWindowTest : public WgcCapturerWinTest {
    419 public:
    420  void SetUp() {
    421    com_initializer_ =
    422        std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
    423    EXPECT_TRUE(com_initializer_->Succeeded());
    424 
    425    if (!IsWgcSupported(CaptureType::kWindow)) {
    426      RTC_LOG(LS_INFO)
    427          << "Skipping WgcCapturerWinTests on unsupported platforms.";
    428      GTEST_SKIP();
    429    }
    430  }
    431 };
    432 
    433 TEST_F(WgcCapturerWindowTest, FocusOnWindow) {
    434  capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
    435      DesktopCaptureOptions::CreateDefault());
    436  window_info_ = CreateTestWindow(kWindowTitle);
    437  source_id_ = GetScreenIdFromSourceList();
    438 
    439  EXPECT_TRUE(capturer_->SelectSource(source_id_));
    440  EXPECT_TRUE(capturer_->FocusOnSelectedSource());
    441 
    442  HWND hwnd = reinterpret_cast<HWND>(source_id_);
    443  EXPECT_EQ(hwnd, ::GetActiveWindow());
    444  EXPECT_EQ(hwnd, ::GetForegroundWindow());
    445  EXPECT_EQ(hwnd, ::GetFocus());
    446  DestroyTestWindow(window_info_);
    447 }
    448 
    449 TEST_F(WgcCapturerWindowTest, SelectMinimizedWindow) {
    450  SetUpForWindowCapture();
    451  MinimizeTestWindow(reinterpret_cast<HWND>(source_id_));
    452  EXPECT_FALSE(capturer_->SelectSource(source_id_));
    453 
    454  UnminimizeTestWindow(reinterpret_cast<HWND>(source_id_));
    455  EXPECT_TRUE(capturer_->SelectSource(source_id_));
    456 }
    457 
    458 TEST_F(WgcCapturerWindowTest, SelectClosedWindow) {
    459  SetUpForWindowCapture();
    460  EXPECT_TRUE(capturer_->SelectSource(source_id_));
    461 
    462  CloseTestWindow();
    463  EXPECT_FALSE(capturer_->SelectSource(source_id_));
    464 }
    465 
    466 TEST_F(WgcCapturerWindowTest, UnsupportedWindowStyle) {
    467  // Create a window with the WS_EX_TOOLWINDOW style, which WGC does not
    468  // support.
    469  window_info_ = CreateTestWindow(kWindowTitle, kMediumWindowWidth,
    470                                  kMediumWindowHeight, WS_EX_TOOLWINDOW);
    471  capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
    472      DesktopCaptureOptions::CreateDefault());
    473  DesktopCapturer::SourceList sources;
    474  EXPECT_TRUE(capturer_->GetSourceList(&sources));
    475  auto it = std::find_if(
    476      sources.begin(), sources.end(), [&](const DesktopCapturer::Source& src) {
    477        return src.id == reinterpret_cast<intptr_t>(window_info_.hwnd);
    478      });
    479 
    480  // We should not find the window, since we filter for unsupported styles.
    481  EXPECT_EQ(it, sources.end());
    482  DestroyTestWindow(window_info_);
    483 }
    484 
    485 TEST_F(WgcCapturerWindowTest, IncreaseWindowSizeMidCapture) {
    486  SetUpForWindowCapture(kSmallWindowWidth, kSmallWindowHeight);
    487  EXPECT_TRUE(capturer_->SelectSource(source_id_));
    488 
    489  capturer_->Start(this);
    490  DoCapture();
    491  ValidateFrame(kSmallWindowWidth, kSmallWindowHeight);
    492 
    493  ResizeTestWindow(window_info_.hwnd, kSmallWindowWidth, kMediumWindowHeight);
    494  DoCapture(kNumCapturesToFlushBuffers);
    495  ValidateFrame(kSmallWindowWidth, kMediumWindowHeight);
    496 
    497  ResizeTestWindow(window_info_.hwnd, kLargeWindowWidth, kMediumWindowHeight);
    498  DoCapture(kNumCapturesToFlushBuffers);
    499  ValidateFrame(kLargeWindowWidth, kMediumWindowHeight);
    500 }
    501 
    502 TEST_F(WgcCapturerWindowTest, ReduceWindowSizeMidCapture) {
    503  SetUpForWindowCapture(kLargeWindowWidth, kLargeWindowHeight);
    504  EXPECT_TRUE(capturer_->SelectSource(source_id_));
    505 
    506  capturer_->Start(this);
    507  DoCapture();
    508  ValidateFrame(kLargeWindowWidth, kLargeWindowHeight);
    509 
    510  ResizeTestWindow(window_info_.hwnd, kLargeWindowWidth, kMediumWindowHeight);
    511  DoCapture(kNumCapturesToFlushBuffers);
    512  ValidateFrame(kLargeWindowWidth, kMediumWindowHeight);
    513 
    514  ResizeTestWindow(window_info_.hwnd, kSmallWindowWidth, kMediumWindowHeight);
    515  DoCapture(kNumCapturesToFlushBuffers);
    516  ValidateFrame(kSmallWindowWidth, kMediumWindowHeight);
    517 }
    518 
    519 TEST_F(WgcCapturerWindowTest, MinimizeWindowMidCapture) {
    520  SetUpForWindowCapture();
    521  EXPECT_TRUE(capturer_->SelectSource(source_id_));
    522 
    523  capturer_->Start(this);
    524 
    525  // Minmize the window and capture should continue but return temporary errors.
    526  MinimizeTestWindow(window_info_.hwnd);
    527  for (int i = 0; i < 5; ++i) {
    528    capturer_->CaptureFrame();
    529    EXPECT_EQ(result_, DesktopCapturer::Result::ERROR_TEMPORARY);
    530  }
    531 
    532  // Reopen the window and the capture should continue normally.
    533  UnminimizeTestWindow(window_info_.hwnd);
    534  DoCapture();
    535  // We can't verify the window size here because the test window does not
    536  // repaint itself after it is unminimized, but capturing successfully is still
    537  // a good test.
    538 }
    539 
    540 TEST_F(WgcCapturerWindowTest, CloseWindowMidCapture) {
    541  SetUpForWindowCapture();
    542  EXPECT_TRUE(capturer_->SelectSource(source_id_));
    543 
    544  capturer_->Start(this);
    545  DoCapture();
    546  ValidateFrame(kMediumWindowWidth, kMediumWindowHeight);
    547 
    548  CloseTestWindow();
    549 
    550  // We need to pump our message queue so the Closed event will be delivered to
    551  // the capturer's event handler. If we are too early and the Closed event
    552  // hasn't arrived yet we should keep trying until the capturer receives it and
    553  // stops.
    554  auto* wgc_capturer = static_cast<WgcCapturerWin*>(capturer_.get());
    555  MSG msg;
    556  for (int i = 0;
    557       wgc_capturer->IsSourceBeingCaptured(source_id_) && i < kMaxTries; ++i) {
    558    // Unlike GetMessage, PeekMessage will not hang if there are no messages in
    559    // the queue.
    560    PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
    561    Thread::SleepMs(1);
    562  }
    563 
    564  EXPECT_FALSE(wgc_capturer->IsSourceBeingCaptured(source_id_));
    565 
    566  // The frame pool can buffer `kNumBuffers` frames. We must consume these
    567  // and then make one more call to CaptureFrame before we expect to see the
    568  // failure.
    569  int num_tries = 0;
    570  do {
    571    capturer_->CaptureFrame();
    572  } while (result_ == DesktopCapturer::Result::SUCCESS &&
    573           ++num_tries <= WgcCaptureSession::kNumBuffers);
    574 
    575  EXPECT_GE(metrics::NumEvents(kCapturerResultHistogram, kSessionStartFailure),
    576            1);
    577  EXPECT_GE(metrics::NumEvents(kCaptureSessionResultHistogram, kSourceClosed),
    578            1);
    579  EXPECT_EQ(result_, DesktopCapturer::Result::ERROR_PERMANENT);
    580 }
    581 
    582 class WgcCapturerFullScreenDetectorTest : public WgcCapturerWindowTest {
    583 public:
    584  void SetUp() override {
    585    capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
    586        DesktopCaptureOptions::CreateDefault());
    587    wgc_capturer_ = static_cast<WgcCapturerWin*>(capturer_.get());
    588 
    589    editor_window_ = CreateEditorWindow();
    590    CreateSlideShowWindow();
    591    WgcCapturerWindowTest::SetUp();
    592  }
    593 
    594  WindowInfo CreateEditorWindow() {
    595    return CreateTestWindow(
    596        L"My - Title - PowerPoint", kMediumWindowHeight, kMediumWindowWidth,
    597        /*extended_styles=*/0, /*window_class=*/L"PPTFrameClass");
    598  }
    599 
    600  void CreateSlideShowWindow() {
    601    slide_show_window_ =
    602        CreateTestWindow(L"PowerPoint Slide Show - [My - Title]",
    603                         kLargeWindowHeight, kLargeWindowWidth,
    604                         /*extended_styles=*/0,
    605                         /*window_class=*/L"screenClass");
    606    ResizeTestWindowToFullScreen(slide_show_window_.hwnd);
    607  }
    608 
    609  WgcCapturerWin* wgc_capturer_;
    610  WindowInfo editor_window_;
    611  WindowInfo slide_show_window_;
    612 };
    613 
    614 TEST_F(WgcCapturerFullScreenDetectorTest, SlideShowNotFoundByDefaultConfig) {
    615  // The default behavior on WGC capturer of `use_heuristic` is false.
    616  wgc_capturer_->SetUpFullScreenDetectorForTest(
    617      /*use_heuristic=*/false,
    618      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd));
    619 
    620  EXPECT_TRUE(wgc_capturer_->SelectSource(
    621      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd)));
    622  wgc_capturer_->Start(this);
    623  DoCapture();
    624 
    625  EXPECT_TRUE(wgc_capturer_->IsSourceBeingCaptured(
    626      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd)));
    627  EXPECT_FALSE(wgc_capturer_->IsSourceBeingCaptured(
    628      reinterpret_cast<DesktopCapturer::SourceId>(slide_show_window_.hwnd)));
    629  EXPECT_EQ(metrics::NumEvents(kCaptureFullscreenDetectorHistogram, true), 0);
    630 }
    631 
    632 TEST_F(WgcCapturerFullScreenDetectorTest,
    633       CorrectSlideShowFoundForEditorWhenSlideShowCreatedAfter) {
    634  wgc_capturer_->SetUpFullScreenDetectorForTest(
    635      /*use_heuristic=*/true,
    636      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd));
    637 
    638  EXPECT_TRUE(wgc_capturer_->SelectSource(
    639      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd)));
    640  wgc_capturer_->Start(this);
    641  DoCapture();
    642 
    643  EXPECT_FALSE(wgc_capturer_->IsSourceBeingCaptured(
    644      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd)));
    645  EXPECT_TRUE(wgc_capturer_->IsSourceBeingCaptured(
    646      reinterpret_cast<DesktopCapturer::SourceId>(slide_show_window_.hwnd)));
    647 
    648  EXPECT_EQ(metrics::NumEvents(kCaptureFullscreenDetectorHistogram, true), 1);
    649  EXPECT_EQ(
    650      metrics::NumEvents(kFullScreenDetectorResult, detector_result_success),
    651      1);
    652  EXPECT_EQ(metrics::NumEvents(kFullScreenDetectorResult,
    653                               detector_result_failure_slide_show_not_chosen),
    654            0);
    655 }
    656 
    657 TEST_F(WgcCapturerFullScreenDetectorTest,
    658       SlideShowNotFoundForEditorWhenSlideShowCreatedBefore) {
    659  wgc_capturer_->SetUpFullScreenDetectorForTest(
    660      /*use_heuristic=*/true,
    661      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd),
    662      /*fullscreen_slide_show_started_after_capture_start=*/false);
    663 
    664  EXPECT_TRUE(wgc_capturer_->SelectSource(
    665      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd)));
    666  wgc_capturer_->Start(this);
    667  DoCapture();
    668 
    669  EXPECT_TRUE(wgc_capturer_->IsSourceBeingCaptured(
    670      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd)));
    671  EXPECT_FALSE(wgc_capturer_->IsSourceBeingCaptured(
    672      reinterpret_cast<DesktopCapturer::SourceId>(slide_show_window_.hwnd)));
    673 
    674  EXPECT_EQ(metrics::NumEvents(kCaptureFullscreenDetectorHistogram, true), 0);
    675  EXPECT_EQ(
    676      metrics::NumEvents(kFullScreenDetectorResult, detector_result_success),
    677      0);
    678  EXPECT_EQ(metrics::NumEvents(kFullScreenDetectorResult,
    679                               detector_result_failure_slide_show_not_chosen),
    680            1);
    681 }
    682 
    683 TEST_F(WgcCapturerFullScreenDetectorTest, LoggedOnlyOnce) {
    684  wgc_capturer_->SetUpFullScreenDetectorForTest(
    685      /*use_heuristic=*/true,
    686      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd));
    687 
    688  EXPECT_TRUE(wgc_capturer_->SelectSource(
    689      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd)));
    690  wgc_capturer_->Start(this);
    691  DoCapture();
    692  DoCapture();
    693 
    694  EXPECT_TRUE(wgc_capturer_->IsSourceBeingCaptured(
    695      reinterpret_cast<DesktopCapturer::SourceId>(slide_show_window_.hwnd)));
    696  EXPECT_EQ(metrics::NumEvents(kCaptureFullscreenDetectorHistogram, true), 1);
    697  EXPECT_EQ(
    698      metrics::NumEvents(kFullScreenDetectorResult, detector_result_success),
    699      1);
    700 }
    701 
    702 TEST_F(WgcCapturerFullScreenDetectorTest,
    703       SlideShowNotFoundWithMultipleSameTitleEditors) {
    704  WindowInfo same_title_editor_window = CreateEditorWindow();
    705  EXPECT_NE(editor_window_.hwnd, same_title_editor_window.hwnd);
    706  wgc_capturer_->SetUpFullScreenDetectorForTest(
    707      /*use_heuristic=*/true,
    708      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd));
    709 
    710  EXPECT_TRUE(wgc_capturer_->SelectSource(
    711      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd)));
    712  wgc_capturer_->Start(this);
    713  DoCapture();
    714 
    715  EXPECT_TRUE(wgc_capturer_->IsSourceBeingCaptured(
    716      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd)));
    717  EXPECT_FALSE(wgc_capturer_->IsSourceBeingCaptured(
    718      reinterpret_cast<DesktopCapturer::SourceId>(slide_show_window_.hwnd)));
    719  EXPECT_EQ(metrics::NumEvents(kCaptureFullscreenDetectorHistogram, true), 0);
    720  EXPECT_EQ(
    721      metrics::NumEvents(kFullScreenDetectorResult, detector_result_success),
    722      0);
    723  EXPECT_EQ(metrics::NumEvents(kFullScreenDetectorResult,
    724                               detector_result_failure_same_title_windows),
    725            1);
    726 }
    727 
    728 TEST_F(WgcCapturerFullScreenDetectorTest,
    729       CaptureTiedToSlideShowIfSlideShowIsShared) {
    730  wgc_capturer_->SetUpFullScreenDetectorForTest(
    731      /*use_heuristic=*/true,
    732      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd));
    733  wgc_capturer_->SetUpFullScreenDetectorForTest(
    734      /*use_heuristic=*/true,
    735      reinterpret_cast<DesktopCapturer::SourceId>(slide_show_window_.hwnd));
    736 
    737  EXPECT_TRUE(wgc_capturer_->SelectSource(
    738      reinterpret_cast<DesktopCapturer::SourceId>(slide_show_window_.hwnd)));
    739  wgc_capturer_->Start(this);
    740  DoCapture();
    741 
    742  EXPECT_FALSE(wgc_capturer_->IsSourceBeingCaptured(
    743      reinterpret_cast<DesktopCapturer::SourceId>(editor_window_.hwnd)));
    744  EXPECT_TRUE(wgc_capturer_->IsSourceBeingCaptured(
    745      reinterpret_cast<DesktopCapturer::SourceId>(slide_show_window_.hwnd)));
    746  EXPECT_EQ(metrics::NumEvents(kCaptureFullscreenDetectorHistogram, true), 0);
    747  EXPECT_EQ(
    748      metrics::NumEvents(kFullScreenDetectorResult, detector_result_success),
    749      0);
    750  EXPECT_EQ(metrics::NumEvents(kFullScreenDetectorResult,
    751                               detector_result_failure_slide_show_not_chosen),
    752            0);
    753 }
    754 
    755 }  // namespace webrtc