From 77b7a1da2d90c46afba21ba38fac63b991b6e88d Mon Sep 17 00:00:00 2001 From: Zijie He Date: Fri, 1 Sep 2017 15:51:14 -0700 Subject: [PATCH] Tests for WindowFinder This change adds several tests for WindowFinder implmenetations. One of the tests uses ScreenDrawer to create a foreground window or console window (on Windows) and ensures WindowFinder can return its window_id(). The other ensures WindowFinder always returns kNullWindowId if the |pos| is out of the screens. Bug: webrtc:7950 Change-Id: I0cc8794e201c2fa042ea8e693434f1b0fa05b47a Reviewed-on: https://chromium-review.googlesource.com/639591 Reviewed-by: Jamie Walch Commit-Queue: Zijie He Cr-Commit-Position: refs/heads/master@{#19676} --- webrtc/modules/desktop_capture/BUILD.gn | 1 + .../modules/desktop_capture/screen_drawer.h | 8 +- .../desktop_capture/screen_drawer_linux.cc | 5 + .../desktop_capture/screen_drawer_win.cc | 5 + .../modules/desktop_capture/window_finder.h | 17 ++ .../desktop_capture/window_finder_mac.mm | 7 + .../desktop_capture/window_finder_unittest.cc | 161 ++++++++++++++++++ .../desktop_capture/window_finder_win.cc | 8 + .../desktop_capture/window_finder_x11.cc | 11 ++ 9 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 webrtc/modules/desktop_capture/window_finder_unittest.cc diff --git a/webrtc/modules/desktop_capture/BUILD.gn b/webrtc/modules/desktop_capture/BUILD.gn index 6b7fb22eaa..2e6b168798 100644 --- a/webrtc/modules/desktop_capture/BUILD.gn +++ b/webrtc/modules/desktop_capture/BUILD.gn @@ -58,6 +58,7 @@ if (rtc_include_tests) { sources += [ "screen_capturer_integration_test.cc", "screen_drawer_unittest.cc", + "window_finder_unittest.cc", ] } } diff --git a/webrtc/modules/desktop_capture/screen_drawer.h b/webrtc/modules/desktop_capture/screen_drawer.h index cde9a2e50e..ee0ebf07b5 100644 --- a/webrtc/modules/desktop_capture/screen_drawer.h +++ b/webrtc/modules/desktop_capture/screen_drawer.h @@ -16,6 +16,7 @@ #include #include "webrtc/modules/desktop_capture/rgba_color.h" +#include "webrtc/modules/desktop_capture/desktop_capture_types.h" #include "webrtc/modules/desktop_capture/desktop_geometry.h" namespace webrtc { @@ -49,7 +50,8 @@ class ScreenDrawer { // Returns the region inside which DrawRectangle() function are expected to // work, in capturer coordinates (assuming ScreenCapturer::SelectScreen has // not been called). This region may exclude regions of the screen reserved by - // the OS for things like menu bars or app launchers. + // the OS for things like menu bars or app launchers. The DesktopRect is in + // system coordinate, i.e. the primary monitor always starts from (0, 0). virtual DesktopRect DrawableRegion() = 0; // Draws a rectangle to cover |rect| with |color|. Note, rect.bottom() and @@ -70,6 +72,10 @@ class ScreenDrawer { // shapes will eventually be drawn on the screen, due to some OS limitations, // these shapes may be partially appeared sometimes. virtual bool MayDrawIncompleteShapes() = 0; + + // Returns the id of the drawer window. This function returns kNullWindowId if + // the implementation does not draw on a window of the system. + virtual WindowId window_id() const = 0; }; } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/screen_drawer_linux.cc b/webrtc/modules/desktop_capture/screen_drawer_linux.cc index fa2cfa4849..1a99e69cf2 100644 --- a/webrtc/modules/desktop_capture/screen_drawer_linux.cc +++ b/webrtc/modules/desktop_capture/screen_drawer_linux.cc @@ -36,6 +36,7 @@ class ScreenDrawerLinux : public ScreenDrawer { void Clear() override; void WaitForPendingDraws() override; bool MayDrawIncompleteShapes() override; + WindowId window_id() const override; private: // Bring the window to the front, this can help to avoid the impact from other @@ -136,6 +137,10 @@ bool ScreenDrawerLinux::MayDrawIncompleteShapes() { return true; } +WindowId ScreenDrawerLinux::window_id() const { + return window_; +} + void ScreenDrawerLinux::BringToFront() { Atom state_above = XInternAtom(display_->display(), "_NET_WM_STATE_ABOVE", 1); Atom window_state = XInternAtom(display_->display(), "_NET_WM_STATE", 1); diff --git a/webrtc/modules/desktop_capture/screen_drawer_win.cc b/webrtc/modules/desktop_capture/screen_drawer_win.cc index 7b1a56f84a..caa0ce2330 100644 --- a/webrtc/modules/desktop_capture/screen_drawer_win.cc +++ b/webrtc/modules/desktop_capture/screen_drawer_win.cc @@ -82,6 +82,7 @@ class ScreenDrawerWin : public ScreenDrawer { void Clear() override; void WaitForPendingDraws() override; bool MayDrawIncompleteShapes() override; + WindowId window_id() const override; private: // Bring the window to the front, this can help to avoid the impact from other @@ -158,6 +159,10 @@ bool ScreenDrawerWin::MayDrawIncompleteShapes() { return true; } +WindowId ScreenDrawerWin::window_id() const { + return reinterpret_cast(window_); +} + void ScreenDrawerWin::DrawLine(DesktopVector start, DesktopVector end, RgbaColor color) { diff --git a/webrtc/modules/desktop_capture/window_finder.h b/webrtc/modules/desktop_capture/window_finder.h index 90f3513015..f1ede97ae7 100644 --- a/webrtc/modules/desktop_capture/window_finder.h +++ b/webrtc/modules/desktop_capture/window_finder.h @@ -11,11 +11,17 @@ #ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_FINDER_H_ #define WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_FINDER_H_ +#include + #include "webrtc/modules/desktop_capture/desktop_capture_types.h" #include "webrtc/modules/desktop_capture/desktop_geometry.h" namespace webrtc { +#if defined(USE_X11) +class XAtomCache; +#endif + // An interface to return the id of the visible window under a certain point. class WindowFinder { public: @@ -28,6 +34,17 @@ class WindowFinder { // |point| is always in system coordinate, i.e. the primary monitor always // starts from (0, 0). virtual WindowId GetWindowUnderPoint(DesktopVector point) = 0; + + struct Options { +#if defined(USE_X11) + XAtomCache* cache = nullptr; +#endif + }; + + // Creates a platform-independent WindowFinder implementation. This function + // returns nullptr if |options| does not contain enough information or + // WindowFinder does not support current platform. + static std::unique_ptr Create(const Options& options); }; } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/window_finder_mac.mm b/webrtc/modules/desktop_capture/window_finder_mac.mm index ae4a911c7b..8bf7150bab 100644 --- a/webrtc/modules/desktop_capture/window_finder_mac.mm +++ b/webrtc/modules/desktop_capture/window_finder_mac.mm @@ -13,6 +13,7 @@ #include #include "webrtc/modules/desktop_capture/mac/window_list_utils.h" +#include "webrtc/rtc_base/ptr_util.h" namespace webrtc { @@ -33,4 +34,10 @@ WindowId WindowFinderMac::GetWindowUnderPoint(DesktopVector point) { return id; } +// static +std::unique_ptr WindowFinder::Create( + const WindowFinder::Options& options) { + return rtc::MakeUnique(); +} + } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/window_finder_unittest.cc b/webrtc/modules/desktop_capture/window_finder_unittest.cc new file mode 100644 index 0000000000..6121ded7b4 --- /dev/null +++ b/webrtc/modules/desktop_capture/window_finder_unittest.cc @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/window_finder.h" + +#include + +#include + +#include "webrtc/modules/desktop_capture/desktop_geometry.h" +#include "webrtc/modules/desktop_capture/screen_drawer.h" +#include "webrtc/rtc_base/logging.h" +#include "webrtc/test/gtest.h" + +#if defined(USE_X11) +#include "webrtc/modules/desktop_capture/x11/shared_x_display.h" +#include "webrtc/modules/desktop_capture/x11/x_atom_cache.h" +#include "webrtc/rtc_base/ptr_util.h" +#endif + +#if defined(WEBRTC_WIN) +#include + +#include "webrtc/modules/desktop_capture/window_finder_win.h" +#include "webrtc/modules/desktop_capture/win/window_capture_utils.h" +#endif + +namespace webrtc { + +namespace { + +#if defined(WEBRTC_WIN) +// ScreenDrawerWin does not have a message loop, so it's unresponsive to user +// inputs. WindowFinderWin cannot detect this kind of unresponsive windows. +// Instead, console window is used to test WindowFinderWin. +TEST(WindowFinderTest, FindConsoleWindow) { + // Creates a ScreenDrawer to avoid this test from conflicting with + // ScreenCapturerIntegrationTest: both tests require its window to be in + // foreground. + // + // In ScreenCapturer related tests, this is controlled by + // ScreenDrawer, which has a global lock to ensure only one ScreenDrawer + // window is active. So even we do not use ScreenDrawer for Windows test, + // creating an instance can block ScreenCapturer related tests until this test + // finishes. + // + // Usually the test framework should take care of this "isolated test" + // requirement, but unfortunately WebRTC trybots do not support this. + std::unique_ptr drawer = ScreenDrawer::Create(); + const int kMaxSize = 10000; + // Enlarges current console window. + system("mode 1000,1000"); + const HWND console_window = GetConsoleWindow(); + MoveWindow(console_window, 0, 0, kMaxSize, kMaxSize, true); + + // Brings console window to top. + SetWindowPos( + console_window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + BringWindowToTop(console_window); + + WindowFinderWin finder; + for (int i = 0; i < kMaxSize; i++) { + const DesktopVector spot(i, i); + const HWND id = reinterpret_cast(finder.GetWindowUnderPoint(spot)); + if (id == console_window) { + return; + } + } + + FAIL(); +} + +#else +TEST(WindowFinderTest, FindDrawerWindow) { + WindowFinder::Options options; +#if defined(USE_X11) + std::unique_ptr cache; + const auto shared_x_display = SharedXDisplay::CreateDefault(); + if (shared_x_display) { + cache = rtc::MakeUnique(shared_x_display->display()); + options.cache = cache.get(); + } +#endif + std::unique_ptr finder = WindowFinder::Create(options); + if (!finder) { + LOG(LS_WARNING) << "No WindowFinder implementation for current platform."; + return; + } + + std::unique_ptr drawer = ScreenDrawer::Create(); + if (!drawer) { + LOG(LS_WARNING) << "No ScreenDrawer implementation for current platform."; + return; + } + + if (drawer->window_id() == kNullWindowId) { + // TODO(zijiehe): WindowFinderTest can use a dedicated window without + // relying on ScreenDrawer. + LOG(LS_WARNING) << "ScreenDrawer implementation for current platform does " + "create a window."; + return; + } + + // ScreenDrawer may not be able to bring the window to the top. So we test + // several spots, at least one of them should succeed. + const DesktopRect region = drawer->DrawableRegion(); + if (region.is_empty()) { + LOG(LS_WARNING) << "ScreenDrawer::DrawableRegion() is too small for the " + "WindowFinderTest."; + return; + } + + for (int i = 0; i < region.width(); i++) { + const DesktopVector spot( + region.left() + i, region.top() + i * region.height() / region.width()); + const WindowId id = finder->GetWindowUnderPoint(spot); + if (id == drawer->window_id()) { + return; + } + } + + FAIL(); +} +#endif + +TEST(WindowFinderTest, ShouldReturnNullWindowIfSpotIsOutOfScreen) { + WindowFinder::Options options; +#if defined(USE_X11) + std::unique_ptr cache; + const auto shared_x_display = SharedXDisplay::CreateDefault(); + if (shared_x_display) { + cache = rtc::MakeUnique(shared_x_display->display()); + options.cache = cache.get(); + } +#endif + std::unique_ptr finder = WindowFinder::Create(options); + if (!finder) { + LOG(LS_WARNING) << "No WindowFinder implementation for current platform."; + return; + } + + ASSERT_EQ(kNullWindowId, finder->GetWindowUnderPoint( + DesktopVector(INT16_MAX, INT16_MAX))); + ASSERT_EQ(kNullWindowId, finder->GetWindowUnderPoint( + DesktopVector(INT16_MAX, INT16_MIN))); + ASSERT_EQ(kNullWindowId, finder->GetWindowUnderPoint( + DesktopVector(INT16_MIN, INT16_MAX))); + ASSERT_EQ(kNullWindowId, finder->GetWindowUnderPoint( + DesktopVector(INT16_MIN, INT16_MIN))); +} + +} // namespace + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/window_finder_win.cc b/webrtc/modules/desktop_capture/window_finder_win.cc index 8d742cda98..19dbea3a9e 100644 --- a/webrtc/modules/desktop_capture/window_finder_win.cc +++ b/webrtc/modules/desktop_capture/window_finder_win.cc @@ -12,6 +12,8 @@ #include +#include "webrtc/rtc_base/ptr_util.h" + namespace webrtc { WindowFinderWin::WindowFinderWin() = default; @@ -35,4 +37,10 @@ WindowId WindowFinderWin::GetWindowUnderPoint(DesktopVector point) { return reinterpret_cast(window); } +// static +std::unique_ptr WindowFinder::Create( + const WindowFinder::Options& options) { + return rtc::MakeUnique(); +} + } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/window_finder_x11.cc b/webrtc/modules/desktop_capture/window_finder_x11.cc index 4c6da9d74e..d929460eca 100644 --- a/webrtc/modules/desktop_capture/window_finder_x11.cc +++ b/webrtc/modules/desktop_capture/window_finder_x11.cc @@ -12,6 +12,7 @@ #include "webrtc/modules/desktop_capture/x11/window_list_utils.h" #include "webrtc/rtc_base/checks.h" +#include "webrtc/rtc_base/ptr_util.h" namespace webrtc { @@ -37,4 +38,14 @@ WindowId WindowFinderX11::GetWindowUnderPoint(DesktopVector point) { return id; } +// static +std::unique_ptr WindowFinder::Create( + const WindowFinder::Options& options) { + if (options.cache == nullptr) { + return nullptr; + } + + return rtc::MakeUnique(options.cache); +} + } // namespace webrtc