diff --git a/debian/control b/debian/control
index 3212398c05aae97d3e88de28423131bbbae815a4..0a1fdc54d94ffe0c6a34d5754fed10de677ce99a 100644
--- a/debian/control
+++ b/debian/control
@@ -13,6 +13,7 @@ Build-Depends:
  gtk-doc-tools,
  libelf-dev,
  libipc-run-perl,
+ perl,
  xsltproc,
 Vcs-Git: https://gitlab.collabora.com/vivek/libcapsule.git
 Vcs-Browser: https://gitlab.collabora.com/vivek/libcapsule
diff --git a/debian/tests/control b/debian/tests/control
index ec8e122954d9f0bb3b24ce58c19944f46ab6ee58..35a7605e7b35fe7dfb39a5456a58ef567c9e7aff 100644
--- a/debian/tests/control
+++ b/debian/tests/control
@@ -1,18 +1,25 @@
 Tests: smoke
+Restrictions: allow-stderr
 Depends:
  build-essential,
  libcapsule-dev,
  pkg-config,
 
 Tests: symbols version
+Restrictions: allow-stderr
 Depends:
  bubblewrap,
  libcapsule-dev,
  libcapsule-dev-bin,
+ libipc-run-perl,
  libjpeg62 (>= 6b1) | libjpeg62-turbo,
+ perl,
  zlib1g,
 
 Tests: gnome-desktop-testing
+Restrictions: allow-stderr
 Depends:
  gnome-desktop-testing,
  libcapsule-tests,
+ libipc-run-perl,
+ perl,
diff --git a/debian/tests/smoke b/debian/tests/smoke
index 8b1def619d2bf290c28d425c3af7ef7500645bfb..1cad598d92c4febabc408babdd5f0139472b06b4 100755
--- a/debian/tests/smoke
+++ b/debian/tests/smoke
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/perl
 
 # Copyright © 2017 Collabora Ltd.
 #
@@ -15,20 +15,16 @@
 # You should have received a copy of the GNU Lesser General Public
 # License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
-set -eu -o pipefail
+use autodie;
+use File::Temp qw();
+use IPC::Run qw(run);
+use Test::More;
 
-exec 3>&1
-BASH_XTRACEFD=3
-PS4='# execute: '
-set -x
+my $scratch = File::Temp->newdir();
+chdir($scratch);
 
-scratch="$(mktemp -d)"
-cd "$scratch"
-trap 'cd /; rm -fr $scratch' EXIT
-
-echo "1..5"
-
-cat > trivial.c <<'EOF'
+open(my $fh, '>', 'trivial.c');
+print {$fh} <<'EOF';
 #define _GNU_SOURCE
 
 #include <stdio.h>
@@ -39,16 +35,20 @@ int
 main (void)
 {
   capsule_init();
-  printf( "ok 4 - capsule_init() didn't crash\n" );
+  printf( "capsule_init() didn't crash\n" );
   return 0;
 }
 EOF
-
-gcc -c -o trivial.o trivial.c $(pkg-config --cflags capsule)
-echo "ok 1 - compiled"
-gcc -o trivial trivial.o $(pkg-config --libs capsule)
-echo "ok 2 - linked"
-test -x trivial
-echo "ok 3 - result is executable"
-./trivial
-echo "ok 5 - exited successfully"
+close($fh);
+
+ok(! system('gcc -c -o trivial.o trivial.c $(pkg-config --cflags capsule)'),
+    'compiled');
+ok(! system('gcc -o trivial trivial.o $(pkg-config --libs capsule)'),
+    'linked');
+ok(-x 'trivial', 'executable');
+my $output;
+ok(run(['./trivial'], '>', \$output), 'executed successfully');
+is($output, "capsule_init() didn't crash\n", 'printed desired output');
+
+chdir('/');
+done_testing;
diff --git a/debian/tests/symbols b/debian/tests/symbols
index cbe861093fef244913e6631fcebb30ef970031dc..ca37869caecdafd84193aadc447a67be88e9be38 100755
--- a/debian/tests/symbols
+++ b/debian/tests/symbols
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/perl
 
 # Copyright © 2017 Collabora Ltd
 #
@@ -17,50 +17,57 @@
 # You should have received a copy of the GNU Lesser General Public
 # License along with libcapsule.  If not, see <http://www.gnu.org/licenses/>.
 
