TAP-Formatter-JUnit-0.17000755001750001750 014751004727 14745 5ustar00grahamgraham000000000000README100644001750001750 666414751004727 15722 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17NAME TAP::Formatter::JUnit - Harness output delegate for JUnit output SYNOPSIS On the command line, with prove: $ prove --formatter TAP::Formatter::JUnit ... Or, in your own scripts: use TAP::Harness; # What TAP output did we save from a previous run, with # PERL_TEST_HARNESS_DUMP_TAP=tap/ my @tests = glob("tap/*.t"); # Convert the TAP to JUnit my $harness = TAP::Harness->new( { formatter_class => 'TAP::Formatter::JUnit', merge => 1, } ); $harness->runtests(@tests); DESCRIPTION This code is currently in alpha state and is subject to change. TAP::Formatter::JUnit provides JUnit output formatting for TAP::Harness. By default (e.g. when run with prove), the entire test suite is gathered together into a single JUnit XML document, which is then displayed on STDOUT. You can, however, have individual JUnit XML files dumped for each individual test, by setting PERL_TEST_HARNESS_DUMP_TAP to a directory that you would like the JUnit XML dumped to. Note, that this will also cause TAP::Harness 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 parseable formats). Timing information is included in the JUnit XML, if you specified --timer when you ran prove. In standard use, a "passing TODO" is treated as failure conditions (and is reported as such in the generated JUnit). If you wish to treat these as a "pass" and not a "fail" condition, setting ALLOW_PASSING_TODOS in your environment will turn these into pass conditions. The JUnit output generated is partial to being grokked by Hudson (http://hudson.dev.java.net/). That's the build tool I'm using at the moment and needed to be able to generate JUnit output for. ATTRIBUTES testsuites List-ref of test suites that have been executed. xml An XML::Generator instance, to be used to generate XML output. METHODS open_test($test, $parser) Over-ridden open_test() method. Creates a TAP::Formatter::JUnit::Session session, instead of a console formatter session. summary() Prints the summary report (in JUnit) after all tests are run. add_testsuite($suite) Adds the given XML test $suite to the list of test suites that we've executed and need to summarize. AUTHOR Graham TerMarsch Many thanks to Andy Armstrong and all those involved for the fabulous set of tests in Test::Harness; 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 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. SEE ALSO TAP::Formatter::Console TAP::Formatter::JUnit::Session Hudson home page JUnitXSchema.xsd JUnit parsing in Bamboo . Changes100644001750001750 1241414751004727 16343 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17Revision history for Perl extension TAP::Formatter::JUnit. 0.17 2025-02-05 17:01:10-08:00 America/Vancouver - Bump minimum required Perl to 5.010; XML::Generator v1.11 now requires that as a minimum acceptable Perl. - Skip BAIL_OUT test when using Test::Harness 3.45_01-3.48, as those versions contained a bug which output a double summary. Fixes GH#15 0.16 2022-05-14 21:50:19-07:00 America/Vancouver - Sigh... neglected to consider development versions of Test::Harness in the recent updates to "t/formatter.t"; although the fixes were released in v3.44, they were also present in the v3.43_* development versions. 0.15 2022-05-06 11:45:06-07:00 America/Vancouver - Use "version.pm" to compare version numbers, in "t/formatter.t". Addresses some CPANTester failures. 0.14 2022-04-30 12:36:14-07:00 America/Vancouver - Update "t/formatter.t" to accommodate fixes made in Test::Harness v3.44 (most specifically w.r.t. the "bailout" test). david-dot-krupicka++ ppisar++ pghmcfc++ 0.13 2021-05-04 10:04:17-07:00 America/Vancouver - Fix install of "script/tap2junit" 0.12 2021-01-05 21:23:07-08:00 America/Vancouver - Switch to Dist::Zilla for release management. - Various POD fixups; syntax, spelling, SYNOPSIS fixes - Cleaned up and removed unused variables - Bump minimum required Perl to 5.008, as that's the oldest version I can still test against. 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 LICENSE100644001750001750 4644314751004727 16066 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17This software is copyright (c) 2025 by Graham TerMarsch. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2025 by Graham TerMarsch. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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 1, 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Perl Artistic License 1.0 --- This software is Copyright (c) 2025 by Graham TerMarsch. This is free software, licensed under: The Perl Artistic License 1.0 The "Artistic License" Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder as specified below. "Copyright Holder" is whoever is named in the copyright or copyrights for the package. "You" is you, if you're thinking about copying or distributing this Package. "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) give non-standard executables non-standard names, and clearly document the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whoever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package. 7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language. 8. Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package. 9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End MANIFEST100644001750001750 564714751004727 16173 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.032. Changes LICENSE MANIFEST META.json META.yml Makefile.PL README lib/TAP/Formatter/JUnit.pm lib/TAP/Formatter/JUnit/Result.pm lib/TAP/Formatter/JUnit/Session.pm script/tap2junit t/00-report-prereqs.dd t/00-report-prereqs.t t/01-compile.t t/data/tap/bad_chars t/data/tap/bailout 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/junit/bad_chars t/data/tap/junit/bailout 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/skip_nomsg t/data/tap/junit/skipall t/data/tap/junit/skipall_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/tap/no_nums t/data/tap/simple t/data/tap/simple_fail t/data/tap/simple_yaml t/data/tap/skip t/data/tap/skip_nomsg t/data/tap/skipall t/data/tap/skipall_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/tests/bad_chars t/data/tests/bailout 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/junit/bad_chars t/data/tests/junit/bailout 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/skip_nomsg t/data/tests/junit/skipall t/data/tests/junit/skipall_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 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/skip_nomsg t/data/tests/skipall t/data/tests/skipall_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/formatter.t t/passing-todos.t t/tap2junit-filter.t t/tap2junit-name.t t/tap2junit.t t/timer.t xt/author/clean-namespaces.t xt/author/distmeta.t xt/author/eof.t xt/author/eol.t xt/author/minimum-version.t xt/author/no-breakpoints.t xt/author/no-tabs.t xt/author/pod-coverage.t xt/author/pod-spell.t xt/author/pod-syntax.t xt/author/synopsis.t xt/release/kwalitee.t xt/release/unused-vars.t xt/timer.t META.yml100644001750001750 226214751004727 16301 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17--- abstract: 'Harness output delegate for JUnit output' author: - 'Graham TerMarsch ' build_requires: ExtUtils::MakeMaker: '0' File::Spec: '0' File::Temp: '0' IO::Handle: '0' IO::Scalar: '0' IPC::Open3: '0' IPC::Run: '0' Test::DiagINC: '0.002' Test::More: '0.96' Test::XML: '0' version: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 generated_by: 'Dist::Zilla version 6.032, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: TAP-Formatter-JUnit no_index: directory: - local - t - xt requires: File::Slurp: '0' Moose: '0' MooseX::NonMoose: '0' TAP::Harness: '3.12' XML::Generator: '0' namespace::clean: '0' perl: '5.010' resources: bugtracker: https://github.com/bleargh45/TAP-Formatter-JUnit/issues homepage: https://metacpan.org/release/TAP-Formatter-JUnit/ repository: git://github.com/bleargh45/TAP-Formatter-JUnit.git version: '0.17' x_generated_by_perl: v5.40.1 x_serialization_backend: 'YAML::Tiny version 1.76' x_spdx_expression: 'Artistic-1.0-Perl OR GPL-1.0-or-later' x_static_install: 1 t000755001750001750 014751004727 15131 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17timer.t100644001750001750 270214751004727 16577 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t#!/usr/bin/perl use strict; use warnings; use if $ENV{AUTOMATED_TESTING}, 'Test::DiagINC'; 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; } META.json100644001750001750 551414751004727 16454 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17{ "abstract" : "Harness output delegate for JUnit output", "author" : [ "Graham TerMarsch " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.032, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "TAP-Formatter-JUnit", "no_index" : { "directory" : [ "local", "t", "xt" ] }, "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "develop" : { "requires" : { "Dist::Zilla" : "5", "Dist::Zilla::PluginBundle::Author::GTERMARS" : "0.04", "Pod::Coverage::TrustPod" : "0", "Software::License::Perl_5" : "0", "Test::CPAN::Meta" : "0", "Test::CleanNamespaces" : "0.15", "Test::EOF" : "0", "Test::EOL" : "0", "Test::Kwalitee" : "1.21", "Test::MinimumVersion" : "0", "Test::More" : "0.88", "Test::NoBreakpoints" : "0.15", "Test::NoTabs" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08", "Test::Spelling" : "0.17", "Test::Synopsis" : "0", "Test::Vars" : "0" } }, "runtime" : { "requires" : { "File::Slurp" : "0", "Moose" : "0", "MooseX::NonMoose" : "0", "TAP::Harness" : "3.12", "XML::Generator" : "0", "namespace::clean" : "0", "perl" : "5.010" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "ExtUtils::MakeMaker" : "0", "File::Spec" : "0", "File::Temp" : "0", "IO::Handle" : "0", "IO::Scalar" : "0", "IPC::Open3" : "0", "IPC::Run" : "0", "Test::DiagINC" : "0.002", "Test::More" : "0.96", "Test::XML" : "0", "version" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/bleargh45/TAP-Formatter-JUnit/issues" }, "homepage" : "https://metacpan.org/release/TAP-Formatter-JUnit/", "repository" : { "type" : "git", "url" : "git://github.com/bleargh45/TAP-Formatter-JUnit.git", "web" : "https://github.com/bleargh45/TAP-Formatter-JUnit" } }, "version" : "0.17", "x_generated_by_perl" : "v5.40.1", "x_serialization_backend" : "Cpanel::JSON::XS version 4.39", "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later", "x_static_install" : 1 } xt000755001750001750 014751004727 15321 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17timer.t100644001750001750 726214751004727 16775 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/xt#!/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; } Makefile.PL100644001750001750 346214751004727 17005 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.032. use strict; use warnings; use 5.010; use ExtUtils::MakeMaker; my %WriteMakefileArgs = ( "ABSTRACT" => "Harness output delegate for JUnit output", "AUTHOR" => "Graham TerMarsch ", "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => 0 }, "DISTNAME" => "TAP-Formatter-JUnit", "EXE_FILES" => [ "script/tap2junit" ], "LICENSE" => "perl", "MIN_PERL_VERSION" => "5.010", "NAME" => "TAP::Formatter::JUnit", "PREREQ_PM" => { "File::Slurp" => 0, "Moose" => 0, "MooseX::NonMoose" => 0, "TAP::Harness" => "3.12", "XML::Generator" => 0, "namespace::clean" => 0 }, "TEST_REQUIRES" => { "ExtUtils::MakeMaker" => 0, "File::Spec" => 0, "File::Temp" => 0, "IO::Handle" => 0, "IO::Scalar" => 0, "IPC::Open3" => 0, "IPC::Run" => 0, "Test::DiagINC" => "0.002", "Test::More" => "0.96", "Test::XML" => 0, "version" => 0 }, "VERSION" => "0.17", "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "ExtUtils::MakeMaker" => 0, "File::Slurp" => 0, "File::Spec" => 0, "File::Temp" => 0, "IO::Handle" => 0, "IO::Scalar" => 0, "IPC::Open3" => 0, "IPC::Run" => 0, "Moose" => 0, "MooseX::NonMoose" => 0, "TAP::Harness" => "3.12", "Test::DiagINC" => "0.002", "Test::More" => "0.96", "Test::XML" => 0, "XML::Generator" => 0, "namespace::clean" => 0, "version" => 0 ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); tap2junit.t100644001750001750 245214751004727 17401 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t#!/usr/bin/perl use strict; use warnings; use if $ENV{AUTOMATED_TESTING}, 'Test::DiagINC'; 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 -Ilib 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; } } formatter.t100644001750001750 552414751004727 17467 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t#!/usr/bin/perl use strict; use warnings; use if $ENV{AUTOMATED_TESTING}, 'Test::DiagINC'; use Test::More; use Test::XML; use File::Slurp qw(slurp); use TAP::Harness; use IO::Scalar; use version; ############################################################################### # 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) { # Where is the JUnit output we should be expecting? (my $junit = $test) =~ s{/tests/}{/tests/junit/}; # Process TAP, and turn it into 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); # OVER-RIDE: With Test::Harness prior to v3.43, the "bailout" test would # result in zero/no output. This was fixed in Test::Harness v3.43_0* # development releases, but WE need to watch for and provide accommodations # for newer/older versions. unless (version->parse($TAP::Harness::VERSION) > version->parse(3.43)) { if ($test =~ /bailout/) { $expected = ''; } } # Skip this test if it is a bailout test, and we have a broken version of # Test::Harness SKIP: { # Should we be skipping "bailout" tests, because of a broken # Test::Harness? # # A handful of Test::Harness releases contained a bug which resulted in # a double-summary being output on BAIL_OUT, and which affect our # expected test output. my $SKIP_BAILOUT = 0; { my $v_harness = version->parse($TAP::Harness::VERSION); my $v_broken_at = version->parse("3.45_01"); my $v_fixed_at = version->parse("3.50"); $SKIP_BAILOUT = 1 if ( ($v_harness >= $v_broken_at) && ($v_harness < $v_fixed_at) ); } skip "Broken Test::Harness installed; skipping BAIL_OUT test", 1 if ($SKIP_BAILOUT && ($test =~ /bailout/)); # 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 or diag "GOT: ", explain($received); } else { is $received, $expected, $test or diag "GOT: ", explain($received); } } } tap000755001750001750 014751004727 16626 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/datadie100644001750001750 014751004727 17360 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap01-compile.t100644001750001750 563214751004727 17332 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/tuse 5.006; use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::Compile 2.058 use if $ENV{AUTOMATED_TESTING}, 'Test::DiagINC'; use Test::More 0.94; plan tests => 4 + ($ENV{AUTHOR_TESTING} ? 1 : 0); my @module_files = ( 'TAP/Formatter/JUnit.pm', 'TAP/Formatter/JUnit/Result.pm', 'TAP/Formatter/JUnit/Session.pm' ); my @scripts = ( 'script/tap2junit' ); # fake home for cpan-testers use File::Temp; local $ENV{HOME} = File::Temp::tempdir( CLEANUP => 1 ); my @switches = ( -d 'blib' ? '-Mblib' : '-Ilib', ); use File::Spec; use IPC::Open3; use IO::Handle; open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!"; my @warnings; for my $lib (@module_files) { # see L my $stderr = IO::Handle->new; diag('Running: ', join(', ', map { my $str = $_; $str =~ s/'/\\'/g; q{'} . $str . q{'} } $^X, @switches, '-e', "require q[$lib]")) if $ENV{PERL_COMPILE_TEST_DEBUG}; my $pid = open3($stdin, '>&STDERR', $stderr, $^X, @switches, '-e', "require q[$lib]"); binmode $stderr, ':crlf' if $^O eq 'MSWin32'; my @_warnings = <$stderr>; waitpid($pid, 0); is($?, 0, "$lib loaded ok"); shift @_warnings if @_warnings and $_warnings[0] =~ /^Using .*\bblib/ and not eval { +require blib; blib->VERSION('1.01') }; if (@_warnings) { warn @_warnings; push @warnings, @_warnings; } } foreach my $file (@scripts) { SKIP: { open my $fh, '<', $file or warn("Unable to open $file: $!"), next; my $line = <$fh>; close $fh and skip("$file isn't perl", 1) unless $line =~ /^#!\s*(?:\S*perl\S*)((?:\s+-\w*)*)(?:\s*#.*)?$/; @switches = (@switches, split(' ', $1)) if $1; close $fh and skip("$file uses -T; not testable with PERL5LIB", 1) if grep { $_ eq '-T' } @switches and $ENV{PERL5LIB}; my $stderr = IO::Handle->new; diag('Running: ', join(', ', map { my $str = $_; $str =~ s/'/\\'/g; q{'} . $str . q{'} } $^X, @switches, '-c', $file)) if $ENV{PERL_COMPILE_TEST_DEBUG}; my $pid = open3($stdin, '>&STDERR', $stderr, $^X, @switches, '-c', $file); binmode $stderr, ':crlf' if $^O eq 'MSWin32'; my @_warnings = <$stderr>; waitpid($pid, 0); is($?, 0, "$file compiled ok"); shift @_warnings if @_warnings and $_warnings[0] =~ /^Using .*\bblib/ and not eval { +require blib; blib->VERSION('1.01') }; # in older perls, -c output is simply the file portion of the path being tested if (@_warnings = grep { !/\bsyntax OK$/ } grep { chomp; $_ ne (File::Spec->splitpath($file))[2] } @_warnings) { warn @_warnings; push @warnings, @_warnings; } } } is(scalar(@warnings), 0, 'no warnings found') or diag 'got warnings: ', explain(\@warnings) if $ENV{AUTHOR_TESTING}; BAIL_OUT("Compilation problems") if !Test::More->builder->is_passing; skip100644001750001750 6314751004727 17616 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap1..5 ok 1 ok 2 # skip rain delay ok 3 ok 4 ok 5 todo100644001750001750 5414751004727 17615 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap1..5 todo 3 2; ok 1 ok 2 not ok 3 ok 4 ok 5 author000755001750001750 014751004727 16623 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/xteol.t100644001750001750 661114751004727 17733 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/xt/authoruse strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::EOL 0.19 use Test::More 0.88; use Test::EOL; my @files = ( 'lib/TAP/Formatter/JUnit.pm', 'lib/TAP/Formatter/JUnit/Result.pm', 'lib/TAP/Formatter/JUnit/Session.pm', 'script/tap2junit', 't/00-report-prereqs.dd', 't/00-report-prereqs.t', 't/01-compile.t', 't/data/tap/bad_chars', 't/data/tap/bailout', '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/junit/bad_chars', 't/data/tap/junit/bailout', '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/skip_nomsg', 't/data/tap/junit/skipall', 't/data/tap/junit/skipall_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/tap/no_nums', 't/data/tap/simple', 't/data/tap/simple_fail', 't/data/tap/simple_yaml', 't/data/tap/skip', 't/data/tap/skip_nomsg', 't/data/tap/skipall', 't/data/tap/skipall_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/tests/bad_chars', 't/data/tests/bailout', '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/junit/bad_chars', 't/data/tests/junit/bailout', '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/skip_nomsg', 't/data/tests/junit/skipall', 't/data/tests/junit/skipall_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', '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/skip_nomsg', 't/data/tests/skipall', 't/data/tests/skipall_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/formatter.t', 't/passing-todos.t', 't/tap2junit-filter.t', 't/tap2junit-name.t', 't/tap2junit.t', 't/timer.t' ); eol_unix_ok($_, { trailing_whitespace => 1 }) foreach @files; done_testing; eof.t100644001750001750 43714751004727 17705 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/xt/authoruse strict; use warnings; use Test::More; # Generated by Dist::Zilla::Plugin::Test::EOF 0.0600 eval "use Test::EOF"; plan skip_all => 'Test::EOF required to test for correct end of file flag' if $@; all_perl_files_ok({ minimum_newlines => 1, maximum_newlines => 4 }); done_testing(); script000755001750001750 014751004727 16172 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17tap2junit100755001750001750 1123714751004727 20224 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/script#!/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; }; 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 empty100644001750001750 014751004727 17755 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/taptests000755001750001750 014751004727 17204 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/datadie100644001750001750 13114751004727 20003 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/testseval "use vmsish 'hushed'" if ($^O eq 'VMS'); exit 1; # exit because die() can be noisy passing-todos.t100644001750001750 260614751004727 20254 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t#!/usr/bin/perl use strict; use warnings; use if $ENV{AUTOMATED_TESTING}, 'Test::DiagINC'; 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'; } simple100644001750001750 3614751004727 20141 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap1..5 ok 1 ok 2 ok 3 ok 4 ok 5 skip100644001750001750 12214751004727 20210 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/testsprint < 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 -Ilib script/tap2junit @args 2>$null }); } bailout100644001750001750 7414751004727 20311 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap1..5 ok 1 ok 2 ok 3 Bail out! GERONIMMMOOOOOO!!! ok 4 ok 5 no_nums100644001750001750 3014751004727 20320 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap1..5 ok ok not ok ok ok skipall100644001750001750 2614751004727 20306 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap1..0 # skipping: rope empty100644001750001750 6014751004727 20361 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests__END__ Used to exercise the "empty test" case. too_many100644001750001750 5014751004727 20471 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap1..3 ok 1 ok 2 ok 3 ok 4 ok 5 ok 6 ok 7 simple100644001750001750 7514751004727 20522 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/testsprint < 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, '-Ilib', 'script/tap2junit', '--name' => 'data_tap_simple', '-'); my ($out, $err); run \@cmd, \$tap, \$out, \$err or die $?; is_xml $out, $xml, 'results generated on STDOUT'; } bad_chars100644001750001750 12514751004727 20575 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap1..3 ok 1 - Control chars  ok 2 - Weird control chars  ok 3 - Unicode Þϴ쌇 junit000755001750001750 014751004727 17757 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tapdie100644001750001750 014751004727 20511 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junitbailout100644001750001750 23414751004727 20705 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests# Sleep makes Mac OS open3 race problem more repeatable sleep 1; print < todo100644001750001750 101714751004727 21006 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit too_many100644001750001750 15514751004727 21075 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/testsprint < 1; use ExtUtils::MakeMaker; use File::Spec; # from $version::LAX my $lax_version_re = qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )? | (?:\.[0-9]+) (?:_[0-9]+)? ) | (?: v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )? | (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)? ) )/x; # hide optional CPAN::Meta modules from prereq scanner # and check if they are available my $cpan_meta = "CPAN::Meta"; my $cpan_meta_pre = "CPAN::Meta::Prereqs"; my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic # Verify requirements? my $DO_VERIFY_PREREQS = 1; sub _max { my $max = shift; $max = ( $_ > $max ) ? $_ : $max for @_; return $max; } sub _merge_prereqs { my ($collector, $prereqs) = @_; # CPAN::Meta::Prereqs object if (ref $collector eq $cpan_meta_pre) { return $collector->with_merged_prereqs( CPAN::Meta::Prereqs->new( $prereqs ) ); } # Raw hashrefs for my $phase ( keys %$prereqs ) { for my $type ( keys %{ $prereqs->{$phase} } ) { for my $module ( keys %{ $prereqs->{$phase}{$type} } ) { $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module}; } } } return $collector; } my @include = qw( ); my @exclude = qw( ); # Add static prereqs to the included modules list my $static_prereqs = do './t/00-report-prereqs.dd'; # Merge all prereqs (either with ::Prereqs or a hashref) my $full_prereqs = _merge_prereqs( ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ), $static_prereqs ); # Add dynamic prereqs to the included modules list (if we can) my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; my $cpan_meta_error; if ( $source && $HAS_CPAN_META && (my $meta = eval { CPAN::Meta->load_file($source) } ) ) { $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs); } else { $cpan_meta_error = $@; # capture error from CPAN::Meta->load_file($source) $source = 'static metadata'; } my @full_reports; my @dep_errors; my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs; # Add static includes into a fake section for my $mod (@include) { $req_hash->{other}{modules}{$mod} = 0; } for my $phase ( qw(configure build test runtime develop other) ) { next unless $req_hash->{$phase}; next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING}); for my $type ( qw(requires recommends suggests conflicts modules) ) { next unless $req_hash->{$phase}{$type}; my $title = ucfirst($phase).' '.ucfirst($type); my @reports = [qw/Module Want Have/]; for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) { next if grep { $_ eq $mod } @exclude; my $want = $req_hash->{$phase}{$type}{$mod}; $want = "undef" unless defined $want; $want = "any" if !$want && $want == 0; if ($mod eq 'perl') { push @reports, ['perl', $want, $]]; next; } my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; my $file = $mod; $file =~ s{::}{/}g; $file .= ".pm"; my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC; if ($prefix) { my $have = MM->parse_version( File::Spec->catfile($prefix, $file) ); $have = "undef" unless defined $have; push @reports, [$mod, $want, $have]; if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) { if ( $have !~ /\A$lax_version_re\z/ ) { push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)"; } elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) { push @dep_errors, "$mod version '$have' is not in required range '$want'"; } } } else { push @reports, [$mod, $want, "missing"]; if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) { push @dep_errors, "$mod is not installed ($req_string)"; } } } if ( @reports ) { push @full_reports, "=== $title ===\n\n"; my $ml = _max( map { length $_->[0] } @reports ); my $wl = _max( map { length $_->[1] } @reports ); my $hl = _max( map { length $_->[2] } @reports ); if ($type eq 'modules') { splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports; } else { splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports; } push @full_reports, "\n"; } } } if ( @full_reports ) { diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports; } if ( $cpan_meta_error || @dep_errors ) { diag "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n"; } if ( $cpan_meta_error ) { my ($orig_source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; diag "\nCPAN::Meta->load_file('$orig_source') failed with: $cpan_meta_error\n"; } if ( @dep_errors ) { diag join("\n", "\nThe following REQUIRED prerequisites were not satisfied:\n", @dep_errors, "\n" ); } pass('Reported prereqs'); # vim: ts=4 sts=4 sw=4 et: pod-spell.t100644001750001750 46214751004727 21031 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/xt/authoruse strict; use warnings; use Test::More; # generated by Dist::Zilla::Plugin::Test::PodSpelling 2.007006 use Test::Spelling 0.17; use Pod::Wordlist; add_stopwords(); all_pod_files_spelling_ok( qw( bin lib ) ); __DATA__ Formatter Graham JUnit Result Session TAP TerMarsch cpan lib script tap2junit release000755001750001750 014751004727 16741 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/xtkwalitee.t100644001750001750 27514751004727 21057 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/xt/release# this test was generated with Dist::Zilla::Plugin::Test::Kwalitee 2.12 use strict; use warnings; use Test::More 0.88; use Test::Kwalitee 1.21 'kwalitee_ok'; kwalitee_ok(); done_testing; descriptive100644001750001750 21114751004727 21204 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap1..5 ok 1 Interlock activated ok 2 Megathrusters are go ok 3 Head formed ok 4 Blazing sword formed ok 5 Robeast destroyed simple_fail100644001750001750 4614751004727 21135 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap1..5 ok 1 not ok 2 ok 3 ok 4 not ok 5 simple_yaml100644001750001750 32014751004727 21177 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tapTAP 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 todo_inline100644001750001750 22614751004727 21174 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap1..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 empty100644001750001750 014751004727 21106 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junitbad_chars100644001750001750 16414751004727 21156 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/testsprint < 00-report-prereqs.dd100644001750001750 564014751004727 21016 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/tdo { my $x = { 'configure' => { 'requires' => { 'ExtUtils::MakeMaker' => '0' } }, 'develop' => { 'requires' => { 'Dist::Zilla' => '5', 'Dist::Zilla::PluginBundle::Author::GTERMARS' => '0.04', 'Pod::Coverage::TrustPod' => '0', 'Software::License::Perl_5' => '0', 'Test::CPAN::Meta' => '0', 'Test::CleanNamespaces' => '0.15', 'Test::EOF' => '0', 'Test::EOL' => '0', 'Test::Kwalitee' => '1.21', 'Test::MinimumVersion' => '0', 'Test::More' => '0.88', 'Test::NoBreakpoints' => '0.15', 'Test::NoTabs' => '0', 'Test::Pod' => '1.41', 'Test::Pod::Coverage' => '1.08', 'Test::Spelling' => '0.17', 'Test::Synopsis' => '0', 'Test::Vars' => '0' } }, 'runtime' => { 'requires' => { 'File::Slurp' => '0', 'Moose' => '0', 'MooseX::NonMoose' => '0', 'TAP::Harness' => '3.12', 'XML::Generator' => '0', 'namespace::clean' => '0', 'perl' => '5.010' } }, 'test' => { 'recommends' => { 'CPAN::Meta' => '2.120900' }, 'requires' => { 'ExtUtils::MakeMaker' => '0', 'File::Spec' => '0', 'File::Temp' => '0', 'IO::Handle' => '0', 'IO::Scalar' => '0', 'IPC::Open3' => '0', 'IPC::Run' => '0', 'Test::DiagINC' => '0.002', 'Test::More' => '0.96', 'Test::XML' => '0', 'version' => '0' } } }; $x; }pod-syntax.t100644001750001750 25214751004727 21235 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/xt/author#!perl # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); die_head_end100644001750001750 2414751004727 21215 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tapok 1 ok 2 ok 3 ok 4 simple100644001750001750 65414751004727 21320 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit skip_nomsg100644001750001750 4614751004727 21400 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/testsprint < todo100644001750001750 102114751004727 21357 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit skipall_nomsg100644001750001750 514751004727 21466 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap1..0 stdout_stderr100644001750001750 12214751004727 21571 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap# comments ok 1 ok 2 ok 3 # comment ok 4 # more ignored stuff # and yet more 1..4 todo_misparse100644001750001750 4714751004727 21522 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap1..1 not ok 1 Hamlette # TODOORNOTTODO bailout100644001750001750 71314751004727 21462 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit no_nums100644001750001750 101614751004727 21516 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit skipall100644001750001750 36614751004727 21466 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit descriptive100644001750001750 25014751004727 21565 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/testsprint < pod-coverage.t100644001750001750 36514751004727 21507 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/xt/author#!perl # This file was automatically generated by Dist::Zilla::Plugin::PodCoverageTests. use strict; use warnings; use Test::Pod::Coverage 1.08; use Pod::Coverage::TrustPod; all_pod_coverage_ok({ coverage_class => 'Pod::Coverage::TrustPod' }); unused-vars.t100644001750001750 14214751004727 21517 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/xt/releaseuse Test::More 0.96 tests => 1; use Test::Vars; subtest 'unused vars' => sub { all_vars_ok(); }; die_unfinished100644001750001750 2414751004727 21622 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap1..4 ok 1 ok 2 ok 3 too_many100644001750001750 157414751004727 21676 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit die_head_end100644001750001750 15214751004727 21615 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/testsprint < Formatter000755001750001750 014751004727 20023 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/lib/TAPJUnit.pm100644001750001750 1231614751004727 21575 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/lib/TAP/Formatterpackage TAP::Formatter::JUnit; use Moose; use MooseX::NonMoose; extends qw( TAP::Formatter::Console ); use XML::Generator; use TAP::Formatter::JUnit::Session; use namespace::clean; our $VERSION = '0.17'; 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() ############################################################################### # Prints the summary report (in JUnit) after all tests are run. sub summary { my $self = shift; return if $self->silent(); my @suites = @{$self->testsuites}; print { $self->stdout } $self->xml->testsuites( @suites ); } 1; =for stopwords xml testsuites TODO parseable JUnitXSchema.xsd =head1 NAME TAP::Formatter::JUnit - Harness output delegate for JUnit output =head1 SYNOPSIS On the command line, with F: =for test_synopsis BEGIN { die "SKIP: This isn't Perl, but shell" } $ prove --formatter TAP::Formatter::JUnit ... Or, in your own scripts: use TAP::Harness; # What TAP output did we save from a previous run, with # PERL_TEST_HARNESS_DUMP_TAP=tap/ my @tests = glob("tap/*.t"); # Convert the TAP to JUnit 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 parseable formats). Timing information is included in the JUnit XML, I you specified C<--timer> when you ran F. In standard use, a "passing TODO" is treated as failure conditions (and is 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 open_test($test, $parser) Over-ridden C method. Creates a C session, instead of a console formatter session. =item summary() Prints the summary report (in JUnit) after all tests are run. =item add_testsuite($suite) 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 and all those involved 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 =over =item L =item L =item L =item L =item L. =back =cut die_last_minute100644001750001750 3114751004727 22010 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tapok 1 ok 2 ok 3 ok 4 1..4 bad_chars100644001750001750 107514751004727 21753 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit skipall_nomsg100644001750001750 3014751004727 22062 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/testsprint "1..0\n"; exit 0; stdout_stderr100644001750001750 20314751004727 22147 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/testsuse Test::More 'no_plan'; diag 'comments'; ok 1; ok 1; ok 1; diag 'comment'; ok 1; diag 'more ignored stuff'; diag 'and yet more'; todo_misparse100644001750001750 7314751004727 22077 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/testsprint <<'END'; 1..1 not ok 1 Hamlette # TODOORNOTTODO END no_nums100644001750001750 102014751004727 22067 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit skipall100644001750001750 37014751004727 22037 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit bailout100644001750001750 57614751004727 22047 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit no-breakpoints.t100644001750001750 31414751004727 22061 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/xt/authoruse strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::NoBreakpoints 0.0.2 use Test::More 0.88; use Test::NoBreakpoints 0.15; all_files_no_breakpoints_ok(); done_testing; skip_nomsg100644001750001750 42714751004727 22176 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit die_unfinished100644001750001750 15214751004727 22222 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/testsprint < minimum-version.t100644001750001750 15414751004727 22266 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/xt/authoruse strict; use warnings; use Test::More; use Test::MinimumVersion; all_minimum_version_from_metayml_ok(); descriptive100644001750001750 117014751004727 22362 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit simple_fail100644001750001750 120714751004727 22326 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit simple_yaml100644001750001750 114314751004727 22354 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit todo_inline100644001750001750 131214751004727 22342 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit die_last_minute100644001750001750 15714751004727 22417 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/testsprint < clean-namespaces.t100644001750001750 36114751004727 22327 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/xt/authoruse strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::CleanNamespaces 0.006 use Test::More 0.94; use Test::CleanNamespaces 0.15; subtest all_namespaces_clean => sub { all_namespaces_clean() }; done_testing; die_head_end100644001750001750 66314751004727 22377 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit skip_nomsg100644001750001750 43114751004727 22547 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit skipall_nomsg100644001750001750 35314751004727 22665 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit stdout_stderr100644001750001750 70414751004727 22730 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit todo_misparse100644001750001750 74214751004727 22675 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit descriptive100644001750001750 117214751004727 22742 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit simple_fail100644001750001750 121114751004727 22677 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit simple_yaml100644001750001750 114514751004727 22734 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit todo_inline100644001750001750 131414751004727 22722 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit descriptive_trailing100644001750001750 21114751004727 23075 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tapok 1 Interlock activated ok 2 Megathrusters are go ok 3 Head formed ok 4 Blazing sword formed ok 5 Robeast destroyed 1..5 die_unfinished100644001750001750 64614751004727 23005 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit die_head_end100644001750001750 100314751004727 22762 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit die_last_minute100644001750001750 61514751004727 23171 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit skipall_nomsg100644001750001750 35514751004727 23245 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit stdout_stderr100644001750001750 70614751004727 23310 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit todo_misparse100644001750001750 74414751004727 23255 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit JUnit000755001750001750 014751004727 21054 5ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/lib/TAP/FormatterResult.pm100644001750001750 175514751004727 23040 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/lib/TAP/Formatter/JUnitpackage TAP::Formatter::JUnit::Result; use Moose; use namespace::clean; 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 descriptive_trailing100644001750001750 25014751004727 23456 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/testsprint < Session.pm100644001750001750 3301714751004727 23221 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/lib/TAP/Formatter/JUnitpackage 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; use namespace::clean; 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; =for stopwords instantiation testcases =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 _initialize($arg_for) Over-ridden private initializer, so we can accept a new "passing_todo_ok" argument at instantiation time. =item result($result) 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 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. =item dump_junit_xml($testsuite) Dumps the JUnit for the given XML C<$testsuite>, to the directory specified by C. =item add_testcase($case) Adds an XML test C<$case> to the list of testcases we've run in this session. =item xml() 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 die_last_minute100644001750001750 101314751004727 23560 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit descriptive_trailing100644001750001750 120114751004727 24246 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tap/junit descriptive_trailing100644001750001750 120314751004727 24626 0ustar00grahamgraham000000000000TAP-Formatter-JUnit-0.17/t/data/tests/junit