test-driver: support testing user units
It is quite complicated to test services using the test-driver when declaring user services with `systemd.user.services` such as many X11-based services like `xautolock.service`. This change adds an optional `$user` parameter to each systemd-related function in the test-driver and runs `systemctl --user` commands using `su -l $user -c ...` and sets the `XDG_RUNTIME_DIR` variable accordingly and a new function named `systemctl` which is able to run a systemd command with or without a specified user. The change can be confirmed with a simple VM declaration like this: ``` import ./nixos/tests/make-test.nix ({ pkgs, lib }: with lib; { name = "systemd-user-test"; nodes.machine = { imports = [ ./nixos/tests/common/user-account.nix ]; services.xserver.enable = true; services.xserver.displayManager.auto.enable = true; services.xserver.displayManager.auto.user = "bob"; services.xserver.xautolock.enable = true; }; testScript = '' $machine->start; $machine->waitForX; $machine->waitForUnit("xautolock.service", "bob"); $machine->stopJob("xautolock.service", "bob"); $machine->startJob("xautolock.service", "bob"); $machine->systemctl("list-jobs --no-pager", "bob"); $machine->systemctl("show 'xautolock.service' --no-pager", "bob"); ''; }) ```
This commit is contained in:
parent
b256afac58
commit
e538e00404
|
@ -362,8 +362,8 @@ sub mustFail {
|
||||||
|
|
||||||
|
|
||||||
sub getUnitInfo {
|
sub getUnitInfo {
|
||||||
my ($self, $unit) = @_;
|
my ($self, $unit, $user) = @_;
|
||||||
my ($status, $lines) = $self->execute("systemctl --no-pager show '$unit'");
|
my ($status, $lines) = $self->systemctl("--no-pager show \"$unit\"", $user);
|
||||||
return undef if $status != 0;
|
return undef if $status != 0;
|
||||||
my $info = {};
|
my $info = {};
|
||||||
foreach my $line (split '\n', $lines) {
|
foreach my $line (split '\n', $lines) {
|
||||||
|
@ -373,6 +373,16 @@ sub getUnitInfo {
|
||||||
return $info;
|
return $info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub systemctl {
|
||||||
|
my ($self, $q, $user) = @_;
|
||||||
|
if ($user) {
|
||||||
|
$q =~ s/'/\\'/g;
|
||||||
|
return $self->execute("su -l $user -c \$'XDG_RUNTIME_DIR=/run/user/`id -u` systemctl --user $q'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $self->execute("systemctl $q");
|
||||||
|
}
|
||||||
|
|
||||||
# Fail if the given systemd unit is not in the "active" state.
|
# Fail if the given systemd unit is not in the "active" state.
|
||||||
sub requireActiveUnit {
|
sub requireActiveUnit {
|
||||||
my ($self, $unit) = @_;
|
my ($self, $unit) = @_;
|
||||||
|
@ -387,16 +397,16 @@ sub requireActiveUnit {
|
||||||
|
|
||||||
# Wait for a systemd unit to reach the "active" state.
|
# Wait for a systemd unit to reach the "active" state.
|
||||||
sub waitForUnit {
|
sub waitForUnit {
|
||||||
my ($self, $unit) = @_;
|
my ($self, $unit, $user) = @_;
|
||||||
$self->nest("waiting for unit ‘$unit’", sub {
|
$self->nest("waiting for unit ‘$unit’", sub {
|
||||||
retry sub {
|
retry sub {
|
||||||
my $info = $self->getUnitInfo($unit);
|
my $info = $self->getUnitInfo($unit, $user);
|
||||||
my $state = $info->{ActiveState};
|
my $state = $info->{ActiveState};
|
||||||
die "unit ‘$unit’ reached state ‘$state’\n" if $state eq "failed";
|
die "unit ‘$unit’ reached state ‘$state’\n" if $state eq "failed";
|
||||||
if ($state eq "inactive") {
|
if ($state eq "inactive") {
|
||||||
# If there are no pending jobs, then assume this unit
|
# If there are no pending jobs, then assume this unit
|
||||||
# will never reach active state.
|
# will never reach active state.
|
||||||
my ($status, $jobs) = $self->execute("systemctl list-jobs --full 2>&1");
|
my ($status, $jobs) = $self->systemctl("list-jobs --full 2>&1", $user);
|
||||||
if ($jobs =~ /No jobs/) { # FIXME: fragile
|
if ($jobs =~ /No jobs/) { # FIXME: fragile
|
||||||
# Handle the case where the unit may have started
|
# Handle the case where the unit may have started
|
||||||
# between the previous getUnitInfo() and
|
# between the previous getUnitInfo() and
|
||||||
|
@ -430,14 +440,14 @@ sub waitForFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub startJob {
|
sub startJob {
|
||||||
my ($self, $jobName) = @_;
|
my ($self, $jobName, $user) = @_;
|
||||||
$self->execute("systemctl start $jobName");
|
$self->systemctl("start $jobName", $user);
|
||||||
# FIXME: check result
|
# FIXME: check result
|
||||||
}
|
}
|
||||||
|
|
||||||
sub stopJob {
|
sub stopJob {
|
||||||
my ($self, $jobName) = @_;
|
my ($self, $jobName, $user) = @_;
|
||||||
$self->execute("systemctl stop $jobName");
|
$self->systemctl("stop $jobName", $user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue