diff options
Diffstat (limited to 't')
-rw-r--r-- | t/001-deb-systemd-helper.t | 473 | ||||
-rw-r--r-- | t/002-deb-systemd-helper-update.t | 196 | ||||
-rw-r--r-- | t/003-deb-systemd-helper-complex.t | 123 | ||||
-rw-r--r-- | t/004-deb-systemd-helper-user.t | 413 | ||||
-rw-r--r-- | t/README | 15 | ||||
-rw-r--r-- | t/helpers.pm | 160 |
6 files changed, 1380 insertions, 0 deletions
diff --git a/t/001-deb-systemd-helper.t b/t/001-deb-systemd-helper.t new file mode 100644 index 0000000..c78ad53 --- /dev/null +++ b/t/001-deb-systemd-helper.t @@ -0,0 +1,473 @@ +#!perl +# vim:ts=4:sw=4:et + +use strict; +use warnings; +use Test::More; +use File::Temp qw(tempfile tempdir); # in core since perl 5.6.1 +use File::Path qw(make_path); # in core since Perl 5.001 +use File::Basename; # in core since Perl 5 +use FindBin; # in core since Perl 5.00307 + +use lib "$FindBin::Bin/."; +use helpers; + +test_setup(); + +my $dpkg_root = $ENV{DPKG_ROOT} // ''; + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “is-enabled” is not true for a random, non-existing unit file. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +my ($fh, $random_unit) = tempfile('unit\x2dXXXXX', + SUFFIX => '.service', + TMPDIR => 1, + UNLINK => 1); +close($fh); +$random_unit = basename($random_unit); + +isnt_enabled($random_unit); +isnt_debian_installed($random_unit); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “is-enabled” is not true for a random, existing unit file. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +my $servicefile_path = "$dpkg_root/lib/systemd/system/$random_unit"; +make_path("$dpkg_root/lib/systemd/system"); +open($fh, '>', $servicefile_path); +print $fh <<'EOT'; +[Unit] +Description=test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +WantedBy=multi-user.target +EOT +close($fh); + +isnt_enabled($random_unit); +isnt_debian_installed($random_unit); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “enable” creates the requested symlinks. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +unless ($ENV{'TEST_ON_REAL_SYSTEM'}) { + # This might exist if we don't start from a fresh directory + ok(! -d "$dpkg_root/etc/systemd/system/multi-user.target.wants", + 'multi-user.target.wants does not exist yet'); +} + +my $retval = dsh('enable', $random_unit); +is($retval, 0, "enable command succeeded"); +my $symlink_path = "$dpkg_root/etc/systemd/system/multi-user.target.wants/$random_unit"; +ok(-l $symlink_path, "$random_unit was enabled"); +is($dpkg_root . readlink($symlink_path), $servicefile_path, + "symlink points to $servicefile_path"); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “is-enabled” now returns true. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +is_enabled($random_unit); +is_debian_installed($random_unit); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify deleting the symlinks and running “enable” again does not ┃ +# ┃ re-create the symlinks. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +unlink($symlink_path); +ok(! -l $symlink_path, 'symlink deleted'); +isnt_enabled($random_unit); +is_debian_installed($random_unit); + +$retval = dsh('enable', $random_unit); +is($retval, 0, "enable command succeeded"); + +isnt_enabled($random_unit); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “disable” when purging deletes the statefile. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +my $statefile = "$dpkg_root/var/lib/systemd/deb-systemd-helper-enabled/$random_unit.dsh-also"; + +ok(-f $statefile, 'state file exists'); + +$ENV{'_DEB_SYSTEMD_HELPER_PURGE'} = '1'; +$retval = dsh('disable', $random_unit); +delete $ENV{'_DEB_SYSTEMD_HELPER_PURGE'}; +is($retval, 0, "disable command succeeded"); +ok(! -f $statefile, 'state file does not exist anymore after purging'); +isnt_debian_installed($random_unit); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “enable” after purging does re-create the symlinks. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +ok(! -l $symlink_path, 'symlink does not exist yet'); +isnt_enabled($random_unit); + +$retval = dsh('enable', $random_unit); +is($retval, 0, "enable command succeeded"); + +is_enabled($random_unit); +is_debian_installed($random_unit); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “disable” removes the symlinks. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +$ENV{'_DEB_SYSTEMD_HELPER_PURGE'} = '1'; +$retval = dsh('disable', $random_unit); +delete $ENV{'_DEB_SYSTEMD_HELPER_PURGE'}; +is($retval, 0, "disable command succeeded"); + +isnt_enabled($random_unit); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “enable” after purging does re-create the symlinks. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +ok(! -l $symlink_path, 'symlink does not exist yet'); +isnt_enabled($random_unit); + +$retval = dsh('enable', $random_unit); +is($retval, 0, "enable command succeeded"); + +is_enabled($random_unit); +is_debian_installed($random_unit); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify the “purge” verb works. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +$retval = dsh('purge', $random_unit); +is($retval, 0, "purge command succeeded"); + +isnt_enabled($random_unit); +isnt_debian_installed($random_unit); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “enable” after purging does re-create the symlinks. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +ok(! -l $symlink_path, 'symlink does not exist yet'); +isnt_enabled($random_unit); + +$retval = dsh('enable', $random_unit); +is($retval, 0, "enable command succeeded"); + +is_enabled($random_unit); +is_debian_installed($random_unit); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “mask” (when enabled) results in the symlink pointing to /dev/null ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +my $mask_path = "$dpkg_root/etc/systemd/system/$random_unit"; +ok(! -l $mask_path, 'mask link does not exist yet'); + +$retval = dsh('mask', $random_unit); +is($retval, 0, "mask command succeeded"); +ok(-l $mask_path, 'mask link exists'); +is(readlink($mask_path), '/dev/null', 'service masked'); + +$retval = dsh('unmask', $random_unit); +is($retval, 0, "unmask command succeeded"); +ok(! -e $mask_path, 'mask link does not exist anymore'); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “mask” (when disabled) works the same way ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +$retval = dsh('disable', $random_unit); +is($retval, 0, "disable command succeeded"); +ok(! -e $symlink_path, 'symlink no longer exists'); + +$retval = dsh('mask', $random_unit); +is($retval, 0, "mask command succeeded"); +ok(-l $mask_path, 'mask link exists'); +is(readlink($mask_path), '/dev/null', 'service masked'); + +$retval = dsh('unmask', $random_unit); +is($retval, 0, "unmask command succeeded"); +ok(! -e $mask_path, 'symlink no longer exists'); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “mask”/unmask don’t do anything when the user already masked. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +ok(! -l $mask_path, 'mask link does not exist yet'); +symlink('/dev/null', $mask_path); +ok(-l $mask_path, 'mask link exists'); +is(readlink($mask_path), '/dev/null', 'service masked'); + +$retval = dsh('mask', $random_unit); +is($retval, 0, "mask command succeeded"); +ok(-l $mask_path, 'mask link exists'); +is(readlink($mask_path), '/dev/null', 'service still masked'); + +$retval = dsh('unmask', $random_unit); +is($retval, 0, "unmask command succeeded"); +ok(-l $mask_path, 'mask link exists'); +is(readlink($mask_path), '/dev/null', 'service still masked'); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “mask”/unmask don’t do anything when the user copied the .service. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +unlink($mask_path); + +open($fh, '>', $mask_path); +print $fh <<'EOT'; +[Unit] +Description=test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +WantedBy=multi-user.target +EOT +close($fh); + +ok(-e $mask_path, 'local service file exists'); +ok(! -l $mask_path, 'local service file is not a symlink'); + +$retval = dsh('mask', $random_unit); +isnt($retval, -1, 'deb-systemd-helper could be executed'); +ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal'); +is($retval >> 8, 0, 'deb-systemd-helper exited with exit code 0'); +ok(-e $mask_path, 'local service file still exists'); +ok(! -l $mask_path, 'local service file is still not a symlink'); + +$retval = dsh('unmask', $random_unit); +isnt($retval, -1, 'deb-systemd-helper could be executed'); +ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal'); +is($retval >> 8, 0, 'deb-systemd-helper exited with exit code 0'); +ok(-e $mask_path, 'local service file still exists'); +ok(! -l $mask_path, 'local service file is still not a symlink'); + +unlink($mask_path); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify Alias= handling. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +$retval = dsh('purge', $random_unit); +is($retval, 0, "purge command succeeded"); + +open($fh, '>', $servicefile_path); +print $fh <<'EOT'; +[Unit] +Description=test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +WantedBy=multi-user.target +Alias=foo\x2dtest.service +EOT +close($fh); + +isnt_enabled($random_unit); +isnt_enabled('foo\x2dtest.service'); +my $alias_path = $dpkg_root . '/etc/systemd/system/foo\x2dtest.service'; +ok(! -l $alias_path, 'alias link does not exist yet'); +$retval = dsh('enable', $random_unit); +is($retval, 0, "enable command succeeded"); +is($dpkg_root . readlink($alias_path), $servicefile_path, 'correct alias link'); +is_enabled($random_unit); +ok(! -l $mask_path, 'mask link does not exist yet'); + +$retval = dsh('mask', $random_unit); +is($retval, 0, "mask command succeeded"); +is($dpkg_root . readlink($alias_path), $servicefile_path, 'correct alias link'); +is(readlink($mask_path), '/dev/null', 'service masked'); + +$retval = dsh('unmask', $random_unit); +is($retval, 0, "unmask command succeeded"); +is($dpkg_root . readlink($alias_path), $servicefile_path, 'correct alias link'); +ok(! -l $mask_path, 'mask link does not exist any more'); + +$retval = dsh('disable', $random_unit); +isnt_enabled($random_unit); +ok(! -l $alias_path, 'alias link does not exist any more'); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify Alias/mask with removed package (as in postrm) ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +$retval = dsh('purge', $random_unit); +is($retval, 0, "purge command succeeded"); +$retval = dsh('enable', $random_unit); +is($retval, 0, "enable command succeeded"); +is($dpkg_root . readlink($alias_path), $servicefile_path, 'correct alias link'); + +unlink($servicefile_path); + +$retval = dsh('mask', $random_unit); +is($retval, 0, "mask command succeeded with uninstalled unit"); +is($dpkg_root . readlink($alias_path), $servicefile_path, 'correct alias link'); +is(readlink($mask_path), '/dev/null', 'service masked'); + +$retval = dsh('purge', $random_unit); +is($retval, 0, "purge command succeeded with uninstalled unit"); +ok(! -l $alias_path, 'alias link does not exist any more'); +is(readlink($mask_path), '/dev/null', 'service masked'); + +$retval = dsh('unmask', $random_unit); +is($retval, 0, "unmask command succeeded with uninstalled unit"); +ok(! -l $mask_path, 'mask link does not exist any more'); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify Alias= to the same unit name ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +open($fh, '>', $servicefile_path); +print $fh <<"EOT"; +[Unit] +Description=test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +WantedBy=multi-user.target +Alias=$random_unit +EOT +close($fh); + +isnt_enabled($random_unit); +isnt_enabled('foo\x2dtest.service'); +# note that in this case $alias_path and $mask_path are identical +$retval = dsh('enable', $random_unit); +is($retval, 0, "enable command succeeded"); +is_enabled($random_unit); +# systemctl enable does create the alias link even if it's not needed +#ok(! -l $mask_path, 'mask link does not exist yet'); + +unlink($servicefile_path); + +$retval = dsh('mask', $random_unit); +is($retval, 0, "mask command succeeded"); +is(readlink($mask_path), '/dev/null', 'service masked'); + +$retval = dsh('unmask', $random_unit); +is($retval, 0, "unmask command succeeded"); +ok(! -l $mask_path, 'mask link does not exist any more'); + +$retval = dsh('purge', $random_unit); +isnt_enabled($random_unit); +ok(! -l $mask_path, 'mask link does not exist any more'); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify WantedBy and Alias with template unit with DefaultInstance. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +($fh, $servicefile_path) = tempfile('unit\x2dXXXXX', + DIR => "$dpkg_root/lib/systemd/system", + SUFFIX => '@.service', + UNLINK => 1); +print $fh <<'EOT'; +[Unit] +Description=template test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +Alias=foo\x2dtest@.service +Alias=foo\x2dbar@baz.service +WantedBy=multi-user.target +DefaultInstance=instance\x2d +EOT +close($fh); + +$random_unit = basename($servicefile_path); +my $random_instance = $random_unit; +$random_instance =~ s/^(.*\@)(\.\w+)$/$1instance\\x2d$2/; + +isnt_enabled($random_unit); +isnt_enabled('foo\x2dtest@.service'); +isnt_enabled('foo\x2dtest@instance\x2d.service'); +isnt_enabled('foo\x2dbar@baz.service'); +isnt_enabled('foo\x2dbar@instance\x2d.service'); + +my $template_alias_path = $dpkg_root . '/etc/systemd/system/foo\x2dtest@.service'; +my $instance_alias_path = $dpkg_root . '/etc/systemd/system/foo\x2dbar@baz.service'; +my $template_wanted_path = "$dpkg_root/etc/systemd/system/multi-user.target.wants/$random_unit"; +my $instance_wanted_path = "$dpkg_root/etc/systemd/system/multi-user.target.wants/$random_instance"; +ok(! -l $template_alias_path, 'template alias link does not exist yet'); +ok(! -l $instance_alias_path, 'instance alias link does not exist yet'); +ok(! -l $template_wanted_path, 'template wanted link does not exist yet'); +ok(! -l $instance_wanted_path, 'instance wanted link does not exist yet'); +$retval = dsh('enable', $random_unit); +is($retval, 0, "enable command succeeded"); +is($dpkg_root . readlink($template_alias_path), $servicefile_path, 'correct template alias link'); +is($dpkg_root . readlink($instance_alias_path), $servicefile_path, 'correct instance alias link'); +ok(! -l $template_wanted_path, 'template wanted link does still not exist'); +is($dpkg_root . readlink($instance_wanted_path), $servicefile_path, 'correct instance wanted link'); +is_enabled($random_unit); + +$retval = dsh('disable', $random_unit); +isnt_enabled($random_unit); +ok(! -l $template_alias_path, 'template alias link does not exist anymore'); +ok(! -l $instance_alias_path, 'instance alias link does not exist anymore'); +ok(! -l $template_wanted_path, 'template wanted link does still not exist'); +ok(! -l $instance_wanted_path, 'instance wanted link does not exist anymore'); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify WantedBy and Alias with template unit without DefaultInstance. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +open($fh, '>', $servicefile_path); +print $fh <<'EOT'; +[Unit] +Description=template test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +Alias=foo\x2dtest@.service +Alias=foo\x2dbar@baz.service +RequiredBy=foo\x2ddepender@.service +EOT +close($fh); + +isnt_enabled($random_unit); +isnt_enabled('foo\x2dtest@.service'); +isnt_enabled('foo\x2dbar@baz.service'); + +$template_alias_path = $dpkg_root . '/etc/systemd/system/foo\x2dtest@.service'; +$instance_alias_path = $dpkg_root . '/etc/systemd/system/foo\x2dbar@baz.service'; +$template_wanted_path = $dpkg_root . '/etc/systemd/system/foo\x2ddepender@.service.requires/' . $random_unit; +$instance_wanted_path = $dpkg_root . '/etc/systemd/system/foo\x2ddepender@.service.requires/' . $random_instance; +ok(! -l $template_alias_path, 'template alias link does not exist yet'); +ok(! -l $instance_alias_path, 'instance alias link does not exist yet'); +ok(! -l $template_wanted_path, 'template wanted link does not exist yet'); +ok(! -l $instance_wanted_path, 'instance wanted link does not exist yet'); +$retval = dsh('enable', $random_unit); +is($retval, 0, "enable command succeeded"); +is($dpkg_root . readlink($template_alias_path), $servicefile_path, 'correct template alias link'); +is($dpkg_root . readlink($instance_alias_path), $servicefile_path, 'correct instance alias link'); +is($dpkg_root . readlink($template_wanted_path), $servicefile_path, 'correct template wanted link'); +ok(! -l $instance_wanted_path, 'instance wanted link does still not exist'); +is_enabled($random_unit); + +$retval = dsh('disable', $random_unit); +isnt_enabled($random_unit); +ok(! -l $template_alias_path, 'template alias link does not exist anymore'); +ok(! -l $instance_alias_path, 'instance alias link does not exist anymore'); +ok(! -l $template_wanted_path, 'template wanted link does still not exist'); +ok(! -l $instance_wanted_path, 'instance wanted link does not exist anymore'); + +done_testing; diff --git a/t/002-deb-systemd-helper-update.t b/t/002-deb-systemd-helper-update.t new file mode 100644 index 0000000..7f7d826 --- /dev/null +++ b/t/002-deb-systemd-helper-update.t @@ -0,0 +1,196 @@ +#!perl +# vim:ts=4:sw=4:et + +use strict; +use warnings; +use Test::More; +use Test::Deep qw(:preload cmp_bag); +use File::Temp qw(tempfile tempdir); # in core since perl 5.6.1 +use File::Path qw(make_path); # in core since Perl 5.001 +use File::Basename; # in core since Perl 5 +use FindBin; # in core since Perl 5.00307 + +use lib "$FindBin::Bin/."; +use helpers; + +test_setup(); + +my $dpkg_root = $ENV{DPKG_ROOT} // ''; + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “is-enabled” is not true for a random, non-existing unit file. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +my ($fh, $random_unit) = tempfile('unitXXXXX', + SUFFIX => '.service', + TMPDIR => 1, + UNLINK => 1); +close($fh); +$random_unit = basename($random_unit); + +my $statefile = "$dpkg_root/var/lib/systemd/deb-systemd-helper-enabled/$random_unit.dsh-also"; +my $servicefile_path = "$dpkg_root/lib/systemd/system/$random_unit"; +make_path("$dpkg_root/lib/systemd/system"); +open($fh, '>', $servicefile_path); +print $fh <<'EOT'; +[Unit] +Description=test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +WantedBy=multi-user.target +EOT +close($fh); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “enable” creates the requested symlinks. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +my $retval = dsh('enable', $random_unit); +my $symlink_path = "$dpkg_root/etc/systemd/system/multi-user.target.wants/$random_unit"; +ok(-l $symlink_path, "$random_unit was enabled"); +is($dpkg_root . readlink($symlink_path), $servicefile_path, + "symlink points to $servicefile_path"); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “is-enabled” now returns true. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +is_enabled($random_unit); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Modify the unit file and verify that “is-enabled” is no longer true. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +open($fh, '>>', $servicefile_path); +print $fh "Alias=newalias.service\n"; +close($fh); + +isnt_enabled($random_unit); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “was-enabled” is still true (operates on the state file). ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +$retval = dsh('was-enabled', $random_unit); +isnt($retval, -1, 'deb-systemd-helper could be executed'); +ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal'); +is($retval >> 8, 0, "random unit file was-enabled"); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify the new symlink is not yet in the state file. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +is_deeply( + [ state_file_entries($statefile) ], + [ $symlink_path ], + 'state file does not contain the new link yet'); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “enable” creates the new symlinks. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +my $new_symlink_path = "$dpkg_root/etc/systemd/system/newalias.service"; +ok(! -l $new_symlink_path, 'new symlink does not exist yet'); + +$retval = dsh('enable', $random_unit); +ok(-l $new_symlink_path, 'new symlink was created'); +is($dpkg_root . readlink($new_symlink_path), $servicefile_path, + "symlink points to $servicefile_path"); + +is_enabled($random_unit); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify the new symlink was recorded in the state file. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +cmp_bag( + [ state_file_entries($statefile) ], + [ $symlink_path, $new_symlink_path ], + 'state file updated'); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Modify the unit file and verify that “is-enabled” is no longer true. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +open($fh, '>>', $servicefile_path); +print $fh "Alias=another.service\n"; +close($fh); + +isnt_enabled($random_unit); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “was-enabled” is still true (operates on the state file). ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +$retval = dsh('was-enabled', $random_unit); +isnt($retval, -1, 'deb-systemd-helper could be executed'); +ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal'); +is($retval >> 8, 0, "random unit file was-enabled"); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify the new symlink is not yet in the state file. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +cmp_bag( + [ state_file_entries($statefile) ], + [ $symlink_path, $new_symlink_path ], + 'state file does not contain the new link yet'); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “update-state” does not create the symlink, but records it in the ┃ +# ┃ state file. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +my $new_symlink_path2 = "$dpkg_root/etc/systemd/system/another.service"; +ok(! -l $new_symlink_path2, 'new symlink does not exist yet'); + +$retval = dsh('update-state', $random_unit); +ok(! -l $new_symlink_path2, 'new symlink still does not exist'); + +isnt_enabled($random_unit); + +cmp_bag( + [ state_file_entries($statefile) ], + [ $symlink_path, $new_symlink_path, $new_symlink_path2 ], + 'state file updated'); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Rewrite the original contents and verify “update-state” removes the old ┃ +# ┃ links that are no longer present. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +open($fh, '>', $servicefile_path); +print $fh <<'EOT'; +[Unit] +Description=test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +WantedBy=multi-user.target +EOT +close($fh); + +unlink($new_symlink_path); + +ok(! -l $new_symlink_path, 'new symlink still does not exist'); +ok(! -l $new_symlink_path2, 'new symlink 2 still does not exist'); + +$retval = dsh('update-state', $random_unit); + +ok(! -l $new_symlink_path, 'new symlink still does not exist'); +ok(! -l $new_symlink_path2, 'new symlink 2 still does not exist'); + +is_enabled($random_unit); + +is_deeply( + [ state_file_entries($statefile) ], + [ $symlink_path ], + 'state file updated'); + + +done_testing; diff --git a/t/003-deb-systemd-helper-complex.t b/t/003-deb-systemd-helper-complex.t new file mode 100644 index 0000000..47d8fb4 --- /dev/null +++ b/t/003-deb-systemd-helper-complex.t @@ -0,0 +1,123 @@ +#!perl +# vim:ts=4:sw=4:et + +use strict; +use warnings; +use Test::More; +use Test::Deep qw(:preload cmp_bag); +use File::Temp qw(tempfile tempdir); # in core since perl 5.6.1 +use File::Path qw(make_path); # in core since Perl 5.001 +use File::Basename; # in core since Perl 5 +use FindBin; # in core since Perl 5.00307 + +use lib "$FindBin::Bin/."; +use helpers; + +test_setup(); + +my $dpkg_root = $ENV{DPKG_ROOT} // ''; + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Create two unit files with random names; they refer to each other (Also=).┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +my ($fh1, $random_unit1) = tempfile('unitXXXXX', + SUFFIX => '.service', + TMPDIR => 1, + UNLINK => 1); +close($fh1); +$random_unit1 = basename($random_unit1); + +my ($fh2, $random_unit2) = tempfile('unitXXXXX', + SUFFIX => '.service', + TMPDIR => 1, + UNLINK => 1); +close($fh2); +$random_unit2 = basename($random_unit2); + +my $servicefile_path1 = "$dpkg_root/lib/systemd/system/$random_unit1"; +my $servicefile_path2 = "$dpkg_root/lib/systemd/system/$random_unit2"; +make_path("$dpkg_root/lib/systemd/system"); +open($fh1, '>', $servicefile_path1); +print $fh1 <<EOT; +[Unit] +Description=test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +WantedBy=multi-user.target +Also=$random_unit2 +EOT +close($fh1); + +open($fh2, '>', $servicefile_path2); +print $fh2 <<EOT; +[Unit] +Description=test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +WantedBy=multi-user.target +Alias=alias2.service +Also=$random_unit1 +EOT +close($fh2); + +isnt_enabled($random_unit1); +isnt_enabled($random_unit2); +isnt_debian_installed($random_unit1); +isnt_debian_installed($random_unit2); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “enable” creates all symlinks. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +unless ($ENV{'TEST_ON_REAL_SYSTEM'}) { + # This might already exist if we don't start from a fresh directory + ok(! -d "$dpkg_root/etc/systemd/system/multi-user.target.wants", + 'multi-user.target.wants does not exist yet'); +} + +my $retval = dsh('enable', $random_unit1); +my %links = map { (basename($_), $dpkg_root . readlink($_)) } + ("$dpkg_root/etc/systemd/system/multi-user.target.wants/$random_unit1", + "$dpkg_root/etc/systemd/system/multi-user.target.wants/$random_unit2"); +is_deeply( + \%links, + { + $random_unit1 => $servicefile_path1, + $random_unit2 => $servicefile_path2, + }, + 'All expected links present'); + +my $alias_path = "$dpkg_root/etc/systemd/system/alias2.service"; +ok(-l $alias_path, 'alias created'); +is($dpkg_root . readlink($alias_path), $servicefile_path2, + 'alias points to the correct service file'); + +cmp_bag( + [ state_file_entries("$dpkg_root/var/lib/systemd/deb-systemd-helper-enabled/$random_unit1.dsh-also") ], + [ "$dpkg_root/etc/systemd/system/multi-user.target.wants/$random_unit1", + "$dpkg_root/etc/systemd/system/multi-user.target.wants/$random_unit2", + "$dpkg_root/etc/systemd/system/alias2.service" ], + 'state file updated'); + +# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +# ┃ Verify “is-enabled” now returns true. ┃ +# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +is_enabled($random_unit1); +is_enabled($random_unit2); +is_debian_installed($random_unit1); + +# $random_unit2 was only enabled _because of_ $random_unit1’s Also= statement +# and thus does not have its own state file. +isnt_debian_installed($random_unit2); + +# TODO: cleanup tests? + +done_testing; diff --git a/t/004-deb-systemd-helper-user.t b/t/004-deb-systemd-helper-user.t new file mode 100644 index 0000000..bd914cb --- /dev/null +++ b/t/004-deb-systemd-helper-user.t @@ -0,0 +1,413 @@ +#!perl +# vim:ts=4:sw=4:et + +use strict; +use warnings; +use Test::More; +use File::Temp qw(tempfile tempdir); # in core since perl 5.6.1 +use File::Path qw(make_path); # in core since Perl 5.001 +use File::Basename; # in core since Perl 5 +use FindBin; # in core since Perl 5.00307 + +use lib "$FindBin::Bin/."; +use helpers; + +test_setup(); + +my $dpkg_root = $ENV{DPKG_ROOT} // ''; + +# +# "is-enabled" is not true for a random, non-existing unit file +# + +my ($fh, $random_unit) = tempfile('unit\x2dXXXXX', + SUFFIX => '.service', + TMPDIR => 1, + UNLINK => 1); +close($fh); +$random_unit = basename($random_unit); + +isnt_enabled($random_unit); +isnt_enabled($random_unit, user => 1); +isnt_debian_installed($random_unit); +isnt_debian_installed($random_unit, user => 1); + +# +# "is-enabled" is not true for a random, existing user unit file +# + +my $servicefile_path = "$dpkg_root/usr/lib/systemd/user/$random_unit"; +make_path("$dpkg_root/usr/lib/systemd/user"); +open($fh, '>', $servicefile_path); +print $fh <<'EOT'; +[Unit] +Description=test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +WantedBy=default.target +EOT +close($fh); + +isnt_enabled($random_unit); +isnt_enabled($random_unit, user => 1); +isnt_debian_installed($random_unit); +isnt_debian_installed($random_unit, user => 1); + +# +# "enable" creates the requested symlinks +# + +unless ($ENV{'TEST_ON_REAL_SYSTEM'}) { + # This might exist if we don't start from a fresh directory + ok(! -d "$dpkg_root/etc/systemd/user/default.target.wants", + 'default.target.wants does not exist yet'); +} + +my $retval = dsh('--user', 'enable', $random_unit); +is($retval, 0, "enable command succeeded"); +my $symlink_path = "$dpkg_root/etc/systemd/user/default.target.wants/$random_unit"; +ok(-l $symlink_path, "$random_unit was enabled"); +is($dpkg_root . readlink($symlink_path), $servicefile_path, + "symlink points to $servicefile_path"); + +# +# "is-enabled" now returns true for the user instance +# + +isnt_enabled($random_unit); +isnt_debian_installed($random_unit); +is_enabled($random_unit, user => 1); +is_debian_installed($random_unit, user => 1); + +# +# deleting the symlinks and running "enable" again does not re-create them +# + +unlink($symlink_path); +ok(! -l $symlink_path, 'symlink deleted'); +isnt_enabled($random_unit); +isnt_enabled($random_unit, user => 1); +isnt_debian_installed($random_unit); +is_debian_installed($random_unit, user => 1); + +$retval = dsh('--user', 'enable', $random_unit); +is($retval, 0, "enable command succeeded"); + +isnt_enabled($random_unit, user => 1); + +# +# "disable" deletes the statefile when purging +# + +my $statefile = "$dpkg_root/var/lib/systemd/deb-systemd-user-helper-enabled/$random_unit.dsh-also"; + +ok(-f $statefile, 'state file exists'); + +$ENV{'_DEB_SYSTEMD_HELPER_PURGE'} = '1'; +$retval = dsh('--user', 'disable', $random_unit); +delete $ENV{'_DEB_SYSTEMD_HELPER_PURGE'}; +is($retval, 0, "disable command succeeded"); +ok(! -f $statefile, 'state file does not exist anymore after purging'); +isnt_debian_installed($random_unit); +isnt_debian_installed($random_unit, user => 1); + +# +# "enable" re-creates the symlinks after purging +# + +ok(! -l $symlink_path, 'symlink does not exist yet'); +isnt_enabled($random_unit); +isnt_enabled($random_unit, user => 1); + +$retval = dsh('--user', 'enable', $random_unit); +is($retval, 0, "enable command succeeded"); + +isnt_enabled($random_unit); +isnt_debian_installed($random_unit); +is_enabled($random_unit, user => 1); +is_debian_installed($random_unit, user => 1); + +# +# "disable" removes the symlinks +# + +$ENV{'_DEB_SYSTEMD_HELPER_PURGE'} = '1'; +$retval = dsh('--user', 'disable', $random_unit); +delete $ENV{'_DEB_SYSTEMD_HELPER_PURGE'}; +is($retval, 0, "disable command succeeded"); + +isnt_enabled($random_unit); +isnt_enabled($random_unit, user => 1); + +# +# "enable" re-creates the symlinks +# + +ok(! -l $symlink_path, 'symlink does not exist yet'); +isnt_enabled($random_unit); +isnt_enabled($random_unit, user => 1); + +$retval = dsh('--user', 'enable', $random_unit); +is($retval, 0, "enable command succeeded"); + +isnt_enabled($random_unit); +isnt_debian_installed($random_unit); +is_enabled($random_unit, user => 1); +is_debian_installed($random_unit, user => 1); + +# +# "purge" works +# + +$retval = dsh('--user', 'purge', $random_unit); +is($retval, 0, "purge command succeeded"); + +isnt_enabled($random_unit, user => 1); +isnt_debian_installed($random_unit, user => 1); + +# +# "enable" re-creates the symlinks after purging +# + +ok(! -l $symlink_path, 'symlink does not exist yet'); +isnt_enabled($random_unit, user => 1); + +$retval = dsh('--user', 'enable', $random_unit); +is($retval, 0, "enable command succeeded"); + +is_enabled($random_unit, user => 1); +is_debian_installed($random_unit, user => 1); + +# +# "mask" (when enabled) results in the symlink pointing to /dev/null +# + +my $mask_path = "$dpkg_root/etc/systemd/user/$random_unit"; +ok(! -l $mask_path, 'mask link does not exist yet'); + +$retval = dsh('--user', 'mask', $random_unit); +is($retval, 0, "mask command succeeded"); +ok(-l $mask_path, 'mask link exists'); +is(readlink($mask_path), '/dev/null', 'service masked'); + +$retval = dsh('--user', 'unmask', $random_unit); +is($retval, 0, "unmask command succeeded"); +ok(! -e $mask_path, 'mask link does not exist anymore'); + +# +# "mask" (when disabled) works the same way +# + +$retval = dsh('--user', 'disable', $random_unit); +is($retval, 0, "disable command succeeded"); +ok(! -e $symlink_path, 'symlink no longer exists'); + +$retval = dsh('--user', 'mask', $random_unit); +is($retval, 0, "mask command succeeded"); +ok(-l $mask_path, 'mask link exists'); +is(readlink($mask_path), '/dev/null', 'service masked'); + +$retval = dsh('--user', 'unmask', $random_unit); +is($retval, 0, "unmask command succeeded"); +ok(! -e $mask_path, 'symlink no longer exists'); + +# +# "mask" / "unmask" don't do anything when the unit is already masked +# + +ok(! -l $mask_path, 'mask link does not exist yet'); +symlink('/dev/null', $mask_path); +ok(-l $mask_path, 'mask link exists'); +is(readlink($mask_path), '/dev/null', 'service masked'); + +$retval = dsh('--user', 'mask', $random_unit); +is($retval, 0, "mask command succeeded"); +ok(-l $mask_path, 'mask link exists'); +is(readlink($mask_path), '/dev/null', 'service still masked'); + +$retval = dsh('--user', 'unmask', $random_unit); +is($retval, 0, "unmask command succeeded"); +ok(-l $mask_path, 'mask link exists'); +is(readlink($mask_path), '/dev/null', 'service still masked'); + +# +# "mask" / "unmask" don't do anything when the user copied the .service. +# + +unlink($mask_path); + +open($fh, '>', $mask_path); +print $fh <<'EOT'; +[Unit] +Description=test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +WantedBy=default.target +EOT +close($fh); + +ok(-e $mask_path, 'local service file exists'); +ok(! -l $mask_path, 'local service file is not a symlink'); + +$retval = dsh('--user', 'mask', $random_unit); +isnt($retval, -1, 'deb-systemd-helper could be executed'); +ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal'); +is($retval >> 8, 0, 'deb-systemd-helper exited with exit code 0'); +ok(-e $mask_path, 'local service file still exists'); +ok(! -l $mask_path, 'local service file is still not a symlink'); + +$retval = dsh('--user', 'unmask', $random_unit); +isnt($retval, -1, 'deb-systemd-helper could be executed'); +ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal'); +is($retval >> 8, 0, 'deb-systemd-helper exited with exit code 0'); +ok(-e $mask_path, 'local service file still exists'); +ok(! -l $mask_path, 'local service file is still not a symlink'); + +unlink($mask_path); + +# +# "Alias=" handling +# + +$retval = dsh('--user', 'purge', $random_unit); +is($retval, 0, "purge command succeeded"); + +open($fh, '>', $servicefile_path); +print $fh <<'EOT'; +[Unit] +Description=test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +WantedBy=default.target +Alias=foo\x2dtest.service +EOT +close($fh); + +isnt_enabled($random_unit, user => 1); +isnt_enabled('foo\x2dtest.service', user => 1); +my $alias_path = $dpkg_root . '/etc/systemd/user/foo\x2dtest.service'; +ok(! -l $alias_path, 'alias link does not exist yet'); +$retval = dsh('--user', 'enable', $random_unit); +is($retval, 0, "enable command succeeded"); +is($dpkg_root . readlink($alias_path), $servicefile_path, 'correct alias link'); +is_enabled($random_unit, user => 1); +ok(! -l $mask_path, 'mask link does not exist yet'); + +$retval = dsh('--user', 'mask', $random_unit); +is($retval, 0, "mask command succeeded"); +is($dpkg_root . readlink($alias_path), $servicefile_path, 'correct alias link'); +is(readlink($mask_path), '/dev/null', 'service masked'); + +$retval = dsh('--user', 'unmask', $random_unit); +is($retval, 0, "unmask command succeeded"); +is($dpkg_root . readlink($alias_path), $servicefile_path, 'correct alias link'); +ok(! -l $mask_path, 'mask link does not exist any more'); + +$retval = dsh('--user', 'disable', $random_unit); +isnt_enabled($random_unit, user => 1); +ok(! -l $alias_path, 'alias link does not exist any more'); + +# +# "Alias=" / "mask" with removed package (as in postrm) +# + +$retval = dsh('--user', 'purge', $random_unit); +is($retval, 0, "purge command succeeded"); +$retval = dsh('--user', 'enable', $random_unit); +is($retval, 0, "enable command succeeded"); +is($dpkg_root . readlink($alias_path), $servicefile_path, 'correct alias link'); + +unlink($servicefile_path); + +$retval = dsh('--user', 'mask', $random_unit); +is($retval, 0, "mask command succeeded with uninstalled unit"); +is($dpkg_root . readlink($alias_path), $servicefile_path, 'correct alias link'); +is(readlink($mask_path), '/dev/null', 'service masked'); + +$retval = dsh('--user', 'purge', $random_unit); +is($retval, 0, "purge command succeeded with uninstalled unit"); +ok(! -l $alias_path, 'alias link does not exist any more'); +is(readlink($mask_path), '/dev/null', 'service masked'); + +$retval = dsh('--user', 'unmask', $random_unit); +is($retval, 0, "unmask command succeeded with uninstalled unit"); +ok(! -l $mask_path, 'mask link does not exist any more'); + +# +# "Alias=" to the same unit name +# + +open($fh, '>', $servicefile_path); +print $fh <<"EOT"; +[Unit] +Description=test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +WantedBy=default.target +Alias=$random_unit +EOT +close($fh); + +isnt_enabled($random_unit, user => 1); +isnt_enabled('foo\x2dtest.service', user => 1); +# note that in this case $alias_path and $mask_path are identical +$retval = dsh('--user', 'enable', $random_unit); +is($retval, 0, "enable command succeeded"); +is_enabled($random_unit, user => 1); +# systemctl enable does create the alias link even if it's not needed +#ok(! -l $mask_path, 'mask link does not exist yet'); + +unlink($servicefile_path); + +$retval = dsh('--user', 'mask', $random_unit); +is($retval, 0, "mask command succeeded"); +is(readlink($mask_path), '/dev/null', 'service masked'); + +$retval = dsh('--user', 'unmask', $random_unit); +is($retval, 0, "unmask command succeeded"); +ok(! -l $mask_path, 'mask link does not exist any more'); + +$retval = dsh('--user', 'purge', $random_unit); +isnt_enabled($random_unit, user => 1); +ok(! -l $mask_path, 'mask link does not exist any more'); + +# +# "Alias=" without "WantedBy=" +# + +open($fh, '>', $servicefile_path); +print $fh <<'EOT'; +[Unit] +Description=test unit + +[Service] +ExecStart=/bin/sleep 1 + +[Install] +Alias=baz\x2dtest.service +EOT +close($fh); + +isnt_enabled($random_unit, user => 1); +isnt_enabled('baz\x2dtest.service', user => 1); +$alias_path = $dpkg_root . '/etc/systemd/user/baz\x2dtest.service'; +ok(! -l $alias_path, 'alias link does not exist yet'); +$retval = dsh('--user', 'enable', $random_unit); +is($retval, 0, "enable command succeeded"); +is_enabled($random_unit, user => 1); +ok(-l $alias_path, 'alias link does exist'); +is($dpkg_root . readlink($alias_path), $servicefile_path, 'correct alias link'); + +done_testing; diff --git a/t/README b/t/README new file mode 100644 index 0000000..eda327d --- /dev/null +++ b/t/README @@ -0,0 +1,15 @@ +These test cases need the Linux::Clone module, which is not yet in Debian. +See http://michael.stapelberg.de/cpan/#Linux::Clone on how to install it. + +Note that you need to run the test cases as root because they use Linux mount +namespaces and bind mounts (requires Linux ≥ 2.4.19). + +Note that you should mark / as a private subtree before running these tests, or +they will fail. Use mount --make-rprivate /. Unfortunately, the version of +util-linux in Debian at the time of writing (2.20.1) is broken and its +--make-rprivate does not actually work. See #731574. + +The intention is that the testcases are _not_ run automatically during package +building because they might be too fragile and additional dependencies make it +harder to port this package to Ubuntu or Debian backports. It is enough if the +test cases are run on every code change. diff --git a/t/helpers.pm b/t/helpers.pm new file mode 100644 index 0000000..72d4f68 --- /dev/null +++ b/t/helpers.pm @@ -0,0 +1,160 @@ +use strict; +use warnings; +use English; +use File::Temp qw(tempdir); # in core since perl 5.6.1 +use File::Copy qw(cp); +use File::Path qw(make_path); + +sub check_fakechroot_running() { + my $content = `FAKECHROOT_DETECT=1 sh -c "echo This should not be printed"`; + my $result = 0; + if ($content =~ /^fakechroot [0-9.]+\n$/) { + $result = 1; + } + return $result; +} + +sub test_setup() { + if (length $ENV{TEST_DPKG_ROOT}) { + print STDERR "test_setup() with DPKG_ROOT\n"; + $ENV{DPKG_ROOT} = tempdir( CLEANUP => 1 ); + return; + } + + if ( !check_fakechroot_running ) { + print STDERR "you have to run this script inside fakechroot and fakeroot:\n"; + print STDERR (" fakechroot fakeroot perl $PROGRAM_NAME" . (join " ", @ARGV) . "\n"); + exit 1; + } + + # Set up a chroot that contains everything necessary to run + # deb-systemd-helper under fakechroot. + print STDERR "test_setup() with fakechroot\n"; + + my $tmpdir = tempdir( CLEANUP => 1 ); + mkdir "$tmpdir/dev"; + 0 == system 'mknod', "$tmpdir/dev/null", 'c', '1', '3' or die "cannot mknod: $?"; + mkdir "$tmpdir/tmp"; + make_path("$tmpdir/usr/bin"); + make_path("$tmpdir/usr/lib/systemd/user"); + make_path("$tmpdir/lib/systemd/system/"); + make_path("$tmpdir/var/lib/systemd"); + make_path("$tmpdir/etc/systemd"); + if ( length $ENV{TEST_INSTALLED} ) { + # if we test the installed deb-systemd-helper we copy it from the + # system's installation + cp "/usr/bin/deb-systemd-helper", "$tmpdir/usr/bin/deb-systemd-helper" + or die "cannot copy: $!"; + } + else { + cp "$FindBin::Bin/../script/deb-systemd-helper", + "$tmpdir/usr/bin/deb-systemd-helper" + or die "cannot copy: $!"; + } + + # make sure that dpkg diversion messages are not translated + local $ENV{LC_ALL} = 'C.UTF-8'; + # the chroot only needs to contain a working perl-base + open my $fh, '-|', 'dpkg-query', '--listfiles', 'perl-base'; + + while ( my $path = <$fh> ) { + chomp $path; + # filter out diversion messages in the same way that dpkg-repack does + # https://git.dpkg.org/cgit/dpkg/dpkg-repack.git/tree/dpkg-repack#n238 + if ($path =~ /^package diverts others to: /) { + next; + } + if ($path =~ /^diverted by [^ ]+ to: /) { + next; + } + if ($path =~ /^locally diverted to: /) { + next; + } + if ($path !~ /^\//) { + die "path must start with a slash"; + } + if ( -e "$tmpdir$path" ) { + # ignore paths that were already created + next; + } elsif ( !-r $path ) { + # if the host's path is not readable, assume it's a directory + mkdir "$tmpdir$path" or die "cannot mkdir $path: $!"; + } elsif ( -l $path ) { + symlink readlink($path), "$tmpdir$path"; + } elsif ( -d $path ) { + mkdir "$tmpdir$path" or die "cannot mkdir $path: $!"; + } elsif ( -f $path ) { + cp $path, "$tmpdir$path" or die "cannot cp $path: $!"; + } else { + die "cannot handle $path"; + } + } + close $fh; + + $ENV{'SYSTEMCTL_INSTALL_CLIENT_SIDE'} = '1'; + + # we run the chroot call in a child process because we need the parent + # process remaining un-chrooted or otherwise it cannot clean-up the + # temporary directory on exit + my $pid = fork() // die "cannot fork: $!"; + if ( $pid == 0 ) { + chroot $tmpdir or die "cannot chroot: $!"; + chdir "/" or die "cannot chdir to /: $!"; + return; + } + waitpid($pid, 0); + + exit $?; +} + +# reads in a whole file +sub slurp { + open my $fh, '<', shift; + local $/; + <$fh>; +} + +sub state_file_entries { + my ($path) = @_; + my $bytes = slurp($path); + my $dpkg_root = $ENV{DPKG_ROOT} // ''; + return map { "$dpkg_root$_" } split("\n", $bytes); +} + +my $dsh = ''; +if ( length $ENV{TEST_INSTALLED} ) { + # if we are to test the installed version of deb-systemd-helper then even + # in DPKG_ROOT mode, we want to run /usr/bin/deb-systemd-helper + $dsh = "/usr/bin/deb-systemd-helper"; +} else { + if ( length $ENV{TEST_DPKG_ROOT} ) { + # when testing deb-systemd-helper from source, then in DPKG_ROOT mode, + # we take the script from the source directory + $dsh = "$FindBin::Bin/../script/deb-systemd-helper"; + } else { + $dsh = "/usr/bin/deb-systemd-helper"; + } +} +$ENV{'DPKG_MAINTSCRIPT_PACKAGE'} = 'deb-systemd-helper-test'; + +sub dsh { + return system($dsh, @_); +} + +sub _unit_check { + my ($cmd, $cb, $verb, $unit, %opts) = @_; + + my $retval = dsh($opts{'user'} ? '--user' : '--system', $cmd, $unit); + + isnt($retval, -1, 'deb-systemd-helper could be executed'); + ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal'); + $cb->($retval >> 8, 0, "random unit file '$unit' $verb $cmd"); +} + +sub is_enabled { _unit_check('is-enabled', \&is, 'is', @_) } +sub isnt_enabled { _unit_check('is-enabled', \&isnt, 'isnt', @_) } + +sub is_debian_installed { _unit_check('debian-installed', \&is, 'is', @_) } +sub isnt_debian_installed { _unit_check('debian-installed', \&isnt, 'isnt', @_) } + +1; |