blob: 98e00fea8092a214b406ee1f974156dfccfd6663 [file] [log] [blame]
Pekka Paalanene844bf22013-04-25 13:57:43 +03001/*
2 * Copyright © 2012 Collabora, Ltd.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#include <string.h>
24
25#include "weston-test-client-helper.h"
26#include "subsurface-client-protocol.h"
Pekka Paalanenbeb080e2013-05-17 16:46:04 +030027#include <stdio.h>
Pekka Paalanene844bf22013-04-25 13:57:43 +030028
29#define NUM_SUBSURFACES 3
30
31struct compound_surface {
32 struct wl_subcompositor *subco;
33 struct wl_surface *parent;
34 struct wl_surface *child[NUM_SUBSURFACES];
35 struct wl_subsurface *sub[NUM_SUBSURFACES];
36};
37
38static struct wl_subcompositor *
39get_subcompositor(struct client *client)
40{
41 struct global *g;
42 struct global *global_sub = NULL;
43 struct wl_subcompositor *sub;
44
45 wl_list_for_each(g, &client->global_list, link) {
46 if (strcmp(g->interface, "wl_subcompositor"))
47 continue;
48
49 if (global_sub)
50 assert(0 && "multiple wl_subcompositor objects");
51
52 global_sub = g;
53 }
54
55 assert(global_sub && "no wl_subcompositor found");
56
57 assert(global_sub->version == 1);
58
59 sub = wl_registry_bind(client->wl_registry, global_sub->name,
60 &wl_subcompositor_interface, 1);
61 assert(sub);
62
63 return sub;
64}
65
66static void
67populate_compound_surface(struct compound_surface *com, struct client *client)
68{
69 int i;
70
71 com->subco = get_subcompositor(client);
72
73 com->parent = wl_compositor_create_surface(client->wl_compositor);
74
75 for (i = 0; i < NUM_SUBSURFACES; i++) {
76 com->child[i] =
77 wl_compositor_create_surface(client->wl_compositor);
78 com->sub[i] =
79 wl_subcompositor_get_subsurface(com->subco,
80 com->child[i],
81 com->parent);
82 }
83}
84
85TEST(test_subsurface_basic_protocol)
86{
87 struct client *client;
88 struct compound_surface com1;
89 struct compound_surface com2;
90
91 client = client_create(100, 50, 123, 77);
92 assert(client);
93
94 populate_compound_surface(&com1, client);
95 populate_compound_surface(&com2, client);
96
97 client_roundtrip(client);
98}
99
100TEST(test_subsurface_position_protocol)
101{
102 struct client *client;
103 struct compound_surface com;
104 int i;
105
106 client = client_create(100, 50, 123, 77);
107 assert(client);
108
109 populate_compound_surface(&com, client);
110 for (i = 0; i < NUM_SUBSURFACES; i++)
111 wl_subsurface_set_position(com.sub[i],
112 (i + 2) * 20, (i + 2) * 10);
113
114 client_roundtrip(client);
115}
116
117TEST(test_subsurface_placement_protocol)
118{
119 struct client *client;
120 struct compound_surface com;
121
122 client = client_create(100, 50, 123, 77);
123 assert(client);
124
125 populate_compound_surface(&com, client);
126
127 wl_subsurface_place_above(com.sub[0], com.child[1]);
128 wl_subsurface_place_above(com.sub[1], com.parent);
129 wl_subsurface_place_below(com.sub[2], com.child[0]);
130 wl_subsurface_place_below(com.sub[1], com.parent);
131
132 client_roundtrip(client);
133}
134
135FAIL_TEST(test_subsurface_paradox)
136{
137 struct client *client;
138 struct wl_surface *parent;
139 struct wl_subcompositor *subco;
140
141 client = client_create(100, 50, 123, 77);
142 assert(client);
143
144 subco = get_subcompositor(client);
145 parent = wl_compositor_create_surface(client->wl_compositor);
146
147 /* surface is its own parent */
148 wl_subcompositor_get_subsurface(subco, parent, parent);
149
150 client_roundtrip(client);
151}
152
153FAIL_TEST(test_subsurface_identical_link)
154{
155 struct client *client;
156 struct compound_surface com;
157
158 client = client_create(100, 50, 123, 77);
159 assert(client);
160
161 populate_compound_surface(&com, client);
162
163 /* surface is already a subsurface */
164 wl_subcompositor_get_subsurface(com.subco, com.child[0], com.parent);
165
166 client_roundtrip(client);
167}
168
169FAIL_TEST(test_subsurface_change_link)
170{
171 struct client *client;
172 struct compound_surface com;
173 struct wl_surface *stranger;
174
175 client = client_create(100, 50, 123, 77);
176 assert(client);
177
178 stranger = wl_compositor_create_surface(client->wl_compositor);
179 populate_compound_surface(&com, client);
180
181 /* surface is already a subsurface */
182 wl_subcompositor_get_subsurface(com.subco, com.child[0], stranger);
183
184 client_roundtrip(client);
185}
186
187TEST(test_subsurface_nesting)
188{
189 struct client *client;
190 struct compound_surface com;
191 struct wl_surface *stranger;
192
193 client = client_create(100, 50, 123, 77);
194 assert(client);
195
196 stranger = wl_compositor_create_surface(client->wl_compositor);
197 populate_compound_surface(&com, client);
198
199 /* parent is a sub-surface */
200 wl_subcompositor_get_subsurface(com.subco, stranger, com.child[0]);
201
202 client_roundtrip(client);
203}
204
205TEST(test_subsurface_nesting_parent)
206{
207 struct client *client;
208 struct compound_surface com;
209 struct wl_surface *stranger;
210
211 client = client_create(100, 50, 123, 77);
212 assert(client);
213
214 stranger = wl_compositor_create_surface(client->wl_compositor);
215 populate_compound_surface(&com, client);
216
217 /* surface is already a parent */
218 wl_subcompositor_get_subsurface(com.subco, com.parent, stranger);
219
220 client_roundtrip(client);
221}
222
Pekka Paalanen5e9bedb2013-05-17 16:46:06 +0300223FAIL_TEST(test_subsurface_loop_paradox)
224{
225 struct client *client;
226 struct wl_surface *surface[3];
227 struct wl_subcompositor *subco;
228
229 client = client_create(100, 50, 123, 77);
230 assert(client);
231
232 subco = get_subcompositor(client);
233 surface[0] = wl_compositor_create_surface(client->wl_compositor);
234 surface[1] = wl_compositor_create_surface(client->wl_compositor);
235 surface[2] = wl_compositor_create_surface(client->wl_compositor);
236
237 /* create a nesting loop */
238 wl_subcompositor_get_subsurface(subco, surface[1], surface[0]);
239 wl_subcompositor_get_subsurface(subco, surface[2], surface[1]);
240 wl_subcompositor_get_subsurface(subco, surface[0], surface[2]);
241
242 client_roundtrip(client);
243}
244
Pekka Paalanene844bf22013-04-25 13:57:43 +0300245FAIL_TEST(test_subsurface_place_above_stranger)
246{
247 struct client *client;
248 struct compound_surface com;
249 struct wl_surface *stranger;
250
251 client = client_create(100, 50, 123, 77);
252 assert(client);
253
254 stranger = wl_compositor_create_surface(client->wl_compositor);
255 populate_compound_surface(&com, client);
256
257 /* bad sibling */
258 wl_subsurface_place_above(com.sub[0], stranger);
259
260 client_roundtrip(client);
261}
262
263FAIL_TEST(test_subsurface_place_below_stranger)
264{
265 struct client *client;
266 struct compound_surface com;
267 struct wl_surface *stranger;
268
269 client = client_create(100, 50, 123, 77);
270 assert(client);
271
272 stranger = wl_compositor_create_surface(client->wl_compositor);
273 populate_compound_surface(&com, client);
274
275 /* bad sibling */
276 wl_subsurface_place_below(com.sub[0], stranger);
277
278 client_roundtrip(client);
279}
280
281FAIL_TEST(test_subsurface_place_above_foreign)
282{
283 struct client *client;
284 struct compound_surface com1;
285 struct compound_surface com2;
286
287 client = client_create(100, 50, 123, 77);
288 assert(client);
289
290 populate_compound_surface(&com1, client);
291 populate_compound_surface(&com2, client);
292
293 /* bad sibling */
294 wl_subsurface_place_above(com1.sub[0], com2.child[0]);
295
296 client_roundtrip(client);
297}
298
299FAIL_TEST(test_subsurface_place_below_foreign)
300{
301 struct client *client;
302 struct compound_surface com1;
303 struct compound_surface com2;
304
305 client = client_create(100, 50, 123, 77);
306 assert(client);
307
308 populate_compound_surface(&com1, client);
309 populate_compound_surface(&com2, client);
310
311 /* bad sibling */
312 wl_subsurface_place_below(com1.sub[0], com2.child[0]);
313
314 client_roundtrip(client);
315}
316
317TEST(test_subsurface_destroy_protocol)
318{
319 struct client *client;
320 struct compound_surface com;
321
322 client = client_create(100, 50, 123, 77);
323 assert(client);
324
325 populate_compound_surface(&com, client);
326
327 /* not needed anymore */
328 wl_subcompositor_destroy(com.subco);
329
330 /* detach child from parent */
331 wl_subsurface_destroy(com.sub[0]);
332
333 /* destroy: child, parent */
334 wl_surface_destroy(com.child[1]);
335 wl_surface_destroy(com.parent);
336
337 /* destroy: parent, child */
338 wl_surface_destroy(com.child[2]);
339
340 /* destroy: sub, child */
341 wl_surface_destroy(com.child[0]);
342
343 /* 2x destroy: child, sub */
344 wl_subsurface_destroy(com.sub[2]);
345 wl_subsurface_destroy(com.sub[1]);
346
347 client_roundtrip(client);
348}
Pekka Paalanenbeb080e2013-05-17 16:46:04 +0300349
350static void
351create_subsurface_tree(struct client *client, struct wl_surface **surfs,
352 struct wl_subsurface **subs, int n)
353{
354 struct wl_subcompositor *subco;
355 int i;
356
357 subco = get_subcompositor(client);
358
359 for (i = 0; i < n; i++)
360 surfs[i] = wl_compositor_create_surface(client->wl_compositor);
361
362 /*
363 * The tree of sub-surfaces:
364 * 0
365 * / \
366 * 1 2 - 10
367 * / \ |\
368 * 3 5 9 6
369 * / / \
370 * 4 7 8
371 * Surface 0 has no wl_subsurface, others do.
372 */
373
374 switch (n) {
375 default:
376 assert(0);
377 break;
378
379#define SUB_LINK(s,p) \
380 subs[s] = wl_subcompositor_get_subsurface(subco, surfs[s], surfs[p])
381
382 case 11:
383 SUB_LINK(10, 2);
384 case 10:
385 SUB_LINK(9, 2);
386 case 9:
387 SUB_LINK(8, 6);
388 case 8:
389 SUB_LINK(7, 6);
390 case 7:
391 SUB_LINK(6, 2);
392 case 6:
393 SUB_LINK(5, 1);
394 case 5:
395 SUB_LINK(4, 3);
396 case 4:
397 SUB_LINK(3, 1);
398 case 3:
399 SUB_LINK(2, 0);
400 case 2:
401 SUB_LINK(1, 0);
402
403#undef SUB_LINK
404 };
405}
406
407static void
408destroy_subsurface_tree(struct wl_surface **surfs,
409 struct wl_subsurface **subs, int n)
410{
411 int i;
412
413 for (i = n; i-- > 0; ) {
414 if (surfs[i])
415 wl_surface_destroy(surfs[i]);
416
417 if (subs[i])
418 wl_subsurface_destroy(subs[i]);
419 }
420}
421
422static int
423has_dupe(int *cnt, int n)
424{
425 int i;
426
427 for (i = 0; i < n; i++)
428 if (cnt[i] == cnt[n])
429 return 1;
430
431 return 0;
432}
433
434/* Note: number of permutations to test is: set_size! / (set_size - NSTEPS)!
435 */
436#define NSTEPS 3
437
438struct permu_state {
439 int set_size;
440 int cnt[NSTEPS];
441};
442
443static void
444permu_init(struct permu_state *s, int set_size)
445{
446 int i;
447
448 s->set_size = set_size;
449 for (i = 0; i < NSTEPS; i++)
450 s->cnt[i] = 0;
451}
452
453static int
454permu_next(struct permu_state *s)
455{
456 int k;
457
458 s->cnt[NSTEPS - 1]++;
459
460 while (1) {
461 if (s->cnt[0] >= s->set_size) {
462 return -1;
463 }
464
465 for (k = 1; k < NSTEPS; k++) {
466 if (s->cnt[k] >= s->set_size) {
467 s->cnt[k - 1]++;
468 s->cnt[k] = 0;
469 break;
470 }
471
472 if (has_dupe(s->cnt, k)) {
473 s->cnt[k]++;
474 break;
475 }
476 }
477
478 if (k == NSTEPS)
479 return 0;
480 }
481}
482
483static void
484destroy_permu_object(struct wl_surface **surfs,
485 struct wl_subsurface **subs, int i)
486{
487 int h = (i + 1) / 2;
488
489 if (i & 1) {
490 fprintf(stderr, " [sub %2d]", h);
491 wl_subsurface_destroy(subs[h]);
492 subs[h] = NULL;
493 } else {
494 fprintf(stderr, " [surf %2d]", h);
495 wl_surface_destroy(surfs[h]);
496 surfs[h] = NULL;
497 }
498}
499
500TEST(test_subsurface_destroy_permutations)
501{
502 /*
503 * Test wl_surface and wl_subsurface destruction orders in a
504 * complex tree of sub-surfaces.
505 *
506 * In the tree of sub-surfaces, go through every possible
507 * permutation of destroying all wl_surface and wl_subsurface
508 * objects. Execpt, to limit running time to a reasonable level,
509 * execute only the first NSTEPS destructions from each
510 * permutation, and ignore identical cases.
511 */
512
513 const int test_size = 11;
514 struct client *client;
515 struct wl_surface *surfs[test_size];
516 struct wl_subsurface *subs[test_size];
517 struct permu_state per;
518 int counter = 0;
519 int i;
520
521 client = client_create(100, 50, 123, 77);
522 assert(client);
523
524 permu_init(&per, test_size * 2 - 1);
525 while (permu_next(&per) != -1) {
526 /* for each permutation of NSTEPS out of test_size */
527 memset(surfs, 0, sizeof surfs);
528 memset(subs, 0, sizeof subs);
529
530 create_subsurface_tree(client, surfs, subs, test_size);
531
532 fprintf(stderr, "permu");
533
534 for (i = 0; i < NSTEPS; i++)
535 fprintf(stderr, " %2d", per.cnt[i]);
536
537 for (i = 0; i < NSTEPS; i++)
538 destroy_permu_object(surfs, subs, per.cnt[i]);
539
540 fprintf(stderr, "\n");
541 client_roundtrip(client);
542
543 destroy_subsurface_tree(surfs, subs, test_size);
544 counter++;
545 }
546
547 client_roundtrip(client);
548 fprintf(stderr, "tried %d destroy permutations\n", counter);
549}