selftest: Replace perl subunit formatter with python subunit formatter,
authorJelmer Vernooij <jelmer@samba.org>
Mon, 29 Mar 2010 22:30:52 +0000 (00:30 +0200)
committerJelmer Vernooij <jelmer@samba.org>
Mon, 29 Mar 2010 23:08:20 +0000 (01:08 +0200)
so we can leverage the work happening in python-subunit.

selftest/format-subunit
selftest/output/plain.pm [deleted file]
selftest/subunithelper.py
source3/Makefile.in
source4/selftest/config.mk

index 1967fb4f852ba654eb4b2f85dc2e4c42d547e3c3..8581dec6cb2b81190095212742d3779c91ae77f9 100755 (executable)
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+# vim: expandtab
 # Pretty-format subunit output
 # Copyright (C) 2008-2010 Jelmer Vernooij <jelmer@samba.org>
 # Published under the GNU GPL, v3 or later
@@ -9,24 +10,179 @@ import sys
 
 import subunithelper
 
+class PlainFormatter(object):
+
+    def __init__(self, summaryfile, verbose, immediate, statistics, totaltests=None):
+        self.verbose = verbose
+        self.immediate = immediate
+        self.statistics = statistics
+        self.start_time = None
+        self.test_output = {}
+        self.suitesfailed = []
+        self.suites_ok = 0
+        self.skips = {}
+        self.summaryfile = summaryfile
+        self.index = 0
+        self.NAME = None
+        self.totalsuites = totaltests
+
+    def testsuite_count(self, count):
+        self.totalsuites = count
+
+    def report_time(self, time):
+        if self.start_time is None:
+            self.start_time = time
+        self.last_time = time
+
+    def start_testsuite(self, name):
+        self.index += 1
+        self.NAME = name
+        self.START_TIME = self.last_time
+
+        duration = self.START_TIME - self.start_time
+
+        if not self.verbose:
+            self.test_output[name] = "" 
+
+        out = ""
+        out += "[%d" % self.index
+        if self.totalsuites is not None:
+            out += "/%d" % self.totalsuites
+        out += " in %ds" % duration
+        if self.suitesfailed:
+            out += ", %d errors" % (len(self.suitesfailed),)
+        out += "] %s" % name 
+        if self.immediate:
+            sys.stdout.write(out + "\n")
+        else:
+            sys.stdout.write(out + ": ")
+
+    def output_msg(self, output):
+        if self.verbose:
+            sys.stdout.write(output)
+        elif self.NAME is not None:
+            self.test_output[self.NAME] += output
+        else:
+            sys.stdout.write(output)
+
+    def control_msg(self, output):
+        #$self->output_msg($output)
+        pass
+
+    def end_testsuite(self, name, result, reason):
+        out = ""
+        unexpected = 0
+
+        if not name in self.test_output:
+            print "no output for name[%s]" % name
+
+        if result in ("success", "xfail"):
+            self.suites_ok+=1
+        else:
+            self.output_msg("ERROR: Testsuite[%s]\nREASON: %s\n" % (name, reason))
+            self.suitesfailed.append(name)
+            if self.immediate and not self.verbose:
+                out += self.test_output[name]
+            unexpected = 1
+
+        if not self.immediate:
+            if not unexpected:
+                out += " ok\n"
+            else:
+                out += " " + result.upper() + "\n"
+
+        sys.stdout.write(out)
+
+    def start_test(self, testname):
+        pass
+
+    def end_test(self, testname, result, unexpected, reason):
+        append = ""
+
+        if not unexpected:
+            self.test_output[self.NAME] = ""
+            if not self.immediate:
+                sys.stdout.write({
+                    'failure': 'f',
+                    'xfail': 'X',
+                    'skip': 's',
+                    'success': '.'}.get(result, "?(%s)" % result))
+            return
+
+        reason = reason.strip()
+
+        append = "UNEXPECTED(%s): %s\nREASON: %s\n" % (result, testname, reason)
+
+        self.test_output[self.NAME] += append
+
+        if self.immediate and not self.verbose:
+            print self.test_output[self.NAME]
+            self.test_output[self.NAME] = ""
+
+        if not self.immediate:
+            sys.stdout.write({
+               'error': 'E',
+               'failure': 'F',
+               'success': 'S'}.get(result, "?"))
+
+    def summary(self):
+        f = open(self.summaryfile, 'w+')
+
+        if self.suitesfailed:
+            f.write("= Failed tests =\n")
+
+            for suite in self.suitesfailed:
+                f.write("== %s ==\n" % suite)
+                f.write(self.test_output[suite]+"\n\n")
+
+            f.write("\n")
+
+        if not self.immediate and not self.verbose:
+            for suite in self.suitesfailed:
+                print "==============================================================================="
+                print "FAIL: %s" % suite
+                print self.test_output[suite]
+                print ""
+
+        f.write("= Skipped tests =\n")
+        for reason in self.skips.keys():
+            f.write(reason + "\n")
+            for name in self.skips[reason]:
+                f.write("\t%s\n" % name)
+            f.write("\n")
+        f.close()
+
+        print "\nA summary with detailed information can be found in:\n  %s\n" % self.summaryfile
+
+        if not self.suitesfailed:
+            ok = self.statistics['TESTS_EXPECTED_OK'] + self.statistics['TESTS_EXPECTED_FAIL']
+            print "\nALL OK (%d tests in %d testsuites)" % (ok, self.suites_ok)
+        else:
+            print "\nFAILED (%d failures and %d errors in %d testsuites)" % (self.statistics['TESTS_UNEXPECTED_FAIL'], self.statistics['TESTS_ERROR'], len(self.suitesfailed))
+
+    def skip_testsuite(self, name, reason="UNKNOWN"):
+        self.skips.setdefault(reason, []).append(name)
+        if self.totalsuites:
+            self.totalsuites-=1
+
 parser = optparse.OptionParser("format-subunit [options]")
 parser.add_option("--verbose", action="store_true",
-       help="Be verbose")
+    help="Be verbose")
 parser.add_option("--immediate", action="store_true", 
-       help="Show failures immediately, don't wait until test run has finished")
+    help="Show failures immediately, don't wait until test run has finished")
 parser.add_option("--prefix", type="string", default=".",
-       help="Prefix to write summary to")
+    help="Prefix to write summary to")
 
 opts, args = parser.parse_args()
 
 statistics = {
-       'SUITES_FAIL': 0,
-       'TESTS_UNEXPECTED_OK': 0,
-       'TESTS_EXPECTED_OK': 0,
-       'TESTS_UNEXPECTED_FAIL': 0,
-       'TESTS_EXPECTED_FAIL': 0,
-       'TESTS_ERROR': 0,
-       'TESTS_SKIP': 0,
+    'SUITES_FAIL': 0,
+    'TESTS_UNEXPECTED_OK': 0,
+    'TESTS_EXPECTED_OK': 0,
+    'TESTS_UNEXPECTED_FAIL': 0,
+    'TESTS_EXPECTED_FAIL': 0,
+    'TESTS_ERROR': 0,
+    'TESTS_SKIP': 0,
 }
 
 msg_ops = PlainFormatter(os.path.join(opts.prefix, "summary"), opts.verbose, opts.immediate, statistics)
