diff options
author | Eric Anholt <eric@anholt.net> | 2018-09-19 18:02:41 -0700 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2018-09-25 21:27:08 -0700 |
commit | 89901e14d2f98def2399fd0546838721e7ac452d (patch) | |
tree | cc258e512c84f64ffa4153477c57182a507e769b /test | |
parent | bc36594e0eb8bfa5a673bcfd8c8168f70994a1df (diff) |
test: Add the start of a testsuite for damage.
Inspired by the previous bug, build something we can use to write
damage testcases, including testing for the bug.
Signed-off-by: Eric Anholt <eric@anholt.net>
Diffstat (limited to 'test')
-rw-r--r-- | test/damage/meson.build | 9 | ||||
-rw-r--r-- | test/damage/primitives.c | 359 | ||||
-rw-r--r-- | test/meson.build | 1 |
3 files changed, 369 insertions, 0 deletions
diff --git a/test/damage/meson.build b/test/damage/meson.build new file mode 100644 index 000000000..091caff5f --- /dev/null +++ b/test/damage/meson.build @@ -0,0 +1,9 @@ +xcb_dep = dependency('xcb', required: false) +xcb_damage_dep = dependency('xcb-damage', required: false) + +if get_option('xvfb') + if xcb_dep.found() and xcb_damage_dep.found() + damage_primitives = executable('damage-primitives', 'primitives.c', dependencies: [xcb_dep, xcb_damage_dep]) + test('damage-primitives', simple_xinit, args: [damage_primitives, '--', xvfb_server]) + endif +endif diff --git a/test/damage/primitives.c b/test/damage/primitives.c new file mode 100644 index 000000000..5ee63b689 --- /dev/null +++ b/test/damage/primitives.c @@ -0,0 +1,359 @@ +/* + * Copyright © 2018 Broadcom + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/** @file + * + * Touch-testing of the damage extension's implementation of various + * primitives. The core initializes the pixmap with some contents, + * turns on damage and each per-primitive test gets to just make a + * rendering call that draws some pixels. Afterwards, the core checks + * what pixels were modified and makes sure the damage report contains + * them. + */ + +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <xcb/damage.h> + +struct test_setup { + xcb_connection_t *c; + xcb_drawable_t d; + xcb_drawable_t start_drawable; + uint32_t *start_drawable_contents; + xcb_screen_t *screen; + xcb_gc_t gc; + int width, height; + uint32_t *expected; + uint32_t *damaged; +}; + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +/** + * Performs a synchronous GetImage for a test pixmap, returning + * uint32_t per pixel. + */ +static uint32_t * +get_image(struct test_setup *setup, xcb_drawable_t drawable) +{ + xcb_get_image_cookie_t cookie = + xcb_get_image(setup->c, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable, + 0, 0, setup->width, setup->height, ~0); + xcb_get_image_reply_t *reply = + xcb_get_image_reply(setup->c, cookie, NULL); + uint8_t *data = xcb_get_image_data(reply); + int len = xcb_get_image_data_length(reply); + + /* Do I understand X protocol and our test environment? */ + assert(reply->depth == 24); + assert(len == 4 * setup->width * setup->height); + + uint32_t *result = malloc(sizeof(uint32_t) * + setup->width * setup->height); + memcpy(result, data, len); + free(reply); + + return result; +} + +/** + * Gets the image drawn by the test and compares it to the starting + * image, producing a bitmask of what pixels were damaged. + */ +static void +compute_expected_damage(struct test_setup *setup) +{ + uint32_t *results = get_image(setup, setup->d); + bool any_modified_pixels = false; + + for (int i = 0; i < setup->width * setup->height; i++) { + if (results[i] != setup->start_drawable_contents[i]) { + setup->expected[i / 32] |= 1 << (i % 32); + any_modified_pixels = true; + } + } + + /* Make sure that the testcases actually render something! */ + assert(any_modified_pixels); +} + +/** + * Processes a damage event, filling in the bitmask of pixels reported + * to be damaged. + */ +static bool +damage_event_handle(struct test_setup *setup, + struct xcb_damage_notify_event_t *event) +{ + xcb_rectangle_t *rect = &event->area; + assert(event->drawable == setup->d); + for (int y = rect->y; y < rect->y + rect->height; y++) { + for (int x = rect->x; x < rect->x + rect->width; x++) { + int bit = y * setup->width + x; + setup->damaged[bit / 32] |= 1 << (bit % 32); + } + } + + return event->level & 0x80; /* XXX: MORE is missing from xcb. */ +} + +/** + * Collects a series of damage events (while the MORE flag is set) + * into the damaged bitmask. + */ +static void +collect_damage(struct test_setup *setup) +{ + const xcb_query_extension_reply_t *ext = + xcb_get_extension_data(setup->c, &xcb_damage_id); + xcb_generic_event_t *ge; + + xcb_flush(setup->c); + while ((ge = xcb_wait_for_event(setup->c))) { + int event = ge->response_type & ~0x80; + + if (event == (ext->first_event + XCB_DAMAGE_NOTIFY)) { + if (!damage_event_handle(setup, + (struct xcb_damage_notify_event_t *)ge)) { + return; + } + } else { + switch (ge->response_type) { + case 0: { + xcb_generic_error_t *error = (xcb_generic_error_t *)ge; + fprintf(stderr, "X error %d at sequence %d\n", + error->error_code, error->sequence); + exit(1); + } + + case XCB_GRAPHICS_EXPOSURE: + case XCB_NO_EXPOSURE: + break; + + default: + fprintf(stderr, "Unexpected event %d\n", ge->response_type); + exit(1); + } + } + } + + fprintf(stderr, "I/O error\n"); + exit(1); +} + +/** + * Wrapper to set up the test pixmap, attach damage to it, and see if + * the reported damage matches the testcase's rendering. + */ +static bool +damage_test(struct test_setup *setup, + void (*test)(struct test_setup *setup), + const char *name) +{ + uint32_t expected[32] = { 0 }; + uint32_t damaged[32] = { 0 }; + + printf("Testing %s\n", name); + + setup->expected = expected; + setup->damaged = damaged; + + /* Create our pixmap for this test and fill it with the + * starting image. + */ + setup->d = xcb_generate_id(setup->c); + xcb_create_pixmap(setup->c, setup->screen->root_depth, + setup->d, setup->screen->root, + setup->width, setup->height); + + setup->gc = xcb_generate_id(setup->c); + uint32_t values[] = { setup->screen->black_pixel }; + xcb_create_gc(setup->c, setup->gc, setup->screen->root, + XCB_GC_FOREGROUND, values); + + xcb_copy_area(setup->c, + setup->start_drawable, + setup->d, + setup->gc, + 0, 0, + 0, 0, + setup->width, setup->height); + + /* Start watching for damage now that we have the initial contents. */ + xcb_damage_damage_t damage = xcb_generate_id(setup->c); + xcb_damage_create(setup->c, damage, setup->d, + XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES); + + test(setup); + + compute_expected_damage(setup); + + xcb_damage_destroy(setup->c, damage); + xcb_free_gc(setup->c, setup->gc); + xcb_free_pixmap(setup->c, setup->d); + collect_damage(setup); + + for (int bit = 0; bit < setup->width * setup->height; bit++) { + if ((expected[bit / 32] & (1 << bit % 32)) && + !(damaged[bit / 32] & (1 << bit % 32))) { + fprintf(stderr, " fail: %s(): Damage report missed %d, %d\n", + name, bit % setup->width, bit / setup->width); + return false; + } + } + + return true; +} + +/** + * Creates the pixmap of contents that will be the initial state of + * each test's drawable. + */ +static void +create_start_pixmap(struct test_setup *setup) +{ + setup->start_drawable = xcb_generate_id(setup->c); + xcb_create_pixmap(setup->c, setup->screen->root_depth, + setup->start_drawable, setup->screen->root, + setup->width, setup->height); + + xcb_gc_t gc = xcb_generate_id(setup->c); + uint32_t values[] = { 0xaaaaaaaa }; + xcb_create_gc(setup->c, gc, setup->screen->root, + XCB_GC_FOREGROUND, values); + + xcb_rectangle_t rect = { 5, 5, 10, 15 }; + xcb_poly_rectangle(setup->c, setup->start_drawable, gc, 1, &rect); + + xcb_free_gc(setup->c, gc); + + /* Capture the rendered start contents once, for comparing each + * test's rendering output to the start contents. + */ + setup->start_drawable_contents = get_image(setup, setup->start_drawable); +} + +static void +test_poly_point_origin(struct test_setup *setup) +{ + struct xcb_point_t points[] = { {1, 2}, {3, 4} }; + xcb_poly_point(setup->c, XCB_COORD_MODE_ORIGIN, setup->d, setup->gc, + ARRAY_SIZE(points), points); +} + +static void +test_poly_point_previous(struct test_setup *setup) +{ + struct xcb_point_t points[] = { {1, 2}, {3, 4} }; + xcb_poly_point(setup->c, XCB_COORD_MODE_PREVIOUS, setup->d, setup->gc, + ARRAY_SIZE(points), points); +} + +static void +test_poly_line_origin(struct test_setup *setup) +{ + struct xcb_point_t points[] = { {1, 2}, {3, 4}, {5, 6} }; + xcb_poly_line(setup->c, XCB_COORD_MODE_ORIGIN, setup->d, setup->gc, + ARRAY_SIZE(points), points); +} + +static void +test_poly_line_previous(struct test_setup *setup) +{ + struct xcb_point_t points[] = { {1, 2}, {3, 4}, {5, 6} }; + xcb_poly_line(setup->c, XCB_COORD_MODE_PREVIOUS, setup->d, setup->gc, + ARRAY_SIZE(points), points); +} + +static void +test_poly_fill_rectangle(struct test_setup *setup) +{ + struct xcb_rectangle_t rects[] = { {1, 2, 3, 4}, + {5, 6, 7, 8} }; + xcb_poly_fill_rectangle(setup->c, setup->d, setup->gc, + ARRAY_SIZE(rects), rects); +} + +static void +test_poly_rectangle(struct test_setup *setup) +{ + struct xcb_rectangle_t rects[] = { {1, 2, 3, 4}, + {5, 6, 7, 8} }; + xcb_poly_rectangle(setup->c, setup->d, setup->gc, + ARRAY_SIZE(rects), rects); +} + +static void +test_poly_segment(struct test_setup *setup) +{ + struct xcb_segment_t segs[] = { {1, 2, 3, 4}, + {5, 6, 7, 8} }; + xcb_poly_segment(setup->c, setup->d, setup->gc, + ARRAY_SIZE(segs), segs); +} + +int main(int argc, char **argv) +{ + int screen; + xcb_connection_t *c = xcb_connect(NULL, &screen); + const xcb_query_extension_reply_t *ext = + xcb_get_extension_data(c, &xcb_damage_id); + + if (!ext->present) { + printf("No XDamage present\n"); + exit(77); + } + + struct test_setup setup = { + .c = c, + .width = 32, + .height = 32, + }; + + /* Get the screen so we have the root window. */ + xcb_screen_iterator_t iter; + iter = xcb_setup_roots_iterator (xcb_get_setup (c)); + setup.screen = iter.data; + + xcb_damage_query_version(c, 1, 1); + + create_start_pixmap(&setup); + + bool pass = true; +#define test(x) pass = damage_test(&setup, x, #x) && pass; + + test(test_poly_point_origin); + test(test_poly_point_previous); + test(test_poly_line_origin); + test(test_poly_line_previous); + test(test_poly_fill_rectangle); + test(test_poly_rectangle); + test(test_poly_segment); + + xcb_disconnect(c); + exit(pass ? 0 : 1); +} diff --git a/test/meson.build b/test/meson.build index d413e9da5..33b7941e0 100644 --- a/test/meson.build +++ b/test/meson.build @@ -24,4 +24,5 @@ if get_option('xvfb') endif subdir('bigreq') +subdir('damage') subdir('sync') |