File Coverage

blib/lib/Test2/Plugin/GitHub/Actions/AnnotateFailedTest.pm
Criterion Covered Total %
statement 49 52 94.2
branch 12 12 100.0
condition 5 9 55.5
subroutine 12 13 92.3
pod 0 1 0.0
total 78 87 89.6


line stmt bran cond sub pod time code
1             package Test2::Plugin::GitHub::Actions::AnnotateFailedTest;
2 8     8   6590 use strict;
  8         26  
  8         277  
3 8     8   49 use warnings;
  8         17  
  8         230  
4 8     8   47 use feature qw(state);
  8         18  
  8         845  
5              
6 8     8   1278 use Encode qw(encode_utf8);
  8         23965  
  8         510  
7 8         661 use Test2::API qw(
8             test2_add_callback_post_load
9             test2_stack
10             test2_stderr
11 8     8   701 );
  8         72166  
12 8     8   3869 use URI::Escape qw(uri_escape);
  8         12293  
  8         5309  
13              
14             our $VERSION = "0.04";
15              
16             sub import {
17 10     10   37247 my ($class) = @_;
18              
19 10 100       83 return unless $ENV{GITHUB_ACTIONS};
20 7         18 state $loaded = 0; # avoid multiple callback addition
21 7 100       22 return if $loaded;
22 5         10 $loaded++;
23              
24             test2_add_callback_post_load(sub {
25 5     5   129 my $hub = test2_stack()->top;
26 5         63 $hub->listen(\&listener, inherit => 1);
27 5         30 });
28             }
29              
30             sub listener {
31 10     10 0 7679 my ($hub, $event) = @_;
32              
33 10 100       60 return unless $event->causes_fail;
34              
35 4         36 my $trace = $event->trace;
36 4         30 my $summary = _extract_summary_from_event($event);
37 4   50     58 my $file = $trace->file // '';
38 4   50     41 my $line = $trace->line // 0;
39 4         30 my $details = _extract_details_from_event($event);
40 4         12 my $message = encode_utf8(join "\n", grep { defined } ($summary, $details)); # avoid Wide character in print warning
  8         42  
41              
42 4         47 _issue_error($file, $line, $message);
43             }
44              
45             sub _extract_summary_from_event {
46 4     4   9 my ($event) = @_;
47              
48 4 100       51 my $name_or_summary = $event->isa('Test2::Event::Fail') ? $event->name : $event->summary;
49             # avoid uninitialized warning for regexp matching
50 4   50     99 $name_or_summary //= '';
51 4 100 66     34 if ($name_or_summary =~ /Nameless Assertion/ || ! length $name_or_summary) {
52 1         3 return 'Test failed';
53             } else {
54 3         9 return $name_or_summary;
55             }
56             }
57              
58             sub _extract_details_from_event {
59 4     4   11 my ($event) = @_;
60              
61 4 100       16 return undef unless exists $event->{info};
62 1         2 return join "\n", map { $_->{details} } @{$event->{info}};
  1         4  
  1         3  
63             }
64              
65             sub _issue_error {
66 0     0   0 my ($file, $line, $detail) = @_;
67              
68 0         0 my $stderr = test2_stderr();
69              
70 0         0 $stderr->printf("::error file=%s,line=%d::%s\n", $file, $line, _escape_data($detail));
71             }
72              
73             # escape a message of workflow command.
74             # see also: https://github.com/actions/toolkit/blob/30e0a77337213de5d4e158b05d1019c6615f69fd/packages/core/src/command.ts#L92-L97
75             sub _escape_data {
76 1     1   129 my ($msg) = @_;
77 1         14 return uri_escape($msg, "%\r\n");
78             }
79              
80             1;
81             __END__