mirror of
https://gitlab.freedesktop.org/pipewire/pipewire
synced 2024-09-06 00:40:34 +00:00
![Peter Hutterer](/assets/img/avatar_default.png)
Debugging is easier if we can explicitly run a single iteration only. This filter could be a range but for now it's sufficient to be able to run a single test only.
571 lines
17 KiB
C
571 lines
17 KiB
C
/* PipeWire
|
|
*
|
|
* Copyright © 2021 Red Hat, Inc.
|
|
*
|
|
* 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"
|
|
|
|
#ifndef PWTEST_H
|
|
#define PWTEST_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#include <limits.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <math.h>
|
|
|
|
#include <spa/utils/string.h>
|
|
#include <spa/utils/dict.h>
|
|
#include "spa/support/plugin.h"
|
|
|
|
/**
|
|
* \defgroup pwtest Test Suite
|
|
* \brief `pwtest` is a test runner framework for PipeWire.
|
|
*
|
|
* It's modelled after other
|
|
* test suites like [check](https://libcheck.github.io/check/)
|
|
* and draws a lot of inspiration from the [libinput test
|
|
* suite](https://wayland.freedesktop.org/libinput/doc/latest/).
|
|
*
|
|
* `pwtest` captures logs from the tests (and the pipewire daemon, if
|
|
* applicable) and collects the output into YAML files printed to `stderr`.
|
|
*
|
|
* ## Tests
|
|
*
|
|
* A `pwtest` test is declared with the `PWTEST()` macro and must return one of
|
|
* the `pwtest` status codes. Those codes are:
|
|
* - \ref PWTEST_PASS for a successful test
|
|
* - \ref PWTEST_FAIL for a test case failure. Usually you should not return this
|
|
* value but rely on the `pwtest` macros to handle this case.
|
|
* - \ref PWTEST_SKIP to skip the current test
|
|
* - \ref PWTEST_SYSTEM_ERROR in case of an error that would cause the test to not run properly. This is not a test case failure but some required precondition not being met.
|
|
*
|
|
* ```c
|
|
* #include "pwtest.h"
|
|
*
|
|
* PWTEST(some_test)
|
|
* {
|
|
* int var = 10;
|
|
* const char *str = "foo";
|
|
*
|
|
* if (access("/", R_OK))
|
|
* pwtest_error_with_message("Oh dear, no root directory?");
|
|
*
|
|
* if (today_is_monday)
|
|
* return PWTEST_SKIP;
|
|
*
|
|
* pwtest_int_lt(var, 20);
|
|
* pwtest_ptr_notnull(&var);
|
|
* pwtest_str_ne(str, "bar");
|
|
*
|
|
* return PWTEST_PASS;
|
|
* }
|
|
* ...
|
|
* ```
|
|
*
|
|
* `pwtest` provides comparison macros for most basic data types with the `lt`,
|
|
* `le`, `eq`, `gt`, `ge` suffixes (`<, <=, ==, >, >=`). Tests usually should not
|
|
* return `PWTEST_FAIL` directly, use the `pwtest_fail()` macros if .
|
|
*
|
|
* By default, a test runs in a forked process, any changes to the
|
|
* process'environment, etc. are discarded in the next test.
|
|
*
|
|
* ## Suites
|
|
*
|
|
* Tests are grouped into suites and declared with the PWTEST_SUITE() macro.
|
|
* Each test must be added with the required arguments, it is acceptable to
|
|
* add the same test multiple times with different arguments.
|
|
*
|
|
* ```c
|
|
* ...
|
|
* PWTEST_SUITE(misc)
|
|
* {
|
|
* if (today_is_monday)
|
|
* return PWTEST_SKIP;
|
|
*
|
|
* // simple test
|
|
* pwtest_add(some_test, PWTEST_NOARG);
|
|
* // starts with its own pipewire daemon instance
|
|
* pwtest_add(some_test, PWTEST_ARG_DAEMON);
|
|
*
|
|
* return PWTEST_SUCCESS;
|
|
* }
|
|
* ```
|
|
* For a list of potential arguments, see \ref pwtest_arg and the
|
|
* `test-examples.c` file in the source directory.
|
|
*
|
|
* Suites are auto-discovered, they do not have to be manually added to a test run.
|
|
*
|
|
* ## Running tests
|
|
*
|
|
* The `pwtest` framework is built into each test binary, so just execute the
|
|
* matching binary. See the `--help` output for the full argument list.
|
|
*
|
|
* The most useful arguments when running the test suite:
|
|
* - `--verbose` to enable logs even when tests pass or are skipped
|
|
* - `--filter-test=glob`, `--filter-suite=glob` uses an `fnmatch()` glob to limit which tests or suites are run
|
|
* - `--no-fork` - see "Debugging test-case failures"
|
|
*
|
|
* ## Debugging test-case failures
|
|
*
|
|
* To debug a single test, disable forking and run the test through gdb:
|
|
*
|
|
* ```
|
|
* $ gdb path/to/test
|
|
* (gdb) break test_function_name
|
|
* Breakpoint 1 at 0xfffffffffffff: file ../test/test-file.c, line 123
|
|
* (gdb) r --no-fork --filter-test=test_function_name
|
|
* ```
|
|
* Disabling forking makes it easy to debug but should always be used with
|
|
* `--filter-test`. Any test that modifies its environment will affect
|
|
* subsequent tests and may invalidate the test results.
|
|
*
|
|
* Where a test has multiple iterations, use `--filter-iteration` to only run
|
|
* one single iteration.
|
|
*/
|
|
|
|
/**
|
|
* \addtogroup pwtest
|
|
* \{
|
|
*/
|
|
|
|
|
|
/** \struct pwtest_context */
|
|
struct pwtest_context;
|
|
/** \struct pwtest_suite */
|
|
struct pwtest_suite;
|
|
/** \struct pwtest_test */
|
|
struct pwtest_test;
|
|
|
|
#include "pwtest-implementation.h"
|
|
|
|
/**
|
|
* Result returned from tests or suites.
|
|
*/
|
|
enum pwtest_result {
|
|
PWTEST_PASS = 75, /**< test successful */
|
|
PWTEST_FAIL = 76, /**< test failed. Should not be returned directly,
|
|
Use the pwtest_ macros instead */
|
|
PWTEST_SKIP = 77, /**< test was skipped */
|
|
PWTEST_TIMEOUT = 78, /**< test aborted after timeout */
|
|
PWTEST_SYSTEM_ERROR = 79, /**< unrelated error occurred */
|
|
};
|
|
|
|
/**
|
|
* If the test was added with a range (see \ref PWTEST_ARG_RANGE), this
|
|
* function returns the current iteration within that range. Otherwise, this
|
|
* function returns zero.
|
|
*/
|
|
int pwtest_get_iteration(struct pwtest_test *t);
|
|
|
|
/**
|
|
* If the test had properties set (see \ref PWTEST_ARG_PROP), this function
|
|
* returns the \ref pw_properties. Otherwise, this function returns NULL.
|
|
*/
|
|
struct pw_properties *pwtest_get_props(struct pwtest_test *t);
|
|
|
|
struct pwtest_context *pwtest_get_context(struct pwtest_test *t);
|
|
|
|
/** Fail the current test */
|
|
#define pwtest_fail() \
|
|
_pwtest_fail_condition(PWTEST_FAIL, __FILE__, __LINE__, __func__, "aborting", "")
|
|
|
|
/** Same as above but more expressive in the code */
|
|
#define pwtest_fail_if_reached() \
|
|
_pwtest_fail_condition(PWTEST_FAIL, __FILE__, __LINE__, __func__, "This line is supposed to be unreachable", "")
|
|
|
|
/** Fail the current test with the given message */
|
|
#define pwtest_fail_with_msg(...) \
|
|
_pwtest_fail_condition(PWTEST_FAIL, __FILE__, __LINE__, __func__, \
|
|
"aborting", __VA_ARGS__)
|
|
|
|
/** Error out of the current test with the given message */
|
|
#define pwtest_error_with_msg(...) \
|
|
_pwtest_fail_condition(PWTEST_SYSTEM_ERROR, __FILE__, __LINE__, __func__, \
|
|
"error", __VA_ARGS__)
|
|
|
|
/** Assert r is not -1 and if it is, print the errno */
|
|
#define pwtest_errno_ok(r_) \
|
|
pwtest_errno_check(r_, 0);
|
|
|
|
/** Assert r is -1 and the errno is the given one */
|
|
#define pwtest_errno(r_, errno_) \
|
|
pwtest_errno_check(r_, errno_);
|
|
|
|
/** Assert r is not < 0 and if it is assume it's a negative errno */
|
|
#define pwtest_neg_errno_ok(r_) \
|
|
pwtest_neg_errno_check(r_, 0);
|
|
|
|
/** Assert r is < 0 and the given negative errno */
|
|
#define pwtest_neg_errno(r_, errno_) \
|
|
pwtest_neg_errno_check(r_, errno_);
|
|
|
|
/** Assert boolean (a == b) */
|
|
#define pwtest_bool_eq(a_, b_) \
|
|
pwtest_comparison_bool_(a_, ==, b_)
|
|
|
|
/** Assert boolean (a != b) */
|
|
#define pwtest_bool_ne(a_, b_) \
|
|
pwtest_comparison_bool_(a_, !=, b_)
|
|
|
|
/** Assert cond to be true. Convenience wrapper for readability */
|
|
#define pwtest_bool_true(cond_) \
|
|
pwtest_comparison_bool_(cond_, ==, true)
|
|
|
|
/** Assert cond to be false. Convenience wrapper for readability */
|
|
#define pwtest_bool_false(cond_) \
|
|
pwtest_comparison_bool_(cond_, ==, false)
|
|
|
|
/** Assert a == b */
|
|
#define pwtest_int_eq(a_, b_) \
|
|
pwtest_comparison_int_(a_, ==, b_)
|
|
|
|
/** Assert a != b */
|
|
#define pwtest_int_ne(a_, b_) \
|
|
pwtest_comparison_int_(a_, !=, b_)
|
|
|
|
/** Assert a < b */
|
|
#define pwtest_int_lt(a_, b_) \
|
|
pwtest_comparison_int_(a_, <, b_)
|
|
|
|
/** Assert a <= b */
|
|
#define pwtest_int_le(a_, b_) \
|
|
pwtest_comparison_int_(a_, <=, b_)
|
|
|
|
/** Assert a >= b */
|
|
#define pwtest_int_ge(a_, b_) \
|
|
pwtest_comparison_int_(a_, >=, b_)
|
|
|
|
/** Assert a > b */
|
|
#define pwtest_int_gt(a_, b_) \
|
|
pwtest_comparison_int_(a_, >, b_)
|
|
|
|
/** Assert ptr1 == ptr2 */
|
|
#define pwtest_ptr_eq(a_, b_) \
|
|
pwtest_comparison_ptr_(a_, ==, b_)
|
|
|
|
/** Assert ptr1 != ptr2 */
|
|
#define pwtest_ptr_ne(a_, b_) \
|
|
pwtest_comparison_ptr_(a_, !=, b_)
|
|
|
|
/** Assert ptr == NULL */
|
|
#define pwtest_ptr_null(a_) \
|
|
pwtest_comparison_ptr_(a_, ==, NULL)
|
|
|
|
/** Assert ptr != NULL */
|
|
#define pwtest_ptr_notnull(a_) \
|
|
pwtest_comparison_ptr_(a_, !=, NULL)
|
|
|
|
/** Assert a == b for a (hardcoded) epsilon */
|
|
#define pwtest_double_eq(a_, b_)\
|
|
pwtest_comparison_double_((a_), ==, (b_))
|
|
|
|
/** Assert a != b for a (hardcoded) epsilon */
|
|
#define pwtest_double_ne(a_, b_)\
|
|
pwtest_comparison_double_((a_), !=, (b_))
|
|
|
|
/** Assert a < b for a (hardcoded) epsilon */
|
|
#define pwtest_double_lt(a_, b_)\
|
|
pwtest_comparison_double_((a_), <, (b_))
|
|
|
|
/** Assert a <= b for a (hardcoded) epsilon */
|
|
#define pwtest_double_le(a_, b_)\
|
|
pwtest_comparison_double_((a_), <=, (b_))
|
|
|
|
/** Assert a >= b for a (hardcoded) epsilon */
|
|
#define pwtest_double_ge(a_, b_)\
|
|
pwtest_comparison_double_((a_), >=, (b_))
|
|
|
|
/** Assert a > b for a (hardcoded) epsilon */
|
|
#define pwtest_double_gt(a_, b_)\
|
|
pwtest_comparison_double_((a_), >, (b_))
|
|
|
|
#define pwtest_int(a_, op_, b_) \
|
|
pwtest_comparison_int_(a_, op_, b_)
|
|
|
|
|
|
/** Assert str1 is equal to str2 */
|
|
#define pwtest_str_eq(a_, b_) \
|
|
do { \
|
|
const char *_a = a_; \
|
|
const char *_b = b_; \
|
|
if (!spa_streq(_a, _b)) \
|
|
_pwtest_fail_comparison_str(__FILE__, __LINE__, __func__, \
|
|
#a_ " equals " #b_, _a, _b); \
|
|
} while(0)
|
|
|
|
/** Assert str1 is equal to str2 for l characters */
|
|
#define pwtest_str_eq_n(a_, b_, l_) \
|
|
do { \
|
|
const char *_a = a_; \
|
|
const char *_b = b_; \
|
|
if (!spa_strneq(_a, _b, l_)) \
|
|
_pwtest_fail_comparison_str(__FILE__, __LINE__, __func__, \
|
|
#a_ " equals " #b_ ", len: " #l_, _a, _b); \
|
|
} while(0)
|
|
|
|
/** Assert str1 is not equal to str2 */
|
|
#define pwtest_str_ne(a_, b_) \
|
|
do { \
|
|
const char *_a = a_; \
|
|
const char *_b = b_; \
|
|
if (spa_streq(_a, _b)) \
|
|
_pwtest_fail_comparison_str(__FILE__, __LINE__, __func__, \
|
|
#a_ " not equal to " #b_, _a, _b); \
|
|
} while(0)
|
|
|
|
/** Assert str1 is not equal to str2 for l characters */
|
|
#define pwtest_str_ne_n(a_, b_, l_) \
|
|
do { \
|
|
__typeof__(a_) _a = a_; \
|
|
__typeof__(b_) _b = b_; \
|
|
if (spa_strneq(_a, _b, l_)) \
|
|
_pwtest_fail_comparison_str(__FILE__, __LINE__, __func__, \
|
|
#a_ " not equal to " #b_ ", len: " #l_, _a, _b); \
|
|
} while(0)
|
|
|
|
|
|
/** Assert haystack contains needle */
|
|
#define pwtest_str_contains(haystack_, needle_) \
|
|
do { \
|
|
const char *_h = haystack_; \
|
|
const char *_n = needle_; \
|
|
if (!strstr(_h, _n)) \
|
|
_pwtest_fail_comparison_str(__FILE__, __LINE__, __func__, \
|
|
#haystack_ " contains " #needle_, _h, _n); \
|
|
} while(0)
|
|
|
|
|
|
/* Needs to be a #define NULL for SPA_SENTINEL */
|
|
enum pwtest_arg {
|
|
PWTEST_NOARG = 0,
|
|
/**
|
|
* The next argument is an int specifying the numerical signal number.
|
|
* The test is expected to raise that signal. The test fails if none
|
|
* or any other signal is raised.
|
|
*
|
|
* Example:
|
|
* ```c
|
|
* pwtest_add(mytest, PWTEST_ARG_SIGNAL, SIGABRT);
|
|
* ```
|
|
*/
|
|
PWTEST_ARG_SIGNAL,
|
|
/**
|
|
* The next two int arguments are the minimum (inclusive) and
|
|
* maximum (exclusive) range for this test.
|
|
*
|
|
* Example:
|
|
* ```c
|
|
* pwtest_add(mytest, PWTEST_ARG_RANGE, -50, 50);
|
|
* ```
|
|
* Use pwtest_get_iteration() in the test function to obtain the current iteration.
|
|
*/
|
|
PWTEST_ARG_RANGE,
|
|
/**
|
|
* The next two const char * arguments are the key and value
|
|
* for a property entry. This argument may be specified multiple times
|
|
* to add multiple properties.
|
|
*
|
|
* Use pwtest_get_props() to get the properties within the test function.
|
|
*
|
|
* Example:
|
|
* ```c
|
|
* pwtest_add(mytest,
|
|
* PWTEST_ARG_PROP, "key1", "value1",
|
|
* PWTEST_ARG_PROP, "key2", "value2");
|
|
* ```
|
|
*/
|
|
PWTEST_ARG_PROP,
|
|
/**
|
|
* The next two const char * arguments are the key and value
|
|
* for the environment variable to be set in the test. This argument
|
|
* may be specified multiple times to add multiple environment
|
|
* variables.
|
|
*
|
|
* Example:
|
|
* ```c
|
|
* pwtest_add(mytest,
|
|
* PWTEST_ARG_ENV, "env1", "value1",
|
|
* PWTEST_ARG_ENV, "env2", "value2");
|
|
* ```
|
|
*
|
|
* These environment variables are only set for the test itself, a
|
|
* a pipewire daemon started with \ref PWTEST_ARG_DAEMON does not share
|
|
* those variables.
|
|
*
|
|
*/
|
|
PWTEST_ARG_ENV,
|
|
/**
|
|
* Takes no extra arguments. If provided, the test case will start a
|
|
* pipewire daemon and stop the daemon when finished.
|
|
*
|
|
* The `PIPEWIRE_REMOTE` environment variable will be set in the
|
|
* test to point to this daemon.
|
|
*
|
|
* Example:
|
|
* ```c
|
|
* pwtest_add(mytest, PWTEST_ARG_DAEMON);
|
|
* ```
|
|
*
|
|
* Environment variables specified with \ref PWTEST_ARG_ENV are
|
|
* **not** available to the daemon, only to the test itself.
|
|
*/
|
|
PWTEST_ARG_DAEMON,
|
|
};
|
|
/**
|
|
* Add function \a func_ to the current test suite.
|
|
*
|
|
* This macro should be used within PWTEST_SUITE() to register the test in that suite, for example:
|
|
*
|
|
* ```c
|
|
* PWTEST_SUITE(mysuite)
|
|
* {
|
|
* pwtest_add(test1, PWTEST_NOARG);
|
|
* pwtest_add(test2, PWTEST_ARG_DAEMON);
|
|
* pwtest_add(test3, PWTEST_ARG_RANGE, 0, 100, PWTEST_ARG_DAEMON);
|
|
* }
|
|
*
|
|
* ```
|
|
*
|
|
* If the test matches the given filters and the suite is executed, the test
|
|
* will be executed with the parameters given to pwtest_add().
|
|
*
|
|
* Arguments take a argument-dependent number of extra parameters, see
|
|
* see the \ref pwtest_arg documentation for details.
|
|
*/
|
|
#define pwtest_add(func_, ...) \
|
|
_pwtest_add(ctx, suite, #func_, func_, __VA_ARGS__, NULL)
|
|
|
|
|
|
/**
|
|
* Declare a test case. To execute the test, add the test case name with pwtest_add().
|
|
*
|
|
* This macro expands so each test has a struct \ref pwtest_test variable
|
|
* named `current_test` available.
|
|
*
|
|
* ```c
|
|
* PWTEST(mytest)
|
|
* {
|
|
* struct pwtest_test *t = current_test;
|
|
*
|
|
* ... do stuff ...
|
|
*
|
|
* return PWTEST_PASS;
|
|
* }
|
|
*
|
|
* PWTEST_SUITE(mysuite)
|
|
* {
|
|
* pwtest_add(mytest);
|
|
*
|
|
* return PWTEST_PASS;
|
|
* }
|
|
* ```
|
|
*/
|
|
#define PWTEST(tname) \
|
|
static enum pwtest_result tname(struct pwtest_test *current_test)
|
|
|
|
/**
|
|
* Initialize a test suite. A test suite is a group of related
|
|
* tests that filters and other conditions may apply to.
|
|
*
|
|
* Test suites are automatically discovered at build-time.
|
|
*/
|
|
#define PWTEST_SUITE(cname) \
|
|
static enum pwtest_result (cname##__setup)(struct pwtest_context *ctx, struct pwtest_suite *suite); \
|
|
static const struct pwtest_suite_decl _test_suite \
|
|
__attribute__((used)) \
|
|
__attribute((section("pwtest_suite_section"))) = { \
|
|
.name = #cname, \
|
|
.setup = cname##__setup, \
|
|
}; \
|
|
static enum pwtest_result (cname##__setup)(struct pwtest_context *ctx, struct pwtest_suite *suite)
|
|
|
|
struct pwtest_spa_plugin {
|
|
#define PWTEST_PLUGIN_MAX 32
|
|
size_t nsupport;
|
|
struct spa_support support[PWTEST_PLUGIN_MAX];
|
|
|
|
size_t ndlls;
|
|
void *dlls[PWTEST_PLUGIN_MAX];
|
|
|
|
size_t nhandles;
|
|
struct spa_handle *handles[PWTEST_PLUGIN_MAX];
|
|
};
|
|
|
|
struct pwtest_spa_plugin* pwtest_spa_plugin_new(void);
|
|
void pwtest_spa_plugin_destroy(struct pwtest_spa_plugin *plugin);
|
|
|
|
/**
|
|
* Identical to pwtest_spa_plugin_try_load_interface() but returns the
|
|
* interface and fails if the interface is NULL.
|
|
*/
|
|
void*
|
|
pwtest_spa_plugin_load_interface(struct pwtest_spa_plugin *plugin,
|
|
const char *libname,
|
|
const char *factory_name,
|
|
const char *interface_name,
|
|
const struct spa_dict *info);
|
|
|
|
/**
|
|
* Load \a interface_name from the factory in \a libname.
|
|
* If successful, the interface is returned and added to \a plugin's
|
|
* support items, i.e. subsequent loads of an interface will be able to
|
|
* make use of previously loaded ones.
|
|
*
|
|
* \return 0 on success or a negative errno on error
|
|
* \retval -ENOENT \a libname does not exist
|
|
* \retval -EINVAL \a factory_name does not exist in \a libname
|
|
* \retval -ENOSYS \a interface_name does not exist in \a factory_name
|
|
*/
|
|
int
|
|
pwtest_spa_plugin_try_load_interface(struct pwtest_spa_plugin *plugin,
|
|
void **iface_return,
|
|
const char *libname,
|
|
const char *factory_name,
|
|
const char *interface_name,
|
|
const struct spa_dict *info);
|
|
|
|
|
|
|
|
/**
|
|
* Create a temporary file and copy its full path to \a path. Fails the test
|
|
* with \ref PWTEST_SYSTEM_ERROR on error.
|
|
*
|
|
* This file does not need to be removed by the test, the pwtest framework
|
|
* will take care of it on exit.
|
|
*/
|
|
void pwtest_mkstemp(char path[PATH_MAX]);
|
|
|
|
|
|
/**
|
|
* \}
|
|
*/
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* PWTEST_H */
|