test: cover org.finit.Device1 end to end

Introspection, queue-state properties, immediate and bus-first
settle, Info by devpath, Trigger(add, net) observed via the
DeviceProcessed signal, and RulesReload -- driven by dummy interface
hotplug like keventd.sh.  New call-ss and call-u client modes, and
getprop learns the b and t variants.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg committed 2026-08-16 22:03:44 +02:00
1 parent 292e87c0d1
commit 230c65a080
3 files changed
+174 -1

No files matched your search

+4
View File
@@ -83,6 +83,7 @@ EXTRA_DIST += dbus-service.sh
EXTRA_DIST += dbus-cond.sh
EXTRA_DIST += dbus-initctl.sh
EXTRA_DIST += dbus-introspect.sh
EXTRA_DIST += dbus-device.sh
AM_TESTS_ENVIRONMENT = SYSROOT='$(abs_builddir)/sysroot/';
AM_TESTS_ENVIRONMENT += export SYSROOT;
@@ -150,6 +151,9 @@ TESTS += dbus-service.sh
TESTS += dbus-cond.sh
TESTS += dbus-initctl.sh
TESTS += dbus-introspect.sh
if KEVENTD
TESTS += dbus-device.sh
endif
TESTS += fuzz-msg-parse.sh
# Needs the plugin to bring up the bus, not just the built-in one
if BUILD_DBUS_PLUGIN
+135
View File
@@ -0,0 +1,135 @@
#!/bin/sh
# keventd: org.finit.Device1 on /run/keventd/bus.
#
# Covers the device manager's own bus: introspection, queue-state
# properties, Settle, Info by devpath, RulesReload, Trigger, and the
# DeviceProcessed signal. Network interfaces are the one device class
# the unprivileged sandbox can hotplug, see keventd.sh.
set -eu
TEST_DIR=$(dirname "$0")
test_teardown()
{
say "Running test teardown."
run "ip link del dummy0 2>/dev/null || true"
run "rm -f $FINIT_CONF"
}
# shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh"
# shellcheck source=/dev/null
. "$TEST_DIR/lib/dbus-setup.sh"
DEVBUS=/run/keventd/bus
if ! texec initctl -p status keventd >/dev/null 2>&1; then
skip "keventd not enabled in this build"
fi
if ! run "ip link add probe0 type dummy 2>/dev/null"; then
skip "cannot create dummy interfaces in test namespace"
fi
run "ip link del probe0"
say "keventd readiness"
retry 'assert_ready "keventd"' 50 0.2
retry "texec test -S $DEVBUS"
say "Device1 introspects with methods, properties, and signal"
xml=$(texec "$CLIENT" introspect "$DEVBUS" /org/finit/device)
for m in Settle Trigger Info RulesReload; do
case "$xml" in
*"<method name=\"$m\">"*) assert "$m declared" 0 -eq 0 ;;
*) fail "$m missing from Device1 XML" ;;
esac
done
case "$xml" in
*'<property name="QueueEmpty" type="b"'*'<signal name="DeviceProcessed">'*)
assert "QueueEmpty property and DeviceProcessed signal declared" 0 -eq 0 ;;
*) fail "property/signal declarations missing: $xml" ;;
esac
say "Queue-state properties are readable"
seq1=$(texec "$CLIENT" getprop "$DEVBUS" /org/finit/device \
org.finit.Device1 SeqnumProcessed)
assert "SeqnumProcessed is numeric (got: $seq1)" "$seq1" -ge 0
qe=$(texec "$CLIENT" getprop "$DEVBUS" /org/finit/device \
org.finit.Device1 QueueEmpty)
case "$qe" in
true|false) assert "QueueEmpty reads $qe" 0 -eq 0 ;;
*) fail "Unexpected QueueEmpty value: $qe" ;;
esac
say "Device1.Settle answers within its timeout"
texec "$CLIENT" call-u "$DEVBUS" /org/finit/device \
org.finit.Device1 Settle 15 >/dev/null \
|| fail "Settle returned non-zero"
assert "Settle completed" 0 -eq 0
say "Interface add advances SeqnumProcessed and Info answers"
run "ip link add dummy0 type dummy"
retry 'assert_cond "class/net/dummy0"'
seq2=$(texec "$CLIENT" getprop "$DEVBUS" /org/finit/device \
org.finit.Device1 SeqnumProcessed)
assert "SeqnumProcessed advanced ($seq1 -> $seq2)" "$seq2" -gt "$seq1"
info=$(texec "$CLIENT" call-s "$DEVBUS" /org/finit/device \
org.finit.Device1 Info /devices/virtual/net/dummy0 2>&1) \
|| fail "Info failed: $info"
assert "Info answered for dummy0" 0 -eq 0
say "keventd -S settles via the bus"
run "/libexec/finit/keventd -S -t 15" || fail "keventd -S failed"
assert "bus-first settle ok" 0 -eq 0
say "Trigger(add, net) re-emits events, DeviceProcessed observed"
rm -f /tmp/dbus-dev-sig.out
( texec "$CLIENT" monitor-signal "$DEVBUS" \
"type='signal',interface='org.finit.Device1',member='DeviceProcessed'" \
5000 > /tmp/dbus-dev-sig.out 2>&1 ) &
mon_pid=$!
sleep 0.5
texec "$CLIENT" call-ss "$DEVBUS" /org/finit/device \
org.finit.Device1 Trigger add net >/dev/null 2>&1 \
|| fail "Trigger returned non-zero"
set +e
wait "$mon_pid"
mon_rc=$?
set -e
assert "monitor saw DeviceProcessed (rc=$mon_rc)" "$mon_rc" -eq 0
case "$(cat /tmp/dbus-dev-sig.out)" in
*"DeviceProcessed"*net*) assert "Signal payload names a net device" 0 -eq 0 ;;
*) fail "Unexpected signal output: $(cat /tmp/dbus-dev-sig.out)" ;;
esac
say "RulesReload succeeds and Trigger rejects a bogus action"
texec "$CLIENT" call-void "$DEVBUS" /org/finit/device \
org.finit.Device1 RulesReload >/dev/null \
|| fail "RulesReload failed"
assert "RulesReload ok" 0 -eq 0
set +e
texec "$CLIENT" call-ss "$DEVBUS" /org/finit/device \
org.finit.Device1 Trigger frobnicate "" >/tmp/dbus-trg.out 2>&1
trg_rc=$?
set -e
assert "Bogus action rejected (rc=$trg_rc)" "$trg_rc" -eq 1
case "$(cat /tmp/dbus-trg.out)" in
*InvalidArgs*) assert "Error is InvalidArgs" 0 -eq 0 ;;
*) fail "Unexpected reply: $(cat /tmp/dbus-trg.out)" ;;
esac
say "Device1.RulesReload from non-root is rejected with AccessDenied"
texec chmod 0666 "$DEVBUS"
set +e
texec "$CLIENT" call-void-as-uid 1 "$DEVBUS" /org/finit/device \
org.finit.Device1 RulesReload >/tmp/dbus-devauthz.out 2>&1
dauthz_rc=$?
set -e
assert "Non-root RulesReload rejected (rc=$dauthz_rc)" "$dauthz_rc" -eq 1
case "$(cat /tmp/dbus-devauthz.out)" in
*AccessDenied*) assert "Device1 authz fires" 0 -eq 0 ;;
*) fail "Unexpected reply: $(cat /tmp/dbus-devauthz.out)" ;;
esac
+35 -1
View File
@@ -342,6 +342,24 @@ static int do_call_u(const char *sock, const char *obj, const char *iface,
return rc;
}
static int mode_call_ss(int argc, char *argv[])
{
link_client_t *c;
int rc;
if (argc != 8) return 2;
c = link_client_open(argv[2]);
if (!c) return 2;
rc = link_client_call_v(c, argv[3], argv[4], argv[5],
"ss", argv[6], argv[7]);
rc = report_rc(c, rc);
if (rc == 0)
printf("OK\n");
link_client_close(c);
return rc;
}
static int mode_call_u(int argc, char *argv[])
{
if (argc != 7) return 2;
@@ -409,7 +427,7 @@ static int mode_get_service(int argc, char *argv[])
/*
* getprop BUS PATH IFACE PROP -- Properties.Get, prints the variant
* value; dispatches on the wire signature, "s" and "u" supported.
* value; dispatches on the wire signature: s, u, b, and t.
*/
static int mode_getprop(int argc, char *argv[])
{
@@ -439,6 +457,21 @@ static int mode_getprop(int argc, char *argv[])
printf("%s\n", s);
else if (type == 'u' && link_r_u32(&reader, &u) == 0)
printf("%u\n", u);
else if (type == 'b') {
int b;
if (link_r_bool(&reader, &b) == 0)
printf("%s\n", b ? "true" : "false");
else
rc = 2;
} else if (type == 't') {
uint64_t t;
if (link_r_u64(&reader, &t) == 0)
printf("%llu\n", (unsigned long long)t);
else
rc = 2;
}
else {
fprintf(stderr, "unsupported variant type '%c'\n", type);
rc = 2;
@@ -547,6 +580,7 @@ int main(int argc, char *argv[])
if (!strcmp(argv[1], "introspect")) return mode_introspect (argc, argv);
if (!strcmp(argv[1], "liststrings")) return mode_liststrings (argc, argv);
if (!strcmp(argv[1], "call-s")) return mode_call_s (argc, argv);
if (!strcmp(argv[1], "call-ss")) return mode_call_ss (argc, argv);
if (!strcmp(argv[1], "call-u")) return mode_call_u (argc, argv);
if (!strcmp(argv[1], "call-su")) return mode_call_su (argc, argv);
if (!strcmp(argv[1], "call-void")) return mode_call_void (argc, argv);