-set -eu -o pipefail
+use autodie;
+use warnings;
+use strict;
 
-. "$(dirname "$0")"/../../tests/libtest.sh
+use File::Temp qw();
+use IPC::Run qw(run);
+use Test::More;
 
-if ! bwrap --ro-bind / / true; then
-    skip_all "1..0 # SKIP - cannot run bwrap"
-fi
+use FindBin;
+use lib "$FindBin::Bin/../../tests";
 
-echo_tap "# Working directory: $test_tempdir"
-cd "$test_tempdir"
-host="${test_tempdir}/host"
+use CapsuleTest;
 
-CAPSULE_SYMBOLS="$(pkg-config --variable=libexecdir capsule)/capsule-symbols"
+skip_all_unless_bwrap;
+
+my $test_tempdir = File::Temp->newdir();
+diag "Working directory: $test_tempdir";
+my $host = "${test_tempdir}/host";
+mkdir($host);
+
+my $libexecdir = `pkg-config --variable=libexecdir capsule`;
+chomp $libexecdir;
+my $CAPSULE_SYMBOLS = "$libexecdir/capsule-symbols";
 
 # We need a well-behaved library with a simple ABI to inspect. Let's use
 # libcapsule itself :-)
-bwrap --ro-bind / / --ro-bind / "${host}" --dev-bind /dev /dev \
-    ${CAPSULE_SYMBOLS} libcapsule.so.0 "${host}" > output
-capture_tap sed -e 's/^/#  /' < output
-pass "Ran ${CAPSULE_SYMBOLS} libcapsule.so.0 ${host}"
+my $output;
+run_ok([qw(bwrap --ro-bind / / --ro-bind /), $host,
+        qw(--dev-bind /dev /dev),
+        $CAPSULE_SYMBOLS, 'libcapsule.so.0', $host], '>', \$output);
+diag_multiline $output;
 
-shell_is 'grep "^capsule_init" output' 0 "capsule_init " \
-    "capsule_init is part of libcapsule's ABI"
-shell_is 'grep -v "^capsule" output' 1 "" \
-    "all of libcapsule's ABI matches /^capsule/"
+like($output, qr{^capsule_init $}m, "capsule_init is an unversioned symbol");
+unlike($output, qr{^(?!capsule)}m, "all of libcapsule's ABI matches /^capsule/");
 
 # Try the same thing without a prefix
-${CAPSULE_SYMBOLS} libcapsule.so.0 / > output
-capture_tap sed -e 's/^/#  /' < output
-pass "Ran ${CAPSULE_SYMBOLS} libcapsule.so.0 /"
+run_ok([$CAPSULE_SYMBOLS, 'libcapsule.so.0', '/'], '>', \$output);
+diag_multiline $output;
 
-shell_is 'grep "^capsule_init" output' 0 "capsule_init " \
-    "capsule_init is an unversioned symbol"
-shell_is 'grep -v "^capsule" output' 1 "" \
-    "all of libcapsule's ABI matches /^capsule/"
+like($output, qr{^capsule_init $}m, "capsule_init is an unversioned symbol");
+unlike($output, qr{^(?!capsule)}m, "all of libcapsule's ABI matches /^capsule/");
 
 # How about versioned symbols?
-${CAPSULE_SYMBOLS} libjpeg.so.62 / > output
-capture_tap sed -e 's/^/#  /' < output
-pass "Ran ${CAPSULE_SYMBOLS} libjpeg.so.62 /"
+run_ok([$CAPSULE_SYMBOLS, 'libjpeg.so.62', '/'], '>', \$output);
+diag_multiline $output;
 
-shell_is 'grep "^jpeg_destroy " output' 0 'jpeg_destroy @@LIBJPEG_6.2' \
-    "jpeg_destroy is a versioned symbol"
+like($output, qr{^jpeg_destroy \@\@LIBJPEG_6\.2$}m,
+    "jpeg_destroy is a versioned symbol");
+unlike($output, qr{^jpeg_destroy (?!\@\@LIBJPEG_6\.2)}m,
+    "jpeg_destroy does not appear unversioned");
 
-done_testing
+done_testing;
 
 # vim:set sw=4 sts=4 et:
diff --git a/debian/tests/version b/debian/tests/version
index 3fbed704829ec7c9ff3ec9a85a7e0b6dfc539e22..4d3dcc2a0223262e88ebb86ba8278e2ffbf3f414 100755
--- a/debian/tests/version
+++ b/debian/tests/version
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/perl
 
 # Copyright © 2017 Collabora Ltd
 #
