App-Prove-Plugin-Elasticsearch

 view release on metacpan or  search on metacpan

lib/App/Prove/Elasticsearch/Parser.pm  view on Meta::CPAN

# PODNAME:  App::Prove::Elasticsearch::Parser
# ABSTRACT: Capture the output of prove, and upload the results of the test to elasticsearch

package App::Prove::Elasticsearch::Parser;
$App::Prove::Elasticsearch::Parser::VERSION = '0.001';
use strict;
use warnings;

use parent qw/TAP::Parser/;

use Clone qw{clone};
use File::Basename qw{basename dirname};
use POSIX qw{strftime};
use App::Prove::Elasticsearch::Utils();

sub new {
    my ($class, $opts) = @_;
    $opts = clone $opts;  #Convenience, if we are passing over and over again...

    #Load our callbacks
    $opts->{'callbacks'} = {
        'test'    => \&testCallback,
        'comment' => \&commentCallback,
        'unknown' => \&unknownCallback,
        'bailout' => \&bailoutCallback,
        'EOF'     => \&EOFCallback,
        'plan'    => \&planCallback,
    };

    my $esopts = {
        'server.host'       => delete $opts->{'server.host'},
        'server.port'       => delete $opts->{'server.port'},
        'client.indexer'    => delete $opts->{'client.indexer'},
        'client.versioner'  => delete $opts->{'client.versioner'} // 'Default',
        'client.blamer'     => delete $opts->{'client.blamer'} // 'Default',
        'client.platformer' => delete $opts->{'client.platformer'} // 'Default',
        'client.autodiscover' => delete $opts->{'client.autodiscover'},
    };

    my $self = $class->SUPER::new($opts);
    if (ref($self->{'_iterator'}->{'command'}) eq 'ARRAY') {
        $self->{'file'} = $self->{'_iterator'}->{'command'}->[-1];
        print "# PROCESSING RESULTS FROM TEST FILE: $self->{'file'}\n";
    }

    my $indexer = $esopts->{'client.indexer'};
    _require_indexer($indexer);
    my $versioner =
      App::Prove::Elasticsearch::Utils::require_versioner($esopts);
    my $platformer =
      App::Prove::Elasticsearch::Utils::require_platformer($esopts);
    my $blamer = App::Prove::Elasticsearch::Utils::require_blamer($esopts);

    $self->{executor} =
      &{\&{$blamer . "::get_responsible_party"}}($self->{file});
    $self->{sut_version} = &{\&{$versioner . "::get_version"}}($self->{file});
    $self->{platform}    = &{\&{$platformer . "::get_platforms"}}();
    $self->{indexer}     = $indexer;

    $self->{test_version} =
      &{\&{$versioner . "::get_file_version"}}($self->{file});
    $self->{steps}     = [];
    $self->{starttime} = [ Time::HiRes::gettimeofday() ];
    $self->{es_opts}   = $esopts;
    return $self;
}

sub _require_indexer {
    my $indexer = shift;
    eval "require $indexer" or die "cannot find needed indexer $indexer";
}

# Look for file boundaries, etc.
sub unknownCallback {
    my ($test) = @_;
    my $self   = $test->{'parser'};
    my $line   = $test->as_string;
    $self->{'raw_output'} .= "$line\n";

    #Unofficial "Extensions" to TAP
    my ($status_override) = $line =~ m/^% mark_status=([A-Z|_]*)/;
    $self->{global_status} = $status_override if $status_override;

    return;
}

# Register the current suite or test desc for use by test callback, if the line begins with the special magic words
sub commentCallback {
    my ($test) = @_;
    my $self   = $test->{'parser'};
    my $line   = $test->as_string;
    $self->{'raw_output'} .= "$line\n";

    return;
}

sub testCallback {
    my ($test) = @_;
    my $self = $test->{'parser'};

lib/App/Prove/Elasticsearch/Parser.pm  view on Meta::CPAN

    my $status = 'OK';

    $status = 'NOT OK' if $self->has_problems();

    $status = 'TODO PASSED'
      if $self->todo_passed()
      && !$self->failed()
      && $self->is_good_plan();    #If no fails, but a TODO pass, mark as TODOP

    $status = 'TODO FAILED'
      if $todo_failed
      && !$self->failed()
      && $self->is_good_plan()
      ;    #If no fails, but a TODO fail, prefer TODOF to TODOP

    $status = "SKIPPED" if $self->skip_all();    #Skip all, whee

    $status = "BAIL OUT" if $self->{is_bailout};

    #Global status override
    $status = $self->{'global_status'} if $self->{'global_status'};
    return if $status eq 'DISCARD';

    #Notify user about bad plan a bit better, supposing we haven't bailed
    if (!$self->is_good_plan() && !$self->{'is_bailout'}) {
        $self->{'raw_output'} .=
            "\n# ERROR: Bad plan.  You ran "
          . $self->tests_run
          . " tests, but planned "
          . $self->tests_planned . ".";
    }

    $self->{upload} = {
        body    => $self->{raw_output},
        elapsed => $self->{elapsed},
        occurred =>
          strftime("%Y-%m-%d %H:%M:%S", localtime($self->{starttime}->[0])),
        status        => $status,
        platform      => $self->{platform},
        executor      => $self->{executor},
        version       => $self->{sut_version},
        test_version  => $self->{test_version},
        name          => basename($self->{file}),
        path          => dirname($self->{file}),
        steps         => $self->{steps},
        steps_planned => $self->tests_planned
    };

    &{\&{$self->{indexer} . "::index_results"}}($self->{upload});
    return $status;
}

sub planCallback {
    my ($plan) = @_;
    my $self = $plan->{'parser'};
    $self->{raw_output} .= $plan->as_string . "\n";
}

sub make_result {
    my ($self, @args) = @_;
    my $res = $self->SUPER::make_result(@args);
    $res->{'parser'} = $self;
    return $res;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

App::Prove::Elasticsearch::Parser - Capture the output of prove, and upload the results of the test to elasticsearch

=head1 VERSION

version 0.001

=head1 SYNOPSIS

    App::Prove::Elasticsearch::Parser->new();

=head1 CONSTRUCTOR

=head2 new

Creates a TAP::Parser that will upload test results to your repository using the provided indexer.

=head1 OVERRIDDEN CALLBACKS

=head2 unknownCallback

Checks for status overrides (% mark_status=DISCARD) and records unknown lines for later upload.

=head2 commentCallback

=head2 planCallback

=head2 bailoutCallback

These do little more than record the information printed during prove for upload to the result index.

=head2 testCallback

Captures step information and runtime, along with the raw text of the assertion.

=head2 EOFCallback

Actually does the uploading of the result to the index.

Sets test global status as the 'most anomalous' result encountered in the test in the following order (most to least):

=over 4

=item Bailout

=item Skipped (when skip_all happens)



( run in 0.465 second using v1.01-cache-2.11-cpan-5a3173703d6 )