diff options
author | Seunghun Lee <shiin.lee@samsung.com> | 2021-06-21 11:15:43 +0900 |
---|---|---|
committer | Seunghun Lee <shiin.lee@samsung.com> | 2021-06-21 13:04:19 +0900 |
commit | 31b8e058d415babc3841db1004b5f5dcadc113a3 (patch) | |
tree | 93487a831f62744dcee5cdc18870e4f129c06080 | |
parent | 4178f86021ba8d9cd6ca235f04d0603a0c985f56 (diff) | |
download | ws-testcase-31b8e058d415babc3841db1004b5f5dcadc113a3.tar.gz ws-testcase-31b8e058d415babc3841db1004b5f5dcadc113a3.tar.bz2 ws-testcase-31b8e058d415babc3841db1004b5f5dcadc113a3.zip |
Add e-tizen-testsuite for Enlightenment on Tizen
It is meant to test primitive wayland protocols and extensions of the
Tizen for Enlightenment Wayland Compositor.
It doesn't contain Enlightenment Wayland Compositor itself. So, it has
to be run on target seperately running Enlightenment. And any test
related to shot can be failed because of screenshot timing, not because
of its malfunctioning.
The initial testcases are as follow.
subsurface, subsurface-shot, tizen-surface, zxdg-shell-v6,
tizen-screenshooter, scaler, scaler-shot, buffer-transform
The core implementation of test runner is brought from weston tests.
Change-Id: I5b880ba523bac6b3dc041ee527869b2317ea2c87
65 files changed, 5758 insertions, 0 deletions
diff --git a/e-tizen-testsuite/meson.build b/e-tizen-testsuite/meson.build new file mode 100644 index 0000000..e26f4d5 --- /dev/null +++ b/e-tizen-testsuite/meson.build @@ -0,0 +1,16 @@ +project('e-tizen-testsuite', 'c', version: '0.0.1') + +dir_prefix = get_option('prefix') +dir_bin = join_paths(dir_prefix, get_option('bindir')) +dir_data = join_paths(dir_prefix, get_option('datadir')) +dir_ref = join_paths(dir_data, 'e-tizen-testsuite') + +common_inc = include_directories('.') + +config_h = configuration_data() +config_h.set_quoted('E_TIZEN_TEST_REFERENCE_PATH', dir_ref) + +subdir('src') +subdir('reference') + +configure_file(output: 'config.h', configuration: config_h) diff --git a/e-tizen-testsuite/packaging/e-tizen-testsuite.manifest b/e-tizen-testsuite/packaging/e-tizen-testsuite.manifest new file mode 100644 index 0000000..75b0fa5 --- /dev/null +++ b/e-tizen-testsuite/packaging/e-tizen-testsuite.manifest @@ -0,0 +1,5 @@ +<manifest> + <request> + <domain name="_"/> + </request> +</manifest> diff --git a/e-tizen-testsuite/packaging/e-tizen-testsuite.spec b/e-tizen-testsuite/packaging/e-tizen-testsuite.spec new file mode 100644 index 0000000..0493669 --- /dev/null +++ b/e-tizen-testsuite/packaging/e-tizen-testsuite.spec @@ -0,0 +1,48 @@ +Name: e-tizen-testsuite +Version: 0.0.1 +Release: 0 +Summary: Test suite +License: MIT +URL: http://www.tizen.org/ +Source: %name-%version.tar.xz +Source1001: %name.manifest + +BuildRequires: meson + +BuildRequires: pkgconfig(wayland-client) + +BuildRequires: pkgconfig(pixman-1) +BuildRequires: pkgconfig(cairo) + +BuildRequires: pkgconfig(screenshooter-client) +BuildRequires: pkgconfig(xdg-shell-unstable-v6-client) +BuildRequires: pkgconfig(tizen-extension-client) + +BuildRequires: pkgconfig(scaler-client) + +BuildRequires: pkgconfig(wtz-foreign-client) + +%description +Test Suite + +%prep +%setup -q +cp %{SOURCE1001} . + +%build +meson setup \ + --prefix /usr \ + --bindir %{_bindir} \ + --buildtype debug \ + builddir +ninja -C builddir all + +%install +export DESTDIR=%{buildroot} +ninja -C builddir install + +%files +%manifest %{name}.manifest +%defattr(-,root,root,-) +%attr(550,root,root) %{_bindir}/* +%{_datadir}/%{name}/* diff --git a/e-tizen-testsuite/reference/basic-test-card.png b/e-tizen-testsuite/reference/basic-test-card.png Binary files differnew file mode 100644 index 0000000..027ca85 --- /dev/null +++ b/e-tizen-testsuite/reference/basic-test-card.png diff --git a/e-tizen-testsuite/reference/meson.build b/e-tizen-testsuite/reference/meson.build new file mode 100644 index 0000000..32cfb80 --- /dev/null +++ b/e-tizen-testsuite/reference/meson.build @@ -0,0 +1,47 @@ +install_data( + [ + 'tizen_logo.png', + 'window_shot_basic-00.png', + 'subsurface_shot_test-00.png', + 'subsurface_shot_test-01.png', + 'subsurface_shot_test-02.png', + 'subsurface_shot_test-03.png', + 'subsurface_shot_test-04.png', + 'subsurface_shot_test-05.png', + 'subsurface_shot_test-06.png', + 'subsurface_shot_test-07.png', + 'subsurface_shot_test-08.png', + 'subsurface_shot_test-09.png', + 'subsurface_shot_test-10.png', + 'tizen_policy_place_subsurface_below_parent_test-00.png', + 'tizen_policy_place_subsurface_below_parent_test-01.png', + 'tizen_policy_place_subsurface_below_parent_test-02.png', + 'tizen_policy_place_subsurface_below_parent_test-03.png', + 'tizen_policy_place_subsurface_below_parent_test-04.png', + 'subsurface_stack_with_activate_by_res_id-00.png', + 'subsurface_stack_with_activate_by_res_id-01.png', + 'subsurface_below_parent_with_alpha-00.png', + 'subsurface_below_parent_with_alpha-01.png', + 'scaler_shot-00.png', + 'scaler_shot-01.png', + 'scaler_shot-02.png', + 'scaler_shot-03.png', + 'scaler_shot-04.png', + 'basic-test-card.png', + 'output_1-NORMAL_buffer_1-90-00.png', + 'output_1-NORMAL_buffer_1-180-00.png', + 'output_1-NORMAL_buffer_1-270-00.png', + 'output_1-NORMAL_buffer_1-FLIPPED-00.png', + 'output_1-NORMAL_buffer_1-FLIPPED_90-00.png', + 'output_1-NORMAL_buffer_1-FLIPPED_180-00.png', + 'output_1-NORMAL_buffer_1-FLIPPED_270-00.png', + 'output_1-NORMAL_buffer_2-NORMAL-00.png', + 'output_1-NORMAL_buffer_2-90-00.png', + 'output_1-NORMAL_buffer_2-180-00.png', + 'output_1-NORMAL_buffer_2-270-00.png', + 'output_1-NORMAL_buffer_2-FLIPPED-00.png', + 'output_1-NORMAL_buffer_3-NORMAL-00.png', + 'output_1-NORMAL_buffer_3-FLIPPED_90-00.png', + ], + install_dir: join_paths(dir_data, 'e-tizen-testsuite') +) diff --git a/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-180-00.png b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-180-00.png Binary files differnew file mode 100644 index 0000000..e83906f --- /dev/null +++ b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-180-00.png diff --git a/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-270-00.png b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-270-00.png Binary files differnew file mode 100644 index 0000000..0a32b39 --- /dev/null +++ b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-270-00.png diff --git a/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-90-00.png b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-90-00.png Binary files differnew file mode 100644 index 0000000..605482a --- /dev/null +++ b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-90-00.png diff --git a/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-FLIPPED-00.png b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-FLIPPED-00.png Binary files differnew file mode 100644 index 0000000..7c2f1b1 --- /dev/null +++ b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-FLIPPED-00.png diff --git a/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-FLIPPED_180-00.png b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-FLIPPED_180-00.png Binary files differnew file mode 100644 index 0000000..fd6a881 --- /dev/null +++ b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-FLIPPED_180-00.png diff --git a/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-FLIPPED_270-00.png b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-FLIPPED_270-00.png Binary files differnew file mode 100644 index 0000000..d6e476a --- /dev/null +++ b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-FLIPPED_270-00.png diff --git a/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-FLIPPED_90-00.png b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-FLIPPED_90-00.png Binary files differnew file mode 100644 index 0000000..748acad --- /dev/null +++ b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_1-FLIPPED_90-00.png diff --git a/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-180-00.png b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-180-00.png Binary files differnew file mode 100644 index 0000000..a2aa038 --- /dev/null +++ b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-180-00.png diff --git a/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-270-00.png b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-270-00.png Binary files differnew file mode 100644 index 0000000..ce9883f --- /dev/null +++ b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-270-00.png diff --git a/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-90-00.png b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-90-00.png Binary files differnew file mode 100644 index 0000000..436b3c0 --- /dev/null +++ b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-90-00.png diff --git a/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-FLIPPED-00.png b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-FLIPPED-00.png Binary files differnew file mode 100644 index 0000000..55d5f57 --- /dev/null +++ b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-FLIPPED-00.png diff --git a/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-NORMAL-00.png b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-NORMAL-00.png Binary files differnew file mode 100644 index 0000000..b8d38ff --- /dev/null +++ b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_2-NORMAL-00.png diff --git a/e-tizen-testsuite/reference/output_1-NORMAL_buffer_3-FLIPPED_90-00.png b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_3-FLIPPED_90-00.png Binary files differnew file mode 100644 index 0000000..291e364 --- /dev/null +++ b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_3-FLIPPED_90-00.png diff --git a/e-tizen-testsuite/reference/output_1-NORMAL_buffer_3-NORMAL-00.png b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_3-NORMAL-00.png Binary files differnew file mode 100644 index 0000000..b7e4b96 --- /dev/null +++ b/e-tizen-testsuite/reference/output_1-NORMAL_buffer_3-NORMAL-00.png diff --git a/e-tizen-testsuite/reference/scaler_shot-00.png b/e-tizen-testsuite/reference/scaler_shot-00.png Binary files differnew file mode 100644 index 0000000..9e10c55 --- /dev/null +++ b/e-tizen-testsuite/reference/scaler_shot-00.png diff --git a/e-tizen-testsuite/reference/scaler_shot-01.png b/e-tizen-testsuite/reference/scaler_shot-01.png Binary files differnew file mode 100644 index 0000000..21a00a6 --- /dev/null +++ b/e-tizen-testsuite/reference/scaler_shot-01.png diff --git a/e-tizen-testsuite/reference/scaler_shot-02.png b/e-tizen-testsuite/reference/scaler_shot-02.png Binary files differnew file mode 100644 index 0000000..70a397b --- /dev/null +++ b/e-tizen-testsuite/reference/scaler_shot-02.png diff --git a/e-tizen-testsuite/reference/scaler_shot-03.png b/e-tizen-testsuite/reference/scaler_shot-03.png Binary files differnew file mode 100644 index 0000000..21a00a6 --- /dev/null +++ b/e-tizen-testsuite/reference/scaler_shot-03.png diff --git a/e-tizen-testsuite/reference/scaler_shot-04.png b/e-tizen-testsuite/reference/scaler_shot-04.png Binary files differnew file mode 100644 index 0000000..9e10c55 --- /dev/null +++ b/e-tizen-testsuite/reference/scaler_shot-04.png diff --git a/e-tizen-testsuite/reference/subsurface_below_parent_with_alpha-00.png b/e-tizen-testsuite/reference/subsurface_below_parent_with_alpha-00.png Binary files differnew file mode 100644 index 0000000..63b5a88 --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_below_parent_with_alpha-00.png diff --git a/e-tizen-testsuite/reference/subsurface_below_parent_with_alpha-01.png b/e-tizen-testsuite/reference/subsurface_below_parent_with_alpha-01.png Binary files differnew file mode 100644 index 0000000..f55a7b2 --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_below_parent_with_alpha-01.png diff --git a/e-tizen-testsuite/reference/subsurface_shot_test-00.png b/e-tizen-testsuite/reference/subsurface_shot_test-00.png Binary files differnew file mode 100644 index 0000000..04608e5 --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_shot_test-00.png diff --git a/e-tizen-testsuite/reference/subsurface_shot_test-01.png b/e-tizen-testsuite/reference/subsurface_shot_test-01.png Binary files differnew file mode 100644 index 0000000..8df8342 --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_shot_test-01.png diff --git a/e-tizen-testsuite/reference/subsurface_shot_test-02.png b/e-tizen-testsuite/reference/subsurface_shot_test-02.png Binary files differnew file mode 100644 index 0000000..b923e8f --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_shot_test-02.png diff --git a/e-tizen-testsuite/reference/subsurface_shot_test-03.png b/e-tizen-testsuite/reference/subsurface_shot_test-03.png Binary files differnew file mode 100644 index 0000000..d2b4881 --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_shot_test-03.png diff --git a/e-tizen-testsuite/reference/subsurface_shot_test-04.png b/e-tizen-testsuite/reference/subsurface_shot_test-04.png Binary files differnew file mode 100644 index 0000000..37d05af --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_shot_test-04.png diff --git a/e-tizen-testsuite/reference/subsurface_shot_test-05.png b/e-tizen-testsuite/reference/subsurface_shot_test-05.png Binary files differnew file mode 100644 index 0000000..29f99f7 --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_shot_test-05.png diff --git a/e-tizen-testsuite/reference/subsurface_shot_test-06.png b/e-tizen-testsuite/reference/subsurface_shot_test-06.png Binary files differnew file mode 100644 index 0000000..8faa893 --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_shot_test-06.png diff --git a/e-tizen-testsuite/reference/subsurface_shot_test-07.png b/e-tizen-testsuite/reference/subsurface_shot_test-07.png Binary files differnew file mode 100644 index 0000000..eebf5b5 --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_shot_test-07.png diff --git a/e-tizen-testsuite/reference/subsurface_shot_test-08.png b/e-tizen-testsuite/reference/subsurface_shot_test-08.png Binary files differnew file mode 100644 index 0000000..f569457 --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_shot_test-08.png diff --git a/e-tizen-testsuite/reference/subsurface_shot_test-09.png b/e-tizen-testsuite/reference/subsurface_shot_test-09.png Binary files differnew file mode 100644 index 0000000..33ff4bc --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_shot_test-09.png diff --git a/e-tizen-testsuite/reference/subsurface_shot_test-10.png b/e-tizen-testsuite/reference/subsurface_shot_test-10.png Binary files differnew file mode 100644 index 0000000..db6ac2d --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_shot_test-10.png diff --git a/e-tizen-testsuite/reference/subsurface_stack_with_activate_by_res_id-00.png b/e-tizen-testsuite/reference/subsurface_stack_with_activate_by_res_id-00.png Binary files differnew file mode 100644 index 0000000..f305ce5 --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_stack_with_activate_by_res_id-00.png diff --git a/e-tizen-testsuite/reference/subsurface_stack_with_activate_by_res_id-01.png b/e-tizen-testsuite/reference/subsurface_stack_with_activate_by_res_id-01.png Binary files differnew file mode 100644 index 0000000..f2a6788 --- /dev/null +++ b/e-tizen-testsuite/reference/subsurface_stack_with_activate_by_res_id-01.png diff --git a/e-tizen-testsuite/reference/tizen_logo.png b/e-tizen-testsuite/reference/tizen_logo.png Binary files differnew file mode 100644 index 0000000..a402da7 --- /dev/null +++ b/e-tizen-testsuite/reference/tizen_logo.png diff --git a/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-00.png b/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-00.png Binary files differnew file mode 100644 index 0000000..7ef7c97 --- /dev/null +++ b/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-00.png diff --git a/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-01.png b/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-01.png Binary files differnew file mode 100644 index 0000000..97332d9 --- /dev/null +++ b/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-01.png diff --git a/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-02.png b/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-02.png Binary files differnew file mode 100644 index 0000000..85a6743 --- /dev/null +++ b/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-02.png diff --git a/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-03.png b/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-03.png Binary files differnew file mode 100644 index 0000000..2f473ea --- /dev/null +++ b/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-03.png diff --git a/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-04.png b/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-04.png Binary files differnew file mode 100644 index 0000000..5a3342c --- /dev/null +++ b/e-tizen-testsuite/reference/tizen_policy_place_subsurface_below_parent_test-04.png diff --git a/e-tizen-testsuite/reference/window_shot_basic-00.png b/e-tizen-testsuite/reference/window_shot_basic-00.png Binary files differnew file mode 100644 index 0000000..1de0c94 --- /dev/null +++ b/e-tizen-testsuite/reference/window_shot_basic-00.png diff --git a/e-tizen-testsuite/src/buffer-transform-test.c b/e-tizen-testsuite/src/buffer-transform-test.c new file mode 100644 index 0000000..682c7e7 --- /dev/null +++ b/e-tizen-testsuite/src/buffer-transform-test.c @@ -0,0 +1,235 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <unistd.h> + +#include <wayland-client-protocol.h> + +#include "e-tizen-test-client-helper.h" + +static int +check_screen(struct client *client, + const char *ref_image, + int ref_seq_no, + const struct rectangle *clip, + int seq_no) +{ + bool match; + + /* wl message flush */ + client_roundtrip(client); + /* wait for the frame applied to screen. */ + usleep(TIME_WAIT_FOR_FRAME); + match = verify_screen_content(client, ref_image, ref_seq_no, clip, + seq_no); + + return match ? 0 : -1; +} + +static struct window * +create_bg_window(struct client *client, pixman_color_t *custom_color) +{ + struct window *window; + struct buffer *buf; + struct wl_surface *surface; + int ow, oh; + pixman_color_t color; + + if (custom_color) + color = *custom_color; + else + color_rgb888(&color, 255, 255, 255); + + window = create_test_window(client); + assert(window); + + ow = client->output->width; + oh = client->output->height; + + buf = create_shm_buffer_a8r8g8b8(client, ow, oh); + fill_image_with_color(buf->image, &color); + + surface = window->surface->wl_surface; + wl_surface_attach(surface, buf->proxy, 0, 0); + wl_surface_damage(surface, 0, 0, ow, oh); + wl_surface_commit(surface); + + window->surface->buffer = buf; + + return window; +} + +struct buffer_args { + int scale; + enum wl_output_transform transform; + const char *transform_name; +}; + +#define TRANSFORM(x) WL_OUTPUT_TRANSFORM_ ## x, #x +static const struct buffer_args my_buffer_args[] = { + { 1, TRANSFORM(90) }, + { 1, TRANSFORM(180) }, + { 1, TRANSFORM(270) }, + { 1, TRANSFORM(FLIPPED) }, + { 1, TRANSFORM(FLIPPED_90) }, + { 1, TRANSFORM(FLIPPED_180) }, + { 1, TRANSFORM(FLIPPED_270) }, + { 2, TRANSFORM(NORMAL) }, + { 2, TRANSFORM(90) }, + { 2, TRANSFORM(180) }, + { 2, TRANSFORM(270) }, + { 2, TRANSFORM(FLIPPED) }, + { 3, TRANSFORM(NORMAL) }, + { 3, TRANSFORM(FLIPPED_90) }, +}; + +TEST_P(buffer_transform_with_image, my_buffer_args) +{ + const struct buffer_args *bargs = data; + struct client *client; + struct window *bg_win, *window; + struct buffer *buf; + struct wl_surface *surf; + pixman_color_t sky; + char *refname; + struct rectangle clip = { 50, 50, 0, 0 }; + int img_w, img_h; + int ret, fail = 0; + + color_rgb888(&sky, 135, 206, 235); + + client = create_client(); + bg_win = create_bg_window(client, &sky); + + window = create_test_window(client); + surf = window->surface->wl_surface; + window_move(window, 100, 100); + + buf = client_buffer_from_image_file(client, + "basic-test-card", + bargs->scale); + img_w = pixman_image_get_width(buf->image); + img_h = pixman_image_get_height(buf->image); + + wl_surface_set_buffer_scale(surf, bargs->scale); + wl_surface_set_buffer_transform(surf, bargs->transform); + + wl_surface_attach(surf, buf->proxy, 0, 0); + wl_surface_damage(surf, 0, 0, img_w, img_h); + wl_surface_commit(surf); + + window_visible_wait(window); + + ret = asprintf(&refname, "output_1-NORMAL_buffer_%d-%s", + bargs->scale, bargs->transform_name); + assert(ret); + + clip.width = img_w + 50; + clip.height = img_h + 50; + + fail += check_screen(client, refname, 0, &clip, 0); + assert(fail == 0); + + buffer_destroy(buf); + window_destroy(window); + window_destroy(bg_win); + client_destroy(client); +} + +#if 0 +static void +image_composite_with_color(pixman_image_t *dst, + pixman_color_t *color, + int x, int y, int w, int h) +{ + pixman_image_t *img; + + img = pixman_image_create_solid_fill(color); + pixman_image_composite32(PIXMAN_OP_OVER, + img, + NULL, + dst, + 0, 0, + 0, 0, + x, y, + w, h); + pixman_image_unref(img); +} + +static struct window * +create_main_window_with_transform(struct client *client, int scale, enum wl_output_transform transform) +{ + struct window *window; + struct buffer *buf; + struct wl_surface *surface; + pixman_color_t red, blue, green; + int width, height; + const int WIN_X = 100, WIN_Y = 50, WIN_W = 400, WIN_H = 500; + const int RECT_W = 100, RECT_H = 150; + const int BLUE_X = 50, BLUE_Y = 50; + const int GREEN_X = 200, GREEN_Y = 250; + + window = create_test_window(client); + assert(window); + + window_move(window, WIN_X, WIN_Y); + + color_rgb888(&red, 255, 0, 0); + color_rgb888(&blue, 0, 0, 255); + color_rgb888(&green, 0, 255, 0); + + width = WIN_W * scale; + height = WIN_H * scale; + + buf = create_shm_buffer_a8r8g8b8(client, width, height); + fill_image_with_color(buf->image, &red); + + image_composite_with_color(buf->image, + &blue, + BLUE_X * scale, + BLUE_Y * scale, + RECT_W * scale, + RECT_H * scale); + image_composite_with_color(buf->image, + &green, + GREEN_X * scale, + GREEN_Y * scale, + RECT_W * scale, + RECT_H * scale); + + surface = window->surface->wl_surface; + wl_surface_set_buffer_scale(surface, scale); + wl_surface_set_buffer_transform(surface, transform); + + wl_surface_attach(surface, buf->proxy, 0, 0); + wl_surface_damage(surface, 0, 0, width, height); + wl_surface_commit(surface); + + window_visible_wait(window); + + window->surface->buffer = buf; + + return window; +} + +TEST_P(buffer_transform_with_simple_rectangles, my_buffer_args) +{ + const struct buffer_args *bargs = data; + struct client *client; + struct window *bg_win, *window; + struct buffer *buf; + struct wl_surface *main_surf; + bool match; + pixman_color_t sky; + + color_rgb888(&sky, 135, 206, 235); + + client = create_client(); + bg_win = create_bg_window(client, &sky); + window = create_main_window_with_transform(client, bargs->scale, bargs->transform); + + match = verify_screen_content(client, NULL, 0, NULL, 0); + + window_destroy(window); + client_destroy(client); +} +#endif diff --git a/e-tizen-testsuite/src/e-tizen-test-client-helper.c b/e-tizen-testsuite/src/e-tizen-test-client-helper.c new file mode 100644 index 0000000..489ce1c --- /dev/null +++ b/e-tizen-testsuite/src/e-tizen-test-client-helper.c @@ -0,0 +1,1974 @@ +/* + * Copyright © 2012 Intel Corporation + * Copyright © 2015 Samsung Electronics Co., Ltd + * Copyright 2016, 2017 Collabora, Ltd. + * + * 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. + */ + +#include "config.h" + +#define _GNU_SOURCE // for using asprintf() +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/mman.h> +#include <cairo.h> +#include <time.h> + +#include "os-compatibility.h" +#include "e-tizen-testsuite-helper.h" +#include "e-tizen-test-client-helper.h" + +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#define min(a, b) (((a) > (b)) ? (b) : (a)) +#define clip(x, a, b) min(max(x, a), b) + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#endif + +static void +frame_callback_handler(void *data, struct wl_callback *callback, uint32_t time) +{ + int *done = data; + + *done = 1; + + wl_callback_destroy(callback); +} + +static const struct wl_callback_listener frame_listener = { + frame_callback_handler +}; + +struct wl_callback * +frame_callback_set(struct wl_surface *surface, int *done) +{ + struct wl_callback *callback; + + *done = 0; + callback = wl_surface_frame(surface); + wl_callback_add_listener(callback, &frame_listener, done); + + return callback; +} + +int +frame_callback_wait_nofail(struct client *client, int *done) +{ + while (!*done) { + if (wl_display_dispatch(client->wl_display) < 0) + return 0; + } + + return 1; +} + +static void +pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *wl_surface, + wl_fixed_t x, wl_fixed_t y) +{ + struct pointer *pointer = data; + + if (wl_surface) + pointer->focus = wl_surface_get_user_data(wl_surface); + else + pointer->focus = NULL; + + pointer->x = wl_fixed_to_int(x); + pointer->y = wl_fixed_to_int(y); + + testlog("test-client: got pointer enter %d %d, surface %p\n", + pointer->x, pointer->y, pointer->focus); +} + +static void +pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *wl_surface) +{ + struct pointer *pointer = data; + + pointer->focus = NULL; + + testlog("test-client: got pointer leave, surface %p\n", + wl_surface ? wl_surface_get_user_data(wl_surface) : NULL); +} + +static void +pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, + uint32_t time_msec, wl_fixed_t x, wl_fixed_t y) +{ + struct pointer *pointer = data; + + pointer->x = wl_fixed_to_int(x); + pointer->y = wl_fixed_to_int(y); + pointer->motion_time_msec = time_msec; + pointer->motion_time_timespec = pointer->input_timestamp; + pointer->input_timestamp = (struct timespec) { 0 }; + + testlog("test-client: got pointer motion %d %d\n", + pointer->x, pointer->y); +} + +static void +pointer_handle_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time_msec, uint32_t button, + uint32_t state) +{ + struct pointer *pointer = data; + + pointer->button = button; + pointer->state = state; + pointer->button_time_msec = time_msec; + pointer->button_time_timespec = pointer->input_timestamp; + pointer->input_timestamp = (struct timespec) { 0 }; + + testlog("test-client: got pointer button %u %u\n", button, state); +} + +static void +pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time_msec, uint32_t axis, wl_fixed_t value) +{ + struct pointer *pointer = data; + + pointer->axis = axis; + pointer->axis_value = wl_fixed_to_double(value); + pointer->axis_time_msec = time_msec; + pointer->axis_time_timespec = pointer->input_timestamp; + pointer->input_timestamp = (struct timespec) { 0 }; + + testlog("test-client: got pointer axis %u %f\n", + axis, wl_fixed_to_double(value)); +} + +static void +pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) +{ + testlog("test-client: got pointer frame\n"); +} + +static void +pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer, + uint32_t source) +{ + testlog("test-client: got pointer axis source %u\n", source); +} + +static void +pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer, + uint32_t time_msec, uint32_t axis) +{ + struct pointer *pointer = data; + + pointer->axis = axis; + pointer->axis_stop_time_msec = time_msec; + pointer->axis_stop_time_timespec = pointer->input_timestamp; + pointer->input_timestamp = (struct timespec) { 0 }; + + testlog("test-client: got pointer axis stop %u\n", axis); +} + +static void +pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer, + uint32_t axis, int32_t value) +{ + testlog("test-client: got pointer axis discrete %u %d\n", axis, value); +} + +static const struct wl_pointer_listener pointer_listener = { + pointer_handle_enter, + pointer_handle_leave, + pointer_handle_motion, + pointer_handle_button, + pointer_handle_axis, + pointer_handle_frame, + pointer_handle_axis_source, + pointer_handle_axis_stop, + pointer_handle_axis_discrete, +}; + +static void +keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, + uint32_t format, int fd, uint32_t size) +{ + close(fd); + + testlog("test-client: got keyboard keymap\n"); +} + +static void +keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, struct wl_surface *wl_surface, + struct wl_array *keys) +{ + struct keyboard *keyboard = data; + + if (wl_surface) + keyboard->focus = wl_surface_get_user_data(wl_surface); + else + keyboard->focus = NULL; + + testlog("test-client: got keyboard enter, surface %p\n", + keyboard->focus); +} + +static void +keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, struct wl_surface *wl_surface) +{ + struct keyboard *keyboard = data; + + keyboard->focus = NULL; + + testlog("test-client: got keyboard leave, surface %p\n", + wl_surface ? wl_surface_get_user_data(wl_surface) : NULL); +} + +static void +keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t time_msec, uint32_t key, + uint32_t state) +{ + struct keyboard *keyboard = data; + + keyboard->key = key; + keyboard->state = state; + keyboard->key_time_msec = time_msec; + keyboard->key_time_timespec = keyboard->input_timestamp; + keyboard->input_timestamp = (struct timespec) { 0 }; + + testlog("test-client: got keyboard key %u %u\n", key, state); +} + +static void +keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, + uint32_t group) +{ + struct keyboard *keyboard = data; + + keyboard->mods_depressed = mods_depressed; + keyboard->mods_latched = mods_latched; + keyboard->mods_locked = mods_locked; + keyboard->group = group; + + testlog("test-client: got keyboard modifiers %u %u %u %u\n", + mods_depressed, mods_latched, mods_locked, group); +} + +static void +keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, + int32_t rate, int32_t delay) +{ + struct keyboard *keyboard = data; + + keyboard->repeat_info.rate = rate; + keyboard->repeat_info.delay = delay; + + testlog("test-client: got keyboard repeat_info %d %d\n", rate, delay); +} + +static const struct wl_keyboard_listener keyboard_listener = { + keyboard_handle_keymap, + keyboard_handle_enter, + keyboard_handle_leave, + keyboard_handle_key, + keyboard_handle_modifiers, + keyboard_handle_repeat_info, +}; + +static void +touch_handle_down(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time_msec, + struct wl_surface *surface, int32_t id, + wl_fixed_t x_w, wl_fixed_t y_w) +{ + struct touch *touch = data; + + touch->down_x = wl_fixed_to_int(x_w); + touch->down_y = wl_fixed_to_int(y_w); + touch->id = id; + touch->down_time_msec = time_msec; + touch->down_time_timespec = touch->input_timestamp; + touch->input_timestamp = (struct timespec) { 0 }; + + testlog("test-client: got touch down %d %d, surf: %p, id: %d\n", + touch->down_x, touch->down_y, surface, id); +} + +static void +touch_handle_up(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time_msec, int32_t id) +{ + struct touch *touch = data; + touch->up_id = id; + touch->up_time_msec = time_msec; + touch->up_time_timespec = touch->input_timestamp; + touch->input_timestamp = (struct timespec) { 0 }; + + testlog("test-client: got touch up, id: %d\n", id); +} + +static void +touch_handle_motion(void *data, struct wl_touch *wl_touch, + uint32_t time_msec, int32_t id, + wl_fixed_t x_w, wl_fixed_t y_w) +{ + struct touch *touch = data; + touch->x = wl_fixed_to_int(x_w); + touch->y = wl_fixed_to_int(y_w); + touch->motion_time_msec = time_msec; + touch->motion_time_timespec = touch->input_timestamp; + touch->input_timestamp = (struct timespec) { 0 }; + + testlog("test-client: got touch motion, %d %d, id: %d\n", + touch->x, touch->y, id); +} + +static void +touch_handle_frame(void *data, struct wl_touch *wl_touch) +{ + struct touch *touch = data; + + ++touch->frame_no; + + testlog("test-client: got touch frame (%d)\n", touch->frame_no); +} + +static void +touch_handle_cancel(void *data, struct wl_touch *wl_touch) +{ + struct touch *touch = data; + + ++touch->cancel_no; + + testlog("test-client: got touch cancel (%d)\n", touch->cancel_no); +} + +static const struct wl_touch_listener touch_listener = { + touch_handle_down, + touch_handle_up, + touch_handle_motion, + touch_handle_frame, + touch_handle_cancel, +}; + +static void +surface_enter(void *data, + struct wl_surface *wl_surface, struct wl_output *output) +{ + struct surface *surface = data; + + surface->output = wl_output_get_user_data(output); + + testlog("test-client: got surface enter output %p\n", surface->output); +} + +static void +surface_leave(void *data, + struct wl_surface *wl_surface, struct wl_output *output) +{ + struct surface *surface = data; + + surface->output = NULL; + + testlog("test-client: got surface leave output %p\n", + wl_output_get_user_data(output)); +} + +static const struct wl_surface_listener surface_listener = { + surface_enter, + surface_leave +}; + +static struct buffer * +create_shm_buffer(struct client *client, int width, int height, + pixman_format_code_t format, uint32_t wlfmt) +{ + struct wl_shm *shm = client->wl_shm; + struct buffer *buf; + size_t stride_bytes; + struct wl_shm_pool *pool; + int fd; + void *data; + size_t bytes_pp; + + assert(width > 0); + assert(height > 0); + + buf = calloc(1, sizeof *buf); + + bytes_pp = PIXMAN_FORMAT_BPP(format) / 8; + stride_bytes = width * bytes_pp; + /* round up to multiple of 4 bytes for Pixman */ + stride_bytes = (stride_bytes + 3) & ~3u; + assert(stride_bytes / bytes_pp >= (unsigned)width); + + buf->len = stride_bytes * height; + assert(buf->len / stride_bytes == (unsigned)height); + + fd = os_create_anonymous_file(buf->len); + assert(fd >= 0); + + data = mmap(NULL, buf->len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { + close(fd); + assert(data != MAP_FAILED); + } + + pool = wl_shm_create_pool(shm, fd, buf->len); + buf->proxy = wl_shm_pool_create_buffer(pool, 0, width, height, + stride_bytes, wlfmt); + wl_shm_pool_destroy(pool); + close(fd); + + buf->image = pixman_image_create_bits(format, width, height, + data, stride_bytes); + + assert(buf->proxy); + assert(buf->image); + + return buf; +} + +struct buffer * +create_shm_buffer_a8r8g8b8(struct client *client, int width, int height) +{ + assert(client->has_argb); + + return create_shm_buffer(client, width, height, + PIXMAN_a8r8g8b8, WL_SHM_FORMAT_ARGB8888); +} + +void +buffer_destroy(struct buffer *buf) +{ + void *pixels; + + pixels = pixman_image_get_data(buf->image); + + if (buf->proxy) { + wl_buffer_destroy(buf->proxy); + assert(munmap(pixels, buf->len) == 0); + } + + assert(pixman_image_unref(buf->image)); + + free(buf); +} + +static void +shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) +{ + struct client *client = data; + + if (format == WL_SHM_FORMAT_ARGB8888) + client->has_argb = 1; +} + +struct wl_shm_listener shm_listener = { + shm_format +}; + +static void +input_destroy(struct input *inp) +{ + if (inp->pointer) { + wl_pointer_release(inp->pointer->wl_pointer); + free(inp->pointer); + } + if (inp->keyboard) { + wl_keyboard_release(inp->keyboard->wl_keyboard); + free(inp->keyboard); + } + if (inp->touch) { + wl_touch_release(inp->touch->wl_touch); + free(inp->touch); + } + wl_list_remove(&inp->link); + wl_seat_release(inp->wl_seat); + free(inp->seat_name); + free(inp); +} + +static void +input_update_devices(struct input *input) +{ + struct pointer *pointer; + struct keyboard *keyboard; + struct touch *touch; + + struct wl_seat *seat = input->wl_seat; + enum wl_seat_capability caps = input->caps; + + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) { + pointer = calloc(1, sizeof *pointer); + pointer->wl_pointer = wl_seat_get_pointer(seat); + wl_pointer_set_user_data(pointer->wl_pointer, pointer); + wl_pointer_add_listener(pointer->wl_pointer, &pointer_listener, + pointer); + input->pointer = pointer; + } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) { + wl_pointer_destroy(input->pointer->wl_pointer); + free(input->pointer); + input->pointer = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) { + keyboard = calloc(1, sizeof *keyboard); + keyboard->wl_keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_set_user_data(keyboard->wl_keyboard, keyboard); + wl_keyboard_add_listener(keyboard->wl_keyboard, &keyboard_listener, + keyboard); + input->keyboard = keyboard; + } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) { + wl_keyboard_destroy(input->keyboard->wl_keyboard); + free(input->keyboard); + input->keyboard = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->touch) { + touch = calloc(1, sizeof *touch); + touch->wl_touch = wl_seat_get_touch(seat); + wl_touch_set_user_data(touch->wl_touch, touch); + wl_touch_add_listener(touch->wl_touch, &touch_listener, + touch); + input->touch = touch; + } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->touch) { + wl_touch_destroy(input->touch->wl_touch); + free(input->touch); + input->touch = NULL; + } +} + +static void +seat_handle_capabilities(void *data, struct wl_seat *seat, + enum wl_seat_capability caps) +{ + struct input *input = data; + + input->caps = caps; + + /* we will create/update the devices only with the right (test) seat. + * If we haven't discovered which seat is the test seat, just + * store capabilities and bail out */ + if (input->seat_name && strcmp(input->seat_name, "test-seat") == 0) + input_update_devices(input); +} + +static void +seat_handle_name(void *data, struct wl_seat *seat, const char *name) +{ + struct input *input = data; + + input->seat_name = strdup(name); + assert(input->seat_name && "No memory"); + + /* We only update the devices and set client input for the test seat */ + if (strcmp(name, "test-seat") == 0) { + assert(!input->client->input && + "Multiple test seats detected!"); + + input_update_devices(input); + input->client->input = input; + } +} + +static const struct wl_seat_listener seat_listener = { + seat_handle_capabilities, + seat_handle_name, +}; + +static void +output_handle_geometry(void *data, + struct wl_output *wl_output, + int x, int y, + int physical_width, + int physical_height, + int subpixel, + const char *make, + const char *model, + int32_t transform) +{ + struct output *output = data; + + output->x = x; + output->y = y; +} + +static void +output_handle_mode(void *data, + struct wl_output *wl_output, + uint32_t flags, + int width, + int height, + int refresh) +{ + struct output *output = data; + + if (flags & WL_OUTPUT_MODE_CURRENT) { + output->width = width; + output->height = height; + } +} + +static void +output_handle_scale(void *data, + struct wl_output *wl_output, + int scale) +{ + struct output *output = data; + + output->scale = scale; +} + +static void +output_handle_done(void *data, + struct wl_output *wl_output) +{ + struct output *output = data; + + output->initialized = 1; +} + +static const struct wl_output_listener output_listener = { + output_handle_geometry, + output_handle_mode, + output_handle_done, + output_handle_scale, +}; + +static void +output_destroy(struct output *output) +{ + wl_proxy_get_version((struct wl_proxy *)output->wl_output); + wl_output_release(output->wl_output); + wl_list_remove(&output->link); + free(output); +} + +static void +screenshooter_handle_done(void *data, struct screenshooter *screenshooter) +{ + struct test *test = data; + + testlog("Screenshot has been captured\n"); + test->screenshot_done = 1; +} + +static const struct screenshooter_listener screenshooter_listener = { + screenshooter_handle_done, +}; + +static void +handle_global(void *data, struct wl_registry *registry, + uint32_t id, const char *interface, uint32_t version) +{ + struct client *client = data; + struct output *output; + struct test *test; + struct global *global; + struct input *input; + + global = calloc(1, sizeof *global); + global->name = id; + global->interface = strdup(interface); + assert(interface); + global->version = version; + wl_list_insert(client->global_list.prev, &global->link); + + /* We deliberately bind all globals with the maximum (advertised) + * version, because this test suite must be kept up-to-date with + * Weston. We must always implement at least the version advertised + * by Weston. This is not ok for normal clients, but it is ok in + * this test suite. + */ + + if (strcmp(interface, "wl_compositor") == 0) { + client->wl_compositor = + wl_registry_bind(registry, id, + &wl_compositor_interface, version); + } + else if (strcmp(interface, "wl_subcompositor") == 0) { + client->wl_subcompositor = + wl_registry_bind(registry, id, + &wl_subcompositor_interface, version); + } + else if (strcmp(interface, "wl_seat") == 0) { + input = calloc(1, sizeof *input); + input->client = client; + input->global_name = global->name; + input->wl_seat = + wl_registry_bind(registry, id, + &wl_seat_interface, version); + wl_seat_add_listener(input->wl_seat, &seat_listener, input); + wl_list_insert(&client->inputs, &input->link); + } + else if (strcmp(interface, "wl_shm") == 0) { + client->wl_shm = + wl_registry_bind(registry, id, + &wl_shm_interface, version); + wl_shm_add_listener(client->wl_shm, &shm_listener, client); + } + else if (strcmp(interface, "wl_output") == 0) { + output = calloc(1, sizeof *output); + output->wl_output = + wl_registry_bind(registry, id, + &wl_output_interface, version); + wl_output_add_listener(output->wl_output, + &output_listener, output); + wl_list_insert(&client->output_list, &output->link); + client->output = output; + } + else if (strcmp(interface, "screenshooter") == 0) { + test = calloc(1, sizeof *test); + test->screenshooter = + wl_registry_bind(registry, id, + &screenshooter_interface, version); + screenshooter_add_listener(test->screenshooter, &screenshooter_listener, test); + client->test = test; + } + else if (strcmp(interface, "wl_drm") == 0) { + client->has_wl_drm = true; + } + else if (strcmp(interface, "zxdg_shell_v6") == 0) { + client->shell = + wl_registry_bind(registry, id, &zxdg_shell_v6_interface, version); + } + else if (strcmp(interface, "tizen_policy") == 0) { + client->tz_pol = + wl_registry_bind(registry, id, &tizen_policy_interface, version); + } +} + +static struct global * +client_find_global_with_name(struct client *client, uint32_t name) +{ + struct global *global; + + wl_list_for_each(global, &client->global_list, link) { + if (global->name == name) + return global; + } + + return NULL; +} + +static struct input * +client_find_input_with_name(struct client *client, uint32_t name) +{ + struct input *input; + + wl_list_for_each(input, &client->inputs, link) { + if (input->global_name == name) + return input; + } + + return NULL; +} + +static void +global_destroy(struct global *global) +{ + wl_list_remove(&global->link); + free(global->interface); + free(global); +} + +static void +handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) +{ + struct client *client = data; + struct global *global; + struct input *input; + + global = client_find_global_with_name(client, name); + assert(global && "Request to remove unknown global"); + + if (strcmp(global->interface, "wl_seat") == 0) { + input = client_find_input_with_name(client, name); + if (input) { + if (client->input == input) + client->input = NULL; + input_destroy(input); + } + } + + /* XXX: handle wl_output */ + + global_destroy(global); +} + +static const struct wl_registry_listener registry_listener = { + handle_global, + handle_global_remove, +}; + +void +expect_protocol_error(struct client *client, + const struct wl_interface *intf, + uint32_t code) +{ + int err; + uint32_t errcode, failed = 0; + const struct wl_interface *interface; + unsigned int id; + + /* if the error has not come yet, make it happen */ + wl_display_roundtrip(client->wl_display); + + err = wl_display_get_error(client->wl_display); + + assert(err && "Expected protocol error but nothing came"); + assert(err == EPROTO && "Expected protocol error but got local error"); + + errcode = wl_display_get_protocol_error(client->wl_display, + &interface, &id); + + /* check error */ + if (errcode != code) { + testlog("Should get error code %d but got %d\n", code, errcode); + failed = 1; + } + + /* this should be definitely set */ + assert(interface); + + if (strcmp(intf->name, interface->name) != 0) { + testlog("Should get interface '%s' but got '%s'\n", + intf->name, interface->name); + failed = 1; + } + + if (failed) { + testlog("Expected other protocol error\n"); + abort(); + } + + /* all OK */ + testlog("Got expected protocol error on '%s' (object id: %d) " + "with code %d\n", interface->name, id, errcode); +} + +static void +log_handler(const char *fmt, va_list args) +{ + fprintf(stderr, "libwayland: "); + vfprintf(stderr, fmt, args); +} + +struct client * +create_client(void) +{ + struct client *client; + + wl_log_set_handler_client(log_handler); + + /* connect to display */ + client = calloc(1, sizeof *client); + client->wl_display = wl_display_connect(NULL); + assert(client->wl_display); + wl_list_init(&client->global_list); + wl_list_init(&client->inputs); + wl_list_init(&client->output_list); + + /* setup registry so we can bind to interfaces */ + client->wl_registry = wl_display_get_registry(client->wl_display); + wl_registry_add_listener(client->wl_registry, ®istry_listener, + client); + + /* this roundtrip makes sure we have all globals and we bound to them */ + client_roundtrip(client); + /* this roundtrip makes sure we got all wl_shm.format and wl_seat.* + * events */ + client_roundtrip(client); + + /* must have WL_SHM_FORMAT_ARGB32 */ + assert(client->has_argb); + + /* must have an output */ + assert(client->output); + + /* the output must be initialized */ + assert(client->output->initialized == 1); + + /* must have seat set */ + //assert(client->input); + + return client; +} + +struct surface * +create_test_surface(struct client *client) +{ + struct surface *surface; + + surface = calloc(1, sizeof *surface); + + surface->wl_surface = + wl_compositor_create_surface(client->wl_compositor); + assert(surface->wl_surface); + + wl_surface_add_listener(surface->wl_surface, &surface_listener, + surface); + + wl_surface_set_user_data(surface->wl_surface, surface); + + return surface; +} + +void +surface_destroy(struct surface *surface) +{ + if (surface->wl_surface) + wl_surface_destroy(surface->wl_surface); + if (surface->buffer) + buffer_destroy(surface->buffer); + free(surface); +} + +static void +zxdg_surface_handle_configure(void *data, struct zxdg_surface_v6 *zxdg_surface, uint32_t serial) +{ + zxdg_surface_v6_ack_configure(zxdg_surface, serial); +} + +static const struct zxdg_surface_v6_listener zxdg_surface_listener = { + zxdg_surface_handle_configure, +}; + +static void +zxdg_toplevel_handle_configure(void *data, struct zxdg_toplevel_v6 *zxdg_toplevel, int32_t width, int32_t heignt, struct wl_array *states) +{ + uint32_t *p; + + wl_array_for_each(p, states) { + uint32_t state = *p; + + switch (state) { + case ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED: + break; + case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN: + break; + case ZXDG_TOPLEVEL_V6_STATE_RESIZING: + break; + case ZXDG_TOPLEVEL_V6_STATE_ACTIVATED: + break; + default: + break; + } + } +} + +static void +zxdg_toplevel_handle_close(void *data, struct zxdg_toplevel_v6 *zxdg_toplevel) +{ + fprintf(stdout, "zxdg_toplevel_v6: close\n"); +} + +static const struct zxdg_toplevel_v6_listener zxdg_toplevel_listener = { + zxdg_toplevel_handle_configure, + zxdg_toplevel_handle_close, +}; + +struct window * +create_test_window(struct client *client) +{ + struct window *win; + + win = calloc(1, sizeof *win); + + win->client = client; + win->surface = create_test_surface(client); + assert(win->surface); + + win->zxdg_surface = + zxdg_shell_v6_get_xdg_surface(client->shell, win->surface->wl_surface); + + zxdg_surface_v6_add_listener(win->zxdg_surface, &zxdg_surface_listener, win); + + win->zxdg_toplevel = zxdg_surface_v6_get_toplevel(win->zxdg_surface); + + zxdg_toplevel_v6_add_listener(win->zxdg_toplevel, &zxdg_toplevel_listener, win); + + return win; +} + +void +window_destroy(struct window *window) +{ + if (window->tz_pos) + tizen_position_destroy(window->tz_pos); + + zxdg_toplevel_v6_destroy(window->zxdg_toplevel); + zxdg_surface_v6_destroy(window->zxdg_surface); + surface_destroy(window->surface); + free(window); +} + +void +window_set_user_geometry(struct window *window) +{ + if (window->user_geometry) + return; + + tizen_policy_add_aux_hint(window->client->tz_pol, + window->surface->wl_surface, + 0, "wm.policy.win.user.geometry", "1"); + + window->user_geometry = true; +} + +void +window_move(struct window *window, int x, int y) +{ + window_set_user_geometry(window); + + if (!window->tz_pos) { + window->tz_pos = tizen_policy_get_position(window->client->tz_pol, + window->surface->wl_surface); + } + + tizen_position_set(window->tz_pos, x, y); +} + +static void +tizen_visibility_handle_notify(void *data, struct tizen_visibility *vis, uint32_t status) +{ + bool *done = data; + + *done = (status == 0) ? true : false; +} + +static void +tizen_visibility_handle_changed(void *data, struct tizen_visibility *vis, uint32_t type, uint32_t option) +{ +} + +static const struct tizen_visibility_listener tizen_visibility_listener = +{ + tizen_visibility_handle_notify, + tizen_visibility_handle_changed +}; + +void +window_visible_wait(struct window *win) +{ + struct tizen_visibility *vis; + bool done = false; + + vis = tizen_policy_get_visibility(win->client->tz_pol, win->surface->wl_surface); + tizen_visibility_add_listener(vis, &tizen_visibility_listener, &done); + + while (!done) { + if (wl_display_dispatch(win->client->wl_display) < 0) + assert(0); + } +} + +struct client * +create_client_and_test_surface(int width, int height) +{ + struct client *client; + struct surface *surface; + pixman_color_t color = { 16384, 16384, 16384, 16384 }; /* uint16_t */ + pixman_image_t *solid; + int done = 0; + + client = create_client(); + + /* initialize the client surface */ + surface = create_test_surface(client); + client->surface = surface; + + surface->width = width; + surface->height = height; + surface->buffer = create_shm_buffer_a8r8g8b8(client, width, height); + + solid = pixman_image_create_solid_fill(&color); + pixman_image_composite32(PIXMAN_OP_SRC, + solid, /* src */ + NULL, /* mask */ + surface->buffer->image, /* dst */ + 0, 0, /* src x,y */ + 0, 0, /* mask x,y */ + 0, 0, /* dst x,y */ + width, height); + pixman_image_unref(solid); + + /* The attach here is necessary because commit() will call configure + * only on surfaces newly attached, and the one that sets the surface + * position is the configure. */ + wl_surface_attach(surface->wl_surface, surface->buffer->proxy, 0, 0); + wl_surface_damage(surface->wl_surface, 0, 0, surface->width, + surface->height); + + frame_callback_set(surface->wl_surface, &done); + + wl_surface_commit(surface->wl_surface); + + frame_callback_wait(client, &done); + + return client; +} + +void +client_destroy(struct client *client) +{ + if (client->surface) + surface_destroy(client->surface); + + while (!wl_list_empty(&client->inputs)) { + input_destroy(container_of(client->inputs.next, + struct input, link)); + } + + while (!wl_list_empty(&client->output_list)) { + output_destroy(container_of(client->output_list.next, + struct output, link)); + } + + while (!wl_list_empty(&client->global_list)) { + global_destroy(container_of(client->global_list.next, + struct global, link)); + } + + if (client->tz_pol) + tizen_policy_destroy(client->tz_pol); + if (client->shell) + zxdg_shell_v6_destroy(client->shell); + if (client->wl_shm) + wl_shm_destroy(client->wl_shm); + if (client->wl_compositor) + wl_compositor_destroy(client->wl_compositor); + if (client->wl_registry) + wl_registry_destroy(client->wl_registry); + + //client_roundtrip(client); + + if (client->wl_display) + wl_display_disconnect(client->wl_display); + free(client); +} + +static const char* +output_path(void) +{ + char *path = getenv("E_TIZEN_TEST_OUTPUT_PATH"); + + if (!path) + return "."; + + return path; +} + +char* +screenshot_output_filename(const char *basename, uint32_t seq) +{ + char *filename; + + if (asprintf(&filename, "%s/%s-%02d.png", + output_path(), basename, seq) < 0) + return NULL; + return filename; +} + +static const char* +reference_path(void) +{ + const char *path = E_TIZEN_TEST_REFERENCE_PATH; + + if (!path) + return "."; + return path; +} + +char* +screenshot_reference_filename(const char *basename, uint32_t seq) +{ + char *filename; + + if (asprintf(&filename, "%s/%s-%02d.png", + reference_path(), basename, seq) < 0) + return NULL; + return filename; +} + +char * +image_filename(const char *basename) +{ + char *filename; + + if (asprintf(&filename, "%s/%s.png", reference_path(), basename) < 0) + assert(0); + return filename; +} + +struct format_map_entry { + cairo_format_t cairo; + pixman_format_code_t pixman; +}; + +static const struct format_map_entry format_map[] = { + { CAIRO_FORMAT_ARGB32, PIXMAN_a8r8g8b8 }, + { CAIRO_FORMAT_RGB24, PIXMAN_x8r8g8b8 }, + { CAIRO_FORMAT_A8, PIXMAN_a8 }, + { CAIRO_FORMAT_RGB16_565, PIXMAN_r5g6b5 }, +}; + +static pixman_format_code_t +format_cairo2pixman(cairo_format_t fmt) +{ + unsigned i; + + for (i = 0; i < ARRAY_LENGTH(format_map); i++) + if (format_map[i].cairo == fmt) + return format_map[i].pixman; + + assert(0 && "unknown Cairo pixel format"); +} + +static cairo_format_t +format_pixman2cairo(pixman_format_code_t fmt) +{ + unsigned i; + + for (i = 0; i < ARRAY_LENGTH(format_map); i++) + if (format_map[i].pixman == fmt) + return format_map[i].cairo; + + assert(0 && "unknown Pixman pixel format"); +} + +/** + * Validate range + * + * \param r Range to validate or NULL. + * \return The given range, or {0, 0} for NULL. + * + * Will abort if range is invalid, that is a > b. + */ +static struct range +range_get(const struct range *r) +{ + if (!r) + return (struct range){ 0, 0 }; + + assert(r->a <= r->b); + return *r; +} + +/** + * Compute the ROI for image comparisons + * + * \param img_a An image. + * \param img_b Another image. + * \param clip_rect Explicit ROI, or NULL for using the whole + * image area. + * + * \return The region of interest (ROI) that is guaranteed to be inside both + * images. + * + * If clip_rect is given, it must fall inside of both images. + * If clip_rect is NULL, the images must be of the same size. + * If any precondition is violated, this function aborts with an error. + * + * The ROI is given as pixman_box32_t, where x2,y2 are non-inclusive. + */ +static pixman_box32_t +image_check_get_roi(pixman_image_t *img_a, pixman_image_t *img_b, + const struct rectangle *clip_rect) +{ + int width_a; + int width_b; + int height_a; + int height_b; + pixman_box32_t box; + + width_a = pixman_image_get_width(img_a); + height_a = pixman_image_get_height(img_a); + + width_b = pixman_image_get_width(img_b); + height_b = pixman_image_get_height(img_b); + + if (clip_rect) { + box.x1 = clip_rect->x; + box.y1 = clip_rect->y; + box.x2 = clip_rect->x + clip_rect->width; + box.y2 = clip_rect->y + clip_rect->height; + } else { + box.x1 = 0; + box.y1 = 0; + box.x2 = max(width_a, width_b); + box.y2 = max(height_a, height_b); + } + + assert(box.x1 >= 0); + assert(box.y1 >= 0); + assert(box.x2 > box.x1); + assert(box.y2 > box.y1); + assert(box.x2 <= width_a); + assert(box.x2 <= width_b); + assert(box.y2 <= height_a); + assert(box.y2 <= height_b); + + return box; +} + +struct image_iterator { + char *data; + int stride; /* bytes */ +}; + +static void +image_iter_init(struct image_iterator *it, pixman_image_t *image) +{ + pixman_format_code_t fmt; + + it->stride = pixman_image_get_stride(image); + it->data = (void *)pixman_image_get_data(image); + + fmt = pixman_image_get_format(image); + assert(PIXMAN_FORMAT_BPP(fmt) == 32); +} + +static uint32_t * +image_iter_get_row(struct image_iterator *it, int y) +{ + return (uint32_t *)(it->data + y * it->stride); +} + +struct pixel_diff_stat { + struct pixel_diff_stat_channel { + int min_diff; + int max_diff; + } ch[4]; +}; + +static void +testlog_pixel_diff_stat(const struct pixel_diff_stat *stat) +{ + int i; + + testlog("Image difference statistics:\n"); + for (i = 0; i < 4; i++) { + testlog("\tch %d: [%d, %d]\n", + i, stat->ch[i].min_diff, stat->ch[i].max_diff); + } +} + +static bool +fuzzy_match_pixels(uint32_t pix_a, uint32_t pix_b, + const struct range *fuzz, + struct pixel_diff_stat *stat) +{ + bool ret = true; + int shift; + int i; + + for (shift = 0, i = 0; i < 4; shift += 8, i++) { + int val_a = (pix_a >> shift) & 0xffu; + int val_b = (pix_b >> shift) & 0xffu; + int d = val_b - val_a; + + stat->ch[i].min_diff = min(stat->ch[i].min_diff, d); + stat->ch[i].max_diff = max(stat->ch[i].max_diff, d); + + if (d < fuzz->a || d > fuzz->b) + ret = false; + } + + return ret; +} + +/** + * Test if a given region within two images are pixel-identical + * + * Returns true if the two images pixel-wise identical, and false otherwise. + * + * \param img_a First image. + * \param img_b Second image. + * \param clip_rect The region of interest, or NULL for comparing the whole + * images. + * \param prec Per-channel allowed difference, or NULL for identical match + * required. + * + * This function hard-fails if clip_rect is not inside both images. If clip_rect + * is given, the images do not have to match in size, otherwise size mismatch + * will be a hard failure. + * + * The per-pixel, per-channel difference is computed as img_b - img_a which is + * required to be in the range [prec->a, prec->b] inclusive. The difference is + * signed. All four channels are compared the same way, without any special + * meaning on alpha channel. + */ +bool +check_images_match(pixman_image_t *img_a, pixman_image_t *img_b, + const struct rectangle *clip_rect, const struct range *prec) +{ + struct range fuzz = range_get(prec); + struct pixel_diff_stat diffstat = {}; + struct image_iterator it_a; + struct image_iterator it_b; + pixman_box32_t box; + int x, y; + uint32_t *pix_a; + uint32_t *pix_b; + + box = image_check_get_roi(img_a, img_b, clip_rect); + + image_iter_init(&it_a, img_a); + image_iter_init(&it_b, img_b); + + for (y = box.y1; y < box.y2; y++) { + pix_a = image_iter_get_row(&it_a, y) + box.x1; + pix_b = image_iter_get_row(&it_b, y) + box.x1; + + for (x = box.x1; x < box.x2; x++) { + if (!fuzzy_match_pixels(*pix_a, *pix_b, + &fuzz, &diffstat)) + return false; + + pix_a++; + pix_b++; + } + } + + return true; +} + +/** + * Tint a color + * + * \param src Source pixel as x8r8g8b8. + * \param add The tint as x8r8g8b8, x8 must be zero; r8, g8 and b8 must be + * no greater than 0xc0 to avoid overflow to another channel. + * \return The tinted pixel color as x8r8g8b8, x8 guaranteed to be 0xff. + * + * The source pixel RGB values are divided by 4, and then the tint is added. + * To achieve colors outside of the range of src, a tint color channel must be + * at least 0x40. (0xff / 4 = 0x3f, 0xff - 0x3f = 0xc0) + */ +static uint32_t +tint(uint32_t src, uint32_t add) +{ + uint32_t v; + + v = ((src & 0xfcfcfcfc) >> 2) | 0xff000000; + + return v + add; +} + +/** + * Create a visualization of image differences. + * + * \param img_a First image, which is used as the basis for the output. + * \param img_b Second image. + * \param clip_rect The region of interest, or NULL for comparing the whole + * images. + * \param prec Per-channel allowed difference, or NULL for identical match + * required. + * \return A new image with the differences highlighted. + * + * Regions outside of the region of interest are shaded with black, matching + * pixels are shaded with green, and differing pixels are shaded with + * bright red. + * + * This function hard-fails if clip_rect is not inside both images. If clip_rect + * is given, the images do not have to match in size, otherwise size mismatch + * will be a hard failure. + * + * The per-pixel, per-channel difference is computed as img_b - img_a which is + * required to be in the range [prec->a, prec->b] inclusive. The difference is + * signed. All four channels are compared the same way, without any special + * meaning on alpha channel. + */ +pixman_image_t * +visualize_image_difference(pixman_image_t *img_a, pixman_image_t *img_b, + const struct rectangle *clip_rect, + const struct range *prec) +{ + struct range fuzz = range_get(prec); + struct pixel_diff_stat diffstat = {}; + pixman_image_t *diffimg; + pixman_image_t *shade; + struct image_iterator it_a; + struct image_iterator it_b; + struct image_iterator it_d; + int width; + int height; + pixman_box32_t box; + int x, y; + uint32_t *pix_a; + uint32_t *pix_b; + uint32_t *pix_d; + pixman_color_t shade_color = { 0, 0, 0, 32768 }; + + width = pixman_image_get_width(img_a); + height = pixman_image_get_height(img_a); + box = image_check_get_roi(img_a, img_b, clip_rect); + + diffimg = pixman_image_create_bits_no_clear(PIXMAN_x8r8g8b8, + width, height, NULL, 0); + + /* Fill diffimg with a black-shaded copy of img_a, and then fill + * the clip_rect area with original img_a. + */ + shade = pixman_image_create_solid_fill(&shade_color); + pixman_image_composite32(PIXMAN_OP_SRC, img_a, shade, diffimg, + 0, 0, 0, 0, 0, 0, width, height); + pixman_image_unref(shade); + pixman_image_composite32(PIXMAN_OP_SRC, img_a, NULL, diffimg, + box.x1, box.y1, 0, 0, box.x1, box.y1, + box.x2 - box.x1, box.y2 - box.y1); + + image_iter_init(&it_a, img_a); + image_iter_init(&it_b, img_b); + image_iter_init(&it_d, diffimg); + + for (y = box.y1; y < box.y2; y++) { + pix_a = image_iter_get_row(&it_a, y) + box.x1; + pix_b = image_iter_get_row(&it_b, y) + box.x1; + pix_d = image_iter_get_row(&it_d, y) + box.x1; + + for (x = box.x1; x < box.x2; x++) { + if (fuzzy_match_pixels(*pix_a, *pix_b, + &fuzz, &diffstat)) + *pix_d = tint(*pix_d, 0x00008000); /* green */ + else + *pix_d = tint(*pix_d, 0x00c00000); /* red */ + + pix_a++; + pix_b++; + pix_d++; + } + } + + testlog_pixel_diff_stat(&diffstat); + + return diffimg; +} + +/** + * Write an image into a PNG file. + * + * \param image The image. + * \param fname The name and path for the file. + * + * \returns true if successfully saved file; false otherwise. + * + * \note Only image formats directly supported by Cairo are accepted, not all + * Pixman formats. + */ +bool +write_image_as_png(pixman_image_t *image, const char *fname) +{ + cairo_surface_t *cairo_surface; + cairo_status_t status; + cairo_format_t fmt; + + fmt = format_pixman2cairo(pixman_image_get_format(image)); + + cairo_surface = cairo_image_surface_create_for_data( + (void *)pixman_image_get_data(image), + fmt, + pixman_image_get_width(image), + pixman_image_get_height(image), + pixman_image_get_stride(image)); + + status = cairo_surface_write_to_png(cairo_surface, fname); + if (status != CAIRO_STATUS_SUCCESS) { + testlog("Failed to save image '%s': %s\n", fname, + cairo_status_to_string(status)); + + return false; + } + + cairo_surface_destroy(cairo_surface); + + return true; +} + +static pixman_image_t * +image_convert_to_a8r8g8b8(pixman_image_t *image) +{ + pixman_image_t *ret; + int width; + int height; + + if (pixman_image_get_format(image) == PIXMAN_a8r8g8b8) + return pixman_image_ref(image); + + width = pixman_image_get_width(image); + height = pixman_image_get_height(image); + + ret = pixman_image_create_bits_no_clear(PIXMAN_a8r8g8b8, width, height, + NULL, 0); + assert(ret); + + pixman_image_composite32(PIXMAN_OP_SRC, image, NULL, ret, + 0, 0, 0, 0, 0, 0, width, height); + + return ret; +} + +static void +destroy_cairo_surface(pixman_image_t *image, void *data) +{ + cairo_surface_t *surface = data; + + cairo_surface_destroy(surface); +} + +/** + * Load an image from a PNG file + * + * Reads a PNG image from disk using the given filename (and path) + * and returns as a Pixman image. Use pixman_image_unref() to free it. + * + * The returned image is always in PIXMAN_a8r8g8b8 format. + * + * @returns Pixman image, or NULL in case of error. + */ +pixman_image_t * +load_image_from_png(const char *fname) +{ + pixman_image_t *image; + pixman_image_t *converted; + cairo_format_t cairo_fmt; + pixman_format_code_t pixman_fmt; + cairo_surface_t *reference_cairo_surface; + cairo_status_t status; + int width; + int height; + int stride; + void *data; + + reference_cairo_surface = cairo_image_surface_create_from_png(fname); + cairo_surface_flush(reference_cairo_surface); + status = cairo_surface_status(reference_cairo_surface); + if (status != CAIRO_STATUS_SUCCESS) { + testlog("Could not open %s: %s\n", fname, + cairo_status_to_string(status)); + cairo_surface_destroy(reference_cairo_surface); + return NULL; + } + + cairo_fmt = cairo_image_surface_get_format(reference_cairo_surface); + pixman_fmt = format_cairo2pixman(cairo_fmt); + + width = cairo_image_surface_get_width(reference_cairo_surface); + height = cairo_image_surface_get_height(reference_cairo_surface); + stride = cairo_image_surface_get_stride(reference_cairo_surface); + data = cairo_image_surface_get_data(reference_cairo_surface); + + /* The Cairo surface will own the data, so we keep it around. */ + image = pixman_image_create_bits_no_clear(pixman_fmt, + width, height, data, stride); + assert(image); + + pixman_image_set_destroy_function(image, destroy_cairo_surface, + reference_cairo_surface); + + converted = image_convert_to_a8r8g8b8(image); + pixman_image_unref(image); + + return converted; +} + +/** + * Take screenshot of a single output + * + * Requests a screenshot from the server of the output that the + * client appears on. + * + * @returns A new buffer object, that should be freed with buffer_destroy(). + */ +struct buffer * +capture_screenshot_of_output(struct client *client) +{ + struct buffer *buffer; + + buffer = create_shm_buffer_a8r8g8b8(client, + client->output->width, + client->output->height); + + client->test->screenshot_done = 0; + screenshooter_shoot(client->test->screenshooter, + client->output->wl_output, + buffer->proxy); + + while (client->test->screenshot_done == 0) { + if (wl_display_dispatch(client->wl_display) < 0) + break; + } + + return buffer; +} + +static void +write_visual_diff(pixman_image_t *ref_image, + struct buffer *shot, + const struct rectangle *clip, + const char *test_name, + int seq_no, + const struct range *fuzz) +{ + char *fname; + char *ext_test_name; + pixman_image_t *diff; + int ret; + + ret = asprintf(&ext_test_name, "%s-diff", test_name); + assert(ret >= 0); + + fname = screenshot_output_filename(ext_test_name, seq_no); + diff = visualize_image_difference(ref_image, shot->image, clip, fuzz); + write_image_as_png(diff, fname); + + pixman_image_unref(diff); + free(fname); + free(ext_test_name); +} + +/** + * Take a screenshot and verify its contents + * + * Takes a screenshot and writes the image into a PNG file named with + * get_test_name() and seq_no. Compares the contents to the given reference + * image over the given clip rectangle, reports whether they match to the + * test log, and if they do not match writes a visual diff into a PNG file. + * + * The compositor output size and the reference image size must both contain + * the clip rectangle. + * + * This function uses the pixel value allowed fuzz approriate for GL-renderer + * with 8 bits per channel data. + * + * \param client The client, for connecting to the compositor. + * \param ref_image The reference image file basename, without sequence number + * and .png suffix. + * \param ref_seq_no The reference image sequence number. + * \param clip The region of interest, or NULL for comparing the whole + * images. + * \param seq_no Test sequence number, for writing output files. + * \return True if the screen contents matches the reference image, + * false otherwise. + * + * For bootstrapping, ref_image can be NULL or the file can be missing. + * In that case the screenshot file is written but no comparison is performed, + * and false is returned. + */ +bool +verify_screen_content(struct client *client, + const char *ref_image, + int ref_seq_no, + const struct rectangle *clip, + int seq_no) +{ + const char *test_name = get_test_name(); + const struct range gl_fuzz = { -3, 4 }; + struct buffer *shot; + pixman_image_t *ref = NULL; + char *ref_fname = NULL; + char *shot_fname; + bool match; + + shot = capture_screenshot_of_output(client); + assert(shot); + shot_fname = screenshot_output_filename(test_name, seq_no); + write_image_as_png(shot->image, shot_fname); + + if (ref_image) { + ref_fname = screenshot_reference_filename(ref_image, ref_seq_no); + ref = load_image_from_png(ref_fname); + } + + if (ref) { + match = check_images_match(ref, shot->image, clip, &gl_fuzz); + testlog("Verify reference image %s vs. shot %s: %s\n", + ref_fname, shot_fname, match ? "PASS" : "FAIL"); + + if (!match) { + write_visual_diff(ref, shot, clip, + test_name, seq_no, &gl_fuzz); + } + + pixman_image_unref(ref); + } else { + testlog("No reference image, shot %s: FAIL\n", shot_fname); + match = false; + } + + free(ref_fname); + buffer_destroy(shot); + free(shot_fname); + + return match; +} + +/** + * Create a wl_buffer from a PNG file + * + * Loads the named PNG file from the directory of reference images, + * creates a wl_buffer with scale times the image dimensions in pixels, + * and copies the image content into the buffer using nearest-neighbor filter. + * + * \param client The client, for the Wayland connection. + * \param basename The PNG file name without .png suffix. + * \param scale Upscaling factor >= 1. + */ +struct buffer * +client_buffer_from_image_file(struct client *client, + const char *basename, + int scale) +{ + struct buffer *buf; + char *fname; + pixman_image_t *img; + int buf_w, buf_h; + pixman_transform_t scaling; + + assert(scale >= 1); + + fname = image_filename(basename); + img = load_image_from_png(fname); + free(fname); + assert(img); + + buf_w = scale * pixman_image_get_width(img); + buf_h = scale * pixman_image_get_height(img); + buf = create_shm_buffer_a8r8g8b8(client, buf_w, buf_h); + + pixman_transform_init_scale(&scaling, + pixman_fixed_1 / scale, + pixman_fixed_1 / scale); + pixman_image_set_transform(img, &scaling); + pixman_image_set_filter(img, PIXMAN_FILTER_NEAREST, NULL, 0); + + pixman_image_composite32(PIXMAN_OP_SRC, + img, /* src */ + NULL, /* mask */ + buf->image, /* dst */ + 0, 0, /* src x,y */ + 0, 0, /* mask x,y */ + 0, 0, /* dst x,y */ + buf_w, buf_h); + pixman_image_unref(img); + + return buf; +} + +/** + * Bind to a singleton global in wl_registry + * + * \param client Client whose registry and globals to use. + * \param iface The Wayland interface to look for. + * \param version The version to bind the interface with. + * \return A struct wl_proxy, which you need to cast to the proper type. + * + * Asserts that the global being searched for is a singleton and is found. + * + * Binds with the exact version given, does not take compositor interface + * version into account. + */ +void * +bind_to_singleton_global(struct client *client, + const struct wl_interface *iface, + int version) +{ + struct global *tmp; + struct global *g = NULL; + struct wl_proxy *proxy; + + wl_list_for_each(tmp, &client->global_list, link) { + if (strcmp(tmp->interface, iface->name)) + continue; + + assert(!g && "multiple singleton objects"); + g = tmp; + } + + assert(g && "singleton not found"); + + proxy = wl_registry_bind(client->wl_registry, g->name, iface, version); + assert(proxy); + + return proxy; +} + +/** + * Fill the image with the given color + * + * \param image The image to write to. + * \param color The color to use. + */ +void +fill_image_with_color(pixman_image_t *image, pixman_color_t *color) +{ + pixman_image_t *solid; + int width; + int height; + + width = pixman_image_get_width(image); + height = pixman_image_get_height(image); + + solid = pixman_image_create_solid_fill(color); + pixman_image_composite32(PIXMAN_OP_SRC, + solid, /* src */ + NULL, /* mask */ + image, /* dst */ + 0, 0, /* src x,y */ + 0, 0, /* mask x,y */ + 0, 0, /* dst x,y */ + width, height); + pixman_image_unref(solid); +} + +/** + * Convert 8-bit RGB to opaque Pixman color + * + * \param tmp Pixman color struct to fill in. + * \param r Red value, 0 - 255. + * \param g Green value, 0 - 255. + * \param b Blue value, 0 - 255. + * \return tmp + */ +pixman_color_t * +color_rgb888(pixman_color_t *tmp, uint8_t r, uint8_t g, uint8_t b) +{ + tmp->alpha = 65535; + tmp->red = (r << 8) + r; + tmp->green = (g << 8) + g; + tmp->blue = (b << 8) + b; + + return tmp; +} diff --git a/e-tizen-testsuite/src/e-tizen-test-client-helper.h b/e-tizen-testsuite/src/e-tizen-test-client-helper.h new file mode 100644 index 0000000..0507880 --- /dev/null +++ b/e-tizen-testsuite/src/e-tizen-test-client-helper.h @@ -0,0 +1,300 @@ +/* + * Copyright © 2012 Intel Corporation + * + * 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. + */ + +#ifndef _E_TIZEN_TEST_CLIENT_HELPER_H_ +#define _E_TIZEN_TEST_CLIENT_HELPER_H_ + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> +#include <time.h> +#include <pixman.h> + +#include <wayland-client-protocol.h> +#include <xdg-shell-unstable-v6-client-protocol.h> +#include <tizen-extension-client-protocol.h> +#include <screenshooter-client-protocol.h> +#include "e-tizen-test-runner.h" + +struct client { + struct wl_display *wl_display; + struct wl_registry *wl_registry; + struct wl_compositor *wl_compositor; + struct wl_subcompositor *wl_subcompositor; + struct wl_shm *wl_shm; + struct zxdg_shell_v6 *shell; + struct tizen_policy *tz_pol; + struct test *test; + /* the seat that is actually used for input events */ + struct input *input; + /* server can have more wl_seats. We need keep them all until we + * find the one that we need. After that, the others + * will be destroyed, so this list will have the length of 1. + * If some day in the future we will need the other seats, + * we can just keep them here. */ + struct wl_list inputs; + struct output *output; + struct surface *surface; + struct window *window; + int has_argb; + struct wl_list global_list; + bool has_wl_drm; + struct wl_list output_list; /* struct output::link */ +}; + +struct global { + uint32_t name; + char *interface; + uint32_t version; + struct wl_list link; +}; + +struct test { + struct screenshooter *screenshooter; + int screenshot_done; +}; + +struct input { + struct client *client; + uint32_t global_name; + struct wl_seat *wl_seat; + struct pointer *pointer; + struct keyboard *keyboard; + struct touch *touch; + char *seat_name; + enum wl_seat_capability caps; + struct wl_list link; +}; + +struct pointer { + struct wl_pointer *wl_pointer; + struct surface *focus; + int x; + int y; + uint32_t button; + uint32_t state; + uint32_t axis; + double axis_value; + uint32_t motion_time_msec; + uint32_t button_time_msec; + uint32_t axis_time_msec; + uint32_t axis_stop_time_msec; + struct timespec input_timestamp; + struct timespec motion_time_timespec; + struct timespec button_time_timespec; + struct timespec axis_time_timespec; + struct timespec axis_stop_time_timespec; +}; + +struct keyboard { + struct wl_keyboard *wl_keyboard; + struct surface *focus; + uint32_t key; + uint32_t state; + uint32_t mods_depressed; + uint32_t mods_latched; + uint32_t mods_locked; + uint32_t group; + struct { + int rate; + int delay; + } repeat_info; + uint32_t key_time_msec; + struct timespec input_timestamp; + struct timespec key_time_timespec; +}; + +struct touch { + struct wl_touch *wl_touch; + int down_x; + int down_y; + int x; + int y; + int id; + int up_id; /* id of last wl_touch.up event */ + int frame_no; + int cancel_no; + uint32_t down_time_msec; + uint32_t up_time_msec; + uint32_t motion_time_msec; + struct timespec input_timestamp; + struct timespec down_time_timespec; + struct timespec up_time_timespec; + struct timespec motion_time_timespec; +}; + +struct output { + struct wl_output *wl_output; + struct wl_list link; /* struct client::output_list */ + int x; + int y; + int width; + int height; + int scale; + int initialized; +}; + +struct buffer { + struct wl_buffer *proxy; + size_t len; + pixman_image_t *image; +}; + +struct surface { + struct wl_surface *wl_surface; + struct output *output; /* not owned */ + int width; + int height; + struct buffer *buffer; +}; + +struct rectangle { + int x; + int y; + int width; + int height; +}; + +struct range { + int a; + int b; +}; + +struct window { + struct client *client; + struct surface *surface; + struct zxdg_surface_v6 *zxdg_surface; + struct zxdg_toplevel_v6 *zxdg_toplevel; + struct tizen_position *tz_pos; + bool user_geometry; +}; + +struct client * +create_client(void); + +void +client_destroy(struct client *client); + +struct surface * +create_test_surface(struct client *client); + +void +surface_destroy(struct surface *surface); + +struct window * +create_test_window(struct client *client); + +void +window_destroy(struct window *window); + +void +window_visible_wait(struct window *window); + +void +window_move(struct window *window, int x, int y); + +struct client * +create_client_and_test_surface(int width, int height); + +struct buffer * +create_shm_buffer_a8r8g8b8(struct client *client, int width, int height); + +void +buffer_destroy(struct buffer *buf); + +int +surface_contains(struct surface *surface, int x, int y); + +#define client_roundtrip(c) do { \ + assert(wl_display_roundtrip((c)->wl_display) >= 0); \ +} while (0) + +struct wl_callback * +frame_callback_set(struct wl_surface *surface, int *done); + +int +frame_callback_wait_nofail(struct client *client, int *done); + +#define frame_callback_wait(c, d) assert(frame_callback_wait_nofail((c), (d))) + +void +expect_protocol_error(struct client *client, + const struct wl_interface *intf, uint32_t code); + +char * +screenshot_output_filename(const char *basename, uint32_t seq); + +char * +screenshot_reference_filename(const char *basename, uint32_t seq); + +char * +image_filename(const char *basename); + +bool +check_images_match(pixman_image_t *img_a, pixman_image_t *img_b, + const struct rectangle *clip, + const struct range *prec); + +pixman_image_t * +visualize_image_difference(pixman_image_t *img_a, pixman_image_t *img_b, + const struct rectangle *clip_rect, + const struct range *prec); + +bool +write_image_as_png(pixman_image_t *image, const char *fname); + +pixman_image_t * +load_image_from_png(const char *fname); + +struct buffer * +capture_screenshot_of_output(struct client *client); + +bool +verify_screen_content(struct client *client, + const char *ref_image, + int ref_seq_no, + const struct rectangle *clip, + int seq_no); + +struct buffer * +client_buffer_from_image_file(struct client *client, + const char *basename, + int scale); + +void * +bind_to_singleton_global(struct client *client, + const struct wl_interface *iface, + int version); + +struct wp_viewport * +client_create_viewport(struct client *client); + +void +fill_image_with_color(pixman_image_t *image, pixman_color_t *color); + +pixman_color_t * +color_rgb888(pixman_color_t *tmp, uint8_t r, uint8_t g, uint8_t b); + +#endif diff --git a/e-tizen-testsuite/src/e-tizen-test-runner.c b/e-tizen-testsuite/src/e-tizen-test-runner.c new file mode 100644 index 0000000..f692467 --- /dev/null +++ b/e-tizen-testsuite/src/e-tizen-test-runner.c @@ -0,0 +1,598 @@ +/* + * Copyright © 2012 Intel Corporation + * + * 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. + */ + +#include <stdbool.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <signal.h> +#include <getopt.h> + +#include "e-tizen-test-runner.h" +#include "e-tizen-testsuite-data.h" + +/** + * \defgroup testharness Test harness + * \defgroup testharness_private Test harness private + */ + +extern const struct e_tizen_test_entry __start_test_section, __stop_test_section; + +struct e_tizen_test_run_info { + char name[512]; + int fixture_nr; +}; + +static const struct e_tizen_test_run_info *test_run_info_; + +/** Get the test name string with counter + * + * \return The test name with fixture number \c -f%%d added. For an array + * driven test, e.g. defined with TEST_P(), the name has also a \c -e%%d + * suffix to indicate the array element number. + * + * This is only usable from code paths inside TEST(), TEST_P(), PLUGIN_TEST() + * etc. defined functions. + * + * \ingroup testharness + */ +const char * +get_test_name(void) +{ + return test_run_info_->name; +} + +/** Get the current fixture index + * + * Returns the current fixture index which can be used directly as an index + * into the array passed as an argument to DECLARE_FIXTURE_SETUP_WITH_ARG(). + * + * This is only usable from code paths inside TEST(), TEST_P(), PLUGIN_TEST() + * etc. defined functions. + * + * \ingroup testharness + */ +int +get_test_fixture_index(void) +{ + return test_run_info_->fixture_nr - 1; +} + +/** Print into test log + * + * This is exactly like printf() except the output goes to the test log, + * which is at stderr. + * + * \param fmt printf format string + * + * \ingroup testharness + */ +void +testlog(const char *fmt, ...) +{ + va_list argp; + + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); +} + +static const struct e_tizen_test_entry * +find_test(const char *name) +{ + const struct e_tizen_test_entry *t; + + for (t = &__start_test_section; t < &__stop_test_section; t++) + if (strcmp(t->name, name) == 0) + return t; + + return NULL; +} + +static enum test_result_code +run_test(int fixture_nr, const struct e_tizen_test_entry *t, void *data, + int iteration) +{ + struct e_tizen_test_run_info info; + + if (data) { + snprintf(info.name, sizeof(info.name), "%s-f%02d-e%02d", + t->name, fixture_nr, iteration); + } else { + snprintf(info.name, sizeof(info.name), "%s-f%02d", + t->name, fixture_nr); + } + info.fixture_nr = fixture_nr; + + test_run_info_ = &info; + t->run(data); + test_run_info_ = NULL; + + /* + * XXX: We should return t->run(data); but that requires changing + * the function signature and stop using assert() in tests. + * https://gitlab.freedesktop.org/wayland/e_tizen/issues/311 + */ + return RESULT_OK; +} + +static void +list_tests(void) +{ + const struct fixture_setup_array *fsa; + const struct e_tizen_test_entry *t; + + fsa = fixture_setup_array_get_(); + + printf("Fixture setups: %d\n", fsa->n_elements); + + for (t = &__start_test_section; t < &__stop_test_section; t++) { + printf(" %s\n", t->name); + if (t->n_elements > 1) + printf(" with array of %d cases\n", t->n_elements); + } +} + +struct e_tizen_test_harness { + int32_t fixt_ind; + char *chosen_testname; + int32_t case_ind; + + struct e_tizen_testsuite_data data; +}; + +typedef void (*e_tizen_test_cb)(struct e_tizen_testsuite_data *suite_data, + const struct e_tizen_test_entry *t, + const void *test_data, + int iteration); + +static void +for_each_test_case(struct e_tizen_testsuite_data *data, e_tizen_test_cb cb) +{ + unsigned i; + + for (i = 0; i < data->tests_count; i++) { + const struct e_tizen_test_entry *t = &data->tests[i]; + const void *current_test_data = t->table_data; + int elem; + int elem_end; + + if (data->case_index == -1) { + elem = 0; + elem_end = t->n_elements; + } else { + elem = data->case_index; + elem_end = elem + 1; + } + + for (; elem < elem_end; elem++) { + current_test_data = (char *)t->table_data + + elem * t->element_size; + cb(data, t, current_test_data, elem); + } + } +} + +static const char * +result_to_str(enum test_result_code ret) +{ + static const char *names[] = { + [RESULT_FAIL] = "fail", + [RESULT_HARD_ERROR] = "hard error", + [RESULT_OK] = "ok", + [RESULT_SKIP] = "skip", + }; + + assert(ret >= 0 && ret < ARRAY_LENGTH(names)); + return names[ret]; +} + +static void +run_case(struct e_tizen_testsuite_data *suite_data, + const struct e_tizen_test_entry *t, + const void *test_data, + int iteration) +{ + enum test_result_code ret; + const char *fail = ""; + const char *skip = ""; + int fixture_nr = suite_data->fixture_iteration + 1; + int iteration_nr = iteration + 1; + + testlog("*** Run fixture %d, %s/%d\n", + fixture_nr, t->name, iteration_nr); + + ret = run_test(fixture_nr, t, (void *)test_data, iteration); + + switch (ret) { + case RESULT_OK: + suite_data->passed++; + break; + case RESULT_FAIL: + case RESULT_HARD_ERROR: + suite_data->failed++; + fail = "not "; + break; + case RESULT_SKIP: + suite_data->skipped++; + skip = " # SKIP"; + break; + } + + testlog("*** Result fixture %d, %s/%d: %s\n", + fixture_nr, t->name, iteration_nr, result_to_str(ret)); + + suite_data->counter++; + printf("%sok %d fixture %d %s/%d%s\n", fail, suite_data->counter, + fixture_nr, t->name, iteration_nr, skip); +} + +/* This function might run in a new thread */ +static void +testsuite_run(struct e_tizen_testsuite_data *data) +{ + for_each_test_case(data, run_case); +} + +static void +count_case(struct e_tizen_testsuite_data *suite_data, + const struct e_tizen_test_entry *t __attribute((unused)), + const void *test_data __attribute((unused)), + int iteration __attribute((unused))) +{ + suite_data->total++; +} + +static void +tap_plan(struct e_tizen_testsuite_data *data, int count_fixtures) +{ + data->total = 0; + for_each_test_case(data, count_case); + + printf("1..%d\n", data->total * count_fixtures); +} + +static void +skip_case(struct e_tizen_testsuite_data *suite_data, + const struct e_tizen_test_entry *t, + const void *test_data __attribute((unused)), + int iteration) +{ + int fixture_nr = suite_data->fixture_iteration + 1; + int iteration_nr = iteration + 1; + + suite_data->counter++; + printf("ok %d fixture %d %s/%d # SKIP fixture\n", suite_data->counter, + fixture_nr, t->name, iteration_nr); +} + +static void +tap_skip_fixture(struct e_tizen_testsuite_data *data) +{ + for_each_test_case(data, skip_case); +} + +static void +help(const char *exe) +{ + printf( + "Usage: %s [options] [testname [index]]\n" + "\n" + "This is a e-tizen test suite executable that runs some tests.\n" + "Options:\n" + " -f, --fixture N Run only fixture index N. Indices start from 1.\n" + " -h, --help Print this help and exit with success.\n" + " -l, --list List all tests in this executable and exit with success.\n" + "testname: Optional; name of the test to execute instead of all tests.\n" + "index: Optional; for a multi-case test, run the given case only.\n", + exe); +} + +/* Convert string to integer + * + * Parses a base-10 number from the given string. Checks that the + * string is not blank, contains only numerical characters, and is + * within the range of INT32_MIN to INT32_MAX. If the validation is + * successful the result is stored in *value; otherwise *value is + * unchanged and errno is set appropriately. + * + * \return true if the number parsed successfully, false on error + */ +static inline bool +safe_strtoint(const char *str, int32_t *value) +{ + long ret; + char *end; + + assert(str != NULL); + + errno = 0; + ret = strtol(str, &end, 10); + if (errno != 0) { + return false; + } else if (end == str || *end != '\0') { + errno = EINVAL; + return false; + } + + if ((long)((int32_t)ret) != ret) { + errno = ERANGE; + return false; + } + *value = (int32_t)ret; + + return true; +} + +static void +parse_command_line(struct e_tizen_test_harness *harness, int argc, char **argv) +{ + int c; + static const struct option opts[] = { + { "fixture", required_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "list", no_argument, NULL, 'l' }, + { 0, 0, NULL, 0 } + }; + + while ((c = getopt_long(argc, argv, "f:hl", opts, NULL)) != -1) { + switch (c) { + case 'f': + if (!safe_strtoint(optarg, &harness->fixt_ind)) { + fprintf(stderr, + "Error: '%s' does not look like a number (command line).\n", + optarg); + exit(RESULT_HARD_ERROR); + } + harness->fixt_ind--; /* convert base-1 to base 0 */ + break; + case 'h': + help(argv[0]); + exit(RESULT_OK); + case 'l': + list_tests(); + exit(RESULT_OK); + case 0: + break; + default: + exit(RESULT_HARD_ERROR); + } + } + + if (optind < argc) + harness->chosen_testname = argv[optind++]; + + if (optind < argc) { + if (!safe_strtoint(argv[optind], &harness->case_ind)) { + fprintf(stderr, + "Error: '%s' does not look like a number (command line).\n", + argv[optind]); + exit(RESULT_HARD_ERROR); + } + harness->case_ind--; /* convert base-1 to base 0 */ + optind++; + } + + if (optind < argc) { + fprintf(stderr, "Unexpected extra arguments given (command line).\n\n"); + help(argv[0]); + exit(RESULT_HARD_ERROR); + } +} + +static struct e_tizen_test_harness * +e_tizen_test_harness_create(int argc __attribute((unused)), char **argv __attribute((unused))) +{ + const struct fixture_setup_array *fsa; + struct e_tizen_test_harness *harness; + + harness = calloc(1, sizeof(*harness)); + assert(harness); + + harness->fixt_ind = -1; + harness->case_ind = -1; + parse_command_line(harness, argc, argv); + + fsa = fixture_setup_array_get_(); + if (harness->fixt_ind < -1 || harness->fixt_ind >= fsa->n_elements) { + fprintf(stderr, + "Error: fixture index %d (command line) is invalid for this program.\n", + harness->fixt_ind + 1); + exit(RESULT_HARD_ERROR); + } + + if (harness->chosen_testname) { + const struct e_tizen_test_entry *t; + + t = find_test(harness->chosen_testname); + if (!t) { + fprintf(stderr, + "Error: test '%s' not found (command line).\n", + harness->chosen_testname); + exit(RESULT_HARD_ERROR); + } + + if (harness->case_ind < -1 || + harness->case_ind >= t->n_elements) { + fprintf(stderr, + "Error: case index %d (command line) is invalid for this test.\n", + harness->case_ind + 1); + exit(RESULT_HARD_ERROR); + } + + harness->data.tests = t; + harness->data.tests_count = 1; + harness->data.case_index = harness->case_ind; + } else { + harness->data.tests = &__start_test_section; + harness->data.tests_count = + &__stop_test_section - &__start_test_section; + harness->data.case_index = -1; + } + + harness->data.run = testsuite_run; + + return harness; +} + +static void +e_tizen_test_harness_destroy(struct e_tizen_test_harness *harness) +{ + free(harness); +} + +static enum test_result_code +counts_to_result(const struct e_tizen_testsuite_data *data) +{ + /* RESULT_SKIP is reserved for fixture setup itself skipping everything */ + if (data->total == data->passed + data->skipped) + return RESULT_OK; + return RESULT_FAIL; +} + +/** Execute all tests as standalone tests + * + * \param harness The test harness context. + * + * Executes all tests in the test program serially without any further setup, + * particularly without any compositor instance created. + * + * \sa DECLARE_FIXTURE_SETUP(), DECLARE_FIXTURE_SETUP_WITH_ARG() + * \ingroup testharness + */ +enum test_result_code +e_tizen_test_harness_execute_standalone(struct e_tizen_test_harness *harness) +{ + struct e_tizen_testsuite_data *data = &harness->data; + + data->type = TEST_TYPE_STANDALONE; + data->run(data); + + return RESULT_OK; +} + +/** Fixture data array getter method + * + * DECLARE_FIXTURE_SETUP_WITH_ARG() overrides this in test programs. + * The default implementation has no data and makes the tests run once. + * + * \ingroup testharness + */ +__attribute__((weak)) const struct fixture_setup_array * +fixture_setup_array_get_(void) +{ + /* A dummy fixture without a data array. */ + static const struct fixture_setup_array default_fsa = { + .array = NULL, + .element_size = 0, + .n_elements = 1, + }; + + return &default_fsa; +} + +/** Fixture setup function + * + * DECLARE_FIXTURE_SETUP() and DECLARE_FIXTURE_SETUP_WITH_ARG() override + * this in test programs. + * The default implementation just calls + * e_tizen_test_harness_execute_standalone(). + * + * \ingroup testharness + */ +__attribute__((weak)) enum test_result_code +fixture_setup_run_(struct e_tizen_test_harness *harness, const void *arg_ __attribute((unused))) +{ + return e_tizen_test_harness_execute_standalone(harness); +} + +static void +fixture_report(const struct e_tizen_testsuite_data *d, enum test_result_code ret) +{ + int fixture_nr = d->fixture_iteration + 1; + + testlog("--- Fixture %d %s: passed %d, skipped %d, failed %d, total %d\n", + fixture_nr, result_to_str(ret), + d->passed, d->skipped, d->failed, d->total); +} + +int +main(int argc, char *argv[]) +{ + struct e_tizen_test_harness *harness; + enum test_result_code ret; + enum test_result_code result = RESULT_OK; + const struct fixture_setup_array *fsa; + const char *array_data; + int fi; + int fi_end; + + harness = e_tizen_test_harness_create(argc, argv); + + fsa = fixture_setup_array_get_(); + array_data = fsa->array; + + if (harness->fixt_ind == -1) { + fi = 0; + fi_end = fsa->n_elements; + } else { + fi = harness->fixt_ind; + fi_end = fi + 1; + } + + tap_plan(&harness->data, fi_end - fi); + testlog("Iterating through %d fixtures.\n", fi_end - fi); + + for (; fi < fi_end; fi++) { + const void *arg = array_data + fi * fsa->element_size; + + testlog("--- Fixture %d...\n", fi + 1); + harness->data.fixture_iteration = fi; + harness->data.passed = 0; + harness->data.skipped = 0; + harness->data.failed = 0; + + ret = fixture_setup_run_(harness, arg); + fixture_report(&harness->data, ret); + + if (ret == RESULT_SKIP) { + tap_skip_fixture(&harness->data); + continue; + } + + if (ret != RESULT_OK && result != RESULT_HARD_ERROR) + result = ret; + else if (counts_to_result(&harness->data) != RESULT_OK) + result = RESULT_FAIL; + } + + e_tizen_test_harness_destroy(harness); + + return result; +} diff --git a/e-tizen-testsuite/src/e-tizen-test-runner.h b/e-tizen-testsuite/src/e-tizen-test-runner.h new file mode 100644 index 0000000..ff137c2 --- /dev/null +++ b/e-tizen-testsuite/src/e-tizen-test-runner.h @@ -0,0 +1,226 @@ +/* + * Copyright © 2012 Intel Corporation + * Copyright © 2013 Sam Spilsbury <smspillaz@gmail.com> + * + * 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. + */ + +#ifndef _E_TIZEN_TEST_RUNNER_H_ +#define _E_TIZEN_TEST_RUNNER_H_ + +#include <stdlib.h> + +#include <wayland-util.h> +#include "e-tizen-testsuite-data.h" +#include "e-tizen-testsuite-helper.h" + +#ifdef NDEBUG +#error "Tests must not be built with NDEBUG defined, they rely on assert()." +#endif + +/** Test program entry + * + * Each invocation of TEST(), TEST_P(), or PLUGIN_TEST() will create one + * more e_tizen_test_entry in a custom named section in the final binary. + * Iterating through the section then allows to iterate through all + * the defined tests. + * + * \ingroup testharness_private + */ +struct e_tizen_test_entry { + const char *name; + void (*run)(void *); + const void *table_data; + size_t element_size; + int n_elements; +} __attribute__ ((aligned (32))); + +#define TEST_BEGIN(name, arg) \ + static void name(arg) + +#define TEST_COMMON(func, name, data, size, n_elem) \ + static void func(void *); \ + \ + const struct e_tizen_test_entry test##name \ + __attribute__ ((used, section ("test_section"))) = \ + { \ + #name, func, data, size, n_elem \ + }; + +#define NO_ARG_TEST(name) \ + TEST_COMMON(wrap##name, name, NULL, 0, 1) \ + static void name(void); \ + static void wrap##name(void *data) \ + { \ + (void) data; \ + name(); \ + } \ + \ + TEST_BEGIN(name, void) + +#define ARG_TEST(name, test_data) \ + TEST_COMMON(name, name, test_data, \ + sizeof(test_data[0]), \ + ARRAY_LENGTH(test_data)) \ + TEST_BEGIN(name, void *data) \ + +/** Add a test with no parameters + * + * This defines one test as a new function. Use this macro in place of the + * function signature and put the function body after this. + * + * \param name Name for the test, must be a valid function name. + * + * \ingroup testharness + */ +#define TEST(name) NO_ARG_TEST(name) + +/** Add an array driven test with a parameter + * + * This defines an array of tests as a new function. Use this macro in place + * of the function signature and put the function body after this. The function + * will be executed once for each element in \c data_array, passing the + * element as the argument <tt>void *data</tt> to the function. + * + * This macro is not usable if fixture setup is using + * e_tizen_test_harness_execute_as_plugin(). + * + * \param name Name for the test, must be a valid function name. + * \param data_array A static const array of any type. The length will be + * recorded automatically. + * + * \ingroup testharness + */ +#define TEST_P(name, data_array) ARG_TEST(name, data_array) + +void +testlog(const char *fmt, ...) WL_PRINTF(1, 2); + +const char * +get_test_name(void); + +int +get_test_fixture_index(void); + +/** Fixture setup array record + * + * Helper to store the attributes of the data array passed in to + * DECLARE_FIXTURE_SETUP_WITH_ARG(). + * + * \ingroup testharness_private + */ +struct fixture_setup_array { + const void *array; + size_t element_size; + int n_elements; +}; + +const struct fixture_setup_array * +fixture_setup_array_get_(void); + +/** Test harness context + * + * \ingroup testharness + */ +struct e_tizen_test_harness; + +enum test_result_code +fixture_setup_run_(struct e_tizen_test_harness *harness, const void *arg_); + +/** Register a fixture setup function + * + * This registers the given (preferably static) function to be used for setting + * up any fixtures you might need. The function must have the signature: + * + * \code + * enum test_result_code func_(struct e_tizen_test_harness *harness) + * \endcode + * + * The function must call one of e_tizen_test_harness_execute_standalone(), + * e_tizen_test_harness_execute_as_plugin() or + * e_tizen_test_harness_execute_as_client() passing in the \c harness argument, + * and return the return value from that call. The function can also return a + * test_result_code on its own if it does not want to run the tests, + * e.g. RESULT_SKIP or RESULT_HARD_ERROR. + * + * The function will be called once to run all tests. + * + * \param func_ The function to be used as fixture setup. + * + * \ingroup testharness + */ +#define DECLARE_FIXTURE_SETUP(func_) \ + enum test_result_code \ + fixture_setup_run_(struct e_tizen_test_harness *harness, \ + const void *arg_) \ + { \ + return func_(harness); \ + } + +/** Register a fixture setup function with a data array + * + * This registers the given (preferably static) function to be used for setting + * up any fixtures you might need. The function must have the signature: + * + * \code + * enum test_result_code func_(struct e_tizen_test_harness *harness, typeof(array_[0]) *arg) + * \endcode + * + * The function must call one of e_tizen_test_harness_execute_standalone(), + * e_tizen_test_harness_execute_as_plugin() or + * e_tizen_test_harness_execute_as_client() passing in the \c harness argument, + * and return the return value from that call. The function can also return a + * test_result_code on its own if it does not want to run the tests, + * e.g. RESULT_SKIP or RESULT_HARD_ERROR. + * + * The function will be called once with each element of the array pointed to + * by \c arg, so that all tests would be repeated for each element in turn. + * + * \param func_ The function to be used as fixture setup. + * \param array_ A static const array of arbitrary type. + * + * \ingroup testharness + */ +#define DECLARE_FIXTURE_SETUP_WITH_ARG(func_, array_) \ + const struct fixture_setup_array * \ + fixture_setup_array_get_(void) \ + { \ + static const struct fixture_setup_array arr = { \ + .array = array_, \ + .element_size = sizeof(array_[0]), \ + .n_elements = ARRAY_LENGTH(array_) \ + }; \ + return &arr; \ + } \ + \ + enum test_result_code \ + fixture_setup_run_(struct e_tizen_test_harness *harness, \ + const void *arg_) \ + { \ + typeof(array_[0]) *arg = arg_; \ + return func_(harness, arg); \ + } + +enum test_result_code +e_tizen_test_harness_execute_standalone(struct e_tizen_test_harness *harness); + +#endif diff --git a/e-tizen-testsuite/src/e-tizen-testsuite-data.h b/e-tizen-testsuite/src/e-tizen-testsuite-data.h new file mode 100644 index 0000000..5ae7244 --- /dev/null +++ b/e-tizen-testsuite/src/e-tizen-testsuite-data.h @@ -0,0 +1,80 @@ +/* + * Copyright 2019 Collabora, Ltd. + * + * 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. + */ + +#ifndef E_TIZEN_TESTSUITE_DATA_H +#define E_TIZEN_TESTSUITE_DATA_H + +/** Standard return codes + * + * Both Autotools and Meson use these codes as test program exit codes + * to denote the test result for the whole process. + * + * \ingroup testharness + */ +enum test_result_code { + RESULT_OK = 0, + RESULT_SKIP = 77, + RESULT_FAIL = 1, + RESULT_HARD_ERROR = 99, +}; + +/** E-Tizen test types + * + * \sa e_tizen_test_harness_execute_standalone + * + * \ingroup testharness_private + */ +enum test_type { + TEST_TYPE_STANDALONE, +}; + +/** Test harness specific data for running tests + * + * \ingroup testharness_private + */ +struct e_tizen_testsuite_data { + void (*run)(struct e_tizen_testsuite_data *); + + /* test definitions */ + const struct e_tizen_test_entry *tests; + unsigned tests_count; + int case_index; + enum test_type type; + + /* client thread control */ + int thread_event_pipe; + + /* informational run state */ + int fixture_iteration; + + /* test counts */ + unsigned counter; + unsigned passed; + unsigned skipped; + unsigned failed; + unsigned total; +}; + +#endif /* E_TIZEN_TESTSUITE_DATA_H */ diff --git a/e-tizen-testsuite/src/e-tizen-testsuite-helper.h b/e-tizen-testsuite/src/e-tizen-testsuite-helper.h new file mode 100644 index 0000000..45c117e --- /dev/null +++ b/e-tizen-testsuite/src/e-tizen-testsuite-helper.h @@ -0,0 +1,12 @@ +#ifndef E_TIZEN_TESTSUITE_HELPER_H +#define E_TIZEN_TESTSUITE_HELPER_H + +/* 34000 > 1 sec / 60 hz * 1,000,000 = 16666.66... (for millisecond) + * wait at least 2 frame for safety. */ +#define TIME_WAIT_FOR_FRAME 68000 + +#ifndef ARRAY_LENGTH +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) +#endif + +#endif diff --git a/e-tizen-testsuite/src/etz_runner.sh.in b/e-tizen-testsuite/src/etz_runner.sh.in new file mode 100644 index 0000000..3c9edeb --- /dev/null +++ b/e-tizen-testsuite/src/etz_runner.sh.in @@ -0,0 +1,90 @@ +#!/bin/sh + +TEST_LIST=@TEST_EXECS@ +TEST_NAME="" +REPEAT=1 + +COLOR_RED=`tput setaf 1` +COLOR_GREEN=`tput setaf 2` +COLOR_RESET=`tput sgr0` + +SILENT=false + +print() { + if [ ${SILENT} == false ]; then + echo -e "$@" + fi +} + +print_pass() { + MSG=${1:-"PASS"} + print "${COLOR_GREEN}${MSG}${COLOR_RESET}" +} + +error_exit() { + MSG=${1:-"FAIL"} + print "${COLOR_RED}${MSG}${COLOR_RESET}" + exit 1 +} + +show_tests() { + for t in $TEST_LIST; do + echo $t + done +} + +show_help() { + print "\n""Usage: $(basename $0) [OPTS] [TEST NAME]" + print "\nArguments:" + print "\t" "-h, --help" "\t\t" "show this help information" + print "\t" "-l, --list" "\t\t" "print all available test" + print "\t" "-r, --repeat" "\t\t" "repeat time" + print "\t" "-t, --test" "\t\t" "test to be run" +} + +run_test() { + $1 +} + +optspec=":hl-:r:t:" +while getopts ${optspec} OPT; do + if [ ${OPT} = "-" ]; then + OPT=${OPTARG%%=*} + OPTARG=${OPTARG#$OPT} + OPTARG=${OPTARG#=} + fi + + case ${OPT} in + h | help ) + show_help + exit 0 + ;; + l | list ) + show_tests + exit 0 + ;; + r | repeat ) REPEAT=$OPTARG ;; + t | test ) TEST_NAME=$OPTARG ;; + * ) + TEST_NAME=${OPT} + ;; + esac +done + +shift $((OPTIND-1)) +TEST_NAME=$1 + +while [[ $REPEAT -gt 0 ]] +do + if [[ ! -z $TEST_NAME ]]; then + run_test $TEST_NAME || error_exit "FAIL: ${TEST_NAME}" + print_pass "PASS: ${TEST_NAME}" + else + for t in $TEST_LIST; do + run_test $t || error_exit "FAIL: ${t}" + print_pass "PASS: ${t}" + done + fi + + ((REPEAT--)) +done diff --git a/e-tizen-testsuite/src/meson.build b/e-tizen-testsuite/src/meson.build new file mode 100644 index 0000000..6193f51 --- /dev/null +++ b/e-tizen-testsuite/src/meson.build @@ -0,0 +1,115 @@ +dep_wayland_client = dependency('wayland-client', version: '>= 1.17.0') +dep_tizen_extension = dependency('tizen-extension-client') +dep_screenshooter = dependency('screenshooter-client') + +lib_test_runner = static_library( + 'test-runner', + 'e-tizen-test-runner.c', + include_directories: common_inc, + dependencies: [dep_wayland_client], + install: false +) + +dep_test_runner = declare_dependency( + dependencies: dep_wayland_client, + link_with: lib_test_runner +) + +dep_pixman = dependency('pixman-1', version: '>= 0.25.2') +dep_cairo = dependency('cairo') + +lib_test_client = static_library( + 'test-client', + [ + 'os-compatibility.c', + 'e-tizen-test-client-helper.c', + ], + include_directories: common_inc, + dependencies: [ + dependency('xdg-shell-unstable-v6-client'), + dep_wayland_client, + dep_screenshooter, + dep_tizen_extension, + dep_pixman, + dep_cairo, + ], + install: false, +) + +dep_test_client = declare_dependency( + link_with: lib_test_client, + dependencies: [ + dep_wayland_client, + dep_screenshooter, + dep_tizen_extension, + dep_test_runner, + dep_pixman, + ] +) + +tests = [ + { 'name': 'subsurface' }, + { 'name': 'subsurface-shot' }, + { 'name': 'window-shot' }, + { 'name': 'tizen-surface', + 'dep_objs': [ + dep_tizen_extension, + ], + }, + { 'name': 'zxdg-shell-v6', + 'dep_objs': [ + dependency('xdg-shell-unstable-v6-client') + ], + }, + { + 'name': 'screenshooter', + 'dep_objs': [ + dep_screenshooter, + ], + }, + { + 'name': 'scaler', + 'dep_objs': [ + dependency('scaler-client') + ], + }, + { + 'name': 'scaler-shot', + 'dep_objs': [ + dependency('scaler-client') + ], + }, + { + 'name': 'buffer-transform', + }, +] + +t_execs= [] +foreach t : tests + t_name = 'test-' + t.get('name') + t_sources = t.get('sources', [t.get('name') + '-test.c']) + t_deps = [ dep_test_client ] + t_deps += t.get('dep_objs', []) + + t_exe = executable( + t_name, + t_sources, + c_args: [ + '-DUNIT_TEST' + ], + dependencies: t_deps, + install: true + ) + + t_execs += t_name +endforeach + +conf_data = configuration_data() +conf_data.set_quoted('TEST_EXECS', ' '.join(t_execs)) +configure_file( + input: 'etz_runner.sh.in', + output: 'etz_runner.sh', + configuration: conf_data, + install: true, + install_dir: dir_bin +) diff --git a/e-tizen-testsuite/src/os-compatibility.c b/e-tizen-testsuite/src/os-compatibility.c new file mode 100644 index 0000000..6a160be --- /dev/null +++ b/e-tizen-testsuite/src/os-compatibility.c @@ -0,0 +1,118 @@ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <unistd.h> +#include <sys/types.h> + +#include <fcntl.h> +#include <sys/epoll.h> + +#include "os-compatibility.h" + +int +os_fd_set_cloexec(int fd) +{ + long flags; + + if (fd == -1) + return -1; + + flags = fcntl(fd, F_GETFD); + if (flags == -1) + return -1; + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + return -1; + + return 0; +} + +static int +set_cloexec_or_close(int fd) +{ + if (os_fd_set_cloexec(fd) != 0) { + close(fd); + return -1; + } + + return fd; +} + +int +os_epoll_create_cloexec(void) +{ + int fd; + +#ifdef EPOLL_CLOEXEC + fd = epoll_create1(EPOLL_CLOEXEC); + if (fd >= 0) + return fd; + if (errno != EINVAL) + return -1; +#endif + + fd = epoll_create(1); + return set_cloexec_or_close(fd); +} + +static int +create_tmpfile_cloexec(char *tmpname) +{ + int fd; + +#ifdef HAVE_MKOSTEMP + fd = mkostemp(tmpname, O_CLOEXEC); + if (fd >= 0) + unlink(tmpname); +#else + fd = mkstemp(tmpname); + if (fd >= 0) { + fd = set_cloexec_or_close(fd); + unlink(tmpname); + } +#endif + + return fd; +} + +int +os_create_anonymous_file(off_t size) +{ + static const char template[] = "/etz-shared-XXXXXX"; + const char *path; + char *name; + int fd; + int ret; + + path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + errno = ENOENT; + return -1; + } + + name = malloc(strlen(path) + sizeof(template)); + if (!name) + return -1; + + strcpy(name, path); + strcat(name, template); + + fd = create_tmpfile_cloexec(name); + + free(name); + + if (fd < 0) + return -1; + + do { + ret = ftruncate(fd, size); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + close(fd); + return -1; + } + + return fd; +} diff --git a/e-tizen-testsuite/src/os-compatibility.h b/e-tizen-testsuite/src/os-compatibility.h new file mode 100644 index 0000000..887378b --- /dev/null +++ b/e-tizen-testsuite/src/os-compatibility.h @@ -0,0 +1,11 @@ +#ifndef OS_COMPATIBILITY_H +#define OS_COMPATIBILITY_H + +#include <sys/types.h> + +int os_fd_set_cloexec(int fd); +int os_epoll_create_cloexec(void); + +int os_create_anonymous_file(off_t size); + +#endif diff --git a/e-tizen-testsuite/src/scaler-shot-test.c b/e-tizen-testsuite/src/scaler-shot-test.c new file mode 100644 index 0000000..78448bb --- /dev/null +++ b/e-tizen-testsuite/src/scaler-shot-test.c @@ -0,0 +1,191 @@ +#include <unistd.h> + +#include <wayland-client-protocol.h> +#include <scaler-client-protocol.h> + +#include "e-tizen-test-client-helper.h" + +static int +check_screen(struct client *client, + const char *ref_image, + int ref_seq_no, + const struct rectangle *clip, + int seq_no) +{ + bool match; + + /* wl message flush */ + client_roundtrip(client); + /* wait for the frame applied to screen. */ + usleep(TIME_WAIT_FOR_FRAME); + match = verify_screen_content(client, ref_image, ref_seq_no, clip, + seq_no); + + return match ? 0 : -1; +} + +static struct window * +create_bg_window(struct client *client) +{ + struct window *window; + struct buffer *buf; + struct wl_surface *surface; + int ow, oh; + pixman_color_t white; + + color_rgb888(&white, 255, 255, 255); + + window = create_test_window(client); + assert(window); + + ow = client->output->width; + oh = client->output->height; + + buf = create_shm_buffer_a8r8g8b8(client, ow, oh); + fill_image_with_color(buf->image, &white); + + surface = window->surface->wl_surface; + wl_surface_attach(surface, buf->proxy, 0, 0); + wl_surface_damage(surface, 0, 0, ow, oh); + wl_surface_commit(surface); + + window->surface->buffer = buf; + + return window; +} + +static void +image_composite_with_color(pixman_image_t *dst, + pixman_color_t *color, + int x, int y, int w, int h) +{ + pixman_image_t *img; + + img = pixman_image_create_solid_fill(color); + pixman_image_composite32(PIXMAN_OP_OVER, + img, + NULL, + dst, + 0, 0, + 0, 0, + x, y, + w, h); + pixman_image_unref(img); +} + +const int WIN_X = 100, WIN_Y = 50, WIN_W = 400, WIN_H = 500; +const int RECT_W = 100, RECT_H = 150; +const int BLUE_X = 50, BLUE_Y = 50; +const int GREEN_X = 200, GREEN_Y = 250; + +static struct window * +create_main_window(struct client *client) +{ + struct window *window; + struct buffer *buf; + struct wl_surface *surface; + pixman_color_t red, blue, green; + + window = create_test_window(client); + assert(window); + + window_move(window, WIN_X, WIN_Y); + + color_rgb888(&red, 255, 0, 0); + color_rgb888(&blue, 0, 0, 255); + color_rgb888(&green, 0, 255, 0); + + buf = create_shm_buffer_a8r8g8b8(client, WIN_W, WIN_H); + fill_image_with_color(buf->image, &red); + + image_composite_with_color(buf->image, + &blue, BLUE_X, BLUE_Y, RECT_W, RECT_H); + image_composite_with_color(buf->image, + &green, GREEN_X, GREEN_Y, RECT_W, RECT_H); + + surface = window->surface->wl_surface; + wl_surface_attach(surface, buf->proxy, 0, 0); + wl_surface_damage(surface, 0, 0, WIN_W, WIN_H); + wl_surface_commit(surface); + + window_visible_wait(window); + + window->surface->buffer = buf; + + return window; +} + +static void +set_source(struct wl_viewport *vp, int x, int y, int w, int h) +{ + wl_viewport_set_source(vp, + wl_fixed_from_int(x), + wl_fixed_from_int(y), + wl_fixed_from_int(w), + wl_fixed_from_int(h)); +} + +TEST(scaler_shot) +{ + struct client *client; + struct window *bg_win, *window; + struct wl_surface *surf; + struct wl_scaler *scaler; + struct wl_viewport *vp; + struct rectangle clip = { + WIN_X - 10, + WIN_Y - 10, + WIN_X + WIN_W + 10, + WIN_Y + WIN_H + 10, + }; + int fail = 0; + + client = create_client(); + assert(client); + + bg_win = create_bg_window(client); + + window = create_main_window(client); + surf = window->surface->wl_surface; + + fail += check_screen(client, "scaler_shot", 0, &clip, 0); + + scaler = bind_to_singleton_global(client, &wl_scaler_interface, 2); + assert(scaler); + + vp = wl_scaler_get_viewport(scaler, surf); + assert(vp); + + wl_scaler_destroy(scaler); + + /* Clip blue rectangle. */ + set_source(vp, BLUE_X, BLUE_Y, RECT_W, RECT_H); + wl_surface_commit(surf); + + fail += check_screen(client, "scaler_shot", 1, &clip, 1); + + /* Scale up to window size with clipped blue rectangle. */ + wl_viewport_set_destination(vp, WIN_W, WIN_H); + wl_surface_commit(surf); + + fail += check_screen(client, "scaler_shot", 2, &clip, 2); + + /* Unset destination size. */ + wl_viewport_set_destination(vp, -1, -1); + wl_surface_commit(surf); + + fail += check_screen(client, "scaler_shot", 3, &clip, 3); + + /* Unset source size. */ + set_source(vp, 0, 0, -1, -1); + wl_surface_commit(surf); + + fail += check_screen(client, "scaler_shot", 4, &clip, 4); + + assert(fail == 0); + + wl_viewport_destroy(vp); + + window_destroy(window); + window_destroy(bg_win); +} diff --git a/e-tizen-testsuite/src/scaler-test.c b/e-tizen-testsuite/src/scaler-test.c new file mode 100644 index 0000000..eb1fb8a --- /dev/null +++ b/e-tizen-testsuite/src/scaler-test.c @@ -0,0 +1,249 @@ +#include <wayland-client-protocol.h> +#include <scaler-client-protocol.h> + +#include "e-tizen-test-client-helper.h" + +static struct wl_viewport * +create_viewport(struct client *client) +{ + struct wl_scaler *scaler; + + scaler = bind_to_singleton_global(client, &wl_scaler_interface, 2); + assert(scaler); + + return wl_scaler_get_viewport(scaler, client->surface->wl_surface); +} + +struct bad_set_args { + int sx, sy, sw, sh; + int dw, dh; +}; + +static const struct bad_set_args bad_set_args[] = { + { 0, 0, -20, 10, 200, 300 }, + { 0, 0, 20, -10, 200, 300 }, + { 0, 0, -20, -10, 200, 300 }, + { 0, 0, 20, 10, 0, 300 }, + { 0, 0, 20, 10, 200, 0 }, + { 0, 0, 20, 10, -1, 300 }, + { 0, 0, 20, 10, 200, -1 }, + { 0, 0, -20, -10, -200, -300 }, +}; + +TEST_P(viewport_bad_set, bad_set_args) +{ + const struct bad_set_args *args = data; + struct client *client; + struct wl_viewport *vp; + + client = create_client_and_test_surface(123, 77); + assert(client); + + vp = create_viewport(client); + assert(vp); + + wl_viewport_set(vp, + wl_fixed_from_int(args->sx), /* src_x */ + wl_fixed_from_int(args->sy), /* src_y */ + wl_fixed_from_int(args->sw), /* src_width */ + wl_fixed_from_int(args->sh), /* src_height */ + args->dw, args->dh /* dst_width, dst_height */); + + expect_protocol_error(client, &wl_viewport_interface, + WL_VIEWPORT_ERROR_BAD_VALUE); +} + +TEST(viewport_set) +{ + struct client *client; + struct wl_viewport *vp; + + client = create_client_and_test_surface(123, 77); + assert(client); + + vp = create_viewport(client); + assert(vp); + + wl_viewport_set(vp, + wl_fixed_from_int(10), /* src_x */ + wl_fixed_from_int(10), /* src_y */ + wl_fixed_from_int(50), /* src_width */ + wl_fixed_from_int(50), /* src_height */ + 200, 300 /* dst_width, dst_height */); + + client_roundtrip(client); +} + +struct bad_destination_args { + int w, h; +}; + +static const struct bad_destination_args bad_destination_args[] = { + { -20, 10 }, + { 20, -10 }, + { -20, -10 }, + { -1, 10 }, + { 20, -1 }, + { 0, 10 }, + { 20, 0 }, + { 0, 0 }, +}; + +struct bad_source_rect_args { + int x, y, w, h; +}; + +TEST_P(viewport_bad_destination_size, bad_destination_args) +{ + const struct bad_destination_args *args = data; + struct client *client; + struct wl_viewport *vp; + + client = create_client_and_test_surface(123, 77); + assert(client); + + vp = create_viewport(client); + assert(vp); + + wl_viewport_set_destination(vp, args->w, args->h); + + expect_protocol_error(client, &wl_viewport_interface, + WL_VIEWPORT_ERROR_BAD_VALUE); +} + +TEST(viewport_unset_destination) +{ + struct client *client; + struct wl_viewport *vp; + + client = create_client_and_test_surface(123, 77); + assert(client); + + vp = create_viewport(client); + assert(vp); + + wl_viewport_set_destination(vp, 50, 50); + wl_viewport_set_destination(vp, -1, -1); + + client_roundtrip(client); +} + +TEST(viewport_set_destination) +{ + struct client *client; + struct wl_viewport *vp; + + client = create_client_and_test_surface(123, 77); + assert(client); + + vp = create_viewport(client); + assert(vp); + + wl_viewport_set_destination(vp, 50, 50); + + client_roundtrip(client); +} + +static const struct bad_source_rect_args bad_source_rect_args[] = { + { 0, 0, -20, 10 }, + { 0, 0, 20, -10 }, + { 0, 0, -20, -10 }, + { 0, 0, -1, 10 }, + { 0, 0, 20, -1 }, + { 0, 0, 0, 10 }, + { 0, 0, 20, 0 }, + { 0, 0, 0, 0 } +}; + +static void +set_source(struct wl_viewport *vp, int x, int y, int w, int h) +{ + wl_viewport_set_source(vp, wl_fixed_from_int(x), wl_fixed_from_int(y), + wl_fixed_from_int(w), wl_fixed_from_int(h)); +} + +TEST_P(viewport_bad_source_rect, bad_source_rect_args) +{ + const struct bad_source_rect_args *args = data; + struct client *client; + struct wl_viewport *vp; + + client = create_client_and_test_surface(123, 77); + assert(client); + + vp = create_viewport(client); + assert(vp); + + set_source(vp, args->x, args->y, args->w, args->h); + + expect_protocol_error(client, &wl_viewport_interface, + WL_VIEWPORT_ERROR_BAD_VALUE); +} + +TEST(viewport_unset_source_rect) +{ + struct client *client; + struct wl_viewport *vp; + + client = create_client_and_test_surface(123, 77); + assert(client); + + vp = create_viewport(client); + assert(vp); + + set_source(vp, 10, 10, 50, 50); + set_source(vp, 0, 0, -1, -1); + + client_roundtrip(client); +} + +TEST(viewport_set_source) +{ + struct client *client; + struct wl_viewport *vp; + + client = create_client_and_test_surface(123, 77); + assert(client); + + vp = create_viewport(client); + assert(vp); + + set_source(vp, 10, 10, 50, 50); + + client_roundtrip(client); +} + +TEST(create_viewport_twice) +{ + struct client *client; + struct wl_scaler *scaler; + struct wl_viewport *vp; + + client = create_client_and_test_surface(123, 77); + assert(client); + + scaler = bind_to_singleton_global(client, &wl_scaler_interface, 2); + assert(scaler); + + vp = wl_scaler_get_viewport(scaler, client->surface->wl_surface); + assert(vp); + + wl_scaler_get_viewport(scaler, client->surface->wl_surface); + + expect_protocol_error(client, &wl_scaler_interface, + WL_SCALER_ERROR_VIEWPORT_EXISTS); +} + +TEST(create_viewport_basic) +{ + struct client *client; + struct wl_viewport *vp; + + client = create_client_and_test_surface(123, 77); + assert(client); + + vp = create_viewport(client); + assert(vp); + + client_roundtrip(client); +} diff --git a/e-tizen-testsuite/src/screenshooter-test.c b/e-tizen-testsuite/src/screenshooter-test.c new file mode 100644 index 0000000..9df220d --- /dev/null +++ b/e-tizen-testsuite/src/screenshooter-test.c @@ -0,0 +1,44 @@ +#include <stdbool.h> +#include <string.h> + +#include <screenshooter-client-protocol.h> +#include "e-tizen-test-client-helper.h" + +static void +screenshooter_handle_done(void *data, struct screenshooter *screenshooter) +{ + *((int *)data) = 1; +} + +static const struct screenshooter_listener listener = { + screenshooter_handle_done, +}; + +TEST(screenshooter_shoot_test) +{ + struct client *client; + struct screenshooter *screenshooter; + struct buffer *buffer; + int done = 0; + + client = create_client(); + + screenshooter = bind_to_singleton_global(client, &screenshooter_interface, 1); + assert(screenshooter); + + screenshooter_add_listener(screenshooter, &listener, &done); + + buffer = create_shm_buffer_a8r8g8b8(client, + client->output->width, client->output->height); + assert(buffer); + + screenshooter_shoot(screenshooter, client->output->wl_output, buffer->proxy); + + while (done == 0) + if (wl_display_dispatch(client->wl_display) < 0) + break; + + assert(done == 1); + + buffer_destroy(buffer); +} diff --git a/e-tizen-testsuite/src/subsurface-shot-test.c b/e-tizen-testsuite/src/subsurface-shot-test.c new file mode 100644 index 0000000..c451c06 --- /dev/null +++ b/e-tizen-testsuite/src/subsurface-shot-test.c @@ -0,0 +1,628 @@ +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <wayland-client-protocol.h> + +#include "e-tizen-test-client-helper.h" + +static struct buffer * +surface_commit_color(struct client *client, struct wl_surface *surface, + pixman_color_t *color, int width, int height) +{ + struct buffer *buf; + + buf = create_shm_buffer_a8r8g8b8(client, width, height); + fill_image_with_color(buf->image, color); + wl_surface_attach(surface, buf->proxy, 0, 0); + wl_surface_damage(surface, 0, 0, width, height); + wl_surface_commit(surface); + + return buf; +} + +static void +fill_image_with_alpha(pixman_image_t *img, struct rectangle *rect) +{ + pixman_image_t *solid; + pixman_color_t alpha = { 0 }; + + solid = pixman_image_create_solid_fill(&alpha); + pixman_image_composite32(PIXMAN_OP_SRC, + solid, + NULL, + img, + 0, 0, + 0, 0, + rect->x, rect->y, + rect->width, rect->height); + pixman_image_unref(solid); +} + +static int +check_screen(struct client *client, + const char *ref_image, + int ref_seq_no, + const struct rectangle *clip, + int seq_no) +{ + bool match; + + /* wl message flush */ + client_roundtrip(client); + /* wait for the frame applied to screen. */ + usleep(TIME_WAIT_FOR_FRAME); + match = verify_screen_content(client, ref_image, ref_seq_no, clip, + seq_no); + + return match ? 0 : -1; +} + +static void +tres_handle_resource_id(void *data, struct tizen_resource *tres, uint32_t id) +{ + *((uint32_t *)data) = id; +} + +static const struct tizen_resource_listener tres_listener = { + tres_handle_resource_id, +}; + +static uint32_t +win_get_res_id(struct tizen_surface *tsurf, struct window *win) +{ + struct tizen_resource *tres; + uint32_t ret_id = 0; + + tres = tizen_surface_get_tizen_resource(tsurf, win->surface->wl_surface); + tizen_resource_add_listener(tres, &tres_listener, &ret_id); + client_roundtrip(win->client); + tizen_resource_destroy(tres); + + return ret_id; +} + +TEST(subsurface_below_parent_with_alpha) +{ + struct client *client; + struct wl_subcompositor *subco; + struct window *win; + struct buffer *bufs[3]; + struct wl_surface *main_surf, *surf[2]; + struct wl_subsurface *sub[2]; + pixman_color_t red, green, blue; + int fail = 0; + struct rectangle sub_rect[2] = + { + {0, 0, 300, 300}, + {200, 200, 300, 300}, + }; + + color_rgb888(&red, 255, 0, 0); + color_rgb888(&blue, 0, 0, 255); + color_rgb888(&green, 0, 255, 0); + + client = create_client(); + assert(client); + + subco = bind_to_singleton_global(client, &wl_subcompositor_interface, 1); + assert(subco); + + /* Create window. */ + win = create_test_window(client); + assert(win); + + main_surf = win->surface->wl_surface; + bufs[0] = create_shm_buffer_a8r8g8b8(client, client->output->width, client->output->height); + fill_image_with_color(bufs[0]->image, &red); + fill_image_with_alpha(bufs[0]->image, &sub_rect[0]); + + wl_surface_attach(main_surf, bufs[0]->proxy, 0, 0); + wl_surface_damage(main_surf, 0, 0, client->output->width, client->output->height); + wl_surface_commit(main_surf); + + /* Create blue sub-surface as a child of red window. */ + surf[0] = wl_compositor_create_surface(client->wl_compositor); + sub[0] = wl_subcompositor_get_subsurface(subco, surf[0], main_surf); + bufs[1] = surface_commit_color(client, surf[0], &blue, sub_rect[0].width, sub_rect[0].height); + + wl_subsurface_set_position(sub[0], sub_rect[0].x, sub_rect[0].y); + wl_subsurface_place_below(sub[0], main_surf); + wl_surface_commit(main_surf); + + /* Make sure the contents of window visible. */ + window_visible_wait(win); + + fail += check_screen(client, "subsurface_below_parent_with_alpha", 0, NULL, 0); + + fill_image_with_alpha(bufs[0]->image, &sub_rect[1]); + wl_surface_damage(main_surf, sub_rect[1].x, sub_rect[1].y, sub_rect[1].width, sub_rect[1].height); + + surf[1] = wl_compositor_create_surface(client->wl_compositor); + sub[1] = wl_subcompositor_get_subsurface(subco, surf[1], main_surf); + bufs[2] = surface_commit_color(client, surf[1], &green, sub_rect[1].width, sub_rect[1].height); + + wl_subsurface_set_position(sub[1], sub_rect[1].x, sub_rect[1].y); + wl_subsurface_place_below(sub[1], surf[0]); + wl_surface_commit(main_surf); + + fail += check_screen(client, "subsurface_below_parent_with_alpha", 1, NULL, 1); + + for (int i = 0; i < ARRAY_LENGTH(bufs); i++) + buffer_destroy(bufs[i]); + + for (int i = 0; i < ARRAY_LENGTH(sub); i++) + wl_subsurface_destroy(sub[i]); + + for (int i = 0; i < ARRAY_LENGTH(surf); i++) + wl_surface_destroy(surf[i]); + + window_destroy(win); +} + +/* 1) Initial state + * -- green window + * -- sub (of red window) + * -- red window + * + * 2) tizen_policy_activate_above_by_res_id(green window, red window) + * -- green window + * -- sub (of red window) + * -- red window + * + * 3) tizen_policy_place_subsurface_below_parent(sub) + * tizen_policy_activate_below_by_res_id(green window, red window) + * -- red window + * -- sub (of red window) + * -- green window + * */ +TEST(subsurface_stack_with_activate_by_res_id) +{ + struct client *client; + struct wl_subcompositor *subco; + struct tizen_surface *tsurf; + struct window *bg_win, *win1, *win2; + struct wl_surface *surf; + struct wl_subsurface *sub; + struct buffer *bufs[4]; + pixman_color_t white, red, blue, green; + uint32_t win1_id, win2_id; + struct rectangle clip = { 40, 40, 480, 400 }; + int fail = 0; + + color_rgb888(&white, 255, 255, 255); + color_rgb888(&red, 255, 0, 0); + color_rgb888(&blue, 0, 0, 255); + color_rgb888(&green, 0, 255, 0); + + client = create_client(); + assert(client); + + subco = bind_to_singleton_global(client, &wl_subcompositor_interface, 1); + assert(subco); + + tsurf = bind_to_singleton_global(client, &tizen_surface_interface, 1); + assert(tsurf); + + /* Create BG window. */ + bg_win = create_test_window(client); + assert(bg_win); + + bufs[0] = surface_commit_color(client, bg_win->surface->wl_surface, &white, client->output->width, client->output->height); + + /* Create red window. */ + win1 = create_test_window(client); + assert(win1); + + window_move(win1, 100, 50); + bufs[1] = surface_commit_color(client, win1->surface->wl_surface, &red, 200, 200); + window_visible_wait(win1); + + /* Create blue sub-surface as a child of red window. */ + surf = wl_compositor_create_surface(client->wl_compositor); + sub = wl_subcompositor_get_subsurface(subco, surf, win1->surface->wl_surface); + bufs[2] = surface_commit_color(client, surf, &blue, 200, 200); + + wl_subsurface_set_position(sub, 20, 20); + wl_surface_commit(win1->surface->wl_surface); + + /* Create green window. */ + win2 = create_test_window(client); + assert(win2); + + window_move(win2, 150, 100); + bufs[3] = surface_commit_color(client, win2->surface->wl_surface, &green, 200, 200); + window_visible_wait(win2); + + /* Acquire global resource id for windows. */ + win1_id = win_get_res_id(tsurf, win1); + win2_id = win_get_res_id(tsurf, win2); + + /* Place green window above red window with resource id. */ + tizen_policy_activate_above_by_res_id(client->tz_pol, win2_id, win1_id); + + fail += check_screen(client, "subsurface_stack_with_activate_by_res_id", 0, &clip, 0); + + /* Place sub-surface under red window. */ + tizen_policy_place_subsurface_below_parent(client->tz_pol, sub); + /* Place green window under red window. + * It must not place green window between red window and its sub-surface. */ + tizen_policy_activate_below_by_res_id(client->tz_pol, win2_id, win1_id); + + fail += check_screen(client, "subsurface_stack_with_activate_by_res_id", 1, &clip, 1); + + assert(fail == 0); + + for (int i = 0; i < ARRAY_LENGTH(bufs); i++) + buffer_destroy(bufs[i]); + + wl_subsurface_destroy(sub); + wl_surface_destroy(surf); + + window_destroy(win2); + window_destroy(win1); + window_destroy(bg_win); + client_destroy(client); +} + +TEST(tizen_policy_place_subsurface_below_parent_test) +{ + const int nbuf = 5, nsub = 3; + const int SW = 200, SH = 200; + struct client *client; + struct window *bg_win, *win; + struct wl_subcompositor *subco; + struct buffer *bufs[nbuf]; + struct wl_surface *main_surf; + struct wl_surface *surf[nsub]; + struct wl_subsurface *sub[nsub]; + pixman_color_t white, red, blue, cyan, green; + struct rectangle clip = { 40, 40, 480, 400 }; + int fail = 0; + + color_rgb888(&white, 255, 255, 255); + color_rgb888(&red, 255, 0, 0); + color_rgb888(&blue, 0, 0, 255); + color_rgb888(&cyan, 0, 255, 255); + color_rgb888(&green, 0, 255, 0); + + client = create_client(); + assert(client); + + subco = bind_to_singleton_global(client, &wl_subcompositor_interface, 1); + + /* Create BG window. */ + bg_win = create_test_window(client); + assert(bg_win); + + bufs[0] = surface_commit_color(client, bg_win->surface->wl_surface, &white, client->output->width, client->output->height); + + /* Create red window. */ + win = create_test_window(client); + assert(win); + + window_move(win, 100, 50); + + main_surf = win->surface->wl_surface; + bufs[1] = surface_commit_color(client, main_surf, &red, 200, 200); + window_visible_wait(win); + + /* 1. Create blue sub-surface(child of red window) under its parent using + * tizen_policy_place_subsurface_below_parent(). + * Expected stack order: + * -- red window + * -- blue sub-surface + */ + surf[0] = wl_compositor_create_surface(client->wl_compositor); + sub[0] = wl_subcompositor_get_subsurface(subco, surf[0], main_surf); + bufs[2] = surface_commit_color(client, surf[0], &blue, SW, SH); + + wl_subsurface_set_position(sub[0], 40, 40); + tizen_policy_place_subsurface_below_parent(client->tz_pol, sub[0]); + wl_surface_commit(main_surf); + + fail += check_screen(client, "tizen_policy_place_subsurface_below_parent_test", 0, &clip, 0); + + /* 2. Create green sub-surface(child of blue sub-surface) under its parent + * using tizen_policy_place_subsurface_below_parent(). + * Expected stack order: + * -- red window + * -- blue sub-surface + * -- green sub-surface + */ + surf[1] = wl_compositor_create_surface(client->wl_compositor); + sub[1] = wl_subcompositor_get_subsurface(subco, surf[1], surf[0]); + bufs[3] = surface_commit_color(client, surf[1], &green, SW, SH); + + wl_subsurface_set_position(sub[1], 40, 40); + tizen_policy_place_subsurface_below_parent(client->tz_pol, sub[1]); + wl_surface_commit(surf[0]); + wl_surface_commit(main_surf); + + fail += check_screen(client, "tizen_policy_place_subsurface_below_parent_test", 1, &clip, 1); + + /* 3. Create cyan sub-surface(child of red window) under its parent + * using tizen_policy_place_subsurface_below_parent(). + * Expected stack order: + * -- red window + * -- cyan sub-surface + * -- blue sub-surface + * -- green sub-surface + */ + surf[2] = wl_compositor_create_surface(client->wl_compositor); + sub[2] = wl_subcompositor_get_subsurface(subco, surf[2], main_surf); + bufs[4] = surface_commit_color(client, surf[2], &cyan, SW, SH); + + wl_subsurface_set_position(sub[2], 20, 20); + tizen_policy_place_subsurface_below_parent(client->tz_pol, sub[2]); + wl_surface_commit(main_surf); + + fail += check_screen(client, "tizen_policy_place_subsurface_below_parent_test", 2, &clip, 2); + + /* 4. Place blue sub-surface under its parent(red window). + * Expected stack order: + * -- red window + * -- blue sub-surface + * -- green sub-surface + * -- cyan sub-surface + */ + tizen_policy_place_subsurface_below_parent(client->tz_pol, sub[0]); + wl_surface_commit(main_surf); + + fail += check_screen(client, "tizen_policy_place_subsurface_below_parent_test", 3, &clip, 3); + + /* 5. Move green sub-surface to see if it's placed between blue and cyan. */ + wl_subsurface_set_position(sub[1], -10, -10); + wl_surface_commit(main_surf); + + fail += check_screen(client, "tizen_policy_place_subsurface_below_parent_test", 4, &clip, 4); + + assert(fail == 0); + + for (int i = 0; i < nbuf; i++) + buffer_destroy(bufs[i]); + + for (int i = 0; i < nsub; i++) { + wl_subsurface_destroy(sub[i]); + wl_surface_destroy(surf[i]); + } + + window_destroy(win); + window_destroy(bg_win); + client_destroy(client); +} + +/** + * The tree of sub-surfaces: + * main(red) + * / \ + * 0(blue-desync) 3(orange) + * / \ + * 1(cyan) 2(green) + * + * 'main' has no wl_subsurface, Others do. + * + * This test captures the contents of output and compare to reference image + * after following each step. + * + * 1. create red main surface (window) + * 2. create blue(0) subsurface in desync mode. geom(40, 40, 200, 200) + * 3. create cyan(1) subsurface in sync mode. geom(40, 40, 200, 200) + * 4. create green(2) subsurface in sync mode. geom(-80, 20, 200, 200) + * 5. create orange(3) subsurface in sync mode. geom(50, 0, 200, 200) + * 6. Place green(2) under cyan(1) and commit 0, and place orange(3) under + * blue(0) but not commit main. + * 7. commit main. Now placing orage(3) under blue(0) is applied. + * 8. Place blue(0) under main surface. -> wl_subsurface_place_below with parent. + * 9. Place orange(3) under blue(0). -> wl_subsurface_place_below with sibling. + * 10. Place blue(0) above main surface. -> wl_subsurface_place_above with parent. + * 11. Place orange(3) above blue(0). -> wl_subsurface_place_above with sibling. + */ +TEST(subsurface_shot_test) +{ + const int SW = 200, SH = 200; + const int nbuf = 6, nsub = 4; + struct client *client; + struct window *bg_win, *win; + struct wl_subcompositor *subco; + struct buffer *bufs[nbuf]; + struct wl_surface *main_surf; + struct wl_surface *surf[nsub]; + struct wl_subsurface *sub[nsub]; + pixman_color_t white, red, blue, cyan, green, orange; + struct rectangle clip = { 40, 40, 480, 400 }; + int fail = 0; + + color_rgb888(&white, 255, 255, 255); + color_rgb888(&red, 255, 0, 0); + color_rgb888(&blue, 0, 0, 255); + color_rgb888(&cyan, 0, 255, 255); + color_rgb888(&green, 0, 255, 0); + color_rgb888(&orange, 255, 165, 0); + + client = create_client(); + assert(client); + + subco = bind_to_singleton_global(client, &wl_subcompositor_interface, 1); + + /* Create BG window. */ + bg_win = create_test_window(client); + assert(bg_win); + + bufs[0] = surface_commit_color(client, bg_win->surface->wl_surface, &white, client->output->width, client->output->height); + + /* Create red window. + * -- red window + */ + win = create_test_window(client); + assert(win); + + window_move(win, 100, 50); + + main_surf = win->surface->wl_surface; + bufs[1] = surface_commit_color(client, main_surf, &red, 200, 200); + + window_visible_wait(win); + + fail += check_screen(client, "subsurface_shot_test", 0, &clip, 0); + + /* Create blue sub-surface with desync mode. + * Expected stack order: + * -- blue sub-surface (desync mode, child of red) + * -- red window + */ + surf[0] = wl_compositor_create_surface(client->wl_compositor); + sub[0] = wl_subcompositor_get_subsurface(subco, surf[0], main_surf); + bufs[2] = surface_commit_color(client, surf[0], &blue, SW, SH); + + wl_subsurface_set_desync(sub[0]); + wl_subsurface_set_position(sub[0], 40, 40); + wl_surface_commit(main_surf); + + fail += check_screen(client, "subsurface_shot_test", 1, &clip, 1); + + /* Create cyan sub-surface. (child of blue sub-surface) + * Expected stack order: + * -- cyan sub-surface + * -- blue sub-surface (desync mode) + * -- red window + */ + surf[1] = wl_compositor_create_surface(client->wl_compositor); + sub[1] = wl_subcompositor_get_subsurface(subco, surf[1], surf[0]); + bufs[3] = surface_commit_color(client, surf[1], &cyan, SW, SH); + + wl_subsurface_set_position(sub[1], 40, 40); + wl_surface_commit(surf[0]); + + fail += check_screen(client, "subsurface_shot_test", 2, &clip, 2); + + /* Create green sub-surface. (child of blue sub-surface) + * Expected stack order: + * -- green sub-surface + * -- cyan sub-surface + * -- blue sub-surface (desync mode) + * -- red window + */ + surf[2] = wl_compositor_create_surface(client->wl_compositor); + sub[2] = wl_subcompositor_get_subsurface(subco, surf[2], surf[0]); + bufs[4] = surface_commit_color(client, surf[2], &green, SW, SH); + + wl_subsurface_set_position(sub[2], -80, 20); + wl_surface_commit(surf[0]); + + fail += check_screen(client, "subsurface_shot_test", 3, &clip, 3); + + /* Create orange sub-surface. (child of red window) + * Expected stack order: + * -- orange sub-surface + * -- green sub-surface + * -- cyan sub-surface + * -- blue sub-surface (desync mode) + * -- red window + */ + surf[3] = wl_compositor_create_surface(client->wl_compositor); + sub[3] = wl_subcompositor_get_subsurface(subco, surf[3], main_surf); + bufs[5] = surface_commit_color(client, surf[3], &orange, SW, SH); + + wl_subsurface_set_position(sub[3], 50, 0); + wl_surface_commit(main_surf); + + fail += check_screen(client, "subsurface_shot_test", 4, &clip, 4); + + /* Place green sub-surface under cyan sub-surface and apply it using + * wl_surface_commit(blue sub-surface). + * Place orange sub-surface under blue sub-surface but not apply it yet. + * Expected stack order: + * -- orange sub-surface + * -- green sub-surface + * -- cyan sub-surface + * -- blue sub-surface (desync mode) + * -- red window + */ + wl_subsurface_place_below(sub[3], surf[0]); + wl_subsurface_place_below(sub[2], surf[1]); + wl_surface_commit(surf[0]); + + /* TODO Not supported because of backward compatibility. */ +#if 0 + fail += check_screen(client, "subsurface_shot_test", 5, &clip, 5); +#endif + /* Apply previous stack change so that orange sub-surface will be placed + * under blue sub-surface. + * Expected stack order: + * -- green sub-surface + * -- cyan sub-surface + * -- blue sub-surface (desync mode) + * -- orange sub-surface + * -- red window + */ + wl_surface_commit(main_surf); + + fail += check_screen(client, "subsurface_shot_test", 6, &clip, 6); + + /* Place blue sub-surface under its parent(red window). + * Expected stack order: + * -- orange sub-surface + * -- red window + * -- green sub-surface + * -- cyan sub-surface + * -- blue sub-surface (desync mode) + */ + wl_subsurface_place_below(sub[0], main_surf); + wl_surface_commit(main_surf); + + fail += check_screen(client, "subsurface_shot_test", 7, &clip, 7); + + /* Place orange sub-surface under its parent(red window). + * Expected stack order: + * -- red window + * -- orange sub-surface + * -- green sub-surface + * -- cyan sub-surface + * -- blue sub-surface (desync mode) + */ + wl_subsurface_place_below(sub[3], surf[0]); + wl_surface_commit(main_surf); + + fail += check_screen(client, "subsurface_shot_test", 8, &clip, 8); + + /* Place blue sub-surface above its parent(red window). + * Expected stack order: + * -- green sub-surface + * -- cyan sub-surface + * -- blue sub-surface (desync mode) + * -- red window + * -- orange sub-surface + */ + wl_subsurface_place_above(sub[0], main_surf); + wl_surface_commit(main_surf); + + fail += check_screen(client, "subsurface_shot_test", 9, &clip, 9); + + /* Place orange sub-surface above blue sub-surface. + * Expected stack order: + * -- orange sub-surface + * -- green sub-surface + * -- cyan sub-surface + * -- blue sub-surface (desync mode) + * -- red window + */ + wl_subsurface_place_above(sub[3], surf[0]); + wl_surface_commit(main_surf); + + fail += check_screen(client, "subsurface_shot_test", 10, &clip, 10); + + assert(fail == 0); + + for (int i = 0; i < nbuf; i++) + buffer_destroy(bufs[i]); + + for (int i = 0; i < nsub; i++) { + wl_subsurface_destroy(sub[i]); + wl_surface_destroy(surf[i]); + } + + window_destroy(win); + window_destroy(bg_win); + client_destroy(client); +} diff --git a/e-tizen-testsuite/src/subsurface-test.c b/e-tizen-testsuite/src/subsurface-test.c new file mode 100644 index 0000000..431eac4 --- /dev/null +++ b/e-tizen-testsuite/src/subsurface-test.c @@ -0,0 +1,504 @@ +#include <stdio.h> +#include <string.h> + +#include <wayland-client-protocol.h> + +#include "e-tizen-test-client-helper.h" + +#define NUM_SUBSURFACES 3 + +struct compound_surface { + struct wl_subcompositor *subco; + struct wl_surface *parent; + struct wl_surface *child[NUM_SUBSURFACES]; + struct wl_subsurface *sub[NUM_SUBSURFACES]; +}; + +static struct wl_subcompositor * +get_subcompositor(struct client *client) +{ + struct global *g; + struct global *global_sub = NULL; + struct wl_subcompositor *sub; + + wl_list_for_each(g, &client->global_list, link) { + if (strcmp(g->interface, "wl_subcompositor")) + continue; + + if (global_sub) + assert(0 && "multiple wl_subcompositor objects"); + + global_sub = g; + } + + assert(global_sub && "no wl_subcompositor found"); + + assert(global_sub->version == 1); + + sub = wl_registry_bind(client->wl_registry, global_sub->name, + &wl_subcompositor_interface, 1); + assert(sub); + + return sub; +} + +static void +populate_compound_surface(struct compound_surface *com, struct client *client) +{ + int i; + + com->subco = bind_to_singleton_global(client, &wl_subcompositor_interface, 1); + + com->parent = wl_compositor_create_surface(client->wl_compositor); + + for (i = 0; i < NUM_SUBSURFACES; i++) { + com->child[i] = + wl_compositor_create_surface(client->wl_compositor); + com->sub[i] = + wl_subcompositor_get_subsurface(com->subco, + com->child[i], + com->parent); + } +} + +TEST(test_subsurface_basic_protocol) +{ + struct client *client; + struct compound_surface com1; + struct compound_surface com2; + + client = create_client_and_test_surface(123, 77); + assert(client); + + populate_compound_surface(&com1, client); + populate_compound_surface(&com2, client); + + client_roundtrip(client); +} + +TEST(test_subsurface_placement_protocol) +{ + struct client *client; + struct compound_surface com; + + client = create_client_and_test_surface(123, 77); + assert(client); + + populate_compound_surface(&com, client); + + wl_subsurface_place_above(com.sub[0], com.child[1]); + wl_subsurface_place_above(com.sub[1], com.parent); + wl_subsurface_place_below(com.sub[2], com.child[0]); + wl_subsurface_place_below(com.sub[1], com.parent); + + client_roundtrip(client); +} + +TEST(test_subsurface_paradox) +{ + struct client *client; + struct wl_surface *parent; + struct wl_subcompositor *subco; + + client = create_client_and_test_surface(123, 77); + assert(client); + + subco = get_subcompositor(client); + parent = wl_compositor_create_surface(client->wl_compositor); + + /* surface is its own parent */ + wl_subcompositor_get_subsurface(subco, parent, parent); + + expect_protocol_error(client, &wl_subcompositor_interface, + WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE); +} + +TEST(test_subsurface_identical_link) +{ + struct client *client; + struct compound_surface com; + + client = create_client_and_test_surface(123, 77); + assert(client); + + populate_compound_surface(&com, client); + + /* surface is already a subsurface */ + wl_subcompositor_get_subsurface(com.subco, com.child[0], com.parent); + + expect_protocol_error(client, &wl_subcompositor_interface, + WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE); +} + +TEST(test_subsurface_change_link) +{ + struct client *client; + struct compound_surface com; + struct wl_surface *stranger; + + client = create_client_and_test_surface(123, 77); + assert(client); + + stranger = wl_compositor_create_surface(client->wl_compositor); + populate_compound_surface(&com, client); + + /* surface is already a subsurface */ + wl_subcompositor_get_subsurface(com.subco, com.child[0], stranger); + + expect_protocol_error(client, &wl_subcompositor_interface, + WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE); +} + +TEST(test_subsurface_nesting) +{ + struct client *client; + struct compound_surface com; + struct wl_surface *stranger; + + client = create_client_and_test_surface(123, 77); + assert(client); + + stranger = wl_compositor_create_surface(client->wl_compositor); + populate_compound_surface(&com, client); + + /* parent is a sub-surface */ + wl_subcompositor_get_subsurface(com.subco, stranger, com.child[0]); + + client_roundtrip(client); +} + +TEST(test_subsurface_nesting_parent) +{ + struct client *client; + struct compound_surface com; + struct wl_surface *stranger; + + client = create_client_and_test_surface(123, 77); + assert(client); + + stranger = wl_compositor_create_surface(client->wl_compositor); + populate_compound_surface(&com, client); + + /* surface is already a parent */ + wl_subcompositor_get_subsurface(com.subco, com.parent, stranger); + + client_roundtrip(client); +} + +TEST(test_subsurface_loop_paradox) +{ + struct client *client; + struct wl_surface *surface[3]; + struct wl_subcompositor *subco; + + client = create_client_and_test_surface(123, 77); + assert(client); + + subco = get_subcompositor(client); + surface[0] = wl_compositor_create_surface(client->wl_compositor); + surface[1] = wl_compositor_create_surface(client->wl_compositor); + surface[2] = wl_compositor_create_surface(client->wl_compositor); + + /* create a nesting loop */ + wl_subcompositor_get_subsurface(subco, surface[1], surface[0]); + wl_subcompositor_get_subsurface(subco, surface[2], surface[1]); + wl_subcompositor_get_subsurface(subco, surface[0], surface[2]); + + expect_protocol_error(client, &wl_subcompositor_interface, + WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE); +} + +TEST(test_subsurface_place_above_nested_parent) +{ + struct client *client; + struct compound_surface com; + struct wl_surface *grandchild; + struct wl_subcompositor *subco; + struct wl_subsurface *sub; + + client = create_client_and_test_surface(123, 77); + assert(client); + + populate_compound_surface(&com, client); + + subco = get_subcompositor(client); + grandchild = wl_compositor_create_surface(client->wl_compositor); + sub = wl_subcompositor_get_subsurface(subco, grandchild, com.child[0]); + + wl_subsurface_place_above(sub, com.child[0]); + + client_roundtrip(client); +} + +TEST(test_subsurface_place_above_grandparent) +{ + struct client *client; + struct compound_surface com; + struct wl_surface *grandchild; + struct wl_subsurface *sub; + struct wl_subcompositor *subco; + + client = create_client_and_test_surface(123, 77); + assert(client); + + populate_compound_surface(&com, client); + + subco = get_subcompositor(client); + grandchild = wl_compositor_create_surface(client->wl_compositor); + sub = wl_subcompositor_get_subsurface(subco, grandchild, com.child[0]); + + /* can't place a subsurface above its grandparent */ + wl_subsurface_place_above(sub, com.parent); + + expect_protocol_error(client, &wl_subsurface_interface, + WL_SUBSURFACE_ERROR_BAD_SURFACE); +} + +TEST(test_subsurface_place_above_great_aunt) +{ + struct client *client; + struct compound_surface com; + struct wl_surface *grandchild; + struct wl_subsurface *sub; + struct wl_subcompositor *subco; + + client = create_client_and_test_surface(123, 77); + assert(client); + + populate_compound_surface(&com, client); + + subco = get_subcompositor(client); + grandchild = wl_compositor_create_surface(client->wl_compositor); + sub = wl_subcompositor_get_subsurface(subco, grandchild, com.child[0]); + + /* can't place a subsurface above its parents' siblings */ + wl_subsurface_place_above(sub, com.child[1]); + + expect_protocol_error(client, &wl_subsurface_interface, + WL_SUBSURFACE_ERROR_BAD_SURFACE); +} + +TEST(test_subsurface_place_above_child) +{ + struct client *client; + struct compound_surface com; + struct wl_surface *grandchild; + struct wl_subcompositor *subco; + + client = create_client_and_test_surface(123, 77); + assert(client); + + populate_compound_surface(&com, client); + + subco = get_subcompositor(client); + grandchild = wl_compositor_create_surface(client->wl_compositor); + wl_subcompositor_get_subsurface(subco, grandchild, com.child[0]); + + /* can't place a subsurface above its own child subsurface */ + wl_subsurface_place_above(com.sub[0], grandchild); + + expect_protocol_error(client, &wl_subsurface_interface, + WL_SUBSURFACE_ERROR_BAD_SURFACE); +} + +TEST(test_subsurface_place_below_nested_parent) +{ + struct client *client; + struct compound_surface com; + struct wl_subcompositor *subco; + struct wl_surface *grandchild; + struct wl_subsurface *sub; + + client = create_client_and_test_surface(123, 77); + assert(client); + + populate_compound_surface(&com, client); + + subco = get_subcompositor(client); + grandchild = wl_compositor_create_surface(client->wl_compositor); + sub = wl_subcompositor_get_subsurface(subco, grandchild, com.child[0]); + + wl_subsurface_place_below(sub, com.child[0]); + + client_roundtrip(client); +} + +TEST(test_subsurface_place_below_grandparent) +{ + struct client *client; + struct compound_surface com; + struct wl_surface *grandchild; + struct wl_subsurface *sub; + struct wl_subcompositor *subco; + + client = create_client_and_test_surface(123, 77); + assert(client); + + populate_compound_surface(&com, client); + + subco = get_subcompositor(client); + grandchild = wl_compositor_create_surface(client->wl_compositor); + sub = wl_subcompositor_get_subsurface(subco, grandchild, com.child[0]); + + /* can't place a subsurface below its grandparent */ + wl_subsurface_place_below(sub, com.parent); + + expect_protocol_error(client, &wl_subsurface_interface, + WL_SUBSURFACE_ERROR_BAD_SURFACE); +} + +TEST(test_subsurface_place_below_great_aunt) +{ + struct client *client; + struct compound_surface com; + struct wl_surface *grandchild; + struct wl_subsurface *sub; + struct wl_subcompositor *subco; + + client = create_client_and_test_surface(123, 77); + assert(client); + + populate_compound_surface(&com, client); + + subco = get_subcompositor(client); + grandchild = wl_compositor_create_surface(client->wl_compositor); + sub = wl_subcompositor_get_subsurface(subco, grandchild, com.child[0]); + + /* can't place a subsurface below its parents' siblings */ + wl_subsurface_place_below(sub, com.child[1]); + + expect_protocol_error(client, &wl_subsurface_interface, + WL_SUBSURFACE_ERROR_BAD_SURFACE); +} + +TEST(test_subsurface_place_below_child) +{ + struct client *client; + struct compound_surface com; + struct wl_surface *grandchild; + struct wl_subcompositor *subco; + + client = create_client_and_test_surface(123, 77); + assert(client); + + populate_compound_surface(&com, client); + + subco = get_subcompositor(client); + grandchild = wl_compositor_create_surface(client->wl_compositor); + wl_subcompositor_get_subsurface(subco, grandchild, com.child[0]); + + /* can't place a subsurface below its own child subsurface */ + wl_subsurface_place_below(com.sub[0], grandchild); + + expect_protocol_error(client, &wl_subsurface_interface, + WL_SUBSURFACE_ERROR_BAD_SURFACE); +} + +TEST(test_subsurface_place_above_stranger) +{ + struct client *client; + struct compound_surface com; + struct wl_surface *stranger; + + client = create_client_and_test_surface(123, 77); + assert(client); + + stranger = wl_compositor_create_surface(client->wl_compositor); + populate_compound_surface(&com, client); + + /* bad sibling */ + wl_subsurface_place_above(com.sub[0], stranger); + + expect_protocol_error(client, &wl_subsurface_interface, + WL_SUBSURFACE_ERROR_BAD_SURFACE); +} + +TEST(test_subsurface_place_below_stranger) +{ + struct client *client; + struct compound_surface com; + struct wl_surface *stranger; + + client = create_client_and_test_surface(123, 77); + assert(client); + + stranger = wl_compositor_create_surface(client->wl_compositor); + populate_compound_surface(&com, client); + + /* bad sibling */ + wl_subsurface_place_below(com.sub[0], stranger); + + expect_protocol_error(client, &wl_subsurface_interface, + WL_SUBSURFACE_ERROR_BAD_SURFACE); +} + +TEST(test_subsurface_place_above_foreign) +{ + struct client *client; + struct compound_surface com1; + struct compound_surface com2; + + client = create_client_and_test_surface(123, 77); + assert(client); + + populate_compound_surface(&com1, client); + populate_compound_surface(&com2, client); + + /* bad sibling */ + wl_subsurface_place_above(com1.sub[0], com2.child[0]); + + expect_protocol_error(client, &wl_subsurface_interface, + WL_SUBSURFACE_ERROR_BAD_SURFACE); +} + +TEST(test_subsurface_place_below_foreign) +{ + struct client *client; + struct compound_surface com1; + struct compound_surface com2; + + client = create_client_and_test_surface(123, 77); + assert(client); + + populate_compound_surface(&com1, client); + populate_compound_surface(&com2, client); + + /* bad sibling */ + wl_subsurface_place_below(com1.sub[0], com2.child[0]); + + expect_protocol_error(client, &wl_subsurface_interface, + WL_SUBSURFACE_ERROR_BAD_SURFACE); +} + +TEST(test_subsurface_destroy_protocol) +{ + struct client *client; + struct compound_surface com; + + client = create_client_and_test_surface(123, 77); + assert(client); + + populate_compound_surface(&com, client); + + /* not needed anymore */ + wl_subcompositor_destroy(com.subco); + + /* detach child from parent */ + wl_subsurface_destroy(com.sub[0]); + + /* destroy: child, parent */ + wl_surface_destroy(com.child[1]); + wl_surface_destroy(com.parent); + + /* destroy: parent, child */ + wl_surface_destroy(com.child[2]); + + /* destroy: sub, child */ + wl_surface_destroy(com.child[0]); + + /* 2x destroy: child, sub */ + wl_subsurface_destroy(com.sub[2]); + wl_subsurface_destroy(com.sub[1]); + + client_roundtrip(client); +} diff --git a/e-tizen-testsuite/src/tizen-surface-test.c b/e-tizen-testsuite/src/tizen-surface-test.c new file mode 100644 index 0000000..d43bf05 --- /dev/null +++ b/e-tizen-testsuite/src/tizen-surface-test.c @@ -0,0 +1,40 @@ +#include <stdio.h> +#include <string.h> + +#include <wayland-client-protocol.h> +#include <tizen-extension-client-protocol.h> + +#include "e-tizen-test-client-helper.h" + +static void +tres_handle_resource_id(void *data, struct tizen_resource *tres, uint32_t id) +{ + *((uint32_t *)data) = id; +} + +static const struct tizen_resource_listener tres_listener = { + tres_handle_resource_id, +}; + +TEST(test_tizen_resource) +{ + struct client *client; + struct tizen_surface *tsurf; + struct tizen_resource *tres; + uint32_t resource_id = 0; + + client = create_client_and_test_surface(123, 77); + assert(client); + + tsurf = bind_to_singleton_global(client, &tizen_surface_interface, 1); + assert(tsurf); + + tres = tizen_surface_get_tizen_resource(tsurf, client->surface->wl_surface); + assert(tres); + + tizen_resource_add_listener(tres, &tres_listener, &resource_id); + + client_roundtrip(client); + + assert(resource_id != 0); +} diff --git a/e-tizen-testsuite/src/window-shot-test.c b/e-tizen-testsuite/src/window-shot-test.c new file mode 100644 index 0000000..e0142ec --- /dev/null +++ b/e-tizen-testsuite/src/window-shot-test.c @@ -0,0 +1,51 @@ +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <wayland-client-protocol.h> + +#include "e-tizen-test-client-helper.h" + +static struct buffer * +surface_commit_color(struct client *client, struct wl_surface *surface, + pixman_color_t *color, int width, int height) +{ + struct buffer *buf; + + buf = create_shm_buffer_a8r8g8b8(client, width, height); + fill_image_with_color(buf->image, color); + wl_surface_attach(surface, buf->proxy, 0, 0); + wl_surface_damage(surface, 0, 0, width, height); + wl_surface_commit(surface); + + return buf; +} + +TEST(window_shot_basic_test) +{ + struct client *client; + struct window *win; + struct buffer *buf; + pixman_color_t red; + bool match; + + color_rgb888(&red, 255, 0, 0); + + client = create_client(); + assert(client); + + win = create_test_window(client); + assert(win); + + buf = surface_commit_color(client, + win->surface->wl_surface, &red, 720, 1280); + + window_visible_wait(win); + + match = verify_screen_content(client, "window_shot_basic", 0, NULL, 0); + assert(match); + + buffer_destroy(buf); + window_destroy(win); + client_destroy(client); +} diff --git a/e-tizen-testsuite/src/zxdg-shell-v6-test.c b/e-tizen-testsuite/src/zxdg-shell-v6-test.c new file mode 100644 index 0000000..530a16d --- /dev/null +++ b/e-tizen-testsuite/src/zxdg-shell-v6-test.c @@ -0,0 +1,176 @@ +#include <stdio.h> +#include <string.h> + +#include <pixman.h> + +#include <wayland-client.h> +#include <xdg-shell-unstable-v6-client-protocol.h> + +#include "e-tizen-test-client-helper.h" + +struct toplevel_window { + struct client *client; + struct zxdg_shell_v6 *shell; + struct zxdg_surface_v6 *zxdg_surface; + struct zxdg_toplevel_v6 *zxdg_toplevel; + int configured; +}; + +static struct zxdg_shell_v6 * +get_zxdg_shell_v6(struct client *client) +{ + struct global *g; + struct global *global_shell = NULL; + + wl_list_for_each(g, &client->global_list, link) { + if (strcmp(g->interface, "zxdg_shell_v6")) + continue; + + if (global_shell) + assert(0 && "multiple tizen_surface objects"); + + global_shell = g; + } + + assert(global_shell && "no tizen_surface found"); + + assert(global_shell->version == 1); + + return wl_registry_bind(client->wl_registry, global_shell->name, + &zxdg_shell_v6_interface, 1); +} + +static void +surface_handle_configure(void *data, struct zxdg_surface_v6 *zxdg_surface, uint32_t serial) +{ + struct toplevel_window *win = data; + + fprintf(stdout, "zxdg_surface_v6: configure\n"); + + zxdg_surface_v6_ack_configure(zxdg_surface, serial); + + win->configured = 1; +} + +static const struct zxdg_surface_v6_listener surface_listener = { + surface_handle_configure, +}; + +static void +toplevel_handle_configure(void *data, struct zxdg_toplevel_v6 *zxdg_toplevel, int32_t width, int32_t heignt, struct wl_array *states) +{ + uint32_t *p; + + fprintf(stdout, "zxdg_toplevel_v6: configured: "); + + wl_array_for_each(p, states) { + uint32_t state = *p; + + switch (state) { + case ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED: + fprintf(stdout, "maximized\n"); + break; + case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN: + fprintf(stdout, "fullscreen\n"); + break; + case ZXDG_TOPLEVEL_V6_STATE_RESIZING: + fprintf(stdout, "resizing\n"); + break; + case ZXDG_TOPLEVEL_V6_STATE_ACTIVATED: + fprintf(stdout, "activated\n"); + break; + default: + break; + } + } +} + +static void +toplevel_handle_close(void *data, struct zxdg_toplevel_v6 *zxdg_toplevel) +{ + fprintf(stdout, "zxdg_toplevel_v6: close\n"); +} + +static const struct zxdg_toplevel_v6_listener toplevel_listener = { + toplevel_handle_configure, + toplevel_handle_close, +}; + +TEST(create_toplevel) +{ + struct toplevel_window win = {0}; + struct client *client; + struct surface *surface; + pixman_color_t color = { 16384, 16384, 16384, 16384 }; /* uint16_t */ + pixman_image_t *solid; + char *fname = NULL; + int done = 0; + + client = create_client(); + assert(client); + + surface = create_test_surface(client); + + win.shell = get_zxdg_shell_v6(client); + assert(win.shell); + + win.zxdg_surface = zxdg_shell_v6_get_xdg_surface(win.shell, surface->wl_surface); + assert(win.zxdg_surface); + + zxdg_surface_v6_add_listener(win.zxdg_surface, &surface_listener, &win); + + win.zxdg_toplevel = zxdg_surface_v6_get_toplevel(win.zxdg_surface); + assert(win.zxdg_toplevel); + + zxdg_toplevel_v6_add_listener(win.zxdg_toplevel, &toplevel_listener, &win); + + win.client = client; + client_roundtrip(win.client); + + surface->width = client->output->width; + surface->height = client->output->height; + surface->buffer = create_shm_buffer_a8r8g8b8(client, + surface->width, surface->height); + + client->surface = surface; + + solid = pixman_image_create_solid_fill(&color); + pixman_image_composite32(PIXMAN_OP_SRC, + solid, /* src */ + NULL, /* mask */ + surface->buffer->image, /* dst */ + 0, 0, /* src x,y */ + 0, 0, /* mask x,y */ + 0, 0, /* dst x,y */ + surface->width, surface->height); + pixman_image_unref(solid); + + fname = image_filename("tizen_logo"); + pixman_image_t *img = load_image_from_png(fname); + assert(img); + + free(fname); + + pixman_image_composite32(PIXMAN_OP_OVER, + img, + NULL, + surface->buffer->image, + 0, 0, + 0, 0, + 0, 0, + pixman_image_get_width(img), + pixman_image_get_height(img)); + pixman_image_unref(img); + + wl_surface_attach(surface->wl_surface, surface->buffer->proxy, 0, 0); + wl_surface_damage(surface->wl_surface, 0, 0, surface->width, + surface->height); + + frame_callback_set(surface->wl_surface, &done); + + wl_surface_commit(surface->wl_surface); + + frame_callback_wait(client, &done); + + assert(win.configured == 1); +} |