@@ -17,65 +17,112 @@
 # You should have received a copy of the GNU Lesser General Public
 # License along with libcapsule.  If not, see <http://www.gnu.org/licenses/>.
 
-set -eu -o pipefail
+use autodie;
+use warnings;
+use strict;
 
-. "$(dirname "$0")"/../../tests/libtest.sh
+use File::Temp qw();
+use IPC::Run qw(run);
+use Test::More;
 
-if ! bwrap --ro-bind / / true; then
-    skip_all "1..0 # SKIP - cannot run bwrap"
-fi
+use FindBin;
+use lib "$FindBin::Bin/../../tests";
 
-echo_tap "# Working directory: $test_tempdir"
-cd "$test_tempdir"
-host="${test_tempdir}/host"
+use CapsuleTest;
 
-CAPSULE_VERSION="$(pkg-config --variable=libexecdir capsule)/capsule-version"
+skip_all_unless_bwrap;
 
-output="$(bwrap --ro-bind / / --ro-bind / "${host}" --dev-bind /dev /dev \
-    ${CAPSULE_VERSION} libz.so.1 "${host}")"
-set -- $output
-is "$1" "${host}"
-is "$2" libz.so.1
-like "$3" "1.*.*"
-like "$4" "${host}/@(usr/lib|lib)/*-linux-gnu*/libz.so.$3"
-test -e "${4#${host}}"
-pass "successfully found libz"
+my $test_tempdir = File::Temp->newdir();
+diag "Working directory: $test_tempdir";
+my $host = "${test_tempdir}/host";
+mkdir($host);
+
+my $libexecdir = `pkg-config --variable=libexecdir capsule`;
+chomp $libexecdir;
+my $CAPSULE_VERSION = "$libexecdir/capsule-version";
+
+my $output;
+my ($root, $soname, $version, $path);
+run_ok([qw(bwrap --ro-bind / / --ro-bind /), $host,
+        qw(--dev-bind /dev /dev), $CAPSULE_VERSION, 'libz.so.1',
+        $host], '>', \$output);
+like($output, qr{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$});
+$output =~ m{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$};
+$root = $1;
+$soname = $2;
+$version = $3;
+$path = $4;
+is($root, $host);
+is($soname, 'libz.so.1');
+like($version, qr{^1\.[0-9]+\.[0-9]+$});
+like($path, qr{^\Q$host\E(/(?:usr/)?lib/.*-linux-gnu.*/libz\.so\.\Q$version\E)$});
+$path =~ s/^\Q$host\E//;
+ok(-e $path);
 
 # Try the same thing without a prefix
