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