Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(300)

Unified Diff: ui/base/x/x11_window_cache_unittest.cc

Issue 2177823002: X11: Add window cache Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix API Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/base/x/x11_window_cache.cc ('k') | ui/events/platform/x11/BUILD.gn » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/base/x/x11_window_cache_unittest.cc
diff --git a/ui/base/x/x11_window_cache_unittest.cc b/ui/base/x/x11_window_cache_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4044f6255c6ca2053a5b8dc1d9909112db56d792
--- /dev/null
+++ b/ui/base/x/x11_window_cache_unittest.cc
@@ -0,0 +1,551 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/x/x11_window_cache.h"
+
+#include <poll.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+// These macros defined by Xlib conflict with gtest
+#undef None
+#undef Bool
+
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/platform/x11/x11_event_source.h"
+#include "ui/gfx/x/x11_types.h"
+
+namespace ui {
+
+class XWindowCacheTest;
+
+class TestX11EventSourceDelegate : public X11EventSourceDelegate {
+ public:
+ TestX11EventSourceDelegate(XWindowCacheTest* cache_test)
+ : cache_(nullptr), cache_test_(cache_test) {}
+
+ void SetCache(XWindowCache* cache) { cache_ = cache; }
+
+ protected:
+ void ProcessXEvent(XEvent* xevent) override;
+
+ private:
+ XWindowCache* cache_;
+ XWindowCacheTest* cache_test_;
+};
+
+class TestX11EventSource : public X11EventSource {
+ public:
+ TestX11EventSource(X11EventSourceDelegate* delegate, XDisplay* display)
+ : X11EventSource(delegate, display) {}
+
+ // Returns false iff there was a connection error or a timeout.
+ bool BlockUntilConnectionIsReadable() {
+ int conn_fd = ConnectionNumber(display_);
+
+ struct pollfd rfd;
+ rfd.fd = conn_fd;
+ rfd.events = POLLIN;
+ rfd.revents = 0;
+
+ static constexpr unsigned int timeout_ms = 3000;
+ base::TimeTicks deadline =
+ base::TimeTicks::Now() + base::TimeDelta::FromMilliseconds(timeout_ms);
+ HANDLE_EINTR(poll(
+ &rfd, 1,
+ std::max(0L, (deadline - base::TimeTicks::Now()).InMilliseconds())));
+ return rfd.revents & POLLIN;
+ }
+
+ // Ensures we have the state of the entire window tree. May block. Returns
+ // false iff there was a connection error or a timeout.
+ bool BlockUntilTreeIsCached() {
+ while (!request_queue_.empty()) {
+ if (!HasNextReply()) {
+ if (!BlockUntilConnectionIsReadable())
+ return false;
+ }
+ DispatchXEvents();
+ }
+ return true;
+ }
+
+ // Ensures we have processed all events sent server-side before Synchronize
+ // was called client-side. We may be forced to wait for more events to come
+ // that are not yet in the queue. You most likely want to
+ // BlockUntilWindowTreeIsCached before and after calling Synchronize. Blocks.
+ // Returns false iff there was a connection error or a timeout.
+ void Synchronize() {
+ XSync(display_, False);
+ DispatchXEvents();
+ }
+
+ size_t NumberOfQueuedRequests() { return request_queue_.size(); }
+
+ private:
+};
+
+class XWindowCacheTest : public testing::Test {
+ public:
+ XWindowCacheTest()
+ : display_(gfx::GetXDisplay()),
+ display_root_(DefaultRootWindow(display_)),
+ cache_root_(XCB_WINDOW_NONE),
+ testing_atom_(XInternAtom(display_, "CHROMIUM_TESTING_ATOM", False)),
+ delegate_(this),
+ event_source_(&delegate_, display_),
+ cache_(nullptr) {}
+ ~XWindowCacheTest() override {}
+
+ void SetUp() override {
+ cache_root_ = CreateWindow(display_root_);
+ cache_ = new XWindowCache(display_, &event_source_, cache_root_);
+ delegate_.SetCache(cache_);
+ }
+
+ void TearDown() override {
+ XDestroyWindow(display_, cache_root_);
+ cache_root_ = XCB_WINDOW_NONE;
+ EnsureMemoryReclaimed();
+ delete cache_;
+ cache_ = nullptr;
+ delegate_.SetCache(cache_);
+ }
+
+ void EnsureMemoryReclaimed() {
+ Synchronize();
+ EXPECT_TRUE(cache_->windows_.empty());
+ EXPECT_EQ(0U, event_source_.NumberOfQueuedRequests());
+ }
+
+ XID CreateWindow(XID parent) {
+ XSetWindowAttributes swa;
+ swa.override_redirect = True;
+ return XCreateWindow(display_, parent, 0, 0, 1, 1, 0, CopyFromParent,
+ InputOutput, CopyFromParent, CWOverrideRedirect, &swa);
+ }
+
+ void ProcessEvent(XWindowCache* cache, XEvent* event) {
+ cache->ProcessEvent(event);
+ }
+
+ bool BlockUntilTreeIsCached() {
+ return event_source_.BlockUntilTreeIsCached();
+ }
+
+ void Synchronize() { event_source_.Synchronize(); }
+
+ void Reset() { cache_->ResetCacheImpl(); }
+
+ void SetProperty8(XID window, XID property, uint8_t value) {
+ XChangeProperty(display_, window, property, XA_CARDINAL, 8, PropModeReplace,
+ &value, 1);
+ }
+
+ template <typename T>
+ void VerifyStackingOrder(const XWindowCache::Window* window,
+ const T& container) {
+ EXPECT_TRUE(window);
+ EXPECT_EQ(container.size(), window->GetChildren().size());
+ auto it = window->GetChildren().begin();
+ for (XID id : container) {
+ EXPECT_EQ(id, (*it)->id());
+ ++it;
+ }
+ }
+
+ XDisplay* display_;
+ XID display_root_;
+ XID cache_root_;
+ XAtom testing_atom_;
+
+ TestX11EventSourceDelegate delegate_;
+ TestX11EventSource event_source_;
+ XWindowCache* cache_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(XWindowCacheTest);
+};
+
+void TestX11EventSourceDelegate::ProcessXEvent(XEvent* xevent) {
+ if (cache_)
+ cache_test_->ProcessEvent(cache_, xevent);
+}
+
+TEST_F(XWindowCacheTest, BasicTest) {
+ XID child1_xid = CreateWindow(cache_root_);
+ XID child2_xid = CreateWindow(cache_root_);
+
+ Synchronize();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+
+ auto parent_window = cache_->GetWindow(cache_root_);
+
+ EXPECT_TRUE(parent_window);
+ EXPECT_EQ(2U, parent_window->GetChildren().size());
+ auto it = parent_window->GetChildren().begin();
+ EXPECT_EQ(child2_xid, (*it)->id());
+ EXPECT_EQ(child1_xid, (*++it)->id());
+}
+
+TEST_F(XWindowCacheTest, NestingTest) {
+ XID child_xid = CreateWindow(cache_root_);
+ XID nested_child_xid = CreateWindow(child_xid);
+ SetProperty8(nested_child_xid, testing_atom_, 0x42);
+
+ Synchronize();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+
+ auto parent_window = cache_->GetWindow(cache_root_);
+ EXPECT_TRUE(parent_window);
+ EXPECT_EQ(1U, parent_window->GetChildren().size());
+
+ auto child_window = *parent_window->GetChildren().begin();
+ EXPECT_TRUE(child_window);
+ EXPECT_EQ(1U, child_window->GetChildren().size());
+
+ auto nested_child_window = *child_window->GetChildren().begin();
+ EXPECT_TRUE(nested_child_window);
+ EXPECT_EQ(0U, nested_child_window->GetChildren().size());
+
+ auto prop = nested_child_window->GetProperty(testing_atom_);
+ EXPECT_TRUE(prop);
+ EXPECT_EQ(XA_CARDINAL, prop->type());
+ EXPECT_EQ(8, prop->format());
+ EXPECT_EQ(1, prop->length());
+ EXPECT_EQ(0x42, *static_cast<const uint8_t*>(prop->data()));
+}
+
+TEST_F(XWindowCacheTest, ResetCacheTest) {
+ XID child1_xid = CreateWindow(cache_root_);
+ XID child2_xid = CreateWindow(cache_root_);
+
+ auto verify = [&]() {
+ auto parent_window = cache_->GetWindow(cache_root_);
+ EXPECT_TRUE(parent_window);
+ EXPECT_EQ(2U, parent_window->GetChildren().size());
+ auto it = parent_window->GetChildren().begin();
+ EXPECT_EQ(child2_xid, (*it)->id());
+ EXPECT_EQ(child1_xid, (*++it)->id());
+ };
+
+ // Normal case.
+ Synchronize();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+ verify();
+
+ // Reset when tree is fully-cached.
+ Reset();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+ verify();
+
+ Reset();
+ Synchronize();
+ // Reset when tree is half-cached.
+ Reset();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+ verify();
+
+ // Multiple resets.
+ for (int i = 0; i < 5; i++)
+ Reset();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+ verify();
+}
+
+TEST_F(XWindowCacheTest, CreateNotifyAndDestroyNotifyTest) {
+ XID child1_xid = CreateWindow(cache_root_);
+ XID child2_xid = CreateWindow(cache_root_);
+
+ Synchronize();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+
+ auto parent_window = cache_->GetWindow(cache_root_);
+ EXPECT_TRUE(parent_window);
+ EXPECT_EQ(2U, parent_window->GetChildren().size());
+ auto it = parent_window->GetChildren().begin();
+ EXPECT_EQ(child2_xid, (*it)->id());
+ EXPECT_EQ(child1_xid, (*++it)->id());
+
+ XDestroyWindow(display_, child1_xid);
+ XDestroyWindow(display_, child2_xid);
+
+ Synchronize();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+
+ parent_window = cache_->GetWindow(cache_root_);
+ EXPECT_TRUE(parent_window);
+ EXPECT_EQ(0U, parent_window->GetChildren().size());
+}
+
+TEST_F(XWindowCacheTest, PropertyNotifyTest) {
+ SetProperty8(cache_root_, testing_atom_, 0x42);
+
+ Synchronize();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+
+ auto window = cache_->GetWindow(cache_root_);
+ EXPECT_TRUE(window);
+ EXPECT_EQ(0U, window->GetChildren().size());
+
+ auto prop = window->GetProperty(testing_atom_);
+ EXPECT_TRUE(prop);
+ EXPECT_EQ(XA_CARDINAL, prop->type());
+ EXPECT_EQ(8, prop->format());
+ EXPECT_EQ(1, prop->length());
+ EXPECT_EQ(0x42, *static_cast<const uint8_t*>(prop->data()));
+
+ SetProperty8(cache_root_, testing_atom_, 0x24);
+ Synchronize();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+
+ prop = window->GetProperty(testing_atom_);
+ EXPECT_TRUE(prop);
+ EXPECT_EQ(XA_CARDINAL, prop->type());
+ EXPECT_EQ(8, prop->format());
+ EXPECT_EQ(1, prop->length());
+ EXPECT_EQ(0x24, *static_cast<const uint8_t*>(prop->data()));
+}
+
+TEST_F(XWindowCacheTest, MapNotifyUnmapNotifyTest) {
+ // Some window managers treat mapping an override-redirect window as an
+ // invitation to add a whole bunch of properties to it. This confuses the
+ // EnsureMemoryReclaimed() check since we would be waiting on property
+ // requests. Do the map/unmap testing on a child of an unmapped window so it
+ // is unviewable and the window manager won't touch it.
+ XID window_xid = CreateWindow(cache_root_);
+
+ Synchronize();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+
+ auto window = cache_->GetWindow(window_xid);
+ EXPECT_TRUE(window);
+ EXPECT_FALSE(window->is_mapped());
+
+ XMapWindow(display_, window_xid);
+ Synchronize();
+ EXPECT_TRUE(window->is_mapped());
+
+ XUnmapWindow(display_, window_xid);
+ Synchronize();
+ EXPECT_FALSE(window->is_mapped());
+}
+
+TEST_F(XWindowCacheTest, CirculateNotifyTest) {
+ std::list<XID> children_xids;
+ for (int i = 0; i < 10; i++) {
+ XID child = CreateWindow(cache_root_);
+ children_xids.push_front(child);
+ XMapWindow(display_, child);
+ }
+
+ Synchronize();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+ VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
+
+ XID child;
+ XCirculateSubwindowsUp(display_, cache_root_);
+ child = children_xids.back();
+ children_xids.pop_back();
+ children_xids.push_front(child);
+ Synchronize();
+ VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
+
+ XCirculateSubwindowsDown(display_, cache_root_);
+ child = children_xids.front();
+ children_xids.pop_front();
+ children_xids.push_back(child);
+ Synchronize();
+ VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
+}
+
+TEST_F(XWindowCacheTest, ConfigureNotifyTest) {
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+ auto window = cache_->GetWindow(cache_root_);
+ EXPECT_TRUE(window);
+
+ EXPECT_EQ(0, window->x());
+ EXPECT_EQ(0, window->y());
+ XMoveWindow(display_, cache_root_, 1, 1);
+ Synchronize();
+ EXPECT_EQ(1, window->x());
+ EXPECT_EQ(1, window->y());
+
+ EXPECT_EQ(1U, window->width());
+ EXPECT_EQ(1U, window->height());
+ XResizeWindow(display_, cache_root_, 2, 2);
+ Synchronize();
+ EXPECT_EQ(2U, window->width());
+ EXPECT_EQ(2U, window->height());
+
+ EXPECT_EQ(0U, window->border_width());
+ XSetWindowBorderWidth(display_, cache_root_, 1);
+ Synchronize();
+ EXPECT_EQ(1U, window->border_width());
+}
+
+TEST_F(XWindowCacheTest, StackingOrderTest) {
+ std::vector<XID> children_xids;
+ for (int i = 0; i < 10; i++) {
+ XID child = CreateWindow(cache_root_);
+ children_xids.push_back(child);
+ XMapWindow(display_, child);
+ }
+ std::reverse(children_xids.begin(), children_xids.end());
+
+ Synchronize();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+ VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
+
+ // XRaiseWindow
+ // Raise window 5
+ XID child = children_xids[5];
+ XRaiseWindow(display_, child);
+ children_xids.erase(children_xids.begin() + 5);
+ children_xids.insert(children_xids.begin(), child);
+ Synchronize();
+ VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
+
+ // XLowerWindow
+ // Lower window 5
+ child = children_xids[5];
+ XLowerWindow(display_, child);
+ children_xids.erase(children_xids.begin() + 5);
+ children_xids.insert(children_xids.end(), child);
+ Synchronize();
+ VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
+
+ // XRestackWindows
+ // Stack window 2 below window 6
+ XID restack_windows[2] = {children_xids[6], children_xids[2]};
+ child = children_xids[2];
+ children_xids.erase(children_xids.begin() + 2);
+ children_xids.insert(children_xids.begin() + 6, child);
+ XRestackWindows(display_, restack_windows, 2);
+ Synchronize();
+ VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
+
+ // XConfigureWindow
+ // Stack window 2 below window 6
+ XWindowChanges changes;
+ child = children_xids[2];
+ changes.sibling = children_xids[6];
+ changes.stack_mode = Below;
+ children_xids.erase(children_xids.begin() + 2);
+ children_xids.insert(children_xids.begin() + 6, child);
+ XConfigureWindow(display_, child, CWSibling | CWStackMode, &changes);
+ Synchronize();
+ VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
+}
+
+TEST_F(XWindowCacheTest, GravityNotifyTest) {
+ XID child_xid = CreateWindow(cache_root_);
+
+ XSetWindowAttributes swa;
+ swa.bit_gravity = ForgetGravity;
+ swa.win_gravity = NorthEastGravity;
+ XChangeWindowAttributes(display_, child_xid, CWBitGravity | CWWinGravity,
+ &swa);
+
+ XResizeWindow(display_, cache_root_, 100, 100);
+ XResizeWindow(display_, child_xid, 25, 25);
+ XMoveWindow(display_, child_xid, 75, 0);
+
+ Synchronize();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+
+ auto parent = cache_->GetWindow(cache_root_);
+ EXPECT_TRUE(parent);
+ EXPECT_EQ(0, parent->x());
+ EXPECT_EQ(0, parent->y());
+ EXPECT_EQ(100U, parent->width());
+ EXPECT_EQ(100U, parent->height());
+ auto child = cache_->GetWindow(child_xid);
+ EXPECT_TRUE(child);
+ EXPECT_EQ(75, child->x());
+ EXPECT_EQ(0, child->y());
+
+ XResizeWindow(display_, cache_root_, 50, 50);
+ Synchronize();
+
+ EXPECT_EQ(25, child->x());
+ EXPECT_EQ(0, child->y());
+}
+
+TEST_F(XWindowCacheTest, ReparentNotifyTest) {
+ // Start with this tree:
+ //
+ // child1 -- grandchild
+ // /
+ // parent
+ // \
+ // child2
+ //
+ XID child1_xid = CreateWindow(cache_root_);
+ XID child2_xid = CreateWindow(cache_root_);
+ XID grandchild_xid = CreateWindow(child1_xid);
+
+ Synchronize();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+ auto parent = cache_->GetWindow(cache_root_);
+ auto child1 = cache_->GetWindow(child1_xid);
+ auto child2 = cache_->GetWindow(child2_xid);
+ auto grandchild = cache_->GetWindow(grandchild_xid);
+
+ EXPECT_TRUE(parent);
+ EXPECT_TRUE(child1);
+ EXPECT_TRUE(child2);
+ EXPECT_TRUE(grandchild);
+ EXPECT_EQ(2U, parent->GetChildren().size());
+ EXPECT_EQ(1U, child1->GetChildren().size());
+ EXPECT_EQ(0U, child2->GetChildren().size());
+ EXPECT_EQ(0U, grandchild->GetChildren().size());
+ EXPECT_EQ(grandchild, *child1->GetChildren().begin());
+ EXPECT_EQ(child1, grandchild->parent());
+
+ // Reparent grandchild so the tree now looks like this:
+ //
+ // child1
+ // /
+ // parent
+ // \
+ // child2 -- grandchild
+ //
+ XReparentWindow(display_, grandchild_xid, child2_xid, 0, 0);
+ Synchronize();
+
+ EXPECT_EQ(2U, parent->GetChildren().size());
+ EXPECT_EQ(0U, child1->GetChildren().size());
+ EXPECT_EQ(1U, child2->GetChildren().size());
+ EXPECT_EQ(0U, grandchild->GetChildren().size());
+ EXPECT_EQ(grandchild, *child2->GetChildren().begin());
+ EXPECT_EQ(child2, grandchild->parent());
+}
+
+TEST_F(XWindowCacheTest, WindowAndPropertyValidationTest) {
+ std::vector<XID> window_chain;
+ XID window_xid = cache_root_;
+ for(int i = 0; i < 10; i++)
+ window_xid = CreateWindow(window_xid);
+ XResizeWindow(display_, window_xid, 2, 2);
+ SetProperty8(window_xid, testing_atom_, 0x42);
+ Synchronize();
+
+ auto window = cache_->GetWindow(window_xid);
+ DCHECK(window);
+ DCHECK_EQ(2, window->width());
+ DCHECK_EQ(2, window->height());
+
+ auto prop = window->GetProperty(testing_atom_);
+ EXPECT_TRUE(prop);
+ EXPECT_EQ(XA_CARDINAL, prop->type());
+ EXPECT_EQ(8, prop->format());
+ EXPECT_EQ(1, prop->length());
+ EXPECT_EQ(0x42, *static_cast<const uint8_t*>(prop->data()));
+}
+
+} // namespace ui
« no previous file with comments | « ui/base/x/x11_window_cache.cc ('k') | ui/events/platform/x11/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698