fix weston launch error
[platform/upstream/weston.git] / tests / viewporter-test.c
1 /*
2  * Copyright © 2014, 2016 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 <stdlib.h>
30 #include <string.h>
31 #include <assert.h>
32 #include <time.h>
33
34 #include "shared/helpers.h"
35 #include "shared/xalloc.h"
36 #include "weston-test-client-helper.h"
37 #include "viewporter-client-protocol.h"
38
39 static struct wp_viewporter *
40 get_viewporter(struct client *client)
41 {
42         struct global *g;
43         struct global *global_wpr = NULL;
44         static struct wp_viewporter *wpr;
45
46         if (wpr)
47                 return wpr;
48
49         wl_list_for_each(g, &client->global_list, link) {
50                 if (strcmp(g->interface, wp_viewporter_interface.name))
51                         continue;
52
53                 if (global_wpr)
54                         assert(0 && "multiple wp_viewporter objects");
55
56                 global_wpr = g;
57         }
58
59         assert(global_wpr && "no wp_viewporter found");
60
61         assert(global_wpr->version == 1);
62
63         wpr = wl_registry_bind(client->wl_registry, global_wpr->name,
64                                &wp_viewporter_interface, 1);
65         assert(wpr);
66
67         return wpr;
68 }
69
70 static struct wp_viewport *
71 create_viewport(struct client *client)
72 {
73         struct wp_viewporter *viewporter;
74         struct wp_viewport *viewport;
75
76         viewporter = get_viewporter(client);
77         viewport = wp_viewporter_get_viewport(viewporter,
78                                               client->surface->wl_surface);
79         assert(viewport);
80
81         return viewport;
82 }
83
84 static void
85 set_source(struct wp_viewport *vp, int x, int y, int w, int h)
86 {
87         wp_viewport_set_source(vp, wl_fixed_from_int(x), wl_fixed_from_int(y),
88                                    wl_fixed_from_int(w), wl_fixed_from_int(h));
89 }
90
91 TEST(test_viewporter_double_create)
92 {
93         struct wp_viewporter *viewporter;
94         struct client *client;
95
96         client = create_client_and_test_surface(100, 50, 123, 77);
97
98         viewporter = get_viewporter(client);
99         wp_viewporter_get_viewport(viewporter, client->surface->wl_surface);
100         wp_viewporter_get_viewport(viewporter, client->surface->wl_surface);
101
102         expect_protocol_error(client, &wp_viewporter_interface,
103                               WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS);
104 }
105
106 struct bad_source_rect_args {
107         int x, y, w, h;
108 };
109
110 static const struct bad_source_rect_args bad_source_rect_args[] = {
111         { -5,  0,  20,  10 },
112         {  0, -5,  20,  10 },
113         {  5,  6,   0,  10 },
114         {  5,  6,  20,   0 },
115         {  5,  6, -20,  10 },
116         {  5,  6,  20, -10 },
117         { -1, -1,  20,  10 },
118         {  5,  6,  -1,  -1 },
119 };
120
121 TEST_P(test_viewporter_bad_source_rect, bad_source_rect_args)
122 {
123         const struct bad_source_rect_args *args = data;
124         struct client *client;
125         struct wp_viewport *vp;
126
127         client = create_client_and_test_surface(100, 50, 123, 77);
128
129         vp = create_viewport(client);
130
131         fprintf(stderr, "wp_viewport.set_source x=%d, y=%d, w=%d, h=%d\n",
132                 args->x, args->y, args->w, args->h);
133         set_source(vp, args->x, args->y, args->w, args->h);
134
135         expect_protocol_error(client, &wp_viewport_interface,
136                               WP_VIEWPORT_ERROR_BAD_VALUE);
137 }
138
139 TEST(test_viewporter_unset_source_rect)
140 {
141         struct client *client;
142         struct wp_viewport *vp;
143
144         client = create_client_and_test_surface(100, 50, 123, 77);
145
146         vp = create_viewport(client);
147         set_source(vp, -1, -1, -1, -1);
148         wl_surface_commit(client->surface->wl_surface);
149
150         client_roundtrip(client);
151 }
152
153 struct bad_destination_args {
154         int w, h;
155 };
156
157 static const struct bad_destination_args bad_destination_args[] = {
158         {   0,  10 },
159         {  20,   0 },
160         { -20,  10 },
161         {  -1,  10 },
162         {  20, -10 },
163         {  20,  -1 },
164 };
165
166 TEST_P(test_viewporter_bad_destination_size, bad_destination_args)
167 {
168         const struct bad_destination_args *args = data;
169         struct client *client;
170         struct wp_viewport *vp;
171
172         client = create_client_and_test_surface(100, 50, 123, 77);
173
174         vp = create_viewport(client);
175
176         fprintf(stderr, "wp_viewport.set_destination w=%d, h=%d\n",
177                 args->w, args->h);
178         wp_viewport_set_destination(vp, args->w, args->h);
179
180         expect_protocol_error(client, &wp_viewport_interface,
181                               WP_VIEWPORT_ERROR_BAD_VALUE);
182 }
183
184 TEST(test_viewporter_unset_destination_size)
185 {
186         struct client *client;
187         struct wp_viewport *vp;
188
189         client = create_client_and_test_surface(100, 50, 123, 77);
190
191         vp = create_viewport(client);
192         wp_viewport_set_destination(vp, -1, -1);
193         wl_surface_commit(client->surface->wl_surface);
194
195         client_roundtrip(client);
196 }
197
198 struct nonint_destination_args {
199         wl_fixed_t w, h;
200 };
201
202 static const struct nonint_destination_args nonint_destination_args[] = {
203 #define F(i,f) ((i) * 256 + (f))
204         { F(20, 0),   F(10, 1) },
205         { F(20, 0),   F(10, -1) },
206         { F(20, 1),   F(10, 0) },
207         { F(20, -1),  F(10, 0) },
208         { F(20, 128), F(10, 128) },
209 #undef F
210 };
211
212 TEST_P(test_viewporter_non_integer_destination_size, nonint_destination_args)
213 {
214         const struct nonint_destination_args *args = data;
215         struct client *client;
216         struct wp_viewport *vp;
217
218         client = create_client_and_test_surface(100, 50, 123, 77);
219
220         vp = create_viewport(client);
221
222         fprintf(stderr, "non-integer size w=%f, h=%f\n",
223                 wl_fixed_to_double(args->w), wl_fixed_to_double(args->h));
224         wp_viewport_set_source(vp, 5, 6, args->w, args->h);
225         wp_viewport_set_destination(vp, -1, -1);
226         wl_surface_commit(client->surface->wl_surface);
227
228         expect_protocol_error(client, &wp_viewport_interface,
229                               WP_VIEWPORT_ERROR_BAD_SIZE);
230 }
231
232 struct source_buffer_args {
233         wl_fixed_t x, y;
234         wl_fixed_t w, h;
235         int buffer_scale;
236         enum wl_output_transform buffer_transform;
237 };
238
239 static int
240 get_surface_width(struct surface *surface,
241                   int buffer_scale,
242                   enum wl_output_transform buffer_transform)
243 {
244         switch (buffer_transform) {
245         case WL_OUTPUT_TRANSFORM_NORMAL:
246         case WL_OUTPUT_TRANSFORM_180:
247         case WL_OUTPUT_TRANSFORM_FLIPPED:
248         case WL_OUTPUT_TRANSFORM_FLIPPED_180:
249                 return surface->width / buffer_scale;
250         case WL_OUTPUT_TRANSFORM_90:
251         case WL_OUTPUT_TRANSFORM_270:
252         case WL_OUTPUT_TRANSFORM_FLIPPED_90:
253         case WL_OUTPUT_TRANSFORM_FLIPPED_270:
254                 return surface->height / buffer_scale;
255         }
256
257         return -1;
258 }
259
260 static int
261 get_surface_height(struct surface *surface,
262                    int buffer_scale,
263                    enum wl_output_transform buffer_transform)
264 {
265         switch (buffer_transform) {
266         case WL_OUTPUT_TRANSFORM_NORMAL:
267         case WL_OUTPUT_TRANSFORM_180:
268         case WL_OUTPUT_TRANSFORM_FLIPPED:
269         case WL_OUTPUT_TRANSFORM_FLIPPED_180:
270                 return surface->height / buffer_scale;
271         case WL_OUTPUT_TRANSFORM_90:
272         case WL_OUTPUT_TRANSFORM_270:
273         case WL_OUTPUT_TRANSFORM_FLIPPED_90:
274         case WL_OUTPUT_TRANSFORM_FLIPPED_270:
275                 return surface->width / buffer_scale;
276         }
277
278         return -1;
279 }
280
281 static void
282 setup_source_vs_buffer(struct client *client,
283                        const struct source_buffer_args *args)
284 {
285         struct wl_surface *surf;
286         struct wp_viewport *vp;
287
288         surf = client->surface->wl_surface;
289         vp = create_viewport(client);
290
291         fprintf(stderr, "surface %dx%d\n",
292                 get_surface_width(client->surface,
293                                   args->buffer_scale, args->buffer_transform),
294                 get_surface_height(client->surface,
295                                    args->buffer_scale, args->buffer_transform));
296         fprintf(stderr, "source x=%f, y=%f, w=%f, h=%f; "
297                 "buffer scale=%d, transform=%d\n",
298                 wl_fixed_to_double(args->x), wl_fixed_to_double(args->y),
299                 wl_fixed_to_double(args->w), wl_fixed_to_double(args->h),
300                 args->buffer_scale, args->buffer_transform);
301
302         wl_surface_set_buffer_scale(surf, args->buffer_scale);
303         wl_surface_set_buffer_transform(surf, args->buffer_transform);
304         wl_surface_attach(surf, client->surface->buffer->proxy, 0, 0);
305         wp_viewport_set_source(vp, args->x, args->y, args->w, args->h);
306         wp_viewport_set_destination(vp, 99, 99);
307         wl_surface_commit(surf);
308 }
309
310 /* buffer dimensions */
311 #define WIN_W 124
312 #define WIN_H 78
313
314 /* source rect base size */
315 #define SRC_W 20
316 #define SRC_H 10
317
318 /* margin */
319 #define MRG 10
320 /* epsilon of wl_fixed_t */
321 #define EPS 1
322
323 TEST(test_viewporter_source_buffer_params)
324 {
325         const int max_scale = 2;
326
327         /* buffer_scale requirement */
328         assert(WIN_W % max_scale == 0);
329         assert(WIN_H % max_scale == 0);
330
331         /* source rect must fit inside regardless of scale and transform */
332         assert(SRC_W < WIN_W / max_scale);
333         assert(SRC_H < WIN_H / max_scale);
334         assert(SRC_W < WIN_H / max_scale);
335         assert(SRC_H < WIN_W / max_scale);
336
337         /* If buffer scale was ignored, source rect should be inside instead */
338         assert(WIN_W / max_scale + SRC_W + MRG < WIN_W);
339         assert(WIN_H / max_scale + SRC_H + MRG < WIN_H);
340         assert(WIN_W / max_scale + SRC_H + MRG < WIN_W);
341         assert(WIN_H / max_scale + SRC_W + MRG < WIN_H);
342 }
343
344 static const struct source_buffer_args bad_source_buffer_args[] = {
345 #define F(i) ((i) * 256)
346
347 /* Flush right-top, but epsilon too far right. */
348         { F(WIN_W - SRC_W) + EPS, F(0), F(SRC_W),       F(SRC_H), 1, WL_OUTPUT_TRANSFORM_NORMAL },
349         { F(WIN_W - SRC_W),       F(0), F(SRC_W) + EPS, F(SRC_H), 1, WL_OUTPUT_TRANSFORM_NORMAL },
350 /* Flush left-bottom, but epsilon too far down. */
351         { F(0), F(WIN_H - SRC_H) + EPS, F(SRC_W), F(SRC_H),       1, WL_OUTPUT_TRANSFORM_NORMAL },
352         { F(0), F(WIN_H - SRC_H),       F(SRC_W), F(SRC_H) + EPS, 1, WL_OUTPUT_TRANSFORM_NORMAL },
353 /* Completely outside on the right. */
354         { F(WIN_W + MRG), F(0), F(SRC_W), F(SRC_H), 1, WL_OUTPUT_TRANSFORM_NORMAL },
355 /* Competely outside on the bottom. */
356         { F(0), F(WIN_H + MRG), F(SRC_W), F(SRC_H), 1, WL_OUTPUT_TRANSFORM_NORMAL },
357
358 /*
359  * buffer_scale=2, so the surface size will be halved.
360  * If buffer_scale was not taken into account, these would all be inside.
361  * These are the same as above, but adapted to buffer_scale=2.
362  */
363         { F(WIN_W / 2 - SRC_W) + EPS, F(0),                       F(SRC_W),       F(SRC_H),       2, WL_OUTPUT_TRANSFORM_NORMAL },
364         { F(WIN_W / 2 - SRC_W),       F(0),                       F(SRC_W) + EPS, F(SRC_H),       2, WL_OUTPUT_TRANSFORM_NORMAL },
365
366         { F(0),                       F(WIN_H / 2 - SRC_H) + EPS, F(SRC_W),       F(SRC_H),       2, WL_OUTPUT_TRANSFORM_NORMAL },
367         { F(0),                       F(WIN_H / 2 - SRC_H),       F(SRC_W),       F(SRC_H) + EPS, 2, WL_OUTPUT_TRANSFORM_NORMAL },
368
369         { F(WIN_W / 2 + MRG),         F(0),                       F(SRC_W),       F(SRC_H),       2, WL_OUTPUT_TRANSFORM_NORMAL },
370
371         { F(0),                       F(WIN_H / 2 + MRG),         F(SRC_W),       F(SRC_H),       2, WL_OUTPUT_TRANSFORM_NORMAL },
372
373 /* Exceeding bottom-right corner by epsilon: */
374 /* non-dimension-swapping transforms */
375         { F(WIN_W - SRC_W), F(WIN_H - SRC_H),       F(SRC_W),       F(SRC_H) + EPS, 1, WL_OUTPUT_TRANSFORM_FLIPPED_180 },
376         { F(WIN_W - SRC_W), F(WIN_H - SRC_H) + EPS, F(SRC_W),       F(SRC_H),       1, WL_OUTPUT_TRANSFORM_FLIPPED },
377         { F(WIN_W - SRC_W), F(WIN_H - SRC_H),       F(SRC_W) + EPS, F(SRC_H),       1, WL_OUTPUT_TRANSFORM_180 },
378
379 /* dimension-swapping transforms */
380         { F(WIN_H - SRC_W) + EPS, F(WIN_W - SRC_H),       F(SRC_W),       F(SRC_H),       1, WL_OUTPUT_TRANSFORM_90 },
381         { F(WIN_H - SRC_W),       F(WIN_W - SRC_H) + EPS, F(SRC_W),       F(SRC_H),       1, WL_OUTPUT_TRANSFORM_270 },
382         { F(WIN_H - SRC_W),       F(WIN_W - SRC_H),       F(SRC_W) + EPS, F(SRC_H),       1, WL_OUTPUT_TRANSFORM_FLIPPED_90 },
383         { F(WIN_H - SRC_W),       F(WIN_W - SRC_H),       F(SRC_W),       F(SRC_H) + EPS, 1, WL_OUTPUT_TRANSFORM_FLIPPED_270 },
384
385 /* non-dimension-swapping transforms, buffer_scale=2 */
386         { F(WIN_W / 2 - SRC_W), F(WIN_H / 2 - SRC_H) + EPS, F(SRC_W),       F(SRC_H),       2, WL_OUTPUT_TRANSFORM_FLIPPED_180 },
387         { F(WIN_W / 2 - SRC_W), F(WIN_H / 2 - SRC_H),       F(SRC_W) + EPS, F(SRC_H),       2, WL_OUTPUT_TRANSFORM_FLIPPED },
388         { F(WIN_W / 2 - SRC_W), F(WIN_H / 2 - SRC_H),       F(SRC_W),       F(SRC_H) + EPS, 2, WL_OUTPUT_TRANSFORM_180 },
389
390 /* dimension-swapping transforms, buffer_scale=2 */
391         { F(WIN_H / 2 - SRC_W) + EPS, F(WIN_W / 2 - SRC_H),       F(SRC_W),       F(SRC_H),       2, WL_OUTPUT_TRANSFORM_90 },
392         { F(WIN_H / 2 - SRC_W),       F(WIN_W / 2 - SRC_H) + EPS, F(SRC_W),       F(SRC_H),       2, WL_OUTPUT_TRANSFORM_270 },
393         { F(WIN_H / 2 - SRC_W),       F(WIN_W / 2 - SRC_H),       F(SRC_W) + EPS, F(SRC_H),       2, WL_OUTPUT_TRANSFORM_FLIPPED_90 },
394         { F(WIN_H / 2 - SRC_W),       F(WIN_W / 2 - SRC_H),       F(SRC_W),       F(SRC_H) + EPS, 2, WL_OUTPUT_TRANSFORM_FLIPPED_270 },
395
396 #undef F
397 };
398
399 TEST_P(test_viewporter_source_outside_buffer, bad_source_buffer_args)
400 {
401         const struct source_buffer_args *args = data;
402         struct client *client;
403
404         client = create_client_and_test_surface(100, 50, WIN_W, WIN_H);
405         setup_source_vs_buffer(client, args);
406
407         expect_protocol_error(client, &wp_viewport_interface,
408                               WP_VIEWPORT_ERROR_OUT_OF_BUFFER);
409 }
410
411 static const struct source_buffer_args good_source_buffer_args[] = {
412 #define F(i) ((i) * 256)
413
414 /* top-left, top-right, bottom-left, and bottom-right corner */
415         { F(0),             F(0),             F(SRC_W), F(SRC_H), 1, WL_OUTPUT_TRANSFORM_NORMAL },
416         { F(WIN_W - SRC_W), F(0),             F(SRC_W), F(SRC_H), 1, WL_OUTPUT_TRANSFORM_NORMAL },
417         { F(0),             F(WIN_H - SRC_H), F(SRC_W), F(SRC_H), 1, WL_OUTPUT_TRANSFORM_NORMAL },
418         { F(WIN_W - SRC_W), F(WIN_H - SRC_H), F(SRC_W), F(SRC_H), 1, WL_OUTPUT_TRANSFORM_NORMAL },
419
420 /* buffer_scale=2, so the surface size will be halved */
421         { F(0),                 F(0),                 F(SRC_W), F(SRC_H), 2, WL_OUTPUT_TRANSFORM_NORMAL },
422         { F(WIN_W / 2 - SRC_W), F(0),                 F(SRC_W), F(SRC_H), 2, WL_OUTPUT_TRANSFORM_NORMAL },
423         { F(0),                 F(WIN_H / 2 - SRC_H), F(SRC_W), F(SRC_H), 2, WL_OUTPUT_TRANSFORM_NORMAL },
424         { F(WIN_W / 2 - SRC_W), F(WIN_H / 2 - SRC_H), F(SRC_W), F(SRC_H), 2, WL_OUTPUT_TRANSFORM_NORMAL },
425
426 /* with half pixel offset */
427         { F(WIN_W / 2 - SRC_W) + 128, F(WIN_H / 2 - SRC_H) + 128, F(SRC_W) - 128, F(SRC_H) - 128, 2, WL_OUTPUT_TRANSFORM_NORMAL },
428
429 /* Flushed to bottom-right corner: */
430 /* non-dimension-swapping transforms */
431         { F(WIN_W - SRC_W), F(WIN_H - SRC_H), F(SRC_W), F(SRC_H), 1, WL_OUTPUT_TRANSFORM_FLIPPED_180 },
432         { F(WIN_W - SRC_W), F(WIN_H - SRC_H), F(SRC_W), F(SRC_H), 1, WL_OUTPUT_TRANSFORM_FLIPPED },
433         { F(WIN_W - SRC_W), F(WIN_H - SRC_H), F(SRC_W), F(SRC_H), 1, WL_OUTPUT_TRANSFORM_180 },
434
435 /* dimension-swapping transforms */
436         { F(WIN_H - SRC_W), F(WIN_W - SRC_H), F(SRC_W), F(SRC_H), 1, WL_OUTPUT_TRANSFORM_90 },
437         { F(WIN_H - SRC_W), F(WIN_W - SRC_H), F(SRC_W), F(SRC_H), 1, WL_OUTPUT_TRANSFORM_270 },
438         { F(WIN_H - SRC_W), F(WIN_W - SRC_H), F(SRC_W), F(SRC_H), 1, WL_OUTPUT_TRANSFORM_FLIPPED_90 },
439         { F(WIN_H - SRC_W), F(WIN_W - SRC_H), F(SRC_W), F(SRC_H), 1, WL_OUTPUT_TRANSFORM_FLIPPED_270 },
440
441 /* non-dimension-swapping transforms, buffer_scale=2 */
442         { F(WIN_W / 2 - SRC_W), F(WIN_H / 2 - SRC_H), F(SRC_W), F(SRC_H), 2, WL_OUTPUT_TRANSFORM_FLIPPED_180 },
443         { F(WIN_W / 2 - SRC_W), F(WIN_H / 2 - SRC_H), F(SRC_W), F(SRC_H), 2, WL_OUTPUT_TRANSFORM_FLIPPED },
444         { F(WIN_W / 2 - SRC_W), F(WIN_H / 2 - SRC_H), F(SRC_W), F(SRC_H), 2, WL_OUTPUT_TRANSFORM_180 },
445
446 /* dimension-swapping transforms, buffer_scale=2 */
447         { F(WIN_H / 2 - SRC_W), F(WIN_W / 2 - SRC_H), F(SRC_W), F(SRC_H), 2, WL_OUTPUT_TRANSFORM_90 },
448         { F(WIN_H / 2 - SRC_W), F(WIN_W / 2 - SRC_H), F(SRC_W), F(SRC_H), 2, WL_OUTPUT_TRANSFORM_270 },
449         { F(WIN_H / 2 - SRC_W), F(WIN_W / 2 - SRC_H), F(SRC_W), F(SRC_H), 2, WL_OUTPUT_TRANSFORM_FLIPPED_90 },
450         { F(WIN_H / 2 - SRC_W), F(WIN_W / 2 - SRC_H), F(SRC_W), F(SRC_H), 2, WL_OUTPUT_TRANSFORM_FLIPPED_270 },
451
452 #undef F
453 };
454
455 TEST_P(test_viewporter_source_inside_buffer, good_source_buffer_args)
456 {
457         const struct source_buffer_args *args = data;
458         struct client *client;
459
460         client = create_client_and_test_surface(100, 50, WIN_W, WIN_H);
461         setup_source_vs_buffer(client, args);
462         client_roundtrip(client);
463 }
464
465 #undef WIN_W
466 #undef WIN_H
467 #undef SRC_W
468 #undef SRC_H
469 #undef MRG
470 #undef EPS
471
472 TEST(test_viewporter_outside_null_buffer)
473 {
474         struct client *client;
475         struct wp_viewport *vp;
476         struct wl_surface *surf;
477
478         client = create_client_and_test_surface(100, 50, 123, 77);
479         surf = client->surface->wl_surface;
480
481         /* If buffer is NULL, does not matter what the source rect is. */
482         vp = create_viewport(client);
483         wl_surface_attach(surf, NULL, 0, 0);
484         set_source(vp, 1000, 1000, 20, 10);
485         wp_viewport_set_destination(vp, 99, 99);
486         wl_surface_commit(surf);
487         client_roundtrip(client);
488
489         /* Try again, with all old values. */
490         wl_surface_commit(surf);
491         client_roundtrip(client);
492
493         /* Try once more with old NULL buffer. */
494         set_source(vp, 1200, 1200, 20, 10);
495         wl_surface_commit(surf);
496         client_roundtrip(client);
497
498         /* When buffer comes back, source rect matters again. */
499         wl_surface_attach(surf, client->surface->buffer->proxy, 0, 0);
500         wl_surface_commit(surf);
501         expect_protocol_error(client, &wp_viewport_interface,
502                               WP_VIEWPORT_ERROR_OUT_OF_BUFFER);
503 }
504
505 TEST(test_viewporter_no_surface_set_source)
506 {
507         struct client *client;
508         struct wp_viewport *vp;
509
510         client = create_client_and_test_surface(100, 50, 123, 77);
511         vp = create_viewport(client);
512         wl_surface_destroy(client->surface->wl_surface);
513         client->surface->wl_surface = NULL;
514
515         /* But the wl_surface does not exist anymore. */
516         set_source(vp, 1000, 1000, 20, 10);
517
518         expect_protocol_error(client, &wp_viewport_interface,
519                               WP_VIEWPORT_ERROR_NO_SURFACE);
520 }
521
522 TEST(test_viewporter_no_surface_set_destination)
523 {
524         struct client *client;
525         struct wp_viewport *vp;
526
527         client = create_client_and_test_surface(100, 50, 123, 77);
528         vp = create_viewport(client);
529         wl_surface_destroy(client->surface->wl_surface);
530         client->surface->wl_surface = NULL;
531
532         /* But the wl_surface does not exist anymore. */
533         wp_viewport_set_destination(vp, 99, 99);
534
535         expect_protocol_error(client, &wp_viewport_interface,
536                               WP_VIEWPORT_ERROR_NO_SURFACE);
537 }
538
539 TEST(test_viewporter_no_surface_destroy)
540 {
541         struct client *client;
542         struct wp_viewport *vp;
543
544         client = create_client_and_test_surface(100, 50, 123, 77);
545         vp = create_viewport(client);
546         wl_surface_destroy(client->surface->wl_surface);
547         client->surface->wl_surface = NULL;
548
549         /* But the wl_surface does not exist anymore. */
550         wp_viewport_destroy(vp);
551
552         client_roundtrip(client);
553 }