1b8851fccSafresh1# Helper functions for test programs written in Perl. 2b8851fccSafresh1# 3b8851fccSafresh1# This module provides a collection of helper functions used by test programs 4b8851fccSafresh1# written in Perl. This is a general collection of functions that can be used 5b8851fccSafresh1# by both C packages with Automake and by stand-alone Perl modules. See 6b8851fccSafresh1# Test::RRA::Automake for additional functions specifically for C Automake 7b8851fccSafresh1# distributions. 8b46d8ef2Safresh1# 9b46d8ef2Safresh1# SPDX-License-Identifier: MIT 10b8851fccSafresh1 11b8851fccSafresh1package Test::RRA; 12b8851fccSafresh1 13*e0680481Safresh1use 5.010; 1456d68f1eSafresh1use base qw(Exporter); 15b8851fccSafresh1use strict; 16b8851fccSafresh1use warnings; 17b8851fccSafresh1 1856d68f1eSafresh1use Carp qw(croak); 195759b3d2Safresh1use File::Temp; 20b8851fccSafresh1 2156d68f1eSafresh1# Abort if Test::More was loaded before Test::RRA to be sure that we get the 2256d68f1eSafresh1# benefits of the Test::More probing below. 2356d68f1eSafresh1if ($INC{'Test/More.pm'}) { 2456d68f1eSafresh1 croak('Test::More loaded before Test::RRA'); 2556d68f1eSafresh1} 2656d68f1eSafresh1 2756d68f1eSafresh1# Red Hat's base perl package doesn't include Test::More (one has to install 2856d68f1eSafresh1# the perl-core package in addition). Try to detect this and skip any Perl 2956d68f1eSafresh1# tests if Test::More is not present. This relies on Test::RRA being included 3056d68f1eSafresh1# before Test::More. 3156d68f1eSafresh1eval { 3256d68f1eSafresh1 require Test::More; 3356d68f1eSafresh1 Test::More->import(); 3456d68f1eSafresh1}; 3556d68f1eSafresh1if ($@) { 3656d68f1eSafresh1 print "1..0 # SKIP Test::More required for test\n" 3756d68f1eSafresh1 or croak('Cannot write to stdout'); 3856d68f1eSafresh1 exit 0; 3956d68f1eSafresh1} 40b8851fccSafresh1 41b8851fccSafresh1# Declare variables that should be set in BEGIN for robustness. 4256d68f1eSafresh1our (@EXPORT_OK, $VERSION); 43b8851fccSafresh1 44b8851fccSafresh1# Set $VERSION and everything export-related in a BEGIN block for robustness 45b8851fccSafresh1# against circular module loading (not that we load any modules, but 46b8851fccSafresh1# consistency is good). 47b8851fccSafresh1BEGIN { 485759b3d2Safresh1 @EXPORT_OK = qw( 495759b3d2Safresh1 is_file_contents skip_unless_author skip_unless_automated use_prereq 505759b3d2Safresh1 ); 51b8851fccSafresh1 52b8851fccSafresh1 # This version should match the corresponding rra-c-util release, but with 53b8851fccSafresh1 # two digits for the minor version, including a leading zero if necessary, 54b8851fccSafresh1 # so that it will sort properly. 55*e0680481Safresh1 $VERSION = '10.03'; 565759b3d2Safresh1} 575759b3d2Safresh1 585759b3d2Safresh1# Compare a string to the contents of a file, similar to the standard is() 595759b3d2Safresh1# function, but to show the line-based unified diff between them if they 605759b3d2Safresh1# differ. 615759b3d2Safresh1# 625759b3d2Safresh1# $got - The output that we received 635759b3d2Safresh1# $expected - The path to the file containing the expected output 645759b3d2Safresh1# $message - The message to use when reporting the test results 655759b3d2Safresh1# 665759b3d2Safresh1# Returns: undef 675759b3d2Safresh1# Throws: Exception on failure to read or write files or run diff 685759b3d2Safresh1sub is_file_contents { 695759b3d2Safresh1 my ($got, $expected, $message) = @_; 705759b3d2Safresh1 715759b3d2Safresh1 # If they're equal, this is simple. 725759b3d2Safresh1 open(my $fh, '<', $expected) or BAIL_OUT("Cannot open $expected: $!\n"); 735759b3d2Safresh1 my $data = do { local $/ = undef; <$fh> }; 745759b3d2Safresh1 close($fh) or BAIL_OUT("Cannot close $expected: $!\n"); 755759b3d2Safresh1 if ($got eq $data) { 765759b3d2Safresh1 is($got, $data, $message); 775759b3d2Safresh1 return; 785759b3d2Safresh1 } 795759b3d2Safresh1 8056d68f1eSafresh1 # Otherwise, we show a diff, but only if we have IPC::System::Simple and 8156d68f1eSafresh1 # diff succeeds. Otherwise, we fall back on showing the full expected and 8256d68f1eSafresh1 # seen output. 8356d68f1eSafresh1 eval { 8456d68f1eSafresh1 require IPC::System::Simple; 855759b3d2Safresh1 865759b3d2Safresh1 my $tmp = File::Temp->new(); 875759b3d2Safresh1 my $tmpname = $tmp->filename; 885759b3d2Safresh1 print {$tmp} $got or BAIL_OUT("Cannot write to $tmpname: $!\n"); 895759b3d2Safresh1 my @command = ('diff', '-u', $expected, $tmpname); 905759b3d2Safresh1 my $diff = IPC::System::Simple::capturex([0 .. 1], @command); 915759b3d2Safresh1 diag($diff); 9256d68f1eSafresh1 }; 9356d68f1eSafresh1 if ($@) { 9456d68f1eSafresh1 diag('Expected:'); 9556d68f1eSafresh1 diag($expected); 9656d68f1eSafresh1 diag('Seen:'); 9756d68f1eSafresh1 diag($data); 9856d68f1eSafresh1 } 995759b3d2Safresh1 10056d68f1eSafresh1 # Report failure. 1015759b3d2Safresh1 ok(0, $message); 1025759b3d2Safresh1 return; 103b8851fccSafresh1} 104b8851fccSafresh1 105b8851fccSafresh1# Skip this test unless author tests are requested. Takes a short description 106b8851fccSafresh1# of what tests this script would perform, which is used in the skip message. 107b8851fccSafresh1# Calls plan skip_all, which will terminate the program. 108b8851fccSafresh1# 109b8851fccSafresh1# $description - Short description of the tests 110b8851fccSafresh1# 111b8851fccSafresh1# Returns: undef 112b8851fccSafresh1sub skip_unless_author { 113b8851fccSafresh1 my ($description) = @_; 114b8851fccSafresh1 if (!$ENV{AUTHOR_TESTING}) { 11556d68f1eSafresh1 plan(skip_all => "$description only run for author"); 116b8851fccSafresh1 } 117b8851fccSafresh1 return; 118b8851fccSafresh1} 119b8851fccSafresh1 120b8851fccSafresh1# Skip this test unless doing automated testing or release testing. This is 121b8851fccSafresh1# used for tests that should be run by CPAN smoke testing or during releases, 122b8851fccSafresh1# but not for manual installs by end users. Takes a short description of what 123b8851fccSafresh1# tests this script would perform, which is used in the skip message. Calls 124b8851fccSafresh1# plan skip_all, which will terminate the program. 125b8851fccSafresh1# 126b8851fccSafresh1# $description - Short description of the tests 127b8851fccSafresh1# 128b8851fccSafresh1# Returns: undef 129b8851fccSafresh1sub skip_unless_automated { 130b8851fccSafresh1 my ($description) = @_; 131b8851fccSafresh1 for my $env (qw(AUTOMATED_TESTING RELEASE_TESTING AUTHOR_TESTING)) { 132b8851fccSafresh1 return if $ENV{$env}; 133b8851fccSafresh1 } 13456d68f1eSafresh1 plan(skip_all => "$description normally skipped"); 135b8851fccSafresh1 return; 136b8851fccSafresh1} 137b8851fccSafresh1 138b8851fccSafresh1# Attempt to load a module and skip the test if the module could not be 139b8851fccSafresh1# loaded. If the module could be loaded, call its import function manually. 140b8851fccSafresh1# If the module could not be loaded, calls plan skip_all, which will terminate 141b8851fccSafresh1# the program. 142b8851fccSafresh1# 143b8851fccSafresh1# The special logic here is based on Test::More and is required to get the 144b8851fccSafresh1# imports to happen in the caller's namespace. 145b8851fccSafresh1# 146b8851fccSafresh1# $module - Name of the module to load 147b8851fccSafresh1# @imports - Any arguments to import, possibly including a version 148b8851fccSafresh1# 149b8851fccSafresh1# Returns: undef 150b8851fccSafresh1sub use_prereq { 151b8851fccSafresh1 my ($module, @imports) = @_; 152b8851fccSafresh1 153b8851fccSafresh1 # If the first import looks like a version, pass it as a bare string. 154b8851fccSafresh1 my $version = q{}; 155b8851fccSafresh1 if (@imports >= 1 && $imports[0] =~ m{ \A \d+ (?: [.][\d_]+ )* \z }xms) { 156b8851fccSafresh1 $version = shift(@imports); 157b8851fccSafresh1 } 158b8851fccSafresh1 159b8851fccSafresh1 # Get caller information to put imports in the correct package. 160b8851fccSafresh1 my ($package) = caller; 161b8851fccSafresh1 162b8851fccSafresh1 # Do the import with eval, and try to isolate it from the surrounding 163b8851fccSafresh1 # context as much as possible. Based heavily on Test::More::_eval. 164b8851fccSafresh1 ## no critic (BuiltinFunctions::ProhibitStringyEval) 165b8851fccSafresh1 ## no critic (ValuesAndExpressions::ProhibitImplicitNewlines) 166b8851fccSafresh1 my ($result, $error, $sigdie); 167b8851fccSafresh1 { 168b8851fccSafresh1 local $@ = undef; 169b8851fccSafresh1 local $! = undef; 170b8851fccSafresh1 local $SIG{__DIE__} = undef; 171b8851fccSafresh1 $result = eval qq{ 172b8851fccSafresh1 package $package; 173b8851fccSafresh1 use $module $version \@imports; 174b8851fccSafresh1 1; 175b8851fccSafresh1 }; 176b8851fccSafresh1 $error = $@; 177b8851fccSafresh1 $sigdie = $SIG{__DIE__} || undef; 178b8851fccSafresh1 } 179b8851fccSafresh1 180b8851fccSafresh1 # If the use failed for any reason, skip the test. 181b8851fccSafresh1 if (!$result || $error) { 182b8851fccSafresh1 my $name = length($version) > 0 ? "$module $version" : $module; 18356d68f1eSafresh1 plan(skip_all => "$name required for test"); 184b8851fccSafresh1 } 185b8851fccSafresh1 186b8851fccSafresh1 # If the module set $SIG{__DIE__}, we cleared that via local. Restore it. 187b8851fccSafresh1 ## no critic (Variables::RequireLocalizedPunctuationVars) 188b8851fccSafresh1 if (defined($sigdie)) { 189b8851fccSafresh1 $SIG{__DIE__} = $sigdie; 190b8851fccSafresh1 } 191b8851fccSafresh1 return; 192b8851fccSafresh1} 193b8851fccSafresh1 194b8851fccSafresh11; 195b8851fccSafresh1__END__ 196b8851fccSafresh1 197b8851fccSafresh1=for stopwords 198b8851fccSafresh1Allbery Allbery's DESC bareword sublicense MERCHANTABILITY NONINFRINGEMENT 199*e0680481Safresh1rra-c-util CPAN diff 200b8851fccSafresh1 201b8851fccSafresh1=head1 NAME 202b8851fccSafresh1 203b8851fccSafresh1Test::RRA - Support functions for Perl tests 204b8851fccSafresh1 205b8851fccSafresh1=head1 SYNOPSIS 206b8851fccSafresh1 207b8851fccSafresh1 use Test::RRA 208b8851fccSafresh1 qw(skip_unless_author skip_unless_automated use_prereq); 209b8851fccSafresh1 210b8851fccSafresh1 # Skip this test unless author tests are requested. 211b8851fccSafresh1 skip_unless_author('Coding style tests'); 212b8851fccSafresh1 213b8851fccSafresh1 # Skip this test unless doing automated or release testing. 214b8851fccSafresh1 skip_unless_automated('POD syntax tests'); 215b8851fccSafresh1 216b8851fccSafresh1 # Load modules, skipping the test if they're not available. 217b8851fccSafresh1 use_prereq('Perl6::Slurp', 'slurp'); 218b8851fccSafresh1 use_prereq('Test::Script::Run', '0.04'); 219b8851fccSafresh1 220b8851fccSafresh1=head1 DESCRIPTION 221b8851fccSafresh1 222b8851fccSafresh1This module collects utility functions that are useful for Perl test scripts. 223b8851fccSafresh1It assumes Russ Allbery's Perl module layout and test conventions and will 224b8851fccSafresh1only be useful for other people if they use the same conventions. 225b8851fccSafresh1 22656d68f1eSafresh1This module B<must> be loaded before Test::More or it will abort during 22756d68f1eSafresh1import. It will skip the test (by printing a skip message to standard output 22856d68f1eSafresh1and exiting with status 0, equivalent to C<plan skip_all>) during import if 22956d68f1eSafresh1Test::More is not available. This allows tests written in Perl using this 23056d68f1eSafresh1module to be skipped if run on a system with Perl but not Test::More, such as 23156d68f1eSafresh1Red Hat systems with the C<perl> package but not the C<perl-core> package 23256d68f1eSafresh1installed. 23356d68f1eSafresh1 234b8851fccSafresh1=head1 FUNCTIONS 235b8851fccSafresh1 236b8851fccSafresh1None of these functions are imported by default. The ones used by a script 237b8851fccSafresh1should be explicitly imported. 238b8851fccSafresh1 239b8851fccSafresh1=over 4 240b8851fccSafresh1 241*e0680481Safresh1=item is_file_contents(GOT, EXPECTED, MESSAGE) 242*e0680481Safresh1 243*e0680481Safresh1Check a string against the contents of a file, showing the differences if any 244*e0680481Safresh1using diff (if IPC::System::Simple and diff are available). GOT is the output 245*e0680481Safresh1the test received. EXPECTED is the path to a file containing the expected 246*e0680481Safresh1output (not the output itself). MESSAGE is a message to display alongside the 247*e0680481Safresh1test results. 248*e0680481Safresh1 249b8851fccSafresh1=item skip_unless_author(DESC) 250b8851fccSafresh1 251b8851fccSafresh1Checks whether AUTHOR_TESTING is set in the environment and skips the whole 252b8851fccSafresh1test (by calling C<plan skip_all> from Test::More) if it is not. DESC is a 253b8851fccSafresh1description of the tests being skipped. A space and C<only run for author> 254b8851fccSafresh1will be appended to it and used as the skip reason. 255b8851fccSafresh1 256b8851fccSafresh1=item skip_unless_automated(DESC) 257b8851fccSafresh1 258b8851fccSafresh1Checks whether AUTHOR_TESTING, AUTOMATED_TESTING, or RELEASE_TESTING are set 259b8851fccSafresh1in the environment and skips the whole test (by calling C<plan skip_all> from 260b8851fccSafresh1Test::More) if they are not. This should be used by tests that should not run 261b8851fccSafresh1during end-user installs of the module, but which should run as part of CPAN 262b8851fccSafresh1smoke testing and release testing. 263b8851fccSafresh1 264b8851fccSafresh1DESC is a description of the tests being skipped. A space and C<normally 265b8851fccSafresh1skipped> will be appended to it and used as the skip reason. 266b8851fccSafresh1 267b8851fccSafresh1=item use_prereq(MODULE[, VERSION][, IMPORT ...]) 268b8851fccSafresh1 269b8851fccSafresh1Attempts to load MODULE with the given VERSION and import arguments. If this 270b8851fccSafresh1fails for any reason, the test will be skipped (by calling C<plan skip_all> 271b8851fccSafresh1from Test::More) with a skip reason saying that MODULE is required for the 272b8851fccSafresh1test. 273b8851fccSafresh1 274b8851fccSafresh1VERSION will be passed to C<use> as a version bareword if it looks like a 275b8851fccSafresh1version number. The remaining IMPORT arguments will be passed as the value of 276b8851fccSafresh1an array. 277b8851fccSafresh1 278b8851fccSafresh1=back 279b8851fccSafresh1 280b8851fccSafresh1=head1 AUTHOR 281b8851fccSafresh1 282b8851fccSafresh1Russ Allbery <eagle@eyrie.org> 283b8851fccSafresh1 284b8851fccSafresh1=head1 COPYRIGHT AND LICENSE 285b8851fccSafresh1 286*e0680481Safresh1Copyright 2016, 2018-2019, 2021 Russ Allbery <eagle@eyrie.org> 28756d68f1eSafresh1 28856d68f1eSafresh1Copyright 2013-2014 The Board of Trustees of the Leland Stanford Junior 289b8851fccSafresh1University 290b8851fccSafresh1 291b8851fccSafresh1Permission is hereby granted, free of charge, to any person obtaining a copy 292b8851fccSafresh1of this software and associated documentation files (the "Software"), to deal 293b8851fccSafresh1in the Software without restriction, including without limitation the rights 294b8851fccSafresh1to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 295b8851fccSafresh1copies of the Software, and to permit persons to whom the Software is 296b8851fccSafresh1furnished to do so, subject to the following conditions: 297b8851fccSafresh1 298b8851fccSafresh1The above copyright notice and this permission notice shall be included in all 299b8851fccSafresh1copies or substantial portions of the Software. 300b8851fccSafresh1 301b8851fccSafresh1THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 302b8851fccSafresh1IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 303b8851fccSafresh1FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 304b8851fccSafresh1AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 305b8851fccSafresh1LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 306b8851fccSafresh1OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 307b8851fccSafresh1SOFTWARE. 308b8851fccSafresh1 309b8851fccSafresh1=head1 SEE ALSO 310b8851fccSafresh1 311b8851fccSafresh1Test::More(3), Test::RRA::Automake(3), Test::RRA::Config(3) 312b8851fccSafresh1 313b8851fccSafresh1This module is maintained in the rra-c-util package. The current version is 3145759b3d2Safresh1available from L<https://www.eyrie.org/~eagle/software/rra-c-util/>. 315b8851fccSafresh1 316b8851fccSafresh1The functions to control when tests are run use environment variables defined 317b8851fccSafresh1by the L<Lancaster 318b8851fccSafresh1Consensus|https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md>. 319b8851fccSafresh1 320b8851fccSafresh1=cut 321b46d8ef2Safresh1 322b46d8ef2Safresh1# Local Variables: 323b46d8ef2Safresh1# copyright-at-end-flag: t 324b46d8ef2Safresh1# End: 325