TAP-Formatter-JUnit-0.11000755001750001750 012413065544 14734 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/Build.PL000444001750001750 120212413065544 16360 0ustar00grahamgraham000000000000use strict; use warnings; use Module::Build; Module::Build->new( 'module_name' => 'TAP::Formatter::JUnit', 'license' => 'perl', 'dist_author' => 'Graham TerMarsch (cpan@howlingfrog.com)', 'requires' => { 'XML::Generator' => 0, 'TAP::Harness' => 3.12, 'Moose' => 0, 'MooseX::NonMoose' => 0, 'File::Slurp' => 0, }, 'build_requires' => { 'Test::More' => 0, 'IO::Scalar' => 0, 'IPC::Run' => 0, 'Test::XML' => 0, }, )->create_build_script(); TAP-Formatter-JUnit-0.11/Changes000444001750001750 760112413065544 16370 0ustar00grahamgraham000000000000Revision history for Perl extension TAP::Formatter::JUnit. 0.11 Wed Oct 1 13:26 PDT 2014 - Use "IPC::Run" instead of "IPC::Open2" in tests, to fix problems with tests freezing on Windows. 0.10 Mon Sep 29 12:38 PDT 2014 - Switch from "Test::Differences" to "Test::XML", to eliminate failures due to differences in ordering of XML attributes. Addresses RT#81552 - Use "File::Spec->null()" to get proper path to NULL. RT#81200, RT#82227. - Moved POD tests to "xt/" directory. - Move timing sensitive tests to "xt/" directory, as they can cause spurious failures for people. While _I_ want to make sure they run successfully, that's about my checking functionality as opposed to verifying if things will run successfully on your own system. RT#69777. 0.09 Wed Jan 25 15:13 PST 2012 - Switch from Class::Field to Moose. Thanks to Dave Lambley for the poke, and the patience. - Track and report timings for "(init)" and "(teardown)" of the test. Without this, Hudson does not properly report on the total time needed for a test suite (it calculates total time by adding up the constituent tests, not by looking at the "time" attribute). - Rewrite internals, switching from a streaming style to an iterative style of processing the TAP. Same results, but easier to work with. 0.08 Thu Jul 15 23:44 PDT 2010 - RT#58838, "Error reporting on die or missing plan". Thanks to Colin Robertson. Output compatible w/Hudson (so it now sees these as errors). - RT#59206, "Plan/Tests Mismatch". Thanks for Phillip Kimmey. JUnit output now reports mismatches with an "" so Hudson detects it. 0.07 Fri Jan 29 23:23 PST 2010 - Fix RT#53927, "Times reported by T:F:JUnit for individual test cases in a .t file are incorrect". Thanks to Marc Abramowitz. 0.06 Wed Jan 13 21:24 PST 2010 - Fix bug in tap2junit which would cause multiple TAP streams to have the *same* name in their output JUnit XML. Introduced in 0.04. 0.05 Wed Jan 13 16:32 PST 2010 - Add support for ALLOW_PASSING_TODOS environment variable, which forces T:F:JUnit to treat passing TODOs as a "pass" and not a "fail" condition. Thanks to Joe McMahon. - Removed need for Test::Output; I forgot that you can pass a FH directly in to TAP::Harness. Doh! 0.04 Wed Jan 13 15:51 PST 2010 - extra escaping/cleanup of characters before inserting them into the XML stream, to keep JUnit parsers like Hudson's from choking. Thanks go out to Joe McMahon and Michael Nachbaur for prodding to get this fixed and for patches. - new "--name" option for tap2junit, allowing for tests to be explicitly named. Aliased to "--junit_name" to provide compatibility with patch from Joe McMahon. - tap2junit can now filter stdin/stdout; use "-" as the filename. Thanks to Joe McMahon for the original patch on which this is based - switch unit tests to use Test::Output for capturing output, instead of trying to run "prove" directly - update unit tests to run against "blib/lib" and "blib/script" instead of just "lib" and "bin" 0.03 Sun Dec 13 22:36 PST 2009 - add timer output for each test case (not just for the suite as a whole); Hudson needs this in order to show timing output for test runs. Thanks to Mark Aufflick for the poke. - internal cleanups 0.02 Fri Jan 9 23:35 PST 2009 - POD updates - minor cleanup to the test names output in JUnit - attempt to fix failing CPAN Tester reports, where an older version of 'prove' was being picked up by t/formatter.t; provide our own 't/bin/my-prove' and use that instead. 0.01 Wed Jan 7 22:06 PST 2009 - initial version - had this sitting around on my HD for several months and am (finally) getting around to uploading it to CPAN TAP-Formatter-JUnit-0.11/MANIFEST.SKIP000444001750001750 7712413065544 16733 0ustar00grahamgraham000000000000Build _build/ .git/ \..*\.swp blib/ ^MYMETA.yml$ ^MYMETA.json$ TAP-Formatter-JUnit-0.11/README000444001750001750 45112413065544 15731 0ustar00grahamgraham000000000000TAP::Formatter::JUnit formats TAP output as JUnit XML Copyright (C) 2008, Graham TerMarsch. All Rights Reserved. This is free software; you can redistribute it and/or modify it under the same terms as Perl itself. To install: perl Build.PL ./Build ./Build test ./Build install TAP-Formatter-JUnit-0.11/MANIFEST000444001750001750 501612413065544 16224 0ustar00grahamgraham000000000000Build.PL Changes MANIFEST MANIFEST.SKIP META.yml META.json README bin/tap2junit lib/TAP/Formatter/JUnit.pm lib/TAP/Formatter/JUnit/Result.pm lib/TAP/Formatter/JUnit/Session.pm t/formatter.t t/passing-todos.t t/tap2junit.t t/tap2junit-filter.t t/tap2junit-name.t t/timer.t t/data/tap/bailout t/data/tap/bad_chars t/data/tap/descriptive t/data/tap/descriptive_trailing t/data/tap/die t/data/tap/die_head_end t/data/tap/die_last_minute t/data/tap/die_unfinished t/data/tap/empty t/data/tap/no_nums t/data/tap/simple t/data/tap/simple_fail t/data/tap/simple_yaml t/data/tap/skip t/data/tap/skipall t/data/tap/skipall_nomsg t/data/tap/skip_nomsg t/data/tap/stdout_stderr t/data/tap/todo t/data/tap/todo_inline t/data/tap/todo_misparse t/data/tap/too_many t/data/tap/junit/bailout t/data/tap/junit/bad_chars t/data/tap/junit/descriptive t/data/tap/junit/descriptive_trailing t/data/tap/junit/die t/data/tap/junit/die_head_end t/data/tap/junit/die_last_minute t/data/tap/junit/die_unfinished t/data/tap/junit/empty t/data/tap/junit/no_nums t/data/tap/junit/simple t/data/tap/junit/simple_fail t/data/tap/junit/simple_yaml t/data/tap/junit/skip t/data/tap/junit/skipall t/data/tap/junit/skipall_nomsg t/data/tap/junit/skip_nomsg t/data/tap/junit/stdout_stderr t/data/tap/junit/todo t/data/tap/junit/todo_inline t/data/tap/junit/todo_misparse t/data/tap/junit/too_many t/data/tests/bailout t/data/tests/bad_chars t/data/tests/descriptive t/data/tests/descriptive_trailing t/data/tests/die t/data/tests/die_head_end t/data/tests/die_last_minute t/data/tests/die_unfinished t/data/tests/empty t/data/tests/no_nums t/data/tests/simple t/data/tests/simple_fail t/data/tests/simple_yaml t/data/tests/skip t/data/tests/skipall t/data/tests/skipall_nomsg t/data/tests/skip_nomsg t/data/tests/stdout_stderr t/data/tests/todo t/data/tests/todo_inline t/data/tests/todo_misparse t/data/tests/too_many t/data/tests/junit/bailout t/data/tests/junit/bad_chars t/data/tests/junit/descriptive t/data/tests/junit/descriptive_trailing t/data/tests/junit/die t/data/tests/junit/die_head_end t/data/tests/junit/die_last_minute t/data/tests/junit/die_unfinished t/data/tests/junit/empty t/data/tests/junit/no_nums t/data/tests/junit/simple t/data/tests/junit/simple_fail t/data/tests/junit/simple_yaml t/data/tests/junit/skip t/data/tests/junit/skipall t/data/tests/junit/skipall_nomsg t/data/tests/junit/skip_nomsg t/data/tests/junit/stdout_stderr t/data/tests/junit/todo t/data/tests/junit/todo_inline t/data/tests/junit/todo_misparse t/data/tests/junit/too_many xt/pod.t xt/pod-coverage.t xt/timer.t TAP-Formatter-JUnit-0.11/META.yml000444001750001750 155312413065544 16346 0ustar00grahamgraham000000000000--- abstract: 'Harness output delegate for JUnit output' author: - 'Graham TerMarsch (cpan@howlingfrog.com)' build_requires: IO::Scalar: 0 IPC::Run: 0 Test::More: 0 Test::XML: 0 configure_requires: Module::Build: 0.40 dynamic_config: 1 generated_by: 'Module::Build version 0.4007, CPAN::Meta::Converter version 2.132140' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: TAP-Formatter-JUnit provides: TAP::Formatter::JUnit: file: lib/TAP/Formatter/JUnit.pm version: 0.11 TAP::Formatter::JUnit::Result: file: lib/TAP/Formatter/JUnit/Result.pm TAP::Formatter::JUnit::Session: file: lib/TAP/Formatter/JUnit/Session.pm requires: File::Slurp: 0 Moose: 0 MooseX::NonMoose: 0 TAP::Harness: 3.12 XML::Generator: 0 resources: license: http://dev.perl.org/licenses/ version: 0.11 TAP-Formatter-JUnit-0.11/META.json000444001750001750 271312413065544 16515 0ustar00grahamgraham000000000000{ "abstract" : "Harness output delegate for JUnit output", "author" : [ "Graham TerMarsch (cpan@howlingfrog.com)" ], "dynamic_config" : 1, "generated_by" : "Module::Build version 0.4007, CPAN::Meta::Converter version 2.132140", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "TAP-Formatter-JUnit", "prereqs" : { "build" : { "requires" : { "IO::Scalar" : "0", "IPC::Run" : "0", "Test::More" : "0", "Test::XML" : "0" } }, "configure" : { "requires" : { "Module::Build" : "0.40" } }, "runtime" : { "requires" : { "File::Slurp" : "0", "Moose" : "0", "MooseX::NonMoose" : "0", "TAP::Harness" : "3.12", "XML::Generator" : "0" } } }, "provides" : { "TAP::Formatter::JUnit" : { "file" : "lib/TAP/Formatter/JUnit.pm", "version" : "0.11" }, "TAP::Formatter::JUnit::Result" : { "file" : "lib/TAP/Formatter/JUnit/Result.pm" }, "TAP::Formatter::JUnit::Session" : { "file" : "lib/TAP/Formatter/JUnit/Session.pm" } }, "release_status" : "stable", "resources" : { "license" : [ "http://dev.perl.org/licenses/" ] }, "version" : "0.11" } TAP-Formatter-JUnit-0.11/lib000755001750001750 012413065544 15502 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/lib/TAP000755001750001750 012413065544 16126 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/lib/TAP/Formatter000755001750001750 012413065544 20071 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/lib/TAP/Formatter/JUnit.pm000444001750001750 1151012413065544 21633 0ustar00grahamgraham000000000000package TAP::Formatter::JUnit; use Moose; use MooseX::NonMoose; extends qw( TAP::Formatter::Console ); use XML::Generator; use TAP::Formatter::JUnit::Session; our $VERSION = '0.11'; has 'testsuites' => ( is => 'rw', isa => 'ArrayRef', default => sub { [] }, traits => [qw( Array )], handles => { add_testsuite => 'push', }, ); has 'xml' => ( is => 'rw', isa => 'XML::Generator', lazy_build => 1, ); sub _build_xml { return XML::Generator->new( ':pretty', ':std', 'escape' => 'always,high-bit,even-entities', 'encoding' => 'UTF-8', ); } ############################################################################### # Subroutine: open_test($test, $parser) ############################################################################### # Over-ridden 'open_test()' method. # # Creates a 'TAP::Formatter::JUnit::Session' session, instead of a console # formatter session. sub open_test { my ($self, $test, $parser) = @_; my $session = TAP::Formatter::JUnit::Session->new( { name => $test, formatter => $self, parser => $parser, passing_todo_ok => $ENV{ALLOW_PASSING_TODOS} ? 1 : 0, } ); return $session; } ############################################################################### # Subroutine: summary($aggregate) ############################################################################### # Prints the summary report (in JUnit) after all tests are run. sub summary { my ($self, $aggregate) = @_; return if $self->silent(); my @suites = @{$self->testsuites}; print { $self->stdout } $self->xml->testsuites( @suites ); } 1; =head1 NAME TAP::Formatter::JUnit - Harness output delegate for JUnit output =head1 SYNOPSIS On the command line, with F: prove --formatter TAP::Formatter::JUnit ... Or, in your own scripts: use TAP::Harness; my $harness = TAP::Harness->new( { formatter_class => 'TAP::Formatter::JUnit', merge => 1, } ); $harness->runtests(@tests); =head1 DESCRIPTION B C provides JUnit output formatting for C. By default (e.g. when run with F), the I test suite is gathered together into a single JUnit XML document, which is then displayed on C. You can, however, have individual JUnit XML files dumped for each individual test, by setting c to a directory that you would like the JUnit XML dumped to. Note, that this will B cause C to dump the original TAP output into that directory as well (but IMHO that's ok as you've now got the data in two parsable formats). Timing information is included in the JUnit XML, I you specified C<--timer> when you ran F. In standard use, "passing TODOs" are treated as failure conditions (and are reported as such in the generated JUnit). If you wish to treat these as a "pass" and not a "fail" condition, setting C in your environment will turn these into pass conditions. The JUnit output generated is partial to being grokked by Hudson (L). That's the build tool I'm using at the moment and needed to be able to generate JUnit output for. =head1 ATTRIBUTES =over =item testsuites List-ref of test suites that have been executed. =item xml An C instance, to be used to generate XML output. =back =head1 METHODS =over =item B Over-ridden C method. Creates a C session, instead of a console formatter session. =item B Prints the summary report (in JUnit) after all tests are run. =item B Adds the given XML test C<$suite> to the list of test suites that we've executed and need to summarize. =back =head1 AUTHOR Graham TerMarsch Many thanks to Andy Armstrong et al. for the B set of tests in C; they became the basis for the unit tests here. Other thanks go out to those that have provided feedback, comments, or patches: Mark Aufflick Joe McMahon Michael Nachbaur Marc Abramowitz Colin Robertson Phillip Kimmey Dave Lambley =head1 COPYRIGHT Copyright 2008-2010, Graham TerMarsch. All Rights Reserved. This is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L, L, L, L, L. =cut TAP-Formatter-JUnit-0.11/lib/TAP/Formatter/JUnit000755001750001750 012413065544 21122 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/lib/TAP/Formatter/JUnit/Session.pm000444001750001750 3274312413065544 23271 0ustar00grahamgraham000000000000package TAP::Formatter::JUnit::Session; use Moose; use MooseX::NonMoose; extends qw( TAP::Formatter::Console::Session ); use Storable qw(dclone); use File::Path qw(mkpath); use IO::File; use TAP::Formatter::JUnit::Result; has 'testcases' => ( is => 'rw', isa => 'ArrayRef', default => sub { [] }, traits => [qw( Array )], handles => { add_testcase => 'push', num_testcases => 'count', }, ); has 'passing_todo_ok' => ( is => 'rw', isa => 'Bool', default => 0, ); has '_queue' => ( is => 'rw', isa => 'ArrayRef', default => sub { [] }, traits => [qw( Array )], handles => { _queue_add => 'push', }, ); ############################################################################### # Subroutine: _initialize($arg_for) ############################################################################### # Custom initializer, so we can accept a new "passing_todo_ok" argument at # instantiation time. sub _initialize { my ($self, $arg_for) = @_; $arg_for ||= {}; my $passing_todo_ok = delete $arg_for->{passing_todo_ok}; $self->passing_todo_ok($passing_todo_ok); return $self->SUPER::_initialize($arg_for); } ############################################################################### # Subroutine: result($result) ############################################################################### # Called by the harness for each line of TAP it receives. # # Queues up all of the TAP output for later conversion to JUnit. sub result { my ($self, $result) = @_; # except for a few things we don't want to process as a "test case", add # the test result to the queue. unless ( ($result->raw() =~ /^# Looks like you failed \d+ tests? of \d+/) || ($result->raw() =~ /^# Looks like you planned \d+ tests? but ran \d+/) || ($result->raw() =~ /^# Looks like your test died before it could output anything/) ) { my $wrapped = TAP::Formatter::JUnit::Result->new( 'time' => $self->get_time, 'result' => $result, ); $self->_queue_add($wrapped); } } ############################################################################### # Subroutine: close_test() ############################################################################### # Called to close the test session. # # Flushes the queue if we've got anything left in it, dumps the JUnit to disk # (if necessary), and adds the XML for this test suite to our formatter. sub close_test { my $self = shift; my $xml = $self->xml; my $parser = $self->parser; # Process the queued up TAP stream my $is_first = 1; my $t_start = $self->parser->start_time; my $t_last_test = $t_start; my $timer_enabled = $self->formatter->timer; my $queue = $self->_queue; my $index = 0; while ($index < @{$queue}) { my $result = $queue->[$index++]; # First line of output generates the "init" timing. if ($is_first) { if ($timer_enabled) { unless ($result->is_test) { my $duration = $result->time - $t_start; my $case = $xml->testcase( { 'name' => _squeaky_clean('(init)'), 'time' => $duration, } ); $self->add_testcase($case); $t_last_test = $result->time; } } $is_first = 0; } # Test output if ($result->is_test) { # how long did it take for this test? my $duration = $result->time - $t_last_test; # slurp in all of the content up until the next test my $content = $result->as_string; while ($index < @{$queue}) { last if ($queue->[$index]->is_test); last if ($queue->[$index]->is_plan); my $stuff = $queue->[$index++]; $content .= "\n" . $stuff->as_string; } # create a failure/error element if the test was bogus my $failure; my $bogosity = $self->_check_for_test_bogosity($result); if ($bogosity) { my $cdata = $self->_cdata($content); my $level = $bogosity->{level}; $failure = $xml->$level( { type => $bogosity->{type}, message => $bogosity->{message}, }, $cdata ); } # add this test to the XML stream my $case = $xml->testcase( { 'name' => _get_testcase_name($result), ( $timer_enabled ? ('time' => $duration) : () ), }, $failure, ); $self->add_testcase($case); # update time of last test seen $t_last_test = $result->time; } } # track time for teardown, if needed if ($timer_enabled) { my $duration = $self->parser->end_time - $queue->[-1]->time; my $case = $xml->testcase( { 'name' => _squeaky_clean('(teardown)'), 'time' => $duration, } ); $self->add_testcase($case); } # collect up all of the captured test output my $captured = join '', map { $_->raw . "\n" } @{$queue}; # if the test died unexpectedly, make note of that my $die_msg; my $exit = $parser->exit(); if ($exit) { my $wstat = $parser->wait(); my $status = sprintf("%d (wstat %d, 0x%x)", $exit, $wstat, $wstat); $die_msg = "Dubious, test returned $status"; } # add system-out/system-err data, as raw CDATA my $sys_out = 'system-out'; $sys_out = $xml->$sys_out($captured ? $self->_cdata($captured) : undef); my $sys_err = 'system-err'; $sys_err = $xml->$sys_err($die_msg ? $self->_cdata("$die_msg\n") : undef); # update the testsuite with aggregate info on this test suite # # tests - total number of tests run # time - wallclock time taken for test run (floating point) # failures - number of tests that we detected as failing # errors - number of errors: # - passing TODOs # - if a plan was provided, mismatch between that and the # number of actual tests that were run # - either "no plan was issued" or "test died" (a dying test # may not have a plan issued, but should still be considered # a single error condition) my $testsrun = $parser->tests_run() || 0; my $time = $parser->end_time() - $parser->start_time(); my $failures = $parser->failed(); my $noplan = $parser->plan() ? 0 : 1; my $planned = $parser->tests_planned() || 0; my $num_errors = 0; $num_errors += $parser->todo_passed() unless $self->passing_todo_ok(); $num_errors += abs($testsrun - $planned) if ($planned); my $suite_err; if ($die_msg) { $suite_err = $xml->error( { message => $die_msg } ); $num_errors ++; } elsif ($noplan) { $suite_err = $xml->error( { message => 'No plan in TAP output' } ); $num_errors ++; } elsif ($planned && ($testsrun != $planned)) { $suite_err = $xml->error( { message => "Looks like you planned $planned tests but ran $testsrun." } ); } my @tests = @{$self->testcases()}; my %attrs = ( 'name' => _get_testsuite_name($self), 'tests' => $testsrun, 'failures' => $failures, 'errors' => $num_errors, ( $timer_enabled ? ('time' => $time) : () ), ); my $testsuite = $xml->testsuite(\%attrs, @tests, $sys_out, $sys_err, $suite_err); $self->formatter->add_testsuite($testsuite); $self->dump_junit_xml($testsuite); } ############################################################################### # Subroutine: dump_junit_xml($testsuite) ############################################################################### # Dumps the JUnit for the given XML '$testsuite', to the directory specified by # 'PERL_TEST_HARNESS_DUMP_TAP'. sub dump_junit_xml { my ($self, $testsuite) = @_; if (my $spool_dir = $ENV{PERL_TEST_HARNESS_DUMP_TAP}) { my $spool = File::Spec->catfile($spool_dir, $self->name() . '.junit.xml'); # clone the testsuite; XML::Generator only lets us auto-vivify the # CDATA sections *ONCE*. $testsuite = dclone($testsuite); # create target dir my ($vol, $dir, undef) = File::Spec->splitpath($spool); my $path = File::Spec->catpath($vol, $dir, ''); mkpath($path); # create JUnit XML, and dump to disk my $junit = $self->xml->xml($self->xml->testsuites($testsuite) ); my $fout = IO::File->new( $spool, '>:utf8' ) || die "Can't write $spool ( $! )\n"; $fout->print($junit); $fout->close(); } } ############################################################################### # Subroutine: xml() ############################################################################### # Returns a new 'XML::Generator' to generate XML output. This is simply a # shortcut to '$self->formatter->xml()'. sub xml { my $self = shift; return $self->formatter->xml(); } ############################################################################### # Checks for bogosity in the test result. sub _check_for_test_bogosity { my $self = shift; my $result = shift; if ($result->todo_passed() && !$self->passing_todo_ok()) { return { level => 'error', type => 'TodoTestSucceeded', message => $result->explanation(), }; } if ($result->is_unplanned()) { return { level => 'error', type => 'UnplannedTest', message => $result->as_string(), }; } if (not $result->is_ok()) { return { level => 'failure', type => 'TestFailed', message => $result->as_string(), }; } return; } ############################################################################### # Generates the name for a test case. sub _get_testcase_name { my $test = shift; my $name = join(' ', $test->number(), _clean_test_description($test)); $name =~ s/\s+$//; return $name; } ############################################################################### # Generates the name for the entire test suite. sub _get_testsuite_name { my $self = shift; my $name = $self->name; $name =~ s{^\./}{}; $name =~ s{^t/}{}; return _clean_to_java_class_name($name); } ############################################################################### # Cleans up the given string, removing any characters that aren't suitable for # use in a Java class name. sub _clean_to_java_class_name { my $str = shift; $str =~ s/[^-:_A-Za-z0-9]+/_/gs; return $str; } ############################################################################### # Cleans up the description of the given test. sub _clean_test_description { my $test = shift; my $desc = $test->description(); return _squeaky_clean($desc); } ############################################################################### # Creates a CDATA block for the given data (which is made squeaky clean first, # so that JUnit parsers like Hudson's don't choke). sub _cdata { my ($self, $data) = @_; $data = _squeaky_clean($data); return $self->xml->xmlcdata($data); } ############################################################################### # Clean a string to the point that JUnit can't possibly have a problem with it. sub _squeaky_clean { my $string = shift; # control characters (except CR and LF) $string =~ s/([\x00-\x09\x0b\x0c\x0e-\x1f])/"^".chr(ord($1)+64)/ge; # high-byte characters $string =~ s/([\x7f-\xff])/'[\\x'.sprintf('%02x',ord($1)).']'/ge; return $string; } 1; =head1 NAME TAP::Formatter::JUnit::Session - Harness output delegate for JUnit output =head1 DESCRIPTION C provides JUnit output formatting for C. =head1 METHODS =over =item B<_initialize($arg_for)> Over-ridden private initializer, so we can accept a new "passing_todo_ok" argument at instantiation time. =item B Called by the harness for each line of TAP it receives. Internally, all of the TAP is added to a queue until we hit the start of the "next" test (at which point we flush the queue. This allows us to capture any error output or diagnostic info that comes after a test failure. =item B Called to close the test session. Flushes the queue if we've got anything left in it, dumps the JUnit to disk (if necessary), and adds the XML for this test suite to our formatter. =item B Dumps the JUnit for the given XML C<$testsuite>, to the directory specified by C. =item B Adds an XML test C<$case> to the list of testcases we've run in this session. =item B Returns a new C to generate XML output. This is simply a shortcut to C<$self-Eformatter-Exml()>. =back =head1 AUTHOR Graham TerMarsch =head1 COPYRIGHT Copyright 2008-2010, Graham TerMarsch. All Rights Reserved. This is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L. =cut TAP-Formatter-JUnit-0.11/lib/TAP/Formatter/JUnit/Result.pm000444001750001750 172712413065544 23102 0ustar00grahamgraham000000000000package TAP::Formatter::JUnit::Result; use Moose; has 'time' => ( is => 'ro', isa => 'Num', required => 1, ); has 'result' => ( is => 'ro', isa => 'TAP::Parser::Result', required => 1, handles => [qw( name number description as_string raw is_test is_plan is_unplanned is_ok todo_passed explanation )], ); 1; =head1 NAME TAP::Formatter::JUnit::Result - Wrapper for a TAP result =head1 DESCRIPTION C is an internal class, used to wrap/augment C objects with timing information. B =head1 AUTHOR Graham TerMarsch =head1 COPYRIGHT Copyright 2011, Graham TerMarsch. All Rights Reserved. This is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut TAP-Formatter-JUnit-0.11/t000755001750001750 012413065544 15177 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/t/passing-todos.t000444001750001750 252512413065544 20317 0ustar00grahamgraham000000000000#!/usr/bin/perl use strict; use warnings; use Test::More tests => 6; use TAP::Harness; use IO::Scalar; ############################################################################### # TEST: passing TODOs are normally treated as failure condition. passing_todo_default_fail: { my $results = undef; my $fh = IO::Scalar->new(\$results); my $harness = TAP::Harness->new( { formatter_class => 'TAP::Formatter::JUnit', stdout => $fh, } ); $harness->runtests('t/data/tests/todo'); ok $results, 'Ran test with passing TODO'; like $results, qr/]+errors="1"/, '... with one error'; like $results, qr/TodoTestSucceeded/, '... passing TODO'; } ############################################################################### # TEST: over-ride allows for passing TODOs to be treated as a pass. passing_todo_ok: { local $ENV{ALLOW_PASSING_TODOS} = 1; my $results = undef; my $fh = IO::Scalar->new(\$results); my $harness = TAP::Harness->new( { formatter_class => 'TAP::Formatter::JUnit', stdout => $fh, } ); $harness->runtests('t/data/tests/todo'); ok $results, 'Re-ran test with passing TODO'; like $results, qr/]+errors="0"/, '... with NO errors'; unlike $results, qr/TodoTestSucceeded/, '... passing TODO was OK'; } TAP-Formatter-JUnit-0.11/t/timer.t000444001750001750 262112413065544 16642 0ustar00grahamgraham000000000000#!/usr/bin/perl use strict; use warnings; use Test::More tests => 4; use TAP::Harness; use IO::Scalar; use File::Slurp qw(write_file); ############################################################################### # When timer is disabled, we should have *NO* timer info in the JUnit output. timer_disabled: { my $test = qq| use Test::More tests => 1; pass 'no timing in this test'; |; my $results = run_test($test, { timer => 0, } ); ok $results, 'got JUnit'; unlike $results, qr/time/ism, '... without any timing information'; } ############################################################################### # When timer is enabled, JUnit output *should* have timer info in it. timer_enabled: { my $test = qq| use Test::More tests => 2; pass 'one'; pass 'two'; |; my $results = run_test($test, { timer => 1, } ); ok $results, 'got JUnit'; like $results, qr/time/ism, '... with timing information'; } sub run_test { my $code = shift; my $opts = shift; my $file = "test-$$.t"; my $junit = undef; my $fh = IO::Scalar->new(\$junit); my $harness = TAP::Harness->new( { formatter_class => 'TAP::Formatter::JUnit', stdout => $fh, %{$opts}, } ); write_file($file, $code); $harness->runtests($file); unlink $file; return $junit; } TAP-Formatter-JUnit-0.11/t/formatter.t000444001750001750 240112413065544 17521 0ustar00grahamgraham000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::XML; use File::Slurp qw(slurp); use TAP::Harness; use IO::Scalar; ############################################################################### # Figure out how many tests we have to run. # # *MANY* thanks Andy Armstrong et al. for the fabulous set of tests in # Test::Harness. :) my @tests = grep { -f $_ } ; plan tests => scalar(@tests); ############################################################################### # Run each of the tests in turn, and compare the output to the expected JUnit # output. foreach my $test (@tests) { (my $junit = $test) =~ s{/tests/}{/tests/junit/}; my $received = ''; my $fh = IO::Scalar->new(\$received); eval { my $harness = TAP::Harness->new( { stdout => $fh, merge => 1, formatter_class => 'TAP::Formatter::JUnit', } ); $harness->runtests($test); }; my $expected = slurp($junit); # Compare results (bearing in mind that some tests produce zero output, and # thus cannot be parsed as XML) if ($received || $expected) { is_xml $received, $expected, $test; } else { is $received, $expected, $test; } } TAP-Formatter-JUnit-0.11/t/tap2junit-name.t000444001750001750 215012413065544 20355 0ustar00grahamgraham000000000000#!/usr/bin/perl use strict; use warnings; use Test::More tests => 2; use File::Slurp qw(slurp); use File::Spec; ############################################################################### # TEST: Run "tap2junit" and let it name the test in the JUnit automatically. tap2junit_default_name: { my $test_file = 't/data/tap/simple'; my $xml_file = "$test_file.xml"; _tap2junit($test_file); my $xml = slurp($xml_file); unlink $xml_file; like $xml, qr/]+name="data_tap_simple"/m, 'default name based on TAP filename'; } ############################################################################### # TEST: Run "tap2junit" with "--name" and rename a test tap2junit_name: { my $test_file = 't/data/tap/simple'; my $xml_file = "$test_file.xml"; _tap2junit($test_file, '--name', 'foo'); my $xml = slurp($xml_file); unlink $xml_file; like $xml, qr/]+name="foo"/m, 'name explicitly provided'; } sub _tap2junit { my @args = @_; my $null = File::Spec->devnull(); system(qq{ $^X -Iblib/lib blib/script/tap2junit @args 2>$null }); } TAP-Formatter-JUnit-0.11/t/tap2junit.t000444001750001750 240312413065544 17440 0ustar00grahamgraham000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::XML; use File::Slurp qw(slurp); use File::Spec; ############################################################################### # Figure out how many TAP files we have to run. Yes, the results *ARE* going # to be different when parsing the raw TAP output than when running under # 'prove'; we won't have any context of "did the test die a horrible death?" my @tests = grep { -f $_ } ; plan tests => scalar(@tests); ############################################################################### # Run each of the TAP files in turn through 'tap2junit', and compare the output # to the expected JUnit output in each case. my $null = File::Spec->devnull(); foreach my $test (@tests) { (my $junit = $test) =~ s{/tap/}{/tap/junit/}; my $rc = system(qq{ $^X -Iblib/lib blib/script/tap2junit $test 2>$null }); my $outfile = "$test.xml"; my $received = slurp($outfile); unlink $outfile; my $expected = slurp($junit); # Compare results (bearing in mind that some tests produce zero output, and # thus cannot be parsed as XML) if ($received || $expected) { is_xml $received, $expected, $test; } else { is $received, $expected, $test; } } TAP-Formatter-JUnit-0.11/t/tap2junit-filter.t000444001750001750 114012413065544 20720 0ustar00grahamgraham000000000000#!/usr/bin/perl use strict; use warnings; use Test::More tests => 1; use Test::XML; use IPC::Run qw(run); use File::Slurp qw(slurp); ############################################################################### # TEST: Run "tap2junit" in filter mode (in STDIN, out STDOUT) tap2junit_filter: { my $tap = slurp('t/data/tap/simple'); my $xml = slurp('t/data/tap/junit/simple'); my @cmd = ($^X, '-Iblib/lib', 'blib/script/tap2junit', '--name' => 'data_tap_simple', '-'); my ($out, $err); run \@cmd, \$tap, \$out, \$err or die $?; is_xml $out, $xml, 'results generated on STDOUT'; } TAP-Formatter-JUnit-0.11/t/data000755001750001750 012413065544 16110 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/t/data/tap000755001750001750 012413065544 16674 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/t/data/tap/no_nums000444001750001750 3012413065544 20363 0ustar00grahamgraham0000000000001..5 ok ok not ok ok ok TAP-Formatter-JUnit-0.11/t/data/tap/descriptive_trailing000444001750001750 21112413065544 23140 0ustar00grahamgraham000000000000ok 1 Interlock activated ok 2 Megathrusters are go ok 3 Head formed ok 4 Blazing sword formed ok 5 Robeast destroyed 1..5 TAP-Formatter-JUnit-0.11/t/data/tap/die_unfinished000444001750001750 2412413065544 21665 0ustar00grahamgraham0000000000001..4 ok 1 ok 2 ok 3 TAP-Formatter-JUnit-0.11/t/data/tap/die000444001750001750 012413065544 17423 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/t/data/tap/simple000444001750001750 3612413065544 20204 0ustar00grahamgraham0000000000001..5 ok 1 ok 2 ok 3 ok 4 ok 5 TAP-Formatter-JUnit-0.11/t/data/tap/skip000444001750001750 6312413065544 17661 0ustar00grahamgraham0000000000001..5 ok 1 ok 2 # skip rain delay ok 3 ok 4 ok 5 TAP-Formatter-JUnit-0.11/t/data/tap/simple_fail000444001750001750 4612413065544 21200 0ustar00grahamgraham0000000000001..5 ok 1 not ok 2 ok 3 ok 4 not ok 5 TAP-Formatter-JUnit-0.11/t/data/tap/bad_chars000444001750001750 12512413065544 20640 0ustar00grahamgraham0000000000001..3 ok 1 - Control chars  ok 2 - Weird control chars  ok 3 - Unicode Þϴ쌇 TAP-Formatter-JUnit-0.11/t/data/tap/die_head_end000444001750001750 2412413065544 21260 0ustar00grahamgraham000000000000ok 1 ok 2 ok 3 ok 4 TAP-Formatter-JUnit-0.11/t/data/tap/todo000444001750001750 5412413065544 17660 0ustar00grahamgraham0000000000001..5 todo 3 2; ok 1 ok 2 not ok 3 ok 4 ok 5 TAP-Formatter-JUnit-0.11/t/data/tap/todo_misparse000444001750001750 4712413065544 21565 0ustar00grahamgraham0000000000001..1 not ok 1 Hamlette # TODOORNOTTODO TAP-Formatter-JUnit-0.11/t/data/tap/skipall_nomsg000444001750001750 512413065544 21531 0ustar00grahamgraham0000000000001..0 TAP-Formatter-JUnit-0.11/t/data/tap/skip_nomsg000444001750001750 2112413065544 21056 0ustar00grahamgraham0000000000001..1 ok 1 # Skip TAP-Formatter-JUnit-0.11/t/data/tap/skipall000444001750001750 2612413065544 20351 0ustar00grahamgraham0000000000001..0 # skipping: rope TAP-Formatter-JUnit-0.11/t/data/tap/too_many000444001750001750 5012413065544 20534 0ustar00grahamgraham0000000000001..3 ok 1 ok 2 ok 3 ok 4 ok 5 ok 6 ok 7 TAP-Formatter-JUnit-0.11/t/data/tap/die_last_minute000444001750001750 3112413065544 22053 0ustar00grahamgraham000000000000ok 1 ok 2 ok 3 ok 4 1..4 TAP-Formatter-JUnit-0.11/t/data/tap/descriptive000444001750001750 21112413065544 21247 0ustar00grahamgraham0000000000001..5 ok 1 Interlock activated ok 2 Megathrusters are go ok 3 Head formed ok 4 Blazing sword formed ok 5 Robeast destroyed TAP-Formatter-JUnit-0.11/t/data/tap/bailout000444001750001750 7412413065544 20354 0ustar00grahamgraham0000000000001..5 ok 1 ok 2 ok 3 Bail out! GERONIMMMOOOOOO!!! ok 4 ok 5 TAP-Formatter-JUnit-0.11/t/data/tap/empty000444001750001750 012413065544 20020 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/t/data/tap/simple_yaml000444001750001750 32012413065544 21242 0ustar00grahamgraham000000000000TAP version 13 1..5 ok 1 ok 2 --- - fnurk: skib ponk: gleeb - bar: krup foo: plink ... ok 3 ok 4 --- expected: - 1 - 2 - 4 got: - 1 - pong - 4 ... ok 5 TAP-Formatter-JUnit-0.11/t/data/tap/stdout_stderr000444001750001750 12212413065544 21634 0ustar00grahamgraham000000000000# comments ok 1 ok 2 ok 3 # comment ok 4 # more ignored stuff # and yet more 1..4 TAP-Formatter-JUnit-0.11/t/data/tap/todo_inline000444001750001750 22612413065544 21237 0ustar00grahamgraham0000000000001..3 not ok 1 - Foo # TODO Just testing the todo interface. ok 2 - Unexpected success # TODO Just testing the todo interface. ok 3 - This is not todo TAP-Formatter-JUnit-0.11/t/data/tap/junit000755001750001750 012413065544 20025 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/t/data/tap/junit/no_nums000444001750001750 101612413065544 21561 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/descriptive_trailing000444001750001750 120112413065544 24311 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/die_unfinished000444001750001750 64612413065544 23050 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/die000444001750001750 012413065544 20554 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/t/data/tap/junit/simple000444001750001750 65412413065544 21363 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/skip000444001750001750 67712413065544 21045 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/simple_fail000444001750001750 120712413065544 22371 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/bad_chars000444001750001750 107512413065544 22016 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/die_head_end000444001750001750 66312413065544 22442 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/todo000444001750001750 101712413065544 21051 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/todo_misparse000444001750001750 74212413065544 22740 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/skipall_nomsg000444001750001750 35312413065544 22730 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/skip_nomsg000444001750001750 42712413065544 22241 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/skipall000444001750001750 36612413065544 21531 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/too_many000444001750001750 157412413065544 21741 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/die_last_minute000444001750001750 61512413065544 23234 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/descriptive000444001750001750 117012413065544 22425 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/bailout000444001750001750 71312413065544 21525 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/empty000444001750001750 012413065544 21151 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/t/data/tap/junit/simple_yaml000444001750001750 114312413065544 22417 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/stdout_stderr000444001750001750 70412413065544 22773 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tap/junit/todo_inline000444001750001750 131212413065544 22405 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests000755001750001750 012413065544 17252 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/t/data/tests/no_nums000444001750001750 6712413065544 20753 0ustar00grahamgraham000000000000print < TAP-Formatter-JUnit-0.11/t/data/tests/junit/descriptive_trailing000444001750001750 120312413065544 24671 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/die_unfinished000444001750001750 74212413065544 23423 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/die000444001750001750 51612413065544 21206 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/simple000444001750001750 65612413065544 21743 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/skip000444001750001750 70112413065544 21407 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/simple_fail000444001750001750 121112413065544 22742 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/bad_chars000444001750001750 107712413065544 22376 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/die_head_end000444001750001750 100312413065544 23025 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/todo000444001750001750 102112413065544 21422 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/todo_misparse000444001750001750 74412413065544 23320 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/skipall_nomsg000444001750001750 35512413065544 23310 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/skip_nomsg000444001750001750 43112413065544 22612 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/skipall000444001750001750 37012413065544 22102 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/too_many000444001750001750 167212413065544 22316 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/die_last_minute000444001750001750 101312413065544 23623 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/descriptive000444001750001750 117212413065544 23005 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/bailout000444001750001750 012413065544 22030 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/t/data/tests/junit/empty000444001750001750 40212413065544 21575 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/simple_yaml000444001750001750 114512413065544 22777 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/stdout_stderr000444001750001750 70612413065544 23353 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/t/data/tests/junit/todo_inline000444001750001750 131412413065544 22765 0ustar00grahamgraham000000000000 TAP-Formatter-JUnit-0.11/xt000755001750001750 012413065544 15367 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/xt/pod.t000444001750001750 20112413065544 16444 0ustar00grahamgraham000000000000use Test::More; eval "use Test::Pod 1.00"; plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; all_pod_files_ok(); TAP-Formatter-JUnit-0.11/xt/timer.t000444001750001750 726212413065544 17040 0ustar00grahamgraham000000000000#!/usr/bin/perl use strict; use warnings; use Test::More tests => 17; use TAP::Harness; use IO::Scalar; use File::Slurp qw(write_file); ############################################################################### # Ensure timing correctness, when test has a plan # # Test once with merged output off, then once with it on; want to make sure that # merging diagnostic output into the TAP doesn't monkey up the timings. correct_timing_test_has_plan: { my $test = qq| BEGIN { sleep 3 }; END { sleep 2 }; use Test::More tests => 3; sleep 0; pass "one"; sleep 2; pass "two"; sleep 1; diag "foo"; sleep 1; diag "bar"; sleep 3; diag "foobar"; pass "three"; |; my $expect = { '(init)' => 3, '1 - one' => 0, '2 - two' => 2, '3 - three' => 5, '(teardown)' => 2, }; unmerged: { my $results = run_test($test, { timer => 1, merge => 0, } ); ok $results, 'got JUnit - timing correctness w/test plan (unmerged)'; verify_timings($results, $expect); } merged: { my $results = run_test($test, { timer => 1, merge => 1, } ); ok $results, 'got JUnit - timing correctness w/test plan (merged)'; verify_timings($results, $expect); } } ############################################################################### # Ensure timing correctness, when test has no plan # # The *first* test isn't going to be predictable/accurate w.r.t. the calculated # timing, as it'll also involve the startup overhead. As such, its skipped (by # denoting it as "skip" in its test name). correct_timing_test_unplanned: { my $test = qq| BEGIN { sleep 3 }; END { sleep 2 }; use Test::More qw(no_plan); sleep 0; pass "one"; sleep 2; pass "two"; sleep 1; diag "foo"; sleep 1; diag "bar"; sleep 3; diag "foobar"; pass "three"; |; my $expect = { '1 - one' => 3, # init time is *hidden* in initial test '2 - two' => 2, '3 - three' => 5, '(teardown)' => 2, }; my $results = run_test($test, { timer => 1, merge => 1, } ); ok $results, 'got JUnit - timing correctness w/o test plan'; verify_timings($results, $expect); } sub run_test { my $code = shift; my $opts = shift; my $file = "test-$$.t"; my $junit = undef; my $fh = IO::Scalar->new(\$junit); my $harness = TAP::Harness->new( { formatter_class => 'TAP::Formatter::JUnit', stdout => $fh, %{$opts}, } ); write_file($file, $code); $harness->runtests($file); unlink $file; return $junit; } sub verify_timings { my $junit = shift; my $expect = shift; my @lines = split /^/, $junit; my @tests = grep { /{$name}) { rounds_to($time, $expect->{$name}, "... test timing: $name"); } else { fail "... unexpected test name: $name"; diag $test; } } } sub rounds_to { my ($got, $expected, $message) = @_; my $r_got = sprintf('%1.0f', $got); my $r_expected = sprintf('%1.0f', $expected); local $Test::Builder::Level = $Test::Builder::Level + 1; is $r_got, $r_expected, $message; } TAP-Formatter-JUnit-0.11/xt/pod-coverage.t000444001750001750 23012413065544 20237 0ustar00grahamgraham000000000000use Test::More; eval "use Test::Pod::Coverage 1.00"; plan skip_all => "Test::Pod::Coverage 1.00 required for testing POD" if $@; all_pod_coverage_ok(); TAP-Formatter-JUnit-0.11/bin000755001750001750 012413065544 15504 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.11/bin/tap2junit000555001750001750 1125412413065544 17532 0ustar00grahamgraham000000000000#!/usr/bin/perl use strict; use warnings; use TAP::Parser; use TAP::Parser::Aggregator; use TAP::Formatter::JUnit; use Getopt::Long; use Pod::Usage; use IO::File; use File::Slurp qw(slurp); ############################################################################### # Read in our command line options. my ($help, $man); my $name; my $verbose; my $suffix = '.xml'; GetOptions( 'suffix=s' => \$suffix, 'verbose' => \$verbose, 'name|junit_name=s' => \$name, 'help|?' => \$help, 'man' => \$man, ) || pod2usage(1); pod2usage(1) if ($help); pod2usage( -exitstatus=>0, -verbose=>2 ) if ($man); ############################################################################### # Get the names of all of the TAP files we're supposed to convert. my @tap_files = @ARGV; pod2usage(1) unless (@tap_files); ############################################################################### # Convert all of the TAP files to JUnit. foreach my $file (@tap_files) { verbose( "converting TAP to JUnit: $file" ); # Slurp in the TAP. my $tap = ($file eq '-') ? do { local $/; ; } : slurp($file); # Open up a FH for where we're going to dump the JUnit my $fout; if ($file eq '-') { open $fout, '>&STDOUT' || die "can't dup STDOUT; $!\n"; } else { my $junit_file = "${file}${suffix}"; $fout = IO::File->new( $junit_file, '>' ) || die "can't open '$junit_file' for writing; $!"; } # Name the test; if one wasn't provided, name it after the file itself. my $test_name = $name || $file; # Convert the TAP to JUnit eval { # Create the TAP formatter, aggregator, and parser that we're going to # use to convert the TAP to JUnit. my $formatter = TAP::Formatter::JUnit->new( { stdout => $fout } ); my $aggregator = TAP::Parser::Aggregator->new(); my $parser = TAP::Parser->new( { tap => $tap } ); # Parse all of the TAP in this file. $aggregator->start(); my $session = $formatter->open_test($test_name, $parser); while (my $result = $parser->next()) { $session->result($result); } $session->close_test(); $aggregator->add($file, $parser); $aggregator->stop(); # Summarize the results (in JUnit) $formatter->summary($aggregator); }; if ($@) { warn $@; } $fout->close(); } ############################################################################### # All done; exit peacefully. exit; sub verbose { print "$_[0]\n" if ($verbose); } =head1 NAME tap2junit - Converts TAP output to JUnit =head1 SYNOPSIS tap2junit [options] ... Options: --suffix Suffix for JUnit output files (default ".xml") --verbose Display verbose status during conversion --name Provide explicit name for the JUnit test --help/-? Display brief help message --man Display full documentation =head1 DESCRIPTION C converts TAP output to JUnit. Give it a list of files containing TAP results and it will create a series of F output files containing the JUnit representations of that TAP contained in the files. If you specify F<-> as the filename, C will read from STDIN and write to STDOUT. You may also want to use the C<--name> option to name the test explicitly (as the default name of "-" isn't going to make much sense). =head1 OPTIONS =over =item B<--suffix EsuffixE> Specifies the suffix which is appended to all of the input files, in order to generate the filename for the JUnit XML file that is being output. If you want to live dangerously and over-write your original TAP files, you can set this to "" and your original TAP files will be over-written. Defaults to F<.xml> =item B<--verbose> Display verbose status information during the conversion (telling you which TAP file its working on). =item B<--name EnameE> Specifies an explicit name for the JUnit test. If no name is provided, a default name will be constructed based on the full path of the TAP file being processed. This option has also been aliased as C<--junit_name> to provide compatibility with a patch from Joe McMahon. =item B<--help/-?> Display brief help message. =item B<--man> Displays the full documentation. =back =head1 AUTHOR Graham TerMarsch =head1 COPYRIGHT Copyright 2008-2010, Graham TerMarsch. All Rights Reserved. This is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L. =cut