From: Michal Sojka Date: Wed, 15 Aug 2012 13:56:23 +0000 (+0200) Subject: Copy several helper scripts from NUL project repository X-Git-Url: https://rtime.felk.cvut.cz/gitweb/wvtest.git/commitdiff_plain/c558d767f03464e93473f20738df27e3f34e761e Copy several helper scripts from NUL project repository --- diff --git a/tools/wvformat b/tools/wvformat new file mode 100755 index 0000000..7b3d5d9 --- /dev/null +++ b/tools/wvformat @@ -0,0 +1,174 @@ +#!/usr/bin/perl -w +# +# WvTest: +# Copyright (C)2007-2009 Versabanq Innovations Inc. and contributors. +# Licensed under the GNU Library General Public License, version 2. +# See the included file named LICENSE for license information. +# +use strict; +use Getopt::Long; + +sub usage() { + print STDERR "Usage: $0 [-vs] \n"; + exit 127; +} + +my ($verbose, $summary, $limit_lines); + +GetOptions ( + "verbose|v" => \$verbose, + "summary|s" => \$summary, + "before-failure|b=i" => \$limit_lines, + ) or usage(); + +my $istty = -t STDOUT && $ENV{'TERM'} ne "dumb"; +my $columns; +if ($istty) { + $columns = `tput cols`; +} else { + $columns = $ENV{'COLUMNS'} || 80; +} + +my @log = (); +my ($file, $sect); +my ($gpasses, $gfails) = (0,0); +my ($lpasses, $lfails) = (0,0); +my ($tpasses, $tfails) = (0,0); + +sub colourize($$) +{ + my ($column, $result) = @_; + my $pass = ($result eq "ok"); + + my $begcolour = $istty ? ($pass ? "\e[32;1m" : "\e[31;1m") : ""; + my $endcolour = $istty ? "\e[0m" : ""; + + my $dots = $columns - 15 - $column%$columns; + $dots += $columns if ($dots < 0); + my $leader = "."x$dots; + return "$begcolour$leader $result$endcolour"; +} + +sub highlight($) +{ + my $msg = shift; + my $begcolour = $istty ? "\e[1m\e[4m" : ""; + my $endcolour = $istty ? "\e[0m" : ""; + return "$begcolour$msg$endcolour"; +} + +sub resultline($$$) +{ + my ($prefix, $name, $result) = @_; + if (!defined $prefix) { $prefix = ''; } + return sprintf("$prefix! %s %s", $name, colourize(length($prefix)+2+length($name)+1, $result)); +} + +sub print_and_clear_log() +{ + print "v"x($columns-1) . "\n"; # Top marker + my @log2print = (); + my $skipped = 0; + foreach (@log) { + if (/^(\([0-9]+\) (# )?)?!\s*(.*?)\s+(\S+)\s*$/) { + my ($prefix, $name, $result) = ($1, $3, $4); + push @log2print, resultline($prefix, $name, $result); + if ($result ne "ok") { + print "wvformat: skipped $skipped lines\n" if $skipped; + print join("\n", @log2print) . "\n"; + @log2print = (); + $skipped = 0; + } + } else { + push @log2print, $_; + } + while (defined $limit_lines && scalar(@log2print) > $limit_lines) { + shift @log2print; + $skipped++; + } + } + print "wvformat: skipped $skipped lines\n" if $skipped; + print join("\n", @log2print) . "\n"; + print "^"x($columns-1) . "\n"; # Bottom marker + @log = (); +} + +sub print_test_summary() +{ + if (!$verbose) { + print(resultline("", sprintf("%s %s [%d/%d]", $file, $sect, $lpasses, $lpasses+$lfails), + $lfails ? "FAILED" : "ok") . "\n"); + print_and_clear_log() if ($lfails && !$summary); + } + if ($lfails) { $tfails++; } + else { $tpasses++; } + ($lpasses, $lfails) = (0,0); +} + +my $startup = 1; + +while (<>) +{ + chomp; + s/\r//g; + + if (/^(\([0-9]+\) )?\s*Testing "(.*)" in (.*):\s*$/) + { + print_test_summary() unless $startup; + $startup = 0; + (undef, $sect, $file) = ($1, $2, $3); + @log = (); + if ($verbose) { + print highlight($_), "\n"; + } + } + elsif (/^(\([0-9]+\) (# )?)?!\s*(.*?)\s+(\S+)\s*$/) + { + my ($prefix, $name, $result) = ($1, $3, $4); + + if ($startup) { + $file = ""; + $sect = "Startup"; + $startup = 0; + } + + if ($verbose) { + print resultline($prefix, $name, $result) . "\n"; + } else { + push @log, $_; + } + + if ($result eq "ok") { + $gpasses++; + $lpasses++; + } else { + $gfails++; + $lfails++; + } + } + else + { + if ($verbose) { + print "$_\n"; + } else { + push @log, $_; + } + } +} +if (!$verbose) { + if (!$startup) { + print_test_summary(); + } else { + print_and_clear_log(); + } +} + +my $gtotal = $gpasses+$gfails; +my $ttotal = $tpasses+$tfails; +printf("\nSummary: %d test%s, %d failure%s". + "\n %d check%s, %d failure%s\n", + $ttotal, $ttotal==1 ? "" : "s", + $tfails, $tfails==1 ? "" : "s", + $gtotal, $gtotal==1 ? "" : "s", + $gfails, $gfails==1 ? "" : "s"); +exit(0); diff --git a/tools/wvnulrun b/tools/wvnulrun new file mode 100755 index 0000000..6ecc4e5 --- /dev/null +++ b/tools/wvnulrun @@ -0,0 +1,215 @@ +#!/usr/bin/perl -w +# +# @file +# Script to supervise the execution of wvtest-based tests. +# +# It takes care of killing test (qemu or serial reader) when the test +# finishes or hangs. +# +# Copyright (C) 2011, 2012, Michal Sojka +# Economic rights: Technische Universitaet Dresden (Germany) +# +# This file is part of NUL (NOVA user land). +# +# NUL is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License version +# 2 as published by the Free Software Foundation. +# +# NUL 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 version 2 for more details. +#/ + +use strict; +use IO::Pty; + +# always flush +$| = 1; + +my $istty = -t STDOUT && $ENV{'TERM'} ne "dumb"; +my $pty = new IO::Pty; + +my $pid = fork(); +die "Cannot fork" if not defined $pid; +if (!$pid) { + # child + $pty->make_slave_controlling_terminal(); +# setpgrp(); # Terminal won't send signals to the child + my $slave = $pty->slave(); + close $pty; + $slave->clone_winsize_from(\*STDIN) if $istty; + $slave->set_raw(); + + open(STDIN,"<&". $slave->fileno()) + or die "Couldn't reopen STDIN for reading, $!\n"; + open(STDOUT,">&". $slave->fileno()) + or die "Couldn't reopen STDOUT for writing, $!\n"; + open(STDERR,">&". $slave->fileno()) + or die "Couldn't reopen STDERR for writing, $!\n"; + + close $slave; + + exec(@ARGV); + die "Cannot exec(@ARGV): $!"; +} + +$pty->close_slave(); +#$pty->set_raw(); # from IO::Pty "try" script. Do we need this? + +sub winch { + $pty->slave->clone_winsize_from(\*STDIN); + kill WINCH => $pid if $pid; + $SIG{WINCH} = \&winch; +} + +$SIG{WINCH} = \&winch if $istty; + +sub bigkill($) +{ + my $pid = shift; + ($pid > 0) || die("pid is '$pid'?!\n"); + my $count; + local $SIG{CHLD} = sub { }; # this will wake us from sleep() faster + $count = kill -15, $pid; + sleep(2); + + kill -9, $pid if ($pid > 1); + + exit(125); +} + +my $timeout = 100; + +# parent +local $SIG{INT} = sub { bigkill($pid); }; +local $SIG{TERM} = sub { bigkill($pid); }; +local $SIG{ALRM} = sub { + print STDERR "! $0: Alarm timed out! No test output for $timeout seconds. FAILED\n"; + bigkill($pid); +}; + +my $allstart = time(); +my ($start, $stop); +my $tests_executed = 0; +my $tests_failed = 0; +my $waits_for_child = 0; +my $kill_ok = 0; +my $ignore_exit_patterns = 0; +my $expected_test_count = 0; +my $expected_test_base = 0; + +sub matches_exit_pattern($) +{ + return 0 if $ignore_exit_patterns; + if ($ENV{WVTEST_EXIT_PATTERN}) { + return /$ENV{WVTEST_EXIT_PATTERN}/ + } else { + return + (/sc: done.$/ && $waits_for_child) || + /resetting machine via method/ || + /wvtest: done\s*$/ || + / # .*System halted. *$/ + ; + } +} + +sub check_number_of_tests() +{ + if ($expected_test_count) { + my $executed = $tests_executed - $expected_test_base; + my $result; + if ($executed == $expected_test_count) { + $result = "ok"; + } else { + $result = "FAILED"; + $tests_failed++; + print "Expected $expected_test_count tests, executed $executed tests.\n" + } + print "! $0: tests_expected == tests_executed $result\n"; + } +} + +my $wvtest_output; +if ($ENV{WVTEST_OUTPUT}) { + open $wvtest_output, ">", $ENV{WVTEST_OUTPUT}; +} + +alarm($timeout); +while (<$pty>) +{ + alarm($timeout); + print; + print $wvtest_output $_ if $wvtest_output; + chomp; + s/\r//g; + + if (/^(\([0-9]+\) (# )?)?!\s*(.*?)\s+(\S+)\s*$/) { + $tests_executed++; + $tests_failed++ if ($4 ne "ok"); + } + elsif (/wvtest: timeout (\d+)\s*$/) { + $timeout=$1; + alarm($timeout); + } + elsif (/sc: wait for child/) { $waits_for_child = 1; } + elsif (/wvtest: ignore exit patterns/) { $ignore_exit_patterns = 1; } + elsif (/wvtest: expect ([0-9]+) tests/) { + check_number_of_tests(); + $expected_test_count = $1; + $expected_test_base = $tests_executed; + } + elsif (matches_exit_pattern($_)) + { + if (!$ENV{WVTEST_NOKILL}) { + kill 15, $pid; # Kill novaboot or qemu + $kill_ok = 1; + } else { + use POSIX; + my $pid = fork(); + if ($pid) { + print "Keeping PID $pid alive\n"; + print $wvtest_output "Keeping PID $pid alive\n" if $wvtest_output; + close($wvtest_output) if $wvtest_output; + POSIX::_exit(0); + } else { + # Continue printing on background + close($wvtest_output) if $wvtest_output; + while (<$pty>) { print; } + POSIX::_exit(0); + } + } + } +} +my $newpid = waitpid($pid, 0); +if ($newpid != $pid) { + die("waitpid returned '$newpid', expected '$pid'\n"); +} + +my $code = $?; +my $ret = ($code >> 8); + +if ($code && !$ret) { + if ($kill_ok && $code == 15) { + # We have killed the child - it is OK + $code = 0; + } else { + # return death-from-signal exits as >128. This is what bash does if you ran + # the program directly. + $ret = $code | 128; + } +} + +if ($ret != 0) { + print "! $0: Program '", join(" ", @ARGV), "' returned non-zero exit code ($ret) FAILED\n"; +} + +if (!$ENV{WVTEST_EXIT_PATTERN}) { + printf "! $0: \$tests_executed > 0 %s\n", ($tests_executed > 0) ? "ok" : "FAILED"; +} +check_number_of_tests(); + +if ($tests_failed > 0) { $ret = 1; } +if ($ret == 0 && $tests_executed == 0) { $ret = 1; } + +exit $ret; diff --git a/tools/wvperf2html.py b/tools/wvperf2html.py new file mode 100755 index 0000000..26fb433 --- /dev/null +++ b/tools/wvperf2html.py @@ -0,0 +1,332 @@ +#!/usr/bin/env python + +import sys +import re +import os +import os.path +import string +import time +import numpy as np + +class Axis: + def __init__(self, name=None, units=None): + self.name = name + self.units = units + self.num = None + def getLabel(self): + if self.units and self.name: + return "%s [%s]" % (self.name, self.units) + elif self.units: + return self.units + else: + return self.name + def __repr__(self): return "Axis(name=%s units=%s, id=%s)" % (self.name, self.units, hex(id(self))) + +class Column: + def __init__(self, name, units, axis): + self.name = name + self.units = units + self.axis = axis + def __repr__(self): return "Column(name=%s units=%s axis=%s)" % (self.name, self.units, repr(self.axis)) + +class Row(dict): + def __init__(self, graph, date): + self.graph = graph + self.date = date + def __getitem__(self, column): + try: + return dict.__getitem__(self, column) + except KeyError: + return None + def getDate(self): + d = time.gmtime(time.mktime(self.date)) + return "Date.UTC(%s, %s, %s, %s, %s, %s)" % \ + (d.tm_year, d.tm_mon-1, d.tm_mday, d.tm_hour, d.tm_min, d.tm_sec) + +class Graph: + def __init__(self, id, title): + self.columns = {} + self.columns_ordered = [] + self.id = id + self.title = title + self.rows = [] + self.date2row = {} + self.axes = {} + self.axes_ordered = [] + + def __getitem__(self, date): + try: + rownum = self.date2row[date] + except KeyError: + rownum = len(self.rows) + self.date2row[date] = rownum + try: + return self.rows[rownum] + except IndexError: + self.rows[rownum:rownum] = [Row(self, date)] + return self.rows[rownum] + + def addValue(self, date, col, val, units): + row = self[date] + row[col] = val + if not self.columns.has_key(col): + axis=self.getAxis(units) or self.addAxis(units, Axis(units=units)) + column = Column(col, units, axis) + self.columns[col] = column + self.columns_ordered.append(column) + else: + column = self.columns[col] + self.columns_ordered.remove(column) + self.columns_ordered.append(column) + self.columns[col].units=units + self.columns[col].axis.units=units + + def addAxis(self, key, axis): + self.axes[key] = axis + return axis + + def setAxis(self, col, key): + self.columns[col].axis = self.getAxis(key) or self.addAxis(key, Axis(name=key)) + + def getAxis(self, key): + if not self.axes.has_key(key): return None + return self.axes[key] + + def findRanges(self): + for axis in self.axes.values(): + cols = [col for col in self.columns.values() if col.axis == axis] + low = None + high = None + all_in_range = True + for col in cols: + values = np.array([row[col.name] for row in self.rows if row[col.name] != None], np.float64) + if low == None and high == None: + lastmonth = values[-30:] + median = np.median(lastmonth) + low = median * 0.95 + high = median * 1.05 + + if (values > high).any() or (values < low).any(): + all_in_range = False + if all_in_range: + axis.yrange_max = high + axis.yrange_min = low + else: + axis.yrange_max = None + axis.yrange_min = None + + def fixupAxisNumbers(self): + # Sort axes according to the columns and number them + num = 0 + for column in self.columns_ordered: + axis = column.axis + if axis not in self.axes_ordered: + self.axes_ordered.insert(0, axis) + axis.num = num + num += 1 + num = 0 + for axis in self.axes_ordered: + axis.num = num + num += 1 + + def jschart(self): + print """ + window.chart = new Highcharts.StockChart({ + chart: { + renderTo: '"""+self.id+"""' + }, + + rangeSelector: { + selected: 1 + }, + + title: { + text: '"""+self.title+"""' + }, + legend: { + enabled: true, + floating: false, + verticalAlign: "top", + x: 100, + y: 60, + }, + tooltip: { + formatter: function() { + var s = ''+ Highcharts.dateFormat('%a, %d %b %Y %H:%M:%S', this.x) +'
'; + s += commitMap[this.x].msg; + $.each(this.points, function(i, point) { + s += '
'+ point.series.name +': '+point.y; + }); + return s; + }, + style: { + whiteSpace: 'normal', + width: '400px', + }, + }, + plotOptions: { + series: { + events: { + click: function(event) { + var lastpoint = null; + for (var i in this.data) { + if (event.point == this.data[i]) { + if (i > 0) lastpoint = this.data[i-1]; + break; + } + } + if (lastpoint) + window.location = "http://os.inf.tu-dresden.de/~jsteckli/cgi-bin/cgit.cgi/nul/log/?qt=range&q="+commitMap[lastpoint.x].hash+'..'+commitMap[event.point.x].hash; + else + window.location = "http://os.inf.tu-dresden.de/~jsteckli/cgi-bin/cgit.cgi/nul/log/?id="+commitMap[event.point.x].hash; + } + } + } + }, + yAxis: [""" + for axis in self.axes_ordered: + print "\t\t\t\t{" + print "\t\t\t\t\tlineWidth: 1," + print "\t\t\t\t\tlabels: { align: 'right', x: -3 }," + print "\t\t\t\t\ttitle: { text: '%s' }," % axis.getLabel() + #print "\t\t\t\t\tplotBands: { from: %s, to: %s, color: '#eee' }," % (col.low, col.high) + if axis.yrange_min: print "\t\t\t\t\tmin: %s," % axis.yrange_min + if axis.yrange_max: print "\t\t\t\t\tmax: %s," % axis.yrange_max + print "\t\t\t\t}," + print """\t\t\t ], + + series: [""" + num = 0 + for col in self.columns_ordered: + print "\t\t\t\t{ name: '%s [%s]', yAxis: %d, data: [" % (col.name, col.units, col.axis.num) + num += 1 + for row in self.rows: + val = row[col.name] + if val == None: val = "null" + print "\t\t\t\t\t[%s, %s], " % (row.getDate(), val) + print "\t\t\t\t]}," + print """\t\t\t ], + });""" + +class Graphs(dict): + pass + +graphs = Graphs() +commits = {} + +re_date = re.compile('^Date: (.*)') +re_testing = re.compile('^(\([0-9]+\) (# )?)?\s*Testing "(.*)" in (.*):\s*$') +re_commit = re.compile('.*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*, commit: (.*)') +re_commithash = re.compile('([0-9a-f]{7})(-dirty)? \(') +re_check = re.compile('^(\([0-9]+\) (# )?)?!\s*(.*?)\s+(\S+)\s*$') +re_perf = re.compile('^(\([0-9]+\) (# )?)?!\s*(.*?)\s+PERF:\s*(.*?)\s+(\S+)\s*$') +re_perfaxis = re.compile('axis="([^"]+)"') + +date = time.localtime(time.time()) + +for line in sys.stdin.readlines(): + line = line.rstrip() + + match = re_date.match(line) + if (match): + date = time.strptime(match.group(1), "%a, %d %b %Y %H:%M:%S +0200") + continue + + match = re_testing.match(line) + if match: + what = match.group(3) + where = match.group(4) + + match = re_commit.match(what) + if match: + date = time.strptime(match.group(1), "%Y-%m-%d %H:%M:%S") + commit = match.group(2) + match = re_commithash.search(commit); + if match: + commithash = match.group(1) + else: + commithash = None + commits[date] = (commit, commithash) + + (basename, ext) = os.path.splitext(os.path.basename(where)) + + if what != "all": title = what + else: title = basename + try: + graph = graphs[basename] + except KeyError: + graph = Graph(basename, title) + graphs[basename] = graph + continue + + match = re_perf.match(line) + if match: + perfstr = match.group(4) + perf = perfstr.split() + col = perf[0] + try: + val = float(perf[1]) + except ValueError: + val = None + try: + units = perf[2] + if '=' in units: units = None + except: + units = None + if match.group(5) != "ok": + val=None + + graph.addValue(date, col, val, units) + + match = re_perfaxis.search(perfstr) + if match: + graph.setAxis(col, match.group(1)); + +graphs = [g for g in graphs.values() if len(g.columns)] +graphs = sorted(graphs, key=lambda g: g.title.lower()) + +for g in graphs: + g.findRanges() + g.fixupAxisNumbers() + +print """ + + + + + NUL Performance Plots + + + + + + +

