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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/base/x/x11_window_cache.h"
6
7 #include <poll.h>
8 #include <X11/Xlib.h>
9 #include <X11/Xatom.h>
10
11 // These macros defined by Xlib conflict with gtest
12 #undef None
13 #undef Bool
14
15 #include "base/macros.h"
16 #include "base/posix/eintr_wrapper.h"
17 #include "base/time/time.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "ui/events/platform/x11/x11_event_source.h"
20 #include "ui/gfx/x/x11_types.h"
21
22 namespace ui {
23
24 class XWindowCacheTest;
25
26 class TestX11EventSourceDelegate : public X11EventSourceDelegate {
27 public:
28 TestX11EventSourceDelegate(XWindowCacheTest* cache_test)
29 : cache_(nullptr), cache_test_(cache_test) {}
30
31 void SetCache(XWindowCache* cache) { cache_ = cache; }
32
33 protected:
34 void ProcessXEvent(XEvent* xevent) override;
35
36 private:
37 XWindowCache* cache_;
38 XWindowCacheTest* cache_test_;
39 };
40
41 class TestX11EventSource : public X11EventSource {
42 public:
43 TestX11EventSource(X11EventSourceDelegate* delegate, XDisplay* display)
44 : X11EventSource(delegate, display) {}
45
46 // Returns false iff there was a connection error or a timeout.
47 bool BlockUntilConnectionIsReadable() {
48 int conn_fd = ConnectionNumber(display_);
49
50 struct pollfd rfd;
51 rfd.fd = conn_fd;
52 rfd.events = POLLIN;
53 rfd.revents = 0;
54
55 static constexpr unsigned int timeout_ms = 3000;
56 base::TimeTicks deadline =
57 base::TimeTicks::Now() + base::TimeDelta::FromMilliseconds(timeout_ms);
58 HANDLE_EINTR(poll(
59 &rfd, 1,
60 std::max(0L, (deadline - base::TimeTicks::Now()).InMilliseconds())));
61 return rfd.revents & POLLIN;
62 }
63
64 // Ensures we have the state of the entire window tree. May block. Returns
65 // false iff there was a connection error or a timeout.
66 bool BlockUntilTreeIsCached() {
67 while (!request_queue_.empty()) {
68 if (!HasNextReply()) {
69 if (!BlockUntilConnectionIsReadable())
70 return false;
71 }
72 DispatchXEvents();
73 }
74 return true;
75 }
76
77 // Ensures we have processed all events sent server-side before Synchronize
78 // was called client-side. We may be forced to wait for more events to come
79 // that are not yet in the queue. You most likely want to
80 // BlockUntilWindowTreeIsCached before and after calling Synchronize. Blocks.
81 // Returns false iff there was a connection error or a timeout.
82 void Synchronize() {
83 XSync(display_, False);
84 DispatchXEvents();
85 }
86
87 size_t NumberOfQueuedRequests() { return request_queue_.size(); }
88
89 private:
90 };
91
92 class XWindowCacheTest : public testing::Test {
93 public:
94 XWindowCacheTest()
95 : display_(gfx::GetXDisplay()),
96 display_root_(DefaultRootWindow(display_)),
97 cache_root_(XCB_WINDOW_NONE),
98 testing_atom_(XInternAtom(display_, "CHROMIUM_TESTING_ATOM", False)),
99 delegate_(this),
100 event_source_(&delegate_, display_),
101 cache_(nullptr) {}
102 ~XWindowCacheTest() override {}
103
104 void SetUp() override {
105 cache_root_ = CreateWindow(display_root_);
106 cache_ = new XWindowCache(display_, &event_source_, cache_root_);
107 delegate_.SetCache(cache_);
108 }
109
110 void TearDown() override {
111 XDestroyWindow(display_, cache_root_);
112 cache_root_ = XCB_WINDOW_NONE;
113 EnsureMemoryReclaimed();
114 delete cache_;
115 cache_ = nullptr;
116 delegate_.SetCache(cache_);
117 }
118
119 void EnsureMemoryReclaimed() {
120 Synchronize();
121 EXPECT_TRUE(cache_->windows_.empty());
122 EXPECT_EQ(0U, event_source_.NumberOfQueuedRequests());
123 }
124
125 XID CreateWindow(XID parent) {
126 XSetWindowAttributes swa;
127 swa.override_redirect = True;
128 return XCreateWindow(display_, parent, 0, 0, 1, 1, 0, CopyFromParent,
129 InputOutput, CopyFromParent, CWOverrideRedirect, &swa);
130 }
131
132 void ProcessEvent(XWindowCache* cache, XEvent* event) {
133 cache->ProcessEvent(event);
134 }
135
136 bool BlockUntilTreeIsCached() {
137 return event_source_.BlockUntilTreeIsCached();
138 }
139
140 void Synchronize() { event_source_.Synchronize(); }
141
142 void Reset() { cache_->ResetCacheImpl(); }
143
144 void SetProperty8(XID window, XID property, uint8_t value) {
145 XChangeProperty(display_, window, property, XA_CARDINAL, 8, PropModeReplace,
146 &value, 1);
147 }
148
149 template <typename T>
150 void VerifyStackingOrder(const XWindowCache::Window* window,
151 const T& container) {
152 EXPECT_TRUE(window);
153 EXPECT_EQ(window->children.size(), container.size());
154 auto it = window->children.begin();
155 for (XID id : container)
156 EXPECT_EQ(id, (*it++)->id);
157 }
158
159 XDisplay* display_;
160 XID display_root_;
161 XID cache_root_;
162 XAtom testing_atom_;
163
164 TestX11EventSourceDelegate delegate_;
165 TestX11EventSource event_source_;
166 XWindowCache* cache_;
167
168 private:
169 DISALLOW_COPY_AND_ASSIGN(XWindowCacheTest);
170 };
171
172 void TestX11EventSourceDelegate::ProcessXEvent(XEvent* xevent) {
173 if (cache_)
174 cache_test_->ProcessEvent(cache_, xevent);
175 }
176
177 TEST_F(XWindowCacheTest, BasicTest) {
178 XID child1_xid = CreateWindow(cache_root_);
179 XID child2_xid = CreateWindow(cache_root_);
180
181 Synchronize();
182 EXPECT_TRUE(BlockUntilTreeIsCached());
183
184 auto parent_window = cache_->GetWindow(cache_root_);
185 EXPECT_TRUE(parent_window);
186 EXPECT_EQ(parent_window->children.size(), 2U);
187 auto it = parent_window->children.begin();
188 EXPECT_EQ((*it++)->id, child2_xid);
189 EXPECT_EQ((*it++)->id, child1_xid);
190 }
191
192 TEST_F(XWindowCacheTest, NestingTest) {
193 XID child_xid = CreateWindow(cache_root_);
194 XID nested_child_xid = CreateWindow(child_xid);
195 SetProperty8(nested_child_xid, testing_atom_, 0x42);
196
197 Synchronize();
198 EXPECT_TRUE(BlockUntilTreeIsCached());
199
200 auto parent_window = cache_->GetWindow(cache_root_);
201 EXPECT_TRUE(parent_window);
202 EXPECT_FALSE(parent_window->children_request);
203 EXPECT_EQ(parent_window->children.size(), 1U);
204 EXPECT_FALSE(parent_window->properties_request);
205 EXPECT_EQ(parent_window->properties.size(), 0U);
206
207 auto child_window = parent_window->children.front().get();
208 EXPECT_TRUE(child_window);
209 EXPECT_FALSE(child_window->children_request);
210 EXPECT_EQ(child_window->children.size(), 1U);
211 EXPECT_FALSE(child_window->properties_request);
212 EXPECT_EQ(child_window->properties.size(), 0U);
213
214 auto nested_child_window = child_window->children.front().get();
215 EXPECT_TRUE(nested_child_window);
216 EXPECT_FALSE(nested_child_window->children_request);
217 EXPECT_EQ(nested_child_window->children.size(), 0U);
218 EXPECT_FALSE(nested_child_window->properties_request);
219 EXPECT_EQ(nested_child_window->properties.size(), 1U);
220
221 auto prop = nested_child_window->GetProperty(testing_atom_);
222 EXPECT_TRUE(prop);
223 EXPECT_FALSE(prop->property_request);
224 EXPECT_EQ(prop->type, XA_CARDINAL);
225 EXPECT_EQ(prop->data_format, 8);
226 EXPECT_EQ(prop->data_length, 1);
227 EXPECT_EQ(prop->data.bits_8[0], 0x42);
228 }
229
230 TEST_F(XWindowCacheTest, ResetCacheTest) {
231 XID child1_xid = CreateWindow(cache_root_);
232 XID child2_xid = CreateWindow(cache_root_);
233
234 auto verify = [&]() {
235 auto parent_window = cache_->GetWindow(cache_root_);
236 EXPECT_TRUE(parent_window);
237 EXPECT_EQ(parent_window->children.size(), 2U);
238 auto it = parent_window->children.begin();
239 EXPECT_EQ((*it++)->id, child2_xid);
240 EXPECT_EQ((*it++)->id, child1_xid);
241 };
242
243 // Normal case.
244 Synchronize();
245 EXPECT_TRUE(BlockUntilTreeIsCached());
246 verify();
247
248 // Reset when tree is fully-cached.
249 Reset();
250 EXPECT_TRUE(BlockUntilTreeIsCached());
251 verify();
252
253 Reset();
254 Synchronize();
255 // Reset when tree is half-cached.
256 Reset();
257 EXPECT_TRUE(BlockUntilTreeIsCached());
258 verify();
259
260 // Multiple resets.
261 for (int i = 0; i < 5; i++)
262 Reset();
263 EXPECT_TRUE(BlockUntilTreeIsCached());
264 verify();
265 }
266
267 TEST_F(XWindowCacheTest, CreateNotifyAndDestroyNotifyTest) {
268 XID child1_xid = CreateWindow(cache_root_);
269 XID child2_xid = CreateWindow(cache_root_);
270
271 Synchronize();
272 EXPECT_TRUE(BlockUntilTreeIsCached());
273
274 auto parent_window = cache_->GetWindow(cache_root_);
275 EXPECT_TRUE(parent_window);
276 EXPECT_EQ(parent_window->children.size(), 2U);
277 auto it = parent_window->children.begin();
278 EXPECT_EQ((*it++)->id, child2_xid);
279 EXPECT_EQ((*it++)->id, child1_xid);
280
281 XDestroyWindow(display_, child1_xid);
282 XDestroyWindow(display_, child2_xid);
283
284 Synchronize();
285 EXPECT_TRUE(BlockUntilTreeIsCached());
286
287 parent_window = cache_->GetWindow(cache_root_);
288 EXPECT_TRUE(parent_window);
289 EXPECT_EQ(parent_window->children.size(), 0U);
290 }
291
292 TEST_F(XWindowCacheTest, PropertyNotifyTest) {
293 SetProperty8(cache_root_, testing_atom_, 0x42);
294
295 Synchronize();
296 EXPECT_TRUE(BlockUntilTreeIsCached());
297
298 auto window = cache_->GetWindow(cache_root_);
299 EXPECT_TRUE(window);
300 EXPECT_EQ(window->children.size(), 0U);
301 EXPECT_EQ(window->properties.size(), 1U);
302
303 auto prop = window->GetProperty(testing_atom_);
304 EXPECT_TRUE(prop);
305 EXPECT_FALSE(prop->property_request);
306 EXPECT_EQ(prop->type, XA_CARDINAL);
307 EXPECT_EQ(prop->data_format, 8);
308 EXPECT_EQ(prop->data_length, 1);
309 EXPECT_EQ(prop->data.bits_8[0], 0x42);
310
311 SetProperty8(cache_root_, testing_atom_, 0x24);
312 Synchronize();
313 EXPECT_TRUE(BlockUntilTreeIsCached());
314
315 prop = window->GetProperty(testing_atom_);
316 EXPECT_TRUE(prop);
317 EXPECT_FALSE(prop->property_request);
318 EXPECT_EQ(prop->type, XA_CARDINAL);
319 EXPECT_EQ(prop->data_format, 8);
320 EXPECT_EQ(prop->data_length, 1);
321 EXPECT_EQ(prop->data.bits_8[0], 0x24);
322 }
323
324 TEST_F(XWindowCacheTest, MapNotifyUnmapNotifyTest) {
325 // Some window managers treat mapping an override-redirect window as an
326 // invitation to add a whole bunch of properties to it. This confuses the
327 // EnsureMemoryReclaimed() check since we would be waiting on property
328 // requests. Do the map/unmap testing on a child of an unmapped window so it
329 // is unviewable and the window manager won't touch it.
330 XID window_xid = CreateWindow(cache_root_);
331
332 Synchronize();
333 EXPECT_TRUE(BlockUntilTreeIsCached());
334
335 auto window = cache_->GetWindow(window_xid);
336 EXPECT_TRUE(window);
337 EXPECT_FALSE(window->attributes_request);
338 EXPECT_FALSE(window->is_mapped);
339
340 XMapWindow(display_, window_xid);
341 Synchronize();
342 EXPECT_TRUE(window->is_mapped);
343
344 XUnmapWindow(display_, window_xid);
345 Synchronize();
346 EXPECT_FALSE(window->is_mapped);
347 }
348
349 TEST_F(XWindowCacheTest, CirculateNotifyTest) {
350 std::list<XID> children_xids;
351 for (int i = 0; i < 10; i++) {
352 XID child = CreateWindow(cache_root_);
353 children_xids.push_front(child);
354 XMapWindow(display_, child);
355 }
356
357 Synchronize();
358 EXPECT_TRUE(BlockUntilTreeIsCached());
359 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
360
361 XID child;
362 XCirculateSubwindowsUp(display_, cache_root_);
363 child = children_xids.back();
364 children_xids.pop_back();
365 children_xids.push_front(child);
366 Synchronize();
367 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
368
369 XCirculateSubwindowsDown(display_, cache_root_);
370 child = children_xids.front();
371 children_xids.pop_front();
372 children_xids.push_back(child);
373 Synchronize();
374 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
375 }
376
377 TEST_F(XWindowCacheTest, ConfigureNotifyTest) {
378 EXPECT_TRUE(BlockUntilTreeIsCached());
379 auto window = cache_->GetWindow(cache_root_);
380 EXPECT_TRUE(window);
381 EXPECT_FALSE(window->attributes_request);
382 EXPECT_FALSE(window->geometry_request);
383
384 EXPECT_EQ(window->x, 0);
385 EXPECT_EQ(window->y, 0);
386 XMoveWindow(display_, cache_root_, 1, 1);
387 Synchronize();
388 EXPECT_EQ(window->x, 1);
389 EXPECT_EQ(window->y, 1);
390
391 EXPECT_EQ(window->width, 1U);
392 EXPECT_EQ(window->height, 1U);
393 XResizeWindow(display_, cache_root_, 2, 2);
394 Synchronize();
395 EXPECT_EQ(window->width, 2U);
396 EXPECT_EQ(window->height, 2U);
397
398 EXPECT_EQ(window->border_width, 0U);
399 XSetWindowBorderWidth(display_, cache_root_, 1);
400 Synchronize();
401 EXPECT_EQ(window->border_width, 1U);
402 }
403
404 TEST_F(XWindowCacheTest, StackingOrderTest) {
405 std::vector<XID> children_xids;
406 for (int i = 0; i < 10; i++) {
407 XID child = CreateWindow(cache_root_);
408 children_xids.push_back(child);
409 XMapWindow(display_, child);
410 }
411 std::reverse(children_xids.begin(), children_xids.end());
412
413 Synchronize();
414 EXPECT_TRUE(BlockUntilTreeIsCached());
415 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
416
417 // XRaiseWindow
418 // Raise window 5
419 XID child = children_xids[5];
420 XRaiseWindow(display_, child);
421 children_xids.erase(children_xids.begin() + 5);
422 children_xids.insert(children_xids.begin(), child);
423 Synchronize();
424 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
425
426 // XLowerWindow
427 // Lower window 5
428 child = children_xids[5];
429 XLowerWindow(display_, child);
430 children_xids.erase(children_xids.begin() + 5);
431 children_xids.insert(children_xids.end(), child);
432 Synchronize();
433 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
434
435 // XRestackWindows
436 // Stack window 2 below window 6
437 XID restack_windows[2] = {children_xids[6], children_xids[2]};
438 child = children_xids[2];
439 children_xids.erase(children_xids.begin() + 2);
440 children_xids.insert(children_xids.begin() + 6, child);
441 XRestackWindows(display_, restack_windows, 2);
442 Synchronize();
443 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
444
445 // XConfigureWindow
446 // Stack window 2 below window 6
447 XWindowChanges changes;
448 child = children_xids[2];
449 changes.sibling = children_xids[6];
450 changes.stack_mode = Below;
451 children_xids.erase(children_xids.begin() + 2);
452 children_xids.insert(children_xids.begin() + 6, child);
453 XConfigureWindow(display_, child, CWSibling | CWStackMode, &changes);
454 Synchronize();
455 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
456 }
457
458 TEST_F(XWindowCacheTest, GravityNotifyTest) {
459 XID child_xid = CreateWindow(cache_root_);
460
461 XSetWindowAttributes swa;
462 swa.bit_gravity = ForgetGravity;
463 swa.win_gravity = NorthEastGravity;
464 XChangeWindowAttributes(display_, child_xid, CWBitGravity | CWWinGravity,
465 &swa);
466
467 XResizeWindow(display_, cache_root_, 100, 100);
468 XResizeWindow(display_, child_xid, 25, 25);
469 XMoveWindow(display_, child_xid, 75, 0);
470
471 Synchronize();
472 EXPECT_TRUE(BlockUntilTreeIsCached());
473
474 auto parent = cache_->GetWindow(cache_root_);
475 EXPECT_TRUE(parent);
476 EXPECT_EQ(parent->x, 0);
477 EXPECT_EQ(parent->y, 0);
478 EXPECT_EQ(parent->width, 100U);
479 EXPECT_EQ(parent->height, 100U);
480 auto child = cache_->GetWindow(child_xid);
481 EXPECT_TRUE(child);
482 EXPECT_EQ(child->x, 75);
483 EXPECT_EQ(child->y, 0);
484
485 XResizeWindow(display_, cache_root_, 50, 50);
486 Synchronize();
487
488 EXPECT_EQ(child->x, 25);
489 EXPECT_EQ(child->y, 0);
490 }
491
492 TEST_F(XWindowCacheTest, ReparentNotifyTest) {
493 // Start with this tree:
494 //
495 // child1 -- grandchild
496 // /
497 // parent
498 // \
499 // child2
500 //
501 XID child1_xid = CreateWindow(cache_root_);
502 XID child2_xid = CreateWindow(cache_root_);
503 XID grandchild_xid = CreateWindow(child1_xid);
504
505 Synchronize();
506 EXPECT_TRUE(BlockUntilTreeIsCached());
507 auto parent = cache_->GetWindow(cache_root_);
508 auto child1 = cache_->GetWindow(child1_xid);
509 auto child2 = cache_->GetWindow(child2_xid);
510 auto grandchild = cache_->GetWindow(grandchild_xid);
511
512 EXPECT_TRUE(parent);
513 EXPECT_TRUE(child1);
514 EXPECT_TRUE(child2);
515 EXPECT_TRUE(grandchild);
516 EXPECT_EQ(parent->children.size(), 2U);
517 EXPECT_EQ(child1->children.size(), 1U);
518 EXPECT_EQ(child2->children.size(), 0U);
519 EXPECT_EQ(grandchild->children.size(), 0U);
520 EXPECT_EQ(child1->children.front().get(), grandchild);
521 EXPECT_EQ(grandchild->parent, child1);
522
523 // Reparent grandchild so the tree now looks like this:
524 //
525 // child1
526 // /
527 // parent
528 // \
529 // child2 -- grandchild
530 //
531 XReparentWindow(display_, grandchild_xid, child2_xid, 0, 0);
532 Synchronize();
533
534 EXPECT_EQ(parent->children.size(), 2U);
535 EXPECT_EQ(child1->children.size(), 0U);
536 EXPECT_EQ(child2->children.size(), 1U);
537 EXPECT_EQ(grandchild->children.size(), 0U);
538 EXPECT_EQ(child2->children.front().get(), grandchild);
539 EXPECT_EQ(grandchild->parent, child2);
540 }
541
542 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698