diff --git a/selftest/output/plain.pm b/selftest/output/plain.pm
deleted file mode 100644 (file)
index eae1e7a..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-#!/usr/bin/perl
-# Plain text output for selftest
-# Copyright (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-package output::plain;
-use Exporter;
-@ISA = qw(Exporter);
-
-use FindBin qw($RealBin);
-use lib "$RealBin/..";
-
-use strict;
-
-sub new($$$$$$$) {
-       my ($class, $summaryfile, $verbose, $immediate, $statistics, $totaltests) = @_;
-       my $self = { 
-               verbose => $verbose, 
-               immediate => $immediate, 
-               statistics => $statistics,
-               start_time => undef,
-               test_output => {},
-               suitesfailed => [],
-               suites_ok => 0,
-               skips => {},
-               summaryfile => $summaryfile,
-               index => 0,
-               totalsuites => $totaltests,
-       };
-       bless($self, $class);
-}
-
-sub testsuite_count($$)
-{
-       my ($self, $count) = @_;
-       $self->{totalsuites} = $count;
-}
-
-sub report_time($$)
-{
-       my ($self, $time) = @_;
-       unless ($self->{start_time}) {
-               $self->{start_time} = $time;
-       }
-       $self->{last_time} = $time;
-}
-
-sub output_msg($$);
-
-sub start_testsuite($$)
-{
-       my ($self, $name) = @_;
-
-       $self->{index}++;
-       $self->{NAME} = $name;
-       $self->{START_TIME} = $self->{last_time};
-
-       my $duration = $self->{START_TIME} - $self->{start_time};
-
-       $self->{test_output}->{$name} = "" unless($self->{verbose});
-
-       my $out = "";
-       $out .= "[$self->{index}";
-       if ($self->{totalsuites}) {
-               $out .= "/$self->{totalsuites}";
-       }
-       $out.= " in ".$duration."s";
-       $out .= sprintf(", %d errors", ($#{$self->{suitesfailed}}+1)) if ($#{$self->{suitesfailed}} > -1);
-       $out .= "] $name"; 
-       if ($self->{immediate}) {
-               print "$out\n";
-       } else {
-               print "$out: ";
-       }
-}
-
-sub output_msg($$)
-{
-       my ($self, $output) = @_;
-
-       if ($self->{verbose}) {
-               require FileHandle;
-               print $output;
-               STDOUT->flush();
-       } elsif (defined($self->{NAME})) {
-               $self->{test_output}->{$self->{NAME}} .= $output;
-       } else {
-               print $output;
-       }
-}
-
-sub control_msg($$)
-{
-       my ($self, $output) = @_;
-
-       #$self->output_msg($output);
-}
-
-sub end_testsuite($$$$)
-{
-       my ($self, $name, $result, $reason) = @_;
-       my $out = "";
-       my $unexpected = 0;
-
-       if (not defined($self->{test_output}->{$name})) {
-               print "no output for name[$name]\n";
-       }
-
-       if ($result eq "success" or $result eq "xfail") {
-               $self->{suites_ok}++;
-       } else {
-               $self->output_msg("ERROR: Testsuite[$name]\nREASON: $reason\n");
-               push (@{$self->{suitesfailed}}, $name);
-               if ($self->{immediate} and not $self->{verbose}) {
-                       $out .= $self->{test_output}->{$name};
-               }
-               $unexpected = 1;
-       }
-
-       if (not $self->{immediate}) {
-               unless($unexpected) {
-                       $out .= " ok\n";
-               } else {
-                       $out .= " " . uc($result) . "\n";
-               }
-       }
-
-       print $out;
-}
-
-sub start_test($$$)
-{
-       my ($self, $testname) = @_;
-}
-
-sub end_test($$$$$)
-{
-       my ($self, $testname, $result, $unexpected, $reason) = @_;
-       
-       my $append = "";
-
-       unless ($unexpected) {
-               $self->{test_output}->{$self->{NAME}} = "";
-               if (not $self->{immediate}) {
-                       if ($result eq "failure") { print "f"; }
-                       elsif ($result eq "xfail") { print "X"; }
-                       elsif ($result eq "skip") { print "s"; }
-                       elsif ($result eq "success") { print "."; }
-                       else { print "?($result)"; }
-               }
-               return;
-       }
-
-       chomp $reason;
-
-       $append = "UNEXPECTED($result): $testname\nREASON: $reason\n";
-
-       $self->{test_output}->{$self->{NAME}} .= $append;
-
-       if ($self->{immediate} and not $self->{verbose}) {
-               print $self->{test_output}->{$self->{NAME}};
-               $self->{test_output}->{$self->{NAME}} = "";
-       }
-
-       if (not $self->{immediate}) {
-               if ($result eq "error") { print "E"; } 
-               elsif ($result eq "failure") { print "F"; }
-               elsif ($result eq "success") { print "S"; }
-               else { print "?"; }
-       }
-}
-
-sub summary($)
-{
-       my ($self) = @_;
-
-       open(SUMMARY, ">$self->{summaryfile}");
-
-       if ($#{$self->{suitesfailed}} > -1) {
-               print SUMMARY "= Failed tests =\n";
-
-               foreach (@{$self->{suitesfailed}}) {
-                       print SUMMARY "== $_ ==\n";
-                       print SUMMARY $self->{test_output}->{$_}."\n\n";
-               }
-
-               print SUMMARY "\n";
-       }
-
-       if (not $self->{immediate} and not $self->{verbose}) {
-               foreach (@{$self->{suitesfailed}}) {
-                       print "===============================================================================\n";
-                       print "FAIL: $_\n";
-                       print $self->{test_output}->{$_};
-                       print "\n";
-               }
-       }
-
-       print SUMMARY "= Skipped tests =\n";
-       foreach my $reason (keys %{$self->{skips}}) {
-               print SUMMARY "$reason\n";
-               foreach my $name (@{$self->{skips}->{$reason}}) {
-                       print SUMMARY "\t$name\n";
-               }
-               print SUMMARY "\n";
-       }
-       close(SUMMARY);
-
-       print "\nA summary with detailed information can be found in:\n  $self->{summaryfile}\n";
-
-       if ($#{$self->{suitesfailed}} == -1) {
-               my $ok = $self->{statistics}->{TESTS_EXPECTED_OK} + 
-                                $self->{statistics}->{TESTS_EXPECTED_FAIL};
-               print "\nALL OK ($ok tests in $self->{suites_ok} testsuites)\n";
-       } else {
-               print "\nFAILED ($self->{statistics}->{TESTS_UNEXPECTED_FAIL} failures and $self->{statistics}->{TESTS_ERROR} errors in ". ($#{$self->{suitesfailed}}+1) ." testsuites)\n";
-       }
-
-}
-
-sub skip_testsuite($$$)
-{
-       my ($self, $name, $reason) = @_;
-
-       unless (defined($reason)) {
-               $reason = "UNKNOWN";
-       }
-       push (@{$self->{skips}->{$reason}}, $name);
-
-       if ($self->{totalsuites}) {
-               $self->{totalsuites}--;
-       }
-}
-
-1;
index 3cd0f013d03f6a2cea30881d6810c5a7f571c5b5..770b14befa46a1fed99722184f7357cc6124475e 100644 (file)
@@ -28,24 +28,29 @@ def parse_results(msg_ops, statistics, fh):
 
     while fh:
         l = fh.readline()
+        if l == "":
+            break
         if l.startswith("test: "):
             msg_ops.control_msg(l)
             name = l.split(":", 1)[1].strip()
             msg_ops.start_test(name)
             open_tests.append(name)
         elif l.startswith("time: "):
-            (year, month, day, hour, minute, second) = re.match(
-                "^time: (\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)\n/", l)
-            msg_ops.report_time(time.mktime(second, minute, hour, day, month-1, year-1900))
+            grp = re.match(
+                "^time: (\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)\n", l)
+            msg_ops.report_time(time.mktime((int(grp.group(1)), int(grp.group(2)), int(grp.group(3)), int(grp.group(4)), int(grp.group(5)), int(grp.group(6)), 0, 0, 0)))
         elif re.match("^(" + "|".join(VALID_RESULTS) + "): (.*?)( \[)?([ \t]*)( multipart)?\n", l):
             msg_ops.control_msg(l)
-            (result, testname, hasreason) = re.match("^(" + "|".join(VALID_RESULTS) + "): (.*?)( \[)?([ \t]*)( multipart)?\n", l)
+            grp = re.match("^(" + "|".join(VALID_RESULTS) + "): (.*?)( \[)?([ \t]*)( multipart)?\n", l)
+            (result, testname, hasreason) = (grp.group(1), grp.group(2), grp.group(3))
             if hasreason:
                 reason = ""
                 # reason may be specified in next lines
                 terminated = False
                 while fh:
                     l = fh.readline()
+                    if l == "":
+                        break
                     msg_ops.control_msg(l)
                     if l == "]\n":
                         terminated = True
@@ -58,6 +63,8 @@ def parse_results(msg_ops, statistics, fh):
                     msg_ops.end_test(testname, "error", 1, 
                                        "reason (%s) interrupted" % result)
                     return 1
+            else:
+                reason = None
             if result in ("success", "successful"):
                 open_tests.pop() #FIXME: Check that popped value == $testname 
                 statistics['TESTS_EXPECTED_OK']+=1
@@ -104,12 +111,6 @@ def parse_results(msg_ops, statistics, fh):
                    "was started but never finished!")
         statistics['TESTS_ERROR']+=1
 
-    # if the Filter module is in use, it will have the right counts
-    if 'total_error' in msg_ops:
-        statistics['TESTS_ERROR'] = msg_ops['total_error']
-        statistics['TESTS_UNEXPECTED_FAIL'] = msg_ops['total_fail']
-        statistics['TESTS_EXPECTED_FAIL'] = msg_ops['total_xfail']
-
     if statistics['TESTS_ERROR'] > 0:
         return 1
     if statistics['TESTS_UNEXPECTED_FAIL'] > 0:
index 9913806fe794d20db8ffec3655a07777e3b0be0c..97b3275e46fba5127ef137dd4d080505d7d09183 100644 (file)
@@ -3253,7 +3253,7 @@ selftest:: all torture timelimit
                --socket-wrapper $(TESTS) | \
                $(PERL) $(selftestdir)/filter-subunit.pl \
                --expected-failures=$(srcdir)/selftest/knownfail | \
-               $(PERL) $(selftestdir)/format-subunit --immediate
+               $(PYTHON) $(selftestdir)/format-subunit --immediate
 
 selftest-%:
        $(MAKE) selftest TESTS=$*
index 4359585e9e26553eee77bfcd74cb98513774a2fc..6057de68f9d77376c65ac234f6e0c1fb120487bd 100644 (file)
@@ -14,7 +14,7 @@ ST_DONE_TEST = @test -f $(selftest_prefix)/st_done || { echo "SELFTEST FAILED";
 SELFTEST_NOSLOW_OPTS = --exclude=$(srcdir)/selftest/slow
 SELFTEST_QUICK_OPTS = $(SELFTEST_NOSLOW_OPTS) --quick --include=$(srcdir)/selftest/quick
 FILTER_XFAIL = $(PERL) $(selftestdir)/filter-subunit.pl --expected-failures=$(srcdir)/selftest/knownfail
-SUBUNIT_FORMATTER ?= $(PERL) $(selftestdir)/format-subunit --prefix=${selftest_prefix} --immediate
+SUBUNIT_FORMATTER ?= $(PYTHON) $(selftestdir)/format-subunit --prefix=${selftest_prefix} --immediate
 FORMAT_TEST_OUTPUT = $(FILTER_XFAIL) | $(SUBUNIT_FORMATTER)
 
 test-subunit:: everything