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

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 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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(container.size(), window->GetChildren().size());
154 auto it = window->GetChildren().begin();
155 for (XID id : container) {
156 EXPECT_EQ(id, (*it)->id());
157 ++it;
158 }
159 }
160
161 XDisplay* display_;
162 XID display_root_;
163 XID cache_root_;
164 XAtom testing_atom_;
165
166 TestX11EventSourceDelegate delegate_;
167 TestX11EventSource event_source_;
168 XWindowCache* cache_;
169
170 private:
171 DISALLOW_COPY_AND_ASSIGN(XWindowCacheTest);
172 };
173
174 void TestX11EventSourceDelegate::ProcessXEvent(XEvent* xevent) {
175 if (cache_)
176 cache_test_->ProcessEvent(cache_, xevent);
177 }
178
179 TEST_F(XWindowCacheTest, BasicTest) {
180 XID child1_xid = CreateWindow(cache_root_);
181 XID child2_xid = CreateWindow(cache_root_);
182
183 Synchronize();
184 EXPECT_TRUE(BlockUntilTreeIsCached());
185
186 auto parent_window = cache_->GetWindow(cache_root_);
187
188 EXPECT_TRUE(parent_window);
189 EXPECT_EQ(2U, parent_window->GetChildren().size());
190 auto it = parent_window->GetChildren().begin();
191 EXPECT_EQ(child2_xid, (*it)->id());
192 EXPECT_EQ(child1_xid, (*++it)->id());
193 }
194
195 TEST_F(XWindowCacheTest, NestingTest) {
196 XID child_xid = CreateWindow(cache_root_);
197 XID nested_child_xid = CreateWindow(child_xid);
198 SetProperty8(nested_child_xid, testing_atom_, 0x42);
199
200 Synchronize();
201 EXPECT_TRUE(BlockUntilTreeIsCached());
202
203 auto parent_window = cache_->GetWindow(cache_root_);
204 EXPECT_TRUE(parent_window);
205 EXPECT_EQ(1U, parent_window->GetChildren().size());
206
207 auto child_window = *parent_window->GetChildren().begin();
208 EXPECT_TRUE(child_window);
209 EXPECT_EQ(1U, child_window->GetChildren().size());
210
211 auto nested_child_window = *child_window->GetChildren().begin();
212 EXPECT_TRUE(nested_child_window);
213 EXPECT_EQ(0U, nested_child_window->GetChildren().size());
214
215 auto prop = nested_child_window->GetProperty(testing_atom_);
216 EXPECT_TRUE(prop);
217 EXPECT_EQ(XA_CARDINAL, prop->type());
218 EXPECT_EQ(8, prop->format());
219 EXPECT_EQ(1, prop->length());
220 EXPECT_EQ(0x42, *static_cast<const uint8_t*>(prop->data()));
221 }
222
223 TEST_F(XWindowCacheTest, ResetCacheTest) {
224 XID child1_xid = CreateWindow(cache_root_);
225 XID child2_xid = CreateWindow(cache_root_);
226
227 auto verify = [&]() {
228 auto parent_window = cache_->GetWindow(cache_root_);
229 EXPECT_TRUE(parent_window);
230 EXPECT_EQ(2U, parent_window->GetChildren().size());
231 auto it = parent_window->GetChildren().begin();
232 EXPECT_EQ(child2_xid, (*it)->id());
233 EXPECT_EQ(child1_xid, (*++it)->id());
234 };
235
236 // Normal case.
237 Synchronize();
238 EXPECT_TRUE(BlockUntilTreeIsCached());
239 verify();
240
241 // Reset when tree is fully-cached.
242 Reset();
243 EXPECT_TRUE(BlockUntilTreeIsCached());
244 verify();
245
246 Reset();
247 Synchronize();
248 // Reset when tree is half-cached.
249 Reset();
250 EXPECT_TRUE(BlockUntilTreeIsCached());
251 verify();
252
253 // Multiple resets.
254 for (int i = 0; i < 5; i++)
255 Reset();
256 EXPECT_TRUE(BlockUntilTreeIsCached());
257 verify();
258 }
259
260 TEST_F(XWindowCacheTest, CreateNotifyAndDestroyNotifyTest) {
261 XID child1_xid = CreateWindow(cache_root_);
262 XID child2_xid = CreateWindow(cache_root_);
263
264 Synchronize();
265 EXPECT_TRUE(BlockUntilTreeIsCached());
266
267 auto parent_window = cache_->GetWindow(cache_root_);
268 EXPECT_TRUE(parent_window);
269 EXPECT_EQ(2U, parent_window->GetChildren().size());
270 auto it = parent_window->GetChildren().begin();
271 EXPECT_EQ(child2_xid, (*it)->id());
272 EXPECT_EQ(child1_xid, (*++it)->id());
273
274 XDestroyWindow(display_, child1_xid);
275 XDestroyWindow(display_, child2_xid);
276
277 Synchronize();
278 EXPECT_TRUE(BlockUntilTreeIsCached());
279
280 parent_window = cache_->GetWindow(cache_root_);
281 EXPECT_TRUE(parent_window);
282 EXPECT_EQ(0U, parent_window->GetChildren().size());
283 }
284
285 TEST_F(XWindowCacheTest, PropertyNotifyTest) {
286 SetProperty8(cache_root_, testing_atom_, 0x42);
287
288 Synchronize();
289 EXPECT_TRUE(BlockUntilTreeIsCached());
290
291 auto window = cache_->GetWindow(cache_root_);
292 EXPECT_TRUE(window);
293 EXPECT_EQ(0U, window->GetChildren().size());
294
295 auto prop = window->GetProperty(testing_atom_);
296 EXPECT_TRUE(prop);
297 EXPECT_EQ(XA_CARDINAL, prop->type());
298 EXPECT_EQ(8, prop->format());
299 EXPECT_EQ(1, prop->length());
300 EXPECT_EQ(0x42, *static_cast<const uint8_t*>(prop->data()));
301
302 SetProperty8(cache_root_, testing_atom_, 0x24);
303 Synchronize();
304 EXPECT_TRUE(BlockUntilTreeIsCached());
305
306 prop = window->GetProperty(testing_atom_);
307 EXPECT_TRUE(prop);
308 EXPECT_EQ(XA_CARDINAL, prop->type());
309 EXPECT_EQ(8, prop->format());
310 EXPECT_EQ(1, prop->length());
311 EXPECT_EQ(0x24, *static_cast<const uint8_t*>(prop->data()));
312 }
313
314 TEST_F(XWindowCacheTest, MapNotifyUnmapNotifyTest) {
315 // Some window managers treat mapping an override-redirect window as an
316 // invitation to add a whole bunch of properties to it. This confuses the
317 // EnsureMemoryReclaimed() check since we would be waiting on property
318 // requests. Do the map/unmap testing on a child of an unmapped window so it
319 // is unviewable and the window manager won't touch it.
320 XID window_xid = CreateWindow(cache_root_);
321
322 Synchronize();
323 EXPECT_TRUE(BlockUntilTreeIsCached());
324
325 auto window = cache_->GetWindow(window_xid);
326 EXPECT_TRUE(window);
327 EXPECT_FALSE(window->is_mapped());
328
329 XMapWindow(display_, window_xid);
330 Synchronize();
331 EXPECT_TRUE(window->is_mapped());
332
333 XUnmapWindow(display_, window_xid);
334 Synchronize();
335 EXPECT_FALSE(window->is_mapped());
336 }
337
338 TEST_F(XWindowCacheTest, CirculateNotifyTest) {
339 std::list<XID> children_xids;
340 for (int i = 0; i < 10; i++) {
341 XID child = CreateWindow(cache_root_);
342 children_xids.push_front(child);
343 XMapWindow(display_, child);
344 }
345
346 Synchronize();
347 EXPECT_TRUE(BlockUntilTreeIsCached());
348 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
349
350 XID child;
351 XCirculateSubwindowsUp(display_, cache_root_);
352 child = children_xids.back();
353 children_xids.pop_back();
354 children_xids.push_front(child);
355 Synchronize();
356 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
357
358 XCirculateSubwindowsDown(display_, cache_root_);
359 child = children_xids.front();
360 children_xids.pop_front();
361 children_xids.push_back(child);
362 Synchronize();
363 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
364 }
365
366 TEST_F(XWindowCacheTest, ConfigureNotifyTest) {
367 EXPECT_TRUE(BlockUntilTreeIsCached());
368 auto window = cache_->GetWindow(cache_root_);
369 EXPECT_TRUE(window);
370
371 EXPECT_EQ(0, window->x());
372 EXPECT_EQ(0, window->y());
373 XMoveWindow(display_, cache_root_, 1, 1);
374 Synchronize();
375 EXPECT_EQ(1, window->x());
376 EXPECT_EQ(1, window->y());
377
378 EXPECT_EQ(1U, window->width());
379 EXPECT_EQ(1U, window->height());
380 XResizeWindow(display_, cache_root_, 2, 2);
381 Synchronize();
382 EXPECT_EQ(2U, window->width());
383 EXPECT_EQ(2U, window->height());
384
385 EXPECT_EQ(0U, window->border_width());
386 XSetWindowBorderWidth(display_, cache_root_, 1);
387 Synchronize();
388 EXPECT_EQ(1U, window->border_width());
389 }
390
391 TEST_F(XWindowCacheTest, StackingOrderTest) {
392 std::vector<XID> children_xids;
393 for (int i = 0; i < 10; i++) {
394 XID child = CreateWindow(cache_root_);
395 children_xids.push_back(child);
396 XMapWindow(display_, child);
397 }
398 std::reverse(children_xids.begin(), children_xids.end());
399
400 Synchronize();
401 EXPECT_TRUE(BlockUntilTreeIsCached());
402 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
403
404 // XRaiseWindow
405 // Raise window 5
406 XID child = children_xids[5];
407 XRaiseWindow(display_, child);
408 children_xids.erase(children_xids.begin() + 5);
409 children_xids.insert(children_xids.begin(), child);
410 Synchronize();
411 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
412
413 // XLowerWindow
414 // Lower window 5
415 child = children_xids[5];
416 XLowerWindow(display_, child);
417 children_xids.erase(children_xids.begin() + 5);
418 children_xids.insert(children_xids.end(), child);
419 Synchronize();
420 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
421
422 // XRestackWindows
423 // Stack window 2 below window 6
424 XID restack_windows[2] = {children_xids[6], children_xids[2]};
425 child = children_xids[2];
426 children_xids.erase(children_xids.begin() + 2);
427 children_xids.insert(children_xids.begin() + 6, child);
428 XRestackWindows(display_, restack_windows, 2);
429 Synchronize();
430 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
431
432 // XConfigureWindow
433 // Stack window 2 below window 6
434 XWindowChanges changes;
435 child = children_xids[2];
436 changes.sibling = children_xids[6];
437 changes.stack_mode = Below;
438 children_xids.erase(children_xids.begin() + 2);
439 children_xids.insert(children_xids.begin() + 6, child);
440 XConfigureWindow(display_, child, CWSibling | CWStackMode, &changes);
441 Synchronize();
442 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids);
443 }
444
445 TEST_F(XWindowCacheTest, GravityNotifyTest) {
446 XID child_xid = CreateWindow(cache_root_);
447
448 XSetWindowAttributes swa;
449 swa.bit_gravity = ForgetGravity;
450 swa.win_gravity = NorthEastGravity;
451 XChangeWindowAttributes(display_, child_xid, CWBitGravity | CWWinGravity,
452 &swa);
453
454 XResizeWindow(display_, cache_root_, 100, 100);
455 XResizeWindow(display_, child_xid, 25, 25);
456 XMoveWindow(display_, child_xid, 75, 0);
457
458 Synchronize();
459 EXPECT_TRUE(BlockUntilTreeIsCached());
460
461 auto parent = cache_->GetWindow(cache_root_);
462 EXPECT_TRUE(parent);
463 EXPECT_EQ(0, parent->x());
464 EXPECT_EQ(0, parent->y());
465 EXPECT_EQ(100U, parent->width());
466 EXPECT_EQ(100U, parent->height());
467 auto child = cache_->GetWindow(child_xid);
468 EXPECT_TRUE(child);
469 EXPECT_EQ(75, child->x());
470 EXPECT_EQ(0, child->y());
471
472 XResizeWindow(display_, cache_root_, 50, 50);
473 Synchronize();
474
475 EXPECT_EQ(25, child->x());
476 EXPECT_EQ(0, child->y());
477 }
478
479 TEST_F(XWindowCacheTest, ReparentNotifyTest) {
480 // Start with this tree:
481 //
482 // child1 -- grandchild
483 // /
484 // parent
485 // \
486 // child2
487 //
488 XID child1_xid = CreateWindow(cache_root_);
489 XID child2_xid = CreateWindow(cache_root_);
490 XID grandchild_xid = CreateWindow(child1_xid);
491
492 Synchronize();
493 EXPECT_TRUE(BlockUntilTreeIsCached());
494 auto parent = cache_->GetWindow(cache_root_);
495 auto child1 = cache_->GetWindow(child1_xid);
496 auto child2 = cache_->GetWindow(child2_xid);
497 auto grandchild = cache_->GetWindow(grandchild_xid);
498
499 EXPECT_TRUE(parent);
500 EXPECT_TRUE(child1);
501 EXPECT_TRUE(child2);
502 EXPECT_TRUE(grandchild);
503 EXPECT_EQ(2U, parent->GetChildren().size());
504 EXPECT_EQ(1U, child1->GetChildren().size());
505 EXPECT_EQ(0U, child2->GetChildren().size());
506 EXPECT_EQ(0U, grandchild->GetChildren().size());
507 EXPECT_EQ(grandchild, *child1->GetChildren().begin());
508 EXPECT_EQ(child1, grandchild->parent());
509
510 // Reparent grandchild so the tree now looks like this:
511 //
512 // child1
513 // /
514 // parent
515 // \
516 // child2 -- grandchild
517 //
518 XReparentWindow(display_, grandchild_xid, child2_xid, 0, 0);
519 Synchronize();
520
521 EXPECT_EQ(2U, parent->GetChildren().size());
522 EXPECT_EQ(0U, child1->GetChildren().size());
523 EXPECT_EQ(1U, child2->GetChildren().size());
524 EXPECT_EQ(0U, grandchild->GetChildren().size());
525 EXPECT_EQ(grandchild, *child2->GetChildren().begin());
526 EXPECT_EQ(child2, grandchild->parent());
527 }
528
529 TEST_F(XWindowCacheTest, WindowAndPropertyValidationTest) {
530 std::vector<XID> window_chain;
531 XID window_xid = cache_root_;
532 for(int i = 0; i < 10; i++)
533 window_xid = CreateWindow(window_xid);
534 XResizeWindow(display_, window_xid, 2, 2);
535 SetProperty8(window_xid, testing_atom_, 0x42);
536 Synchronize();
537
538 auto window = cache_->GetWindow(window_xid);
539 DCHECK(window);
540 DCHECK_EQ(2, window->width());
541 DCHECK_EQ(2, window->height());
542
543 auto prop = window->GetProperty(testing_atom_);
544 EXPECT_TRUE(prop);
545 EXPECT_EQ(XA_CARDINAL, prop->type());
546 EXPECT_EQ(8, prop->format());
547 EXPECT_EQ(1, prop->length());
548 EXPECT_EQ(0x42, *static_cast<const uint8_t*>(prop->data()));
549 }
550
551 } // namespace ui
OLDNEW
« 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