-output="$(${CAPSULE_VERSION} libz.so.1 "/")"
-set -- $output
-is "$1" "/"
-is "$2" libz.so.1
-# TODO: This just returns 1?
-#like "$3" "1.*.*"
-# TODO: Would be nice if this didn't start with //
-like "$4" "+(/)@(usr/lib|lib)/*-linux-gnu*/libz.so.$3"
-test -e "$4"
-pass "successfully found libz"
+run_ok([$CAPSULE_VERSION, 'libz.so.1', '/'], '>', \$output);
+like($output, qr{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$});
+$output =~ m{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$};
+$root = $1;
+$soname = $2;
+$version = $3;
+$path = $4;
+is($root, '/');
+is($soname, 'libz.so.1');
+TODO: {
+    local $TODO = 'returns 1 for some reason';
+    like($version, qr{^1\.[0-9]+\.[0-9]+$});
+}
+TODO: {
+    local $TODO = "would be nice if this didn't start with //";
+    like($path, qr{^(/?(?:usr/)?lib/.*-linux-gnu.*/libz\.so\.\Q$version\E)$});
+}
+like($4, qr{^(//?(?:usr/)?lib/.*-linux-gnu.*/libz\.so\.\Q$version\E)$});
+ok(-e $1);
 
 # A different way
-output="$(${CAPSULE_VERSION} libz.so.1 "")"
-set -- $output
-is "$1" libz.so.1
-# TODO: This just returns 1?
-#like "$2" "1.*.*"
-like "$3" "/@(usr/lib|lib)/*-linux-gnu*/libz.so.$2"
-test -e "$3"
-pass "successfully found libz"
+run_ok([$CAPSULE_VERSION, 'libz.so.1', ''], '>', \$output);
+like($output, qr{^(\S*)\s+(\S+)\s+(\S+)\s+(\S+)$});
+$output =~ m{^(\S*)\s+(\S+)\s+(\S+)\s+(\S+)$};
+$root = $1;
+$soname = $2;
+$version = $3;
+$path = $4;
+like($root, qr{/?});
+is($soname, 'libz.so.1');
+TODO: {
+    local $TODO = 'returns 1 for some reason';
+    like($version, qr{^1\.[0-9]+\.[0-9]+$});
+}
+TODO: {
+    local $TODO = "would be nice if this didn't start with //";
+    like($path, qr{^(/?(?:usr/)?lib/.*-linux-gnu.*/libz\.so\.\Q$version\E)$});
+}
+like($path, qr{^(//?(?:usr/)?lib/.*-linux-gnu.*/libz\.so\.\Q$version\E)$});
+ok(-e $path);
 
 # Another different way
-output="$(${CAPSULE_VERSION} libz.so.1 "/")"
-set -- $output
+run_ok([$CAPSULE_VERSION, 'libz.so.1'], '>', \$output);
+like($output, qr{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$});
+$output =~ m{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$};
+$root = $1;
+$soname = $2;
+$version = $3;
+$path = $4;
 # TODO: This should probably not try to print NULL
-#is "$1" "(null)"
-is "$2" libz.so.1
-# TODO: This just returns 1?
-#like "$3" "1.*.*"
-# TODO: Would be nice if this didn't start with //
-like "$4" "+(/)@(usr/lib|lib)/*-linux-gnu*/libz.so.$3"
-test -e "$4"
-pass "successfully found libz"
+unlike($root, qr{^/.+$});   # actually it's (null)
+is($soname, 'libz.so.1');
+TODO: {
+    local $TODO = 'returns 1 for some reason';
+    like($version, qr{^1\.[0-9]+\.[0-9]+$});
+}
+TODO: {
+    local $TODO = "would be nice if this didn't start with //";
+    like($path, qr{^(/?(?:usr/)?lib/.*-linux-gnu.*/libz\.so\.\Q$3\E)$});
+}
+like($path, qr{^(//?(?:usr/)?lib/.*-linux-gnu.*/libz\.so\.\Q$3\E)$});
+ok(-e $path);
 
-done_testing
+done_testing;
 
 # vim:set sw=4 sts=4 et:
diff --git a/tests/CapsuleTest.pm b/tests/CapsuleTest.pm
index dc653473022ed44586572a23fe1c039b3ab6f095..2345164321f7bab1ab369491cf120056ad9a0b71 100644
--- a/tests/CapsuleTest.pm
+++ b/tests/CapsuleTest.pm
@@ -57,7 +57,7 @@ installed-tests.
 
 # G_TEST_* convention stolen from GLib, even though we aren't using GTest
 our $srcdir = $ENV{G_TEST_SRCDIR};
-$srcdir = abs_path($FindBin::Bin) unless defined $srcdir;
+$srcdir = abs_path($FindBin::Bin."/..") unless defined $srcdir;
 
 =item $builddir
 
@@ -68,7 +68,7 @@ installed-tests.
 =cut
 
 our $builddir = $ENV{G_TEST_BUILDDIR};
-$builddir = abs_path($FindBin::Bin) unless defined $builddir;
+$builddir = abs_path($FindBin::Bin."/..") unless defined $builddir;
 
 diag "Source or installation directory: $srcdir";
 diag "Build or installation directory: $builddir";
diff --git a/tests/notgl.pl b/tests/notgl.pl
index 4d8ce0d8ba1b67ae9ee92d244344898e5e2d5c24..3dbf8d1795b9fea66a087eb85e0757e4ccb7ca92 100755
--- a/tests/notgl.pl
+++ b/tests/notgl.pl
@@ -19,6 +19,7 @@
 # You should have received a copy of the GNU Lesser General Public
 # License along with libcapsule.  If not, see <http://www.gnu.org/licenses/>.
 
+use autodie;
 use warnings;
 use strict;
 
@@ -28,6 +29,7 @@ use Test::More;
 
 use FindBin;
 use lib $FindBin::Bin;
+
 use CapsuleTest;
 
 skip_all_unless_bwrap;