/usr/share/perl5/Test/Spec/Example.pm is in libtest-spec-perl 0.51-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | package Test::Spec::Example;
# Purpose: represents an `it` block
use strict;
use warnings;
########################################################################
# NO USER-SERVICEABLE PARTS INSIDE.
########################################################################
use Carp ();
use Scalar::Util ();
sub new {
my ($class, $args) = @_;
if (!$args || ref($args) ne 'HASH') {
Carp::croak "usage: $class->new(\\%args)";
}
my $self = bless {}, $class;
foreach my $attr ( qw/name description code builder context/ ) {
$self->{$attr} = $args->{$attr} || Carp::croak "$attr missing";
}
Scalar::Util::weaken($self->{context});
return $self;
}
sub name { shift->{name} }
sub description { shift->{description} }
sub code { shift->{code} }
sub builder { shift->{builder} }
sub context { shift->{context} }
# Build a stack from the starting context
# down to the current context
sub stack {
my ($self) = @_;
my $ctx = $self->context;
my @ancestors = $ctx;
while ( $ctx = $ctx->parent ) {
push @ancestors, $ctx;
}
return reverse(@ancestors);
}
sub run {
my ($self) = @_;
# clobber Test::Builder's ok() method just like Test::Class does,
# but without screwing up underscores.
no warnings 'redefine';
my $orig_builder_ok = \&Test::Builder::ok;
local *Test::Builder::ok = sub {
my ($builder,$test,$desc) = splice(@_,0,3);
$desc ||= $self->description;
local $Test::Builder::Level = $Test::Builder::Level+1;
$orig_builder_ok->($builder, $test, $desc, @_);
};
# Run the test
eval { $self->_runner($self->stack) };
# And trap any errors
if (my $err = $@) {
my $builder = $self->builder;
my $description = $self->description;
# eval in case stringification overload croaks
chomp($err = eval { $err . '' } || 'unknown error');
my ($file,$line);
($file,$line) = ($1,$2) if ($err =~ s/ at (.+?) line (\d+)\.\Z//);
# disable ok()'s diagnostics so we can generate a custom TAP message
my $old_diag = $builder->no_diag;
$builder->no_diag(1);
# make sure we can restore no_diag
eval { $builder->ok(0, $description) };
my $secondary_err = $@;
# no_diag needs a defined value, so double-negate it to get either '' or 1
$builder->no_diag(!!$old_diag);
unless ($builder->no_diag) {
# emulate Test::Builder::ok's diagnostics, but with more details
my ($msg,$diag_fh);
if ($builder->in_todo) {
$msg = "Failed (TODO)";
$diag_fh = $builder->todo_output;
}
else {
$msg = "Failed";
$diag_fh = $builder->failure_output;
}
print {$diag_fh} "\n" if $ENV{HARNESS_ACTIVE};
print {$builder->failure_output} qq[# $msg test '$description' by dying:\n];
print {$builder->failure_output} qq[# $err\n];
print {$builder->failure_output} qq[# at $file line $line.\n] if defined($file);
}
die $secondary_err if $secondary_err;
}
}
sub _runner {
my ($self, $ctx, @remainder) = @_;
# This recursive closure essentially does this
# $outer->contextualize {
# $outer->before_each
# $inner->contextualize {
# $inner->before_each
# $anon->contextualize {
# $anon->before_each (no-op)
# execute test
# $anon->after_each (no-op)
# }
# $inner->after_each
# }
# $outer->after_each
# }
#
return $ctx->contextualize(sub {
$ctx->_run_before_all_once;
$ctx->_run_before('each');
if ( @remainder ) {
$self->_runner(@remainder);
}
else {
$ctx->_in_anonymous_context($self->code);
}
$ctx->_run_after('each');
# "after 'all'" only happens during context destruction (DEMOLISH).
# This is the only way I can think to make this work right
# in the case that only specific test methods are run.
# Otherwise, the global teardown would only happen when you
# happen to run the last test of the context.
});
}
1;
|