NUL Performance Plots

+ +
    +""" +for graph in graphs: + print "
  • %s
  • " % (graph.title, graph.title) +print "
" +for graph in graphs: + print "

%s

" % (graph.title, graph.title) + print '
' % graph.id +print """ + + +""" + +# Local Variables: +# compile-command: "cat nul-nightly/nul_*.log|./wvperfpreprocess.py|./wvperf2html.py > graphs.html" +# End: diff --git a/tools/wvtest2html.py b/tools/wvtest2html.py new file mode 100755 index 0000000..ff0deba --- /dev/null +++ b/tools/wvtest2html.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python + +import sys +import re +import os +import os.path +import string +import time +import numpy as np +import cgi + +re_date = re.compile('^Date: (.*)') +re_testing = re.compile('^(\([0-9]+\) (# )?)?\s*Testing "(.*)" in (.*):\s*$') +re_commit = re.compile('.*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*, commit: (.*)') +re_commithash = re.compile('([0-9a-f]{7})(-dirty)? \(') +re_check = re.compile('^(\([0-9]+\) (# )?)?!\s*(.*?)\s+(\S+)\s*$') +re_perf = re.compile('^(\([0-9]+\) (# )?)?!\s*(.*?)\s+PERF:\s*(.*?)\s+(\S+)\s*$') +re_perfaxis = re.compile('axis="([^"]+)"') + +date = time.localtime(time.time()) + +class Test: + def __init__(self, what, where): + self.what = what + self.where = where + self.output = [] + self.status = 'ok' + self.check_count = 0 + self.failures = 0 + self.num = None + + def add_line(self, line): + self.output.append(line) + match = re_check.match(line) + if match: + self.check_count += 1 + result = match.group(4) + if result != "ok": + self.status = result + self.failures += 1 + def title(self): + if self.what == "all": + title = self.where + else: + title = '%s (%s)' % (self.what, self.where) + return title + + def printSummaryHtml(self, file): + if self.status == "ok": status_class="ok" + else: status_class = "failed" + file.write("%d.%s" + % (status_class, self.num, self.num, cgi.escape(self.title()))) + file.write("%s\n" % (cgi.escape(self.status))) + + def printDetailHtml(self, file): + file.write("""\ + + + + +NUL Test Report + + + + +

