diff --git a/doc/sphinx/toc/test-suite.rst b/doc/sphinx/toc/test-suite.rst index 7fafe3e9..f7803292 100644 --- a/doc/sphinx/toc/test-suite.rst +++ b/doc/sphinx/toc/test-suite.rst @@ -213,6 +213,34 @@ clients: } +DRM-backend tests +----------------- + +DRM-backend tests require a DRM device, so they are a special case. To select a +device the test suite will simply look at the environment variable +``WESTON_TEST_SUITE_DRM_DEVICE``. So the first thing the user has to do in order +to run DRM-backend tests is to set this environment variable with the card that +should run the tests. For instance, in order to run DRM-backend tests with +``card0`` we need to run ``export WESTON_TEST_SUITE_DRM_DEVICE=card0``. + +Note that the card should not be in use by a desktop environment (or any other +program that requires master status), as there can only be one user at a time +with master status in a DRM device. Also, this is the reason why we can not run +two or more DRM-backend tests simultaneously. Since the test suite tries to run +the tests in parallel, we have a lock mechanism to enforce that DRM-backend +tests run sequentially, one at a time. Note that this will not avoid them to run +in parallel with other types of tests. + +Another specificity of DRM-backend tests is that they run using the non-default +seat ``seat-weston-test``. This avoids unnecessarily opening input devices that +may be present in the default seat ``seat0``. On the other hand, this will make +``launcher-logind`` fail, as we are trying to use a seat that is different from +the one we are logged in. In the CI we do not rely on ``logind``, so it should +fallback to ``launcher-direct`` anyway. It requires root, but this is also not a +problem for the CI, as ``virtme`` starts as root. The problem is that to run +the tests locally with a real hardware the users need to run as root. + + Writing tests ------------- diff --git a/tests/meson.build b/tests/meson.build index 231746c1..d26bc582 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -52,6 +52,7 @@ dep_test_client = declare_dependency( dep_wayland_client, dep_test_runner, dep_pixman, + dependency('libudev', version: '>= 136'), ] ) diff --git a/tests/weston-test-fixture-compositor.c b/tests/weston-test-fixture-compositor.c index ee7a5d1e..0c9855f6 100644 --- a/tests/weston-test-fixture-compositor.c +++ b/tests/weston-test-fixture-compositor.c @@ -27,6 +27,10 @@ #include #include +#include +#include +#include +#include #include "shared/helpers.h" #include "weston-test-fixture-compositor.h" @@ -89,6 +93,71 @@ prog_args_fini(struct prog_args *p) prog_args_init(p); } +static char * +get_lock_path(void) +{ + const char *env_path, *suffix; + char *lock_path; + + suffix = "weston-test-suite-drm-lock"; + env_path = getenv("XDG_RUNTIME_DIR"); + if (!env_path) { + fprintf(stderr, "Failed to compute lock file path. " \ + "XDG_RUNTIME_DIR is not set.\n"); + return NULL; + } + + if (asprintf(&lock_path, "%s/%s", env_path, suffix) == -1) + return NULL; + + return lock_path; +} + +/* + * DRM-backend tests need to be run sequentially, since there can only be + * one user at a time with master status in a DRM KMS device. Since the + * test suite runs the tests in parallel, there's a mechanism to assure + * only one DRM-backend test is running at a time: tests of this type keep + * waiting until they acquire a lock (which is hold until they end). + * + * Returns -1 in failure and fd in success. + */ +static int +wait_for_lock(void) +{ + char *lock_path; + int fd; + + lock_path = get_lock_path(); + if (!lock_path) + goto err_path; + + fd = open(lock_path, O_RDWR | O_CLOEXEC | O_CREAT, 00700); + if (fd == -1) { + fprintf(stderr, "Could not open lock file %s: %s\n", lock_path, strerror(errno)); + goto err_open; + } + fprintf(stderr, "Waiting for lock on %s...\n", lock_path); + + /* The call is blocking, so we don't need a 'while'. Also, as we + * have a timeout for each test, this won't get stuck waiting. */ + if (flock(fd, LOCK_EX) == -1) { + fprintf(stderr, "Could not lock %s: %s\n", lock_path, strerror(errno)); + goto err_lock; + } + fprintf(stderr, "Lock %s acquired.\n", lock_path); + free(lock_path); + + return fd; + +err_lock: + close(fd); +err_open: + free(lock_path); +err_path: + return -1; +} + /** Initialize part of compositor setup * * \param setup The variable to initialize. @@ -204,8 +273,8 @@ execute_compositor(const struct compositor_setup *setup, { struct prog_args args; char *tmp; - const char *ctmp; - int ret; + const char *ctmp, *drm_device; + int ret, lock_fd = -1; if (setenv("WESTON_MODULE_MAP", WESTON_MODULE_MAP, 0) < 0 || setenv("WESTON_DATA_DIR", WESTON_DATA_DIR, 0) < 0) { @@ -271,6 +340,28 @@ execute_compositor(const struct compositor_setup *setup, asprintf(&tmp, "--backend=%s", backend_to_str(setup->backend)); prog_args_take(&args, tmp); + if (setup->backend == WESTON_BACKEND_DRM) { + + drm_device = getenv("WESTON_TEST_SUITE_DRM_DEVICE"); + if (!drm_device) { + fprintf(stderr, "Skipping DRM-backend tests because " \ + "WESTON_TEST_SUITE_DRM_DEVICE is not set. " \ + "See test suite documentation to learn how " \ + "to run them.\n"); + return RESULT_SKIP; + } + asprintf(&tmp, "--drm-device=%s", drm_device); + prog_args_take(&args, tmp); + + prog_args_take(&args, strdup("--seat=weston-test-seat")); + + prog_args_take(&args, strdup("--continue-without-input")); + + lock_fd = wait_for_lock(); + if (lock_fd == -1) + return RESULT_FAIL; + } + asprintf(&tmp, "--socket=%s", setup->testset_name); prog_args_take(&args, tmp); @@ -327,5 +418,10 @@ execute_compositor(const struct compositor_setup *setup, prog_args_fini(&args); + /* We acquired a lock (if this is a DRM-backend test) and now we can + * close its fd and release it, as it has already been run. */ + if (lock_fd != -1) + close(lock_fd); + return ret; }