Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 30 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,26 +65,46 @@ jobs:
- name: Test the daemon against workers that die on cue, and the declared protocol
run: make test

# The daemon with the installed worker over jack, in a PipeWire of its own. It needs a private user
# namespace with its own /proc; a runner that refuses one cannot run it, and the step says so.
- name: Test the daemon with a real worker over jack, then break every guard on purpose
# mod-host itself, the LV2 worker, from the FreeMixer channel: the build the RPM job packages, with the patches
# FreeMixer/.github carries. The floor is the one the spec requires of the protocol library.
- name: Install mod-host from the channel
run: |
set -euo pipefail
dnf install -y --setopt=retries=10 mod-host
rpm -q mod-host mod-host-protocol
floor=$(sed -n 's/^Requires:.*mod-host-protocol.*>=[[:space:]]*\([^[:space:]]*\).*/\1/p' packaging/plugin-hostd.spec | head -n 1)
test -n "$floor"
have=$(rpm -q --qf '%{VERSION}-%{RELEASE}' mod-host)
test "$(printf '%s\n%s\n' "$floor" "$have" | sort -V | head -n 1)" = "$floor" || { echo "mod-host $have is below the floor $floor"; exit 1; }

# The daemon with the installed workers over jack, in a PipeWire of its own: omx-clap-host for CLAP and mod-host
# for LV2, driving the fixture bundle of this repository (a stereo gain, tests/lv2_fixture.c) from a world of its
# own, so that the worker's bundle set is one directory. It needs a private user namespace with its own /proc; a
# runner that refuses one cannot run it, and the step says so. The LV2 steps are required to have run: a run that
# skipped them fails here.
- name: Test the daemon with real workers over jack, CLAP and LV2, then break every guard on purpose
run: |
set -euo pipefail
export FAKE_COMPRESSOR_CLAP=/usr/libexec/omx-clap-host/tests/fake_compressor.clap
export JACK_METER_SOURCE=/usr/libexec/omx-clap-host/tests/jack_meter_source
export FAKE_CLAP=/usr/libexec/omx-clap-host/tests/fake.clap
make tests/plugin-hostd-fixture.lv2/plugin-hostd-fixture.so
mkdir -p "$PWD/lv2-world"
cp -r tests/plugin-hostd-fixture.lv2 "$PWD/lv2-world/"
export MOD_HOST=/usr/bin/mod-host LV2_DIR=$PWD/lv2-world LV2_URI=urn:plugin-hostd:test:stereo
export LV2_BUNDLE=plugin-hostd-fixture.lv2 LV2_PARAM=gain
if unshare --user --map-root-user --net --pid --fork --mount-proc true; then
make test-jack OMX_CLAP_HOST=/usr/bin/omx-clap-host
OMX_CLAP_HOST=/usr/bin/omx-clap-host make sabotage
make test-jack OMX_CLAP_HOST=/usr/bin/omx-clap-host 2>&1 | tee test-jack.log
for t in mod_host_scenarios_pass_unchanged_with_an_lv2_worker lv2_own_worker_sees_one_bundle_and_replays_after_a_kill; do
grep -qx "ok $t" test-jack.log || { echo "the LV2 step $t did not run"; exit 1; }
done
OMX_CLAP_HOST=/usr/bin/omx-clap-host make sabotage 2>&1 | tee sabotage.log
if grep -q 'skip sabotage (no LV2 bundle' sabotage.log; then echo 'an LV2 sabotage was skipped'; exit 1; fi
else
echo "::warning::test-jack SKIPPED: this runner refuses a private user and pid namespace with its own /proc"
make sabotage
fi

- name: Name what this job does not run
run: |
echo "The LV2 steps of test-jack are NOT run here: they need mod-host and an LV2 bundle (MOD_HOST, LV2_DIR, LV2_URI, LV2_BUNDLE, LV2_PARAM)."
echo "They stay in the Makefile for the desk."

