From 1cbfc07df0c90d68a3e629afe10056e277c8e6f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 18 Dec 2024 00:16:43 +0100 Subject: [PATCH] tests: Add get state D-Bus test using gdctl Test is ref test like, with gdctl outputs being compared. Part-of: --- src/tests/gdctl/show | 29 ++++ src/tests/gdctl/show-modes | 35 ++++ src/tests/gdctl/show-properties | 57 ++++++ src/tests/gdctl/show-verbose | 93 ++++++++++ src/tests/generic.test.in | 2 +- src/tests/meson.build | 12 ++ src/tests/monitor-dbus-tests.c | 298 ++++++++++++++++++++++++++++++++ tools/meson.build | 1 + 8 files changed, 526 insertions(+), 1 deletion(-) create mode 100644 src/tests/gdctl/show create mode 100644 src/tests/gdctl/show-modes create mode 100644 src/tests/gdctl/show-properties create mode 100644 src/tests/gdctl/show-verbose create mode 100644 src/tests/monitor-dbus-tests.c diff --git a/src/tests/gdctl/show b/src/tests/gdctl/show new file mode 100644 index 000000000..62f9c0ba5 --- /dev/null +++ b/src/tests/gdctl/show @@ -0,0 +1,29 @@ +Monitors: +├──Monitor DP-1 (MetaProduct's Inc. 14") +│ ├──Vendor: MetaProduct's Inc. +│ ├──Product: MetaMonitor +│ ├──Serial: 0x1234560 +│ └──Current mode +│ └──3840x2160@60.000 +└──Monitor DP-2 (MetaProduct's Inc. 13") + ├──Vendor: MetaProduct's Inc. + ├──Product: MetaMonitor + ├──Serial: 0x1234561 + └──Current mode + └──2560x1440@60.000 + +Logical monitors: +├──Logical monitor #1 +│ ├──Position: (0, 0) +│ ├──Scale: 2.2018349170684814 +│ ├──Transform: normal +│ ├──Primary: yes +│ └──Monitors: (1) +│ └──DP-1 (MetaProduct's Inc. 14") +└──Logical monitor #2 + ├──Position: (1744, 0) + ├──Scale: 1.7582417726516724 + ├──Transform: normal + ├──Primary: no + └──Monitors: (1) + └──DP-2 (MetaProduct's Inc. 13") diff --git a/src/tests/gdctl/show-modes b/src/tests/gdctl/show-modes new file mode 100644 index 000000000..b175e1fed --- /dev/null +++ b/src/tests/gdctl/show-modes @@ -0,0 +1,35 @@ +Monitors: +├──Monitor DP-1 (MetaProduct's Inc. 14") +│ ├──Vendor: MetaProduct's Inc. +│ ├──Product: MetaMonitor +│ ├──Serial: 0x1234560 +│ └──Modes (4) +│ ├──3840x2160@60.000 +│ ├──3840x2160@30.000 +│ ├──2560x1440@60.000 +│ └──1440x900@60.000 +└──Monitor DP-2 (MetaProduct's Inc. 13") + ├──Vendor: MetaProduct's Inc. + ├──Product: MetaMonitor + ├──Serial: 0x1234561 + └──Modes (4) + ├──2560x1440@60.000 + ├──1440x900@60.000 + ├──1366x768@60.000 + └──800x600@60.000 + +Logical monitors: +├──Logical monitor #1 +│ ├──Position: (0, 0) +│ ├──Scale: 2.2018349170684814 +│ ├──Transform: normal +│ ├──Primary: yes +│ └──Monitors: (1) +│ └──DP-1 (MetaProduct's Inc. 14") +└──Logical monitor #2 + ├──Position: (1744, 0) + ├──Scale: 1.7582417726516724 + ├──Transform: normal + ├──Primary: no + └──Monitors: (1) + └──DP-2 (MetaProduct's Inc. 13") diff --git a/src/tests/gdctl/show-properties b/src/tests/gdctl/show-properties new file mode 100644 index 000000000..d8c7aa03d --- /dev/null +++ b/src/tests/gdctl/show-properties @@ -0,0 +1,57 @@ +Monitors: +├──Monitor DP-1 (MetaProduct's Inc. 14") +│ ├──Vendor: MetaProduct's Inc. +│ ├──Product: MetaMonitor +│ ├──Serial: 0x1234560 +│ ├──Current mode +│ │ └──3840x2160@60.000 +│ │ ├──Dimension: 3840x2160 +│ │ ├──Refresh rate: 60.000 +│ │ ├──Preferred scale: 2.2018349170684814 +│ │ ├──Supported scales: [1.0, 1.25, 1.5, 1.7518248558044434, 2.0, 2.2018349170684814, 2.5, 2.7586207389831543, 3.0, 3.2432432174682617, 3.4782607555389404, 3.75, 4.0] +│ │ └──Properties: (2) +│ │ ├──is-current ⇒ yes +│ │ └──is-preferred ⇒ yes +│ └──Properties: (3) +│ ├──is-builtin ⇒ no +│ ├──display-name ⇒ MetaProduct's Inc. 14" +│ └──is-for-lease ⇒ no +└──Monitor DP-2 (MetaProduct's Inc. 13") + ├──Vendor: MetaProduct's Inc. + ├──Product: MetaMonitor + ├──Serial: 0x1234561 + ├──Current mode + │ └──2560x1440@60.000 + │ ├──Dimension: 2560x1440 + │ ├──Refresh rate: 60.000 + │ ├──Preferred scale: 1.7582417726516724 + │ ├──Supported scales: [1.0, 1.25, 1.495327115058899, 1.7582417726516724, 2.0, 2.253521203994751, 2.5, 2.7586207389831543, 3.0188679695129395] + │ └──Properties: (2) + │ ├──is-current ⇒ yes + │ └──is-preferred ⇒ yes + └──Properties: (3) + ├──is-builtin ⇒ no + ├──display-name ⇒ MetaProduct's Inc. 13" + └──is-for-lease ⇒ no + +Logical monitors: +├──Logical monitor #1 +│ ├──Position: (0, 0) +│ ├──Scale: 2.2018349170684814 +│ ├──Transform: normal +│ ├──Primary: yes +│ ├──Monitors: (1) +│ │ └──DP-1 (MetaProduct's Inc. 14") +│ └──Properties: (0) +└──Logical monitor #2 + ├──Position: (1744, 0) + ├──Scale: 1.7582417726516724 + ├──Transform: normal + ├──Primary: no + ├──Monitors: (1) + │ └──DP-2 (MetaProduct's Inc. 13") + └──Properties: (0) + +Properties: (2) +├──layout-mode ⇒ logical +└──supports-changing-layout-mode ⇒ yes diff --git a/src/tests/gdctl/show-verbose b/src/tests/gdctl/show-verbose new file mode 100644 index 000000000..c88a0a8e3 --- /dev/null +++ b/src/tests/gdctl/show-verbose @@ -0,0 +1,93 @@ +Monitors: +├──Monitor DP-1 (MetaProduct's Inc. 14") +│ ├──Vendor: MetaProduct's Inc. +│ ├──Product: MetaMonitor +│ ├──Serial: 0x1234560 +│ ├──Modes (4) +│ │ ├──3840x2160@60.000 +│ │ │ ├──Dimension: 3840x2160 +│ │ │ ├──Refresh rate: 60.000 +│ │ │ ├──Preferred scale: 2.2018349170684814 +│ │ │ ├──Supported scales: [1.0, 1.25, 1.5, 1.7518248558044434, 2.0, 2.2018349170684814, 2.5, 2.7586207389831543, 3.0, 3.2432432174682617, 3.4782607555389404, 3.75, 4.0] +│ │ │ └──Properties: (2) +│ │ │ ├──is-current ⇒ yes +│ │ │ └──is-preferred ⇒ yes +│ │ ├──3840x2160@30.000 +│ │ │ ├──Dimension: 3840x2160 +│ │ │ ├──Refresh rate: 30.000 +│ │ │ ├──Preferred scale: 2.2018349170684814 +│ │ │ ├──Supported scales: [1.0, 1.25, 1.5, 1.7518248558044434, 2.0, 2.2018349170684814, 2.5, 2.7586207389831543, 3.0, 3.2432432174682617, 3.4782607555389404, 3.75, 4.0] +│ │ │ └──Properties: (0) +│ │ ├──2560x1440@60.000 +│ │ │ ├──Dimension: 2560x1440 +│ │ │ ├──Refresh rate: 60.000 +│ │ │ ├──Preferred scale: 1.495327115058899 +│ │ │ ├──Supported scales: [1.0, 1.25, 1.495327115058899, 1.7582417726516724, 2.0, 2.253521203994751, 2.5, 2.7586207389831543, 3.0188679695129395] +│ │ │ └──Properties: (0) +│ │ └──1440x900@60.000 +│ │ ├──Dimension: 1440x900 +│ │ ├──Refresh rate: 60.000 +│ │ ├──Preferred scale: 1.0 +│ │ ├──Supported scales: [1.0, 1.25, 1.5, 1.7475727796554565] +│ │ └──Properties: (0) +│ └──Properties: (3) +│ ├──is-builtin ⇒ no +│ ├──display-name ⇒ MetaProduct's Inc. 14" +│ └──is-for-lease ⇒ no +└──Monitor DP-2 (MetaProduct's Inc. 13") + ├──Vendor: MetaProduct's Inc. + ├──Product: MetaMonitor + ├──Serial: 0x1234561 + ├──Modes (4) + │ ├──2560x1440@60.000 + │ │ ├──Dimension: 2560x1440 + │ │ ├──Refresh rate: 60.000 + │ │ ├──Preferred scale: 1.7582417726516724 + │ │ ├──Supported scales: [1.0, 1.25, 1.495327115058899, 1.7582417726516724, 2.0, 2.253521203994751, 2.5, 2.7586207389831543, 3.0188679695129395] + │ │ └──Properties: (2) + │ │ ├──is-current ⇒ yes + │ │ └──is-preferred ⇒ yes + │ ├──1440x900@60.000 + │ │ ├──Dimension: 1440x900 + │ │ ├──Refresh rate: 60.000 + │ │ ├──Preferred scale: 1.0 + │ │ ├──Supported scales: [1.0, 1.25, 1.5, 1.7475727796554565] + │ │ └──Properties: (0) + │ ├──1366x768@60.000 + │ │ ├──Dimension: 1366x768 + │ │ ├──Refresh rate: 60.000 + │ │ ├──Preferred scale: 1.0 + │ │ ├──Supported scales: [1.0] + │ │ └──Properties: (0) + │ └──800x600@60.000 + │ ├──Dimension: 800x600 + │ ├──Refresh rate: 60.000 + │ ├──Preferred scale: 1.0 + │ ├──Supported scales: [1.0] + │ └──Properties: (0) + └──Properties: (3) + ├──is-builtin ⇒ no + ├──display-name ⇒ MetaProduct's Inc. 13" + └──is-for-lease ⇒ no + +Logical monitors: +├──Logical monitor #1 +│ ├──Position: (0, 0) +│ ├──Scale: 2.2018349170684814 +│ ├──Transform: normal +│ ├──Primary: yes +│ ├──Monitors: (1) +│ │ └──DP-1 (MetaProduct's Inc. 14") +│ └──Properties: (0) +└──Logical monitor #2 + ├──Position: (1744, 0) + ├──Scale: 1.7582417726516724 + ├──Transform: normal + ├──Primary: no + ├──Monitors: (1) + │ └──DP-2 (MetaProduct's Inc. 13") + └──Properties: (0) + +Properties: (2) +├──layout-mode ⇒ logical +└──supports-changing-layout-mode ⇒ yes diff --git a/src/tests/generic.test.in b/src/tests/generic.test.in index 623e51f24..d9b8ed3c8 100644 --- a/src/tests/generic.test.in +++ b/src/tests/generic.test.in @@ -1,5 +1,5 @@ [Test] Description=Mutter test: @testname@ -Exec=sh -ec 'env G_TEST_SRCDIR=@pkgdatadir@ G_TEST_BUILDDIR=@libexecdir@/installed-tests/mutter-@apiversion@ MUTTER_REF_TEST_RESULT_DIR=@reftestresultdir@ @libexecdir@/installed-tests/mutter-@apiversion@/mutter-installed-dbus-session.py @runnerargs@ -- @libexecdir@/installed-tests/mutter-@apiversion@/@testexecutable@ @testargs@' +Exec=sh -ec 'env G_TEST_SRCDIR=@pkgdatadir@ G_TEST_BUILDDIR=@libexecdir@/installed-tests/mutter-@apiversion@ MUTTER_REF_TEST_RESULT_DIR=@reftestresultdir@ MUTTER_GDCTL_TEST_RESULT_DIR=@gdctltestresultdir@ @libexecdir@/installed-tests/mutter-@apiversion@/mutter-installed-dbus-session.py @runnerargs@ -- @libexecdir@/installed-tests/mutter-@apiversion@/@testexecutable@ @testargs@' Type=session Output=TAP diff --git a/src/tests/meson.build b/src/tests/meson.build index 65a3e7cd1..a231ada3a 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -148,6 +148,7 @@ test_env_variables = { 'XDG_CONFIG_HOME': mutter_builddir / '.config', 'MUTTER_TEST_PLUGIN_PATH': '@0@'.format(default_plugin.full_path()), 'MUTTER_REF_TEST_RESULT_DIR': mutter_builddir / 'meson-logs' / 'tests' / 'ref-tests', + 'MUTTER_GDCTL_TEST_RESULT_DIR': mutter_builddir / 'meson-logs' / 'tests' / 'gdctl', 'GSETTINGS_SCHEMA_DIR': ':'.join([mutter_builddir / 'src' / 'tests', locally_compiled_schemas_dir]), } @@ -206,6 +207,9 @@ install_data( install_subdir('dbusmock-templates', install_dir: tests_datadir, ) +install_subdir('gdctl', + install_dir: tests_datadir, +) if have_installed_tests configure_file( @@ -277,6 +281,12 @@ test_cases += [ 'suite': 'backend', 'sources': [ 'monitor-backlight-tests.c', ] }, + { + 'name': 'monitor-dbus', + 'suite': 'backend', + 'sources': [ 'monitor-dbus-tests.c', ], + 'args': gdctl.full_path(), + }, { 'name': 'stage-views', 'suite': 'compositor', @@ -808,6 +818,7 @@ foreach test_case: test_cases args = test_case.get('args', []) + message('test @0@ args: @1@'.format(test_case['name'], args)) test(test_case['name'], test_executable, suite: ['core', 'mutter/' + test_case['suite']], args: args, @@ -1058,6 +1069,7 @@ if have_installed_tests installed_tests_cdata.set('libexecdir', libexecdir) installed_tests_cdata.set('pkgdatadir', pkgdatadir) installed_tests_cdata.set('reftestresultdir', '/tmp/mutter-ref-test-results') + installed_tests_cdata.set('gdctltestresultdir', '/tmp/mutter-gdctl-test-results') installed_tests_cdata.set('testname', test_case['name']) installed_tests_cdata.set('testexecutable', 'mutter-' + test_case['name']) installed_tests_cdata.set('testargs', ' '.join(test_case.get('args', []))) diff --git a/src/tests/monitor-dbus-tests.c b/src/tests/monitor-dbus-tests.c new file mode 100644 index 000000000..f8f56e1cd --- /dev/null +++ b/src/tests/monitor-dbus-tests.c @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2024 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include "config.h" + +#include + +#include "tests/meta-monitor-test-utils.h" +#include "tests/meta-test/meta-context-test.h" + +static MetaContext *test_context; +static char *gdctl_path; + +static MonitorTestCaseSetup test_case_setup = { + .modes = { + { + .width = 3840, + .height = 2160, + .refresh_rate = 60.0 + }, + { + .width = 3840, + .height = 2160, + .refresh_rate = 30.0 + }, + { + .width = 2560, + .height = 1440, + .refresh_rate = 60.0 + }, + { + .width = 1440, + .height = 900, + .refresh_rate = 60.0 + }, + { + .width = 1366, + .height = 768, + .refresh_rate = 60.0 + }, + { + .width = 800, + .height = 600, + .refresh_rate = 60.0 + }, + }, + .n_modes = 6, + .outputs = { + { + .crtc = 0, + .modes = { 0, 1, 2, 3 }, + .n_modes = 4, + .preferred_mode = 0, + .possible_crtcs = { 0 }, + .n_possible_crtcs = 1, + .width_mm = 300, + .height_mm = 190, + .dynamic_scale = TRUE, + }, + { + .crtc = 1, + .modes = { 2, 3, 4, 5 }, + .n_modes = 4, + .preferred_mode = 2, + .possible_crtcs = { 1 }, + .n_possible_crtcs = 1, + .width_mm = 290, + .height_mm = 180, + .dynamic_scale = TRUE, + }, + }, + .n_outputs = 2, + .n_crtcs = 2 +}; + +static void +read_all_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + gboolean *done = user_data; + GError *error = NULL; + + g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), + res, + NULL, + &error); + g_assert_no_error (error); + + *done = TRUE; +} + +static void +wait_check_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + gboolean *done = user_data; + GError *error = NULL; + + g_subprocess_wait_check_finish (G_SUBPROCESS (source_object), res, &error); + g_assert_no_error (error); + + *done = TRUE; +} + +static char * +save_output (const char *output, + const char *expected_output_file) +{ + const char *gdctl_result_dir; + char *output_path; + GError *error = NULL; + + gdctl_result_dir = g_getenv ("MUTTER_GDCTL_TEST_RESULT_DIR"); + g_assert_no_errno (g_mkdir_with_parents (gdctl_result_dir, 0755)); + + output_path = g_strdup_printf ("%s/%s", + gdctl_result_dir, + expected_output_file); + + g_file_set_contents (output_path, output, -1, &error); + g_assert_no_error (error); + + return output_path; +} + +static void +run_diff (const char *output_path, + const char *expected_output_path) +{ + g_autoptr (GSubprocessLauncher) launcher = NULL; + g_autoptr (GSubprocess) subprocess = NULL; + GError *error = NULL; + + launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE); + subprocess = g_subprocess_launcher_spawn (launcher, + &error, + "diff", + "-u", + expected_output_path, + output_path, + NULL); + g_subprocess_wait (subprocess, NULL, &error); + g_assert_no_error (error); +} + +static void +check_gdctl_output (const char *expected_output_file, + ...) +{ + g_autoptr (GPtrArray) args = NULL; + va_list va_args; + char *arg; + g_autoptr (GSubprocessLauncher) launcher = NULL; + g_autoptr (GSubprocess) subprocess = NULL; + GInputStream *stdout_pipe; + size_t max_output_size; + g_autofree char *output = NULL; + gboolean read_done = FALSE; + gboolean process_done = FALSE; + GError *error = NULL; + g_autofree char *expected_output_path = NULL; + g_autofree char *expected_output = NULL; + + args = g_ptr_array_new (); + g_ptr_array_add (args, gdctl_path); + va_start (va_args, expected_output_file); + while ((arg = va_arg (va_args, char *))) + g_ptr_array_add (args, arg); + va_end (va_args); + g_ptr_array_add (args, NULL); + + launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE | + G_SUBPROCESS_FLAGS_STDERR_PIPE); + + subprocess = g_subprocess_launcher_spawnv (launcher, + (const char * const*) args->pdata, + &error); + stdout_pipe = g_subprocess_get_stdout_pipe (subprocess); + max_output_size = 1024 * 1024; + output = g_malloc0 (max_output_size); + g_input_stream_read_all_async (stdout_pipe, + output, + max_output_size - 1, + G_PRIORITY_DEFAULT, + NULL, + read_all_cb, + &read_done); + + g_subprocess_wait_check_async (subprocess, NULL, + wait_check_cb, &process_done); + + while (!read_done || !process_done) + g_main_context_iteration (NULL, TRUE); + + expected_output_path = g_test_build_filename (G_TEST_DIST, + "tests", + "gdctl", + expected_output_file, + NULL); + g_file_get_contents (expected_output_path, + &expected_output, + NULL, + &error); + g_assert_no_error (error); + + if (g_strcmp0 (expected_output, output) != 0) + { + g_autofree char *output_path = NULL; + + output_path = save_output (output, expected_output_file); + run_diff (output_path, expected_output_path); + g_error ("Incorrect gdctl output"); + } +} + +static void +meta_test_monitor_dbus_get_state (void) +{ + MetaBackend *backend = meta_context_get_backend (test_context); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + MetaMonitorManagerTest *monitor_manager_test = + META_MONITOR_MANAGER_TEST (monitor_manager); + MetaMonitorTestSetup *test_setup; + + test_setup = meta_create_monitor_test_setup (backend, + &test_case_setup, + MONITOR_TEST_FLAG_NO_STORED); + meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup); + + check_gdctl_output ("show", + "show", NULL); + check_gdctl_output ("show-properties", + "show", "--properties", NULL); + check_gdctl_output ("show-modes", + "show", "--modes", NULL); + check_gdctl_output ("show-verbose", + "show", "--verbose", NULL); +} + +static void +init_tests (void) +{ + g_test_add_func ("/backends/native/monitor/dbus/get-state", + meta_test_monitor_dbus_get_state); +} + +int +main (int argc, + char **argv) +{ + g_autoptr (MetaContext) context = NULL; + g_autoptr (GFile) gdctl_file = NULL; + char **argv_ignored = NULL; + GOptionEntry options[] = { + { + G_OPTION_REMAINING, + .arg = G_OPTION_ARG_STRING_ARRAY, + &argv_ignored, + .arg_description = "GDCTL-PATH" + }, + { NULL } + }; + + context = meta_create_test_context (META_CONTEXT_TEST_TYPE_TEST, + META_CONTEXT_TEST_FLAG_NO_X11); + meta_context_add_option_entries (context, options, NULL); + g_assert_true (meta_context_configure (context, &argc, &argv, NULL)); + + g_assert_nonnull (argv_ignored); + g_assert_nonnull (argv_ignored[0]); + g_assert_null (argv_ignored[1]); + gdctl_path = argv_ignored[0]; + + test_context = context; + + init_tests (); + + return meta_context_test_run_tests (META_CONTEXT_TEST (context), + META_TEST_RUN_FLAG_NONE); +} diff --git a/tools/meson.build b/tools/meson.build index 2603ed3d8..763507c2e 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -3,4 +3,5 @@ install_data( install_dir: bindir, ) +gdctl = find_program('gdctl') get_state_tool = find_program('get-state.py')