--- /dev/null
+#!/usr/bin/perl -w
+# Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
+#
+
+use Data::Dumper;
+use File::Temp qw(tempfile);
+
+
+my @livepatch_files = qw(xen_hello_world.livepatch
+ xen_replace_world.livepatch
+ xen_bye_world.livepatch
+ xen_nop.livepatch);
+
+my $livepatch_dir="/usr/lib/debug/livepatch";
+my $xen_extra_info;
+my $xen_minor_ver;
+
+sub populate_data {
+ my @lines = split('\n', $_);
+ foreach my $line (@lines) {
+ my ($key, $values) = split /:/, $line;
+ $values = join("", $values);
+ chomp($values);
+ if ($key =~ m/xen_extra/) {
+ $xen_extra_info = $values;
+ }
+ if ($key =~ m/xen_minor/) {
+ $xen_minor_ver = $values;
+ }
+ }
+ return 1 if $xen_extra_info && $xen_minor_ver;
+ return 0;
+}
+
+sub check_for_hello_world {
+ return m/xen_extra/ && m/Hello World/;
+}
+
+sub check_for_stock {
+ return m/xen_extra/ && m/$xen_extra_info/;
+}
+
+# Make sure the xen_major or xen_minor are not the same as
+# $xen_major_ver or $xen_minor_ver
+sub check_versions {
+ my @lines = split('\n', $_);
+ foreach my $line (@lines) {
+ my ($key, $values) = split /:/, $line;
+ $values = join("", $values);
+ chomp($values);
+ if ($key =~ m/xen_minor/) {
+ if ($values ne $xen_minor_ver ) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+my @livepatch_tests = (
+ # Whether we can actually execute it.
+ { C => "xen-livepatch list" },
+ # And we better have a clean slate..
+ { C => "xen-livepatch list", OutputCheck => sub { return !m/xen_/; } },
+ # Sets the default values
+ { C => "xl info", OutputCheck => \&populate_data },
+ # Sanity check that populate_data did its job.
+ { C => "xl info",
+ OutputCheck => \&check_for_stock },
+ # Let it rip!
+ { C => "xen-livepatch revert xen_hello_world", rc => 256 },
+ { C => "xen-livepatch load xen_hello_world.livepatch" },
+ { C => "xen-livepatch load xen_hello_world.livepatch", rc => 256 },
+ { C => "xen-livepatch list",
+ OutputCheck => sub { m/xen_hello_world/ } },
+ { C => "xl info",
+ OutputCheck => \&check_for_hello_world },
+ { C => "xen-livepatch revert xen_hello_world" },
+ { C => "xl info",
+ OutputCheck => \&check_for_stock },
+ { C => "xen-livepatch unload xen_hello_world" },
+ { C => "xen-livepatch unload xen_hello_world", rc => 256 },
+ { C => "xl info",
+ OutputCheck => \&check_for_stock },
+ { C => "xen-livepatch load xen_hello_world.livepatch" },
+ { C => "xl info",
+ OutputCheck => \&check_for_hello_world },
+ { C => "xen-livepatch load xen_bye_world.livepatch" },
+ { C => "xl info",
+ OutputCheck => sub { m/xen_extra/ && m/Bye World/ } },
+ { C => "xen-livepatch upload xen_replace xen_replace_world.livepatch" },
+ { C => "xen-livepatch replace xen_replace" },
+ { C => "xl info",
+ OutputCheck => sub { m/xen_extra/ && m/Hello Again Wo/ } },
+ { C => "xen-livepatch apply xen_hello_world", rc => 256 },
+ { C => "xen-livepatch apply xen_bye_world", rc => 256 },
+ { C => "xen-livepatch apply xen_replace" },
+ { C => "xen-livepatch revert xen_replace" },
+ { C => "xen-livepatch unload xen_replace" },
+ { C => "xen-livepatch unload xen_hello_world" },
+ { C => "xen-livepatch unload xen_bye_world" },
+ { C => "xen-livepatch list",
+ OutputCheck => sub { !m/xen_/ } },
+ { C => "xl info",
+ OutputCheck => \&check_for_stock },
+ { C => "xen-livepatch load xen_nop.livepatch" },
+ { C => "xen-livepatch revert xen_nop" },
+ { C => "xen-livepatch apply xen_nop" },
+ { C => "xl info",
+ OutputCheck => \&check_versions },
+ { C => "xen-livepatch unload xen_nop", rc => 256 },
+ { C => "xen-livepatch revert xen_nop" },
+ { C => "xen-livepatch unload xen_nop" },
+ );
+
+# Copied from https://stackoverflow.com/questions/11514947/capture-the-output-of-perl-system
+sub mysystem {
+ my $cmd = shift; #"rsync -avz --progress -h $fullfile $copyfile";
+ my ($fh, $filename) = tempfile();
+ # http://stackoverflow.com/a/6872163/2923406
+ # I want to have rsync progress output on the terminal AND capture it in case of error.
+ # Need to use pipefail because 'tee' would be the last cmd otherwise and hence $? would be wrong.
+ my @cmd = ("bash", "-c", "set -o pipefail && $cmd 2>&1 | tee $filename");
+ my $ret = system(@cmd);
+ my $outerr = join('', <$fh>);
+ close $fh;
+ system("rm $filename");
+ return ($ret,$outerr);
+}
+
+sub livepatch_test () {
+ print "Have ".(scalar @livepatch_tests)." test-cases\n";
+ my $rc;
+ my $output;
+
+ foreach my $test (@livepatch_tests) {
+ # Default rc is zero.
+ my $expected_rc = defined($test->{rc}) ? $test->{rc} : 0;
+ my $cmd = "(set -e;cd $livepatch_dir;$test->{C})";
+ print "Executing: '$cmd' ..";
+ my ($rc, $output)=mysystem($cmd);
+
+ if ($rc != $expected_rc) {
+ print "FAILED (got $rc, expected: $expected_rc): \n";
+ die $rc;
+ }
+ if (defined($test->{OutputCheck})) {
+ $_ = $output;
+ $rc=$test->{OutputCheck}->();
+ if ($rc ne 1) {
+ die "FAILED! OutputCheck=$test->{OutputCheck}, input=$output\n";
+ }
+ }
+ }
+ return 0;
+}
+
+my $livepatch_result = livepatch_test();
+print("Livepatch test returned $livepatch_result");
+exit $livepatch_result;