From dd3f14d9d6e8188541eb5ba8eea1797b910ddf48 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 7 Jun 2021 13:13:52 +1000 Subject: [PATCH] test: add a function to load a SPA interface Helper function to load a SPA interface. This enables a test to easily load a specific interface and run tests against that interface without having to instantiate a whole pipewire daemon. --- test/meson.build | 1 + test/pwtest.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++ test/pwtest.h | 48 +++++++++++++++++++++++ 3 files changed, 148 insertions(+) diff --git a/test/meson.build b/test/meson.build index e05a539f2..cf01b74ed 100644 --- a/test/meson.build +++ b/test/meson.build @@ -7,6 +7,7 @@ pwtest_sources = [ pwtest_deps = [ pipewire_dep, mathlib, + dl_lib, ] pwtest_c_args = [ diff --git a/test/pwtest.c b/test/pwtest.c index e6754ede7..066774560 100644 --- a/test/pwtest.c +++ b/test/pwtest.c @@ -27,6 +27,7 @@ #endif #include +#include #include #include #include @@ -51,6 +52,7 @@ #include "spa/utils/string.h" #include "spa/utils/defs.h" #include "spa/utils/list.h" +#include "spa/support/plugin.h" #include "pipewire/array.h" #include "pipewire/utils.h" @@ -282,6 +284,103 @@ void _pwtest_fail_comparison_str(const char *file, int line, const char *func, exit(PWTEST_FAIL); } +struct pwtest_spa_plugin * +pwtest_spa_plugin_new(void) +{ + return calloc(1, sizeof(struct pwtest_spa_plugin)); +} + +void +pwtest_spa_plugin_destroy(struct pwtest_spa_plugin *plugin) +{ + void **dll; + struct spa_handle **hnd; + + SPA_FOR_EACH_ELEMENT(plugin->handles, hnd) { + if (*hnd) + free(*hnd); + } + SPA_FOR_EACH_ELEMENT(plugin->dlls, dll) { + if (*dll) + dlclose(dll); + } + free(plugin); +} + +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) +{ + char *libdir = getenv("SPA_PLUGIN_DIR"); + char path[PATH_MAX]; + void *hnd, *iface; + spa_handle_factory_enum_func_t enum_func; + const struct spa_handle_factory *factory; + uint32_t index = 0; + int r; + bool found = false; + struct spa_handle *handle; + + spa_assert(libdir != NULL); + spa_scnprintf(path, sizeof(path), "%s/%s.so", libdir, libname); + + hnd = dlopen(path, RTLD_NOW); + if (hnd == NULL) + return -ENOENT; + + enum_func = dlsym(hnd, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME); + pwtest_ptr_notnull(enum_func); + + while ((r = enum_func(&factory, &index)) > 0) { + pwtest_int_ge(factory->version, 1U); + if (spa_streq(factory->name, factory_name)) { + found = true; + break; + } + } + pwtest_neg_errno_ok(r); + if (!found) { + dlclose(hnd); + return -EINVAL; + } + + handle = calloc(1, spa_handle_factory_get_size(factory, info)); + pwtest_ptr_notnull(handle); + + r = spa_handle_factory_init(factory, handle, info, plugin->support, plugin->nsupport); + pwtest_neg_errno_ok(r); + if ((r = spa_handle_get_interface(handle, interface_name, &iface)) != 0) { + dlclose(hnd); + free(handle); + return -ENOSYS; + } + + plugin->handles[plugin->ndlls++] = hnd; + plugin->handles[plugin->nhandles++] = handle; + plugin->support[plugin->nsupport++] = SPA_SUPPORT_INIT(interface_name, iface); + + *iface_return = iface; + return 0; +} + +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) +{ + void *iface; + int r = pwtest_spa_plugin_try_load_interface(plugin, &iface, libname, + factory_name, interface_name, info); + pwtest_neg_errno_ok(r); + return iface; +} + void _pwtest_add(struct pwtest_context *ctx, struct pwtest_suite *suite, const char *funcname, const void *func, ...) { diff --git a/test/pwtest.h b/test/pwtest.h index fa1146fcf..768482df8 100644 --- a/test/pwtest.h +++ b/test/pwtest.h @@ -38,6 +38,8 @@ extern "C" { #include #include +#include +#include "spa/support/plugin.h" /** * \defgroup pwtest The pwtest PipeWire Test Suite @@ -487,6 +489,52 @@ enum pwtest_arg { }; \ 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); + + /** * \} */