NUL Test Report

+%s +

%d. %s

+ +""" % (date_and_commit, self.num, cgi.escape(self.title()))) + for line in self.output: + match = re_check.match(line) + if match: + result = match.group(4) + if result == "ok": + status_class = "ok" + else: + status_class = "failed" + linestatus = " status-%s" % status_class + resultstatus = " class='status-%s'" % status_class + else: + linestatus = '' + resultstatus = '' + result = '' + + file.write("%s\n" % \ + (linestatus, cgi.escape(line), resultstatus, cgi.escape(result))) + file.write("
%s
") + +tests = [] +test = None + +for line in sys.stdin.readlines(): + line = line.rstrip() + + match = re_date.match(line) + if (match): + date = time.strptime(match.group(1), "%a, %d %b %Y %H:%M:%S +0200") + continue + + match = re_testing.match(line) + if match: + what = match.group(3) + where = match.group(4) + + test = Test(what, where) + tests.append(test) + + match = re_commit.match(what) + if match: + date = time.strptime(match.group(1), "%Y-%m-%d %H:%M:%S") + commit = match.group(2) + match = re_commithash.search(commit); + if match: + commithash = match.group(1) + else: + commithash = None + continue + + if test: test.add_line(line) + +tests_nonempty = [t for t in tests if t.check_count > 0] +num = 1 +for t in tests_nonempty: + t.num = num + num += 1 + +try: + date_and_commit = (time.strftime("%a, %d %b %Y %H:%M:%S +0000", date) + " " + commit) +except: + date_and_commit = time.strftime("%a, %d %b %Y %H:%M:%S %Z") + pass + +targetDir = sys.argv[1] +if not os.path.isdir(targetDir): + os.mkdir(targetDir) + +wvtest_css = open(os.path.join(targetDir, "wvtest.css"), 'w') +wvtest_css.write("""\ +table { + border: solid 1px black; + max-width: 100%%; +} +.status-ok { background: lightgreen; } +.status-failed { background: red; } +.testnum { text-align: right; } +.outputrow { display: none; } +.output { width: 100%%; } +.outputline { white-space: pre-wrap; font-family: monospace; } +.testheader { font-weight: bold; } +""") +wvtest_css.close() + +index_html = open(os.path.join(targetDir, "index.html"), 'w') + +index_html.write("""\ + + + + +NUL Test Report + + + + +

