[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [OSSTEST PATCH v14 19/24] TestSupport: Implement target_subunit_cmd a subunit stream parser into substeps



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@xxxxxxxxxx>
---

Notes:
    Changes in V14:
    - prepend '/' to substep names
    - prepend 'subunit-' to test log filename
    - cleanup
    - simple match of content-type
    - in the loop that parse chunks, if no chunk size are found, now drop
      back to the multipart parser.
    
    Changes in v13:
    - also parse multipart output
    - add every possible test result
    - use target_cmd_stashed

 Osstest/TestSupport.pm | 115 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 114 insertions(+), 1 deletion(-)

diff --git a/Osstest/TestSupport.pm b/Osstest/TestSupport.pm
index c5790da..47ade09 100644
--- a/Osstest/TestSupport.pm
+++ b/Osstest/TestSupport.pm
@@ -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
-- 
Anthony PERARD


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.