jack2:
name: build and test over JACK2's jackd (x86_64)
runs-on: ubuntu-latest
Expand Down
6 changes: 6 additions & 0 deletions BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ installed by the omx-clap-host-tests package, which the spec and `debian/control
also reads the meters of a CLAP compressor as `output_set` on the daemon's feedback port, before and after its
worker is killed.

Given `MOD_HOST` and a bundle, it also runs mod-host's scenario corpus against the daemon with an LV2 worker, and kills
an LV2 worker to read the graph and a parameter back. The CI job does this with the mod-host package of the FreeMixer
channel and this repository's fixture bundle: build `tests/plugin-hostd-fixture.lv2/plugin-hostd-fixture.so`, copy the
bundle into a directory of its own, and give `MOD_HOST=/usr/bin/mod-host LV2_DIR=<that directory>
LV2_URI=urn:plugin-hostd:test:stereo LV2_BUNDLE=plugin-hostd-fixture.lv2 LV2_PARAM=gain`.

make test-jack2 OMX_CLAP_HOST=<omx-clap-host> [...]

is `test-jack` against JACK2's `jackd` (`-d dummy`) instead of pipewire's own jack implementation: the server
Expand Down
46 changes: 45 additions & 1 deletion tests/lv2_fixture.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
* under http://plugin-hostd.invalid/fixture/based, named in a manifest the test writes with an @base, so lilv finds it
* and the adapter's search of the manifest text does not (resp -101). "stress" is "gain" that breaks on cue, for the
* probe runner's LV2 tests (tests/probe_test.py). The ports, by index: 0 in, 1 out, 2 gain (a factor), 3 events
* (atom-in only). */
* (atom-in only). "stereo" is the stereo effect of the LV2 steps of tests/jack_e2e.py, which run it in mod-host: the
* gain on two channels, ports 0 and 1 in, 2 and 3 out, 4 gain. */

#include <signal.h>
#include <stdint.h>
Expand Down Expand Up @@ -50,6 +51,48 @@ static void connect_port(LV2_Handle instance, uint32_t port, void *data)
f->gain = data;
}

typedef struct {
const float *in[2];
float *out[2];
const float *gain;
} stereo_t;

static LV2_Handle instantiate_stereo(const LV2_Descriptor *descriptor, double rate, const char *bundle, const LV2_Feature *const *features)
{
(void)descriptor;
(void)rate;
(void)bundle;
(void)features;
return calloc(1, sizeof(stereo_t));
}

static void connect_port_stereo(LV2_Handle instance, uint32_t port, void *data)
{
stereo_t *f = instance;

if (port < 2)
f->in[port] = data;
else if (port < 4)
f->out[port - 2] = data;
else if (port == 4)
f->gain = data;
}

static void run_stereo(LV2_Handle instance, uint32_t frames)
{
stereo_t *f = instance;
float gain = f->gain ? *f->gain : 1.0f;
uint32_t c, n;

for (c = 0; c < 2; c++)
{
if (!f->in[c] || !f->out[c])
continue;
for (n = 0; n < frames; n++)
f->out[c][n] = f->in[c][n] * gain;
}
}

static void run(LV2_Handle instance, uint32_t frames)
{
fixture_t *f = instance;
Expand Down Expand Up @@ -114,6 +157,7 @@ static const LV2_Descriptor DESCRIPTORS[] = {
{ FIXTURE_URI "atom-in", instantiate, connect_port, NULL, run, NULL, cleanup, NULL },
{ FIXTURE_URI "fixed-block", instantiate, connect_port, NULL, run, NULL, cleanup, NULL },
{ FIXTURE_URI "stress", instantiate, connect_port, NULL, run_stress, NULL, cleanup, NULL },
{ FIXTURE_URI "stereo", instantiate_stereo, connect_port_stereo, NULL, run_stereo, NULL, cleanup, NULL },
{ FIXTURE_BASE "based", instantiate, connect_port, NULL, run, NULL, cleanup, NULL },
};

Expand Down
5 changes: 5 additions & 0 deletions tests/plugin-hostd-fixture.lv2/manifest.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@
a lv2:Plugin ;
lv2:binary <plugin-hostd-fixture.so> ;
rdfs:seeAlso <plugin-hostd-fixture.ttl> .

<urn:plugin-hostd:test:stereo>
a lv2:Plugin ;
lv2:binary <plugin-hostd-fixture.so> ;
rdfs:seeAlso <plugin-hostd-fixture.ttl> .
16 changes: 16 additions & 0 deletions tests/plugin-hostd-fixture.lv2/plugin-hostd-fixture.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,19 @@
a lv2:ControlPort , lv2:InputPort ; lv2:index 2 ; lv2:symbol "gain" ; lv2:name "Gain" ;
lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 2.0
] .

<urn:plugin-hostd:test:stereo>
a lv2:Plugin , lv2:AmplifierPlugin ;
doap:name "plugin-hostd fixture gain on two channels" ;
lv2:port [
a lv2:AudioPort , lv2:InputPort ; lv2:index 0 ; lv2:symbol "lv2_audio_in_1" ; lv2:name "In 1"
] , [
a lv2:AudioPort , lv2:InputPort ; lv2:index 1 ; lv2:symbol "lv2_audio_in_2" ; lv2:name "In 2"
] , [
a lv2:AudioPort , lv2:OutputPort ; lv2:index 2 ; lv2:symbol "lv2_audio_out_1" ; lv2:name "Out 1"
] , [
a lv2:AudioPort , lv2:OutputPort ; lv2:index 3 ; lv2:symbol "lv2_audio_out_2" ; lv2:name "Out 2"
] , [
a lv2:ControlPort , lv2:InputPort ; lv2:index 4 ; lv2:symbol "gain" ; lv2:name "Gain" ;
lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 2.0
] .
4 changes: 4 additions & 0 deletions tests/sabotage.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@
"audio_of_the_other_strip_never_changes_when_one_worker_is_killed", "jack"),
("an own lv2 worker keeps the whole bundle set", "src/supervisor.c", " if (!f->narrow_world || w->pool || !uri)\n return NULL;",
" return NULL;", "lv2_own_worker_sees_one_bundle_and_replays_after_a_kill", "lv2"),
("an LV2 strip's parameter writes are not replayed to the respawned mod-host", "src/supervisor.c",
" for (m = 0; m < i->ntail; m++)\n if (replay_line(w, i->tail[m].line) != RPC_OK)",
" for (m = 0; m < 0; m++)\n if (replay_line(w, i->tail[m].line) != RPC_OK)",
"lv2_own_worker_sees_one_bundle_and_replays_after_a_kill", "lv2"),
("the checkpoint is not loaded after a real respawn", "src/supervisor.c", " if (any_ckpt)\n", " if (0 && any_ckpt)\n",
"clap_own_crash_replays_bit_identically_and_the_neighbour_never_notices", "jack"),
("add checks no pin", "src/supervisor.c", "rc = pins_check(format, fwd, search_of(fmt), &layout);\n /* a plugin", "rc = SUCCESS;\n /* a plugin",
Expand Down
Loading