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 146 147 148 149
|
use v5.14;
use warnings;
package TAP::Parser::SourceHandler::Feature 0.87;
=head1 NAME
TAP::Parser::SourceHandler::Feature - Test::BDD::Cucumber's prove integration
=head1 VERSION
version 0.87
=cut
use Path::Class qw/file/;
use Test2::API qw/context/;
use base 'TAP::Parser::SourceHandler';
use TAP::Parser::Iterator::PherkinStream;
use App::pherkin;
use Test::BDD::Cucumber::Loader;
use Test::BDD::Cucumber::Harness::TAP;
use Path::Class qw/file/;
TAP::Parser::IteratorFactory->register_handler(__PACKAGE__);
sub can_handle {
my ( $class, $source ) = @_;
if ( $source->meta->{'is_file'}
&& $source->meta->{'file'}->{'basename'} =~ m/\.feature$/ )
{
my $dir = $source->meta->{'file'}->{'dir'};
unless ( $source->{'pherkins'}->{$dir} ) {
my $pherkin = App::pherkin->new();
# Reformulate before passing to the cmd line parser
my @cmd_line;
my %options = %{ $source->config_for($class) };
while ( my ( $key, $value ) = each %options ) {
# --feature-option 'tags=@something'
# --feature-option 'tags=@somethingelse'
#
# is passed in %options as:
# { ..., tags => [ '@something', '@somethingelse' ], ... }
#
# Now unfold that back into an argument array as
# --tags @something --tags @somethingelse
$value = [ $value ] unless ref $value;
for my $v (@$value) {
# Nasty hack
if ( length $key > 1 ) {
push( @cmd_line, "--$key", $v );
} else {
push( @cmd_line, "-$key", $v );
}
}
}
my ( $executor, @features )
= $pherkin->_pre_run( @cmd_line, $dir );
$source->{'pherkins'}->{$dir} = {
pherkin => $pherkin,
executor => $executor,
features => {
map { ( file( $_->document->filename ) . '' ) => $_ }
@features
}
};
}
return 1;
}
return 0;
}
sub make_iterator {
my ( $class, $source ) = @_;
my ( $input_out_fh, $output_out_fh );
pipe $input_out_fh, $output_out_fh;
binmode $output_out_fh, ':utf8';
my ( $input_err_fh, $output_err_fh );
pipe $input_err_fh, $output_err_fh;
binmode $output_err_fh, ':utf8';
# Don't cache the output so prove sees it immediately
# (pipes are stdio buffered by default)
$output_out_fh->autoflush(1);
$output_err_fh->autoflush(1);
my $dir = $source->meta->{'file'}->{'dir'};
my $runtime = $source->{'pherkins'}->{$dir}
|| die "No pherkin instantiation for [$dir]";
my $executor = $runtime->{'executor'};
my $pherkin = $runtime->{'pherkin'};
my $pid = fork;
if ($pid) {
close $output_out_fh;
close $output_err_fh;
return TAP::Parser::Iterator::PherkinStream
->new($input_out_fh, $input_err_fh, $pherkin, $pid);
}
# prevent uncaught exceptions to return up the call stack
# causing essentially two prove instances to run.
eval {
close $input_out_fh;
close $input_err_fh;
my $harness =
Test::BDD::Cucumber::Harness::TAP->new({ fail_skip => 1 });
my $context = context();
# Without the step to set the handles TAP will end up on STDOUT/STDERR
$context->hub->format->set_handles([$output_out_fh, $output_err_fh]);
$context->release;
$pherkin->harness($harness);
my $filename =
file( $dir . $source->meta->{'file'}->{'basename'} ) . '';
my $feature = $runtime->{'features'}->{$filename}
|| die "Feature not pre-loaded: [$filename]; have: "
. ( join '; ', keys %{ $runtime->{'features'} } );
my $exit_code = $pherkin->_run_tests( $executor, $feature );
close $output_out_fh;
close $output_err_fh;
$pherkin->_post_run;
exit $exit_code;
};
exit 255;
}
1;
|