]> xenbits.xensource.com Git - people/aperard/osstest.git/commitdiff
TestSupport: Implement target_subunit_cmd a subunit stream parser into substeps
authorAnthony PERARD <anthony.perard@citrix.com>
Tue, 4 Jul 2017 11:19:19 +0000 (12:19 +0100)
committerAnthony PERARD <anthony.perard@citrix.com>
Mon, 23 Oct 2017 15:50:35 +0000 (16:50 +0100)
target_subunit_cmd can be used like target_cmd, but the command would
needs to output a subunit v1 stream, which will be parsed and turned
into osstest substeps. The command can be `| subunit-2to1` in order to
turn a subunit v2 stream into v1.

Currently, time is not taken into account, and all substeps will have
bogus timestamp as the output of the command is parsed after it has
runned.

This is a description of the subunit v1 protocol, taken from
python-subunit README, or https://pypi.python.org/pypi/python-subunit

test|testing|test:|testing: test LABEL
success|success:|successful|successful: test LABEL
success|success:|successful|successful: test LABEL DETAILS
failure: test LABEL
failure: test LABEL DETAILS
error: test LABEL
error: test LABEL DETAILS
skip[:] test LABEL
skip[:] test LABEL DETAILS
xfail[:] test LABEL
xfail[:] test LABEL DETAILS
uxsuccess[:] test LABEL
uxsuccess[:] test LABEL DETAILS
progress: [+|-]X
progress: push
progress: pop
tags: [-]TAG ...
time: YYYY-MM-DD HH:MM:SSZ

LABEL: UTF8*
NAME: UTF8*
DETAILS ::= BRACKETED | MULTIPART
BRACKETED ::= '[' CR UTF8-lines ']' CR
MULTIPART ::= '[ multipart' CR PART* ']' CR
PART ::= PART_TYPE CR NAME CR PART_BYTES CR
PART_TYPE ::= Content-Type: type/sub-type(;parameter=value,parameter=value)
PART_BYTES ::= (DIGITS CR LF BYTE{DIGITS})* '0' CR LF

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
Osstest/TestSupport.pm

index 69f168128e2931772f0d818f6bc805edfb17e8b7..2c1bf0697a093bf85064c9d3a9ac3fa3bf9fab54 100644 (file)
@@ -55,7 +55,7 @@ BEGIN {
 
                       target_cmd_root_status target_cmd_output_root_status
                       target_cmd_root target_cmd target_cmd_build
-                      target_cmd_stashed
+                      target_cmd_stashed target_subunit_cmd
                       target_cmd_output_root target_cmd_output
                       target_cmd_inputfh_root sshuho
                       target_getfile target_getfile_root
@@ -775,6 +775,119 @@ sub target_cmd_stashed ($$$;$$) {
     return "$stash/$$leafref";
 }
 
+sub subunit_result_to_osstest_result ($) {
+    my ($ret) = @_;
+    return "pass" if $ret eq "success" or $ret eq "successful";
+    return "fail" if $ret eq "failure";
+    return "skip" if $ret eq "skip";
+    return "fail" if $ret eq "error";
+    # expected failure
+    return "pass" if $ret eq "xfail";
+    # unexpected success
+    return "fail" if $ret eq "uxsuccess";
+    die "subunit_result_to_osstest_result unexpected result $ret";
+}
+sub subunit_sanitize_testname ($) {
+    my ($testname) = @_;
+    $testname =~ s'[^_.()\[\]/~0-9a-zA-Z-]'_'g;
+    return $testname;
+}
+
+# Like target_cmd, but parse the command output as a subunit v1 stream and make
+# a substep out of each subunit test.
+sub target_subunit_cmd ($$;$$) {
+    my ($tho,$tcmd,$timeout,$extrasshopts) = @_;
+    my $filename = "subunit-output";
+    my $path = target_cmd_stashed($tho, \$filename, $tcmd, $timeout,
+        $extrasshopts);
+
+    open my $stdout, "$path" or die "$path: $!";
+
+    my $logfilename = undef;
+    my $fh = undef;
+
+    while (<$stdout>) {
+        if (/^time: \d+-\d+-\d+ \d+:\d+:\d+(?:\.\d+)?Z$/) {
+            # This is the timestamp for the next events
+        } elsif (/^test(?:ing)?:? (.+)\n/) {
+            # Start of a new test.
+            my $testname = subunit_sanitize_testname($1);
+            $logfilename = 'subunit-' . $testname . '.log';
+            $fh = open_unique_stashfile(\$logfilename);
+            substep_start('/' . $testname, $logfilename);
+        } elsif (/^(success(?:ful)?|failure|skip|error|xfail|uxsuccess):
+                   \ (.+?)(\ \[(\ multipart)?)?$/x) {
+            # Result of a test, with its output.
+            my ($result, $testname, $have_details, $is_multipart) =
+                ($1,$2,$3,$4);
+
+            if ($have_details) {
+                if ($is_multipart) {
+                    # Test output
+                    while (<$stdout>) {
+                        if (m{^content-type:}i) {
+                            print $fh $_ or die $!;
+
+                            # part name
+                            my $line = <$stdout>;
+                            print $fh $line or die $!;
+
+                            # Read chunks of a part
+                            while (<$stdout>) {
+                                if (/^([0-9A-F]+)\r$/i) {
+                                    # The chunk size is in hex even though this
+                                    # does not seem to be documented in the
+                                    # subunit protocol description.
+                                    my $chunk_size = hex($1);
+                                    my $chunk;
+
+                                    last if $chunk_size == 0;
+                                    read $stdout, $chunk, $chunk_size;
+                                    print $fh $chunk or die $!;
+                                } else {
+                                    # Unexpected output, was expecting a chunk
+                                    # size.
+                                    chomp;
+                                    logm("*** $_");
+                                    # Drop back to multipart "test output"
+                                    # parser, which is more likely to find
+                                    # a line that match.
+                                    last;
+                                }
+                            }
+                        } elsif (/^\]$/) {
+                            last;
+                        } else {
+                            # Unexpected output in multipart parser
+                            chomp;
+                            logm("*** $_");
+                        }
+                    }
+                } else {
+                    # Simple non-multipart test output.
+                    while (<$stdout>) {
+                        last if (/^\]$/);
+                        print $fh $_ or die $!;
+                    }
+                }
+            }
+            close $fh or die $!;
+            substep_finish("/" .subunit_sanitize_testname($testname),
+                subunit_result_to_osstest_result($result));
+        } elsif (/^tags: .+/) {
+            # unused
+        } elsif (/^progress: (?:[+-]?\d+|push|pop)$/) {
+            # unused
+        } else {
+            # Unexpected output
+            chomp;
+            logm("*** $_");
+        }
+    }
+
+    close $stdout or die $!;
+}
+
 sub poll_loop ($$$&) {
     my ($maxwait, $interval, $what, $code) = @_;
     # $code should return undef when all is well