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.
This commit is contained in:
Peter Hutterer 2021-06-07 13:13:52 +10:00
parent 50180532a4
commit dd3f14d9d6
3 changed files with 148 additions and 0 deletions

View file

@ -7,6 +7,7 @@ pwtest_sources = [
pwtest_deps = [
pipewire_dep,
mathlib,
dl_lib,
]
pwtest_c_args = [

View file

@ -27,6 +27,7 @@
#endif
#include <assert.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
@ -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, ...)
{

View file

@ -38,6 +38,8 @@ extern "C" {
#include <math.h>
#include <spa/utils/string.h>
#include <spa/utils/dict.h>
#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);
/**
* \}
*/