malloc + memset -> zalloc
[platform/upstream/weston.git] / tests / subsurface-test.c
1 /*
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"
27 #include <stdio.h>
28
29 #define NUM_SUBSURFACES 3
30
31 struct 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
38 static struct wl_subcompositor *
39 get_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
66 static void
67 populate_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
85 TEST(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
100 TEST(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
117 TEST(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
135 FAIL_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
153 FAIL_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
169 FAIL_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
187 TEST(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
205 TEST(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
223 FAIL_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
245 FAIL_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
263 FAIL_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
281 FAIL_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
299 FAIL_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
317 TEST(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 }
349
350 static void
351 create_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
407 static void
408 destroy_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
422 static int
423 has_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
438 struct permu_state {
439         int set_size;
440         int cnt[NSTEPS];
441 };
442
443 static void
444 permu_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
453 static int
454 permu_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
483 static void
484 destroy_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
500 TEST(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 }