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

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 test compilation Created 4 years, 3 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
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..7449be3cf010200bd5e060b7b98cbb35cb9bf27b
--- /dev/null
+++ b/ui/base/x/x11_window_cache_unittest.cc
@@ -0,0 +1,542 @@
+// 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(window->children.size(), container.size());
+ auto it = window->children.begin();
+ for (XID id : container)
+ EXPECT_EQ(id, (*it++)->id);
+ }
+
+ 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(parent_window->children.size(), 2U);
+ auto it = parent_window->children.begin();
+ EXPECT_EQ((*it++)->id, child2_xid);
+ EXPECT_EQ((*it++)->id, child1_xid);
+}
+
+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_FALSE(parent_window->children_request);
+ EXPECT_EQ(parent_window->children.size(), 1U);
+ EXPECT_FALSE(parent_window->properties_request);
+ EXPECT_EQ(parent_window->properties.size(), 0U);
+
+ auto child_window = parent_window->children.front().get();
+ EXPECT_TRUE(child_window);
+ EXPECT_FALSE(child_window->children_request);
+ EXPECT_EQ(child_window->children.size(), 1U);
+ EXPECT_FALSE(child_window->properties_request);
+ EXPECT_EQ(child_window->properties.size(), 0U);
+
+ auto nested_child_window = child_window->children.front().get();
+ EXPECT_TRUE(nested_child_window);
+ EXPECT_FALSE(nested_child_window->children_request);
+ EXPECT_EQ(nested_child_window->children.size(), 0U);
+ EXPECT_FALSE(nested_child_window->properties_request);
+ EXPECT_EQ(nested_child_window->properties.size(), 1U);
+
+ auto prop = nested_child_window->GetProperty(testing_atom_);
+ EXPECT_TRUE(prop);
+ EXPECT_FALSE(prop->property_request);
+ EXPECT_EQ(prop->type, XA_CARDINAL);
+ EXPECT_EQ(prop->data_format, 8);
+ EXPECT_EQ(prop->data_length, 1);
+ EXPECT_EQ(prop->data.bits_8[0], 0x42);
+}
+
+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(parent_window->children.size(), 2U);
+ auto it = parent_window->children.begin();
+ EXPECT_EQ((*it++)->id, child2_xid);
+ EXPECT_EQ((*it++)->id, child1_xid);
+ };
+
+ // 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(parent_window->children.size(), 2U);
+ auto it = parent_window->children.begin();
+ EXPECT_EQ((*it++)->id, child2_xid);
+ EXPECT_EQ((*it++)->id, child1_xid);
+
+ XDestroyWindow(display_, child1_xid);
+ XDestroyWindow(display_, child2_xid);
+
+ Synchronize();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+
+ parent_window = cache_->GetWindow(cache_root_);
+ EXPECT_TRUE(parent_window);
+ EXPECT_EQ(parent_window->children.size(), 0U);
+}
+
+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(window->children.size(), 0U);
+ EXPECT_EQ(window->properties.size(), 1U);
+
+ auto prop = window->GetProperty(testing_atom_);
+ EXPECT_TRUE(prop);
+ EXPECT_FALSE(prop->property_request);
+ EXPECT_EQ(prop->type, XA_CARDINAL);
+ EXPECT_EQ(prop->data_format, 8);
+ EXPECT_EQ(prop->data_length, 1);
+ EXPECT_EQ(prop->data.bits_8[0], 0x42);
+
+ SetProperty8(cache_root_, testing_atom_, 0x24);
+ Synchronize();
+ EXPECT_TRUE(BlockUntilTreeIsCached());
+
+ prop = window->GetProperty(testing_atom_);
+ EXPECT_TRUE(prop);
+ EXPECT_FALSE(prop->property_request);
+ EXPECT_EQ(prop->type, XA_CARDINAL);
+ EXPECT_EQ(prop->data_format, 8);
+ EXPECT_EQ(prop->data_length, 1);
+ EXPECT_EQ(prop->data.bits_8[0], 0x24);
+}
+
+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->attributes_request);
+ 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_FALSE(window->attributes_request);
+ EXPECT_FALSE(window->geometry_request);
+
+ EXPECT_EQ(window->x, 0);
+ EXPECT_EQ(window->y, 0);
+ XMoveWindow(display_, cache_root_, 1, 1);
+ Synchronize();
+ EXPECT_EQ(window->x, 1);
+ EXPECT_EQ(window->y, 1);
+
+ EXPECT_EQ(window->width, 1U);
+ EXPECT_EQ(window->height, 1U);
+ XResizeWindow(display_, cache_root_, 2, 2);
+ Synchronize();
+ EXPECT_EQ(window->width, 2U);
+ EXPECT_EQ(window->height, 2U);
+
+ EXPECT_EQ(window->border_width, 0U);
+ XSetWindowBorderWidth(display_, cache_root_, 1);
+ Synchronize();
+ EXPECT_EQ(window->border_width, 1U);
+}
+
+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(parent->x, 0);
+ EXPECT_EQ(parent->y, 0);
+ EXPECT_EQ(parent->width, 100U);
+ EXPECT_EQ(parent->height, 100U);
+ auto child = cache_->GetWindow(child_xid);
+ EXPECT_TRUE(child);
+ EXPECT_EQ(child->x, 75);
+ EXPECT_EQ(child->y, 0);
+
+ XResizeWindow(display_, cache_root_, 50, 50);
+ Synchronize();
+
+ EXPECT_EQ(child->x, 25);
+ EXPECT_EQ(child->y, 0);
+}
+
+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(parent->children.size(), 2U);
+ EXPECT_EQ(child1->children.size(), 1U);
+ EXPECT_EQ(child2->children.size(), 0U);
+ EXPECT_EQ(grandchild->children.size(), 0U);
+ EXPECT_EQ(child1->children.front().get(), grandchild);
+ EXPECT_EQ(grandchild->parent, child1);
+
+ // Reparent grandchild so the tree now looks like this:
+ //
+ // child1
+ // /
+ // parent
+ // \
+ // child2 -- grandchild
+ //
+ XReparentWindow(display_, grandchild_xid, child2_xid, 0, 0);
+ Synchronize();
+
+ EXPECT_EQ(parent->children.size(), 2U);
+ EXPECT_EQ(child1->children.size(), 0U);
+ EXPECT_EQ(child2->children.size(), 1U);
+ EXPECT_EQ(grandchild->children.size(), 0U);
+ EXPECT_EQ(child2->children.front().get(), grandchild);
+ EXPECT_EQ(grandchild->parent, child2);
+}
+
+} // namespace ui

Powered by Google App Engine
This is Rietveld 408576698