NUL Test Report

+%s + +""" % date_and_commit) +for test in tests_nonempty: + test.printSummaryHtml(index_html) +index_html.write("""\ +
+ + +""") + +for test in tests_nonempty: + f = open(os.path.join(targetDir, "test%d.html" % test.num), 'w') + test.printDetailHtml(f) + f.close() + + +# Local Variables: +# compile-command: "cat $(ls nul-nightly/nul_*.log|tail -n 1)|./wvtest2html.py html" +# End: diff --git a/tools/wvwrap b/tools/wvwrap new file mode 100755 index 0000000..189481d --- /dev/null +++ b/tools/wvwrap @@ -0,0 +1,43 @@ +#!/usr/bin/perl -w +# +# WvTest: +# Copyright (C) 2007-2009 Versabanq Innovations Inc. and contributors. +# Copyright (C) 2012 Michal Sojka +# Licensed under the GNU Library General Public License, version 2. +# See the included file named LICENSE for license information. +# +use strict; +use Getopt::Long; + +sub usage() { + print STDERR "Usage: $0 < wvtest.log\n"; + exit 127; +} + +usage() if (@ARGV > 0); + +my $istty = -t STDOUT && $ENV{'TERM'} ne "dumb"; +my $columns; +if ($istty) { + $columns = `tput cols`; +} else { + $columns = $ENV{'COLUMNS'} || 80; +} + +$| = 1; + +while (<>) +{ + chomp; + s/\r//g; + + if (/^(\([0-9]+\) (# )?)?!\s*(.*?)\s+(\S+)\s*$/) { + my $line = $_; + do { + print substr($line, 0, $columns) . "\n"; + $line = length($line) > $columns ? substr($line, $columns) : ''; + } while (length($line)); + } else { + print "$_\n"; + } +}