File Coverage

blib/lib/Test/Given/Check.pm
Criterion Covered Total %
statement 214 214 100.0
branch 22 24 91.6
condition 2 3 66.6
subroutine 59 59 100.0
pod 0 4 0.0
total 297 304 97.7


line stmt bran cond sub pod time code
1             package Test::Given::Check;
2 41     41   198 use strict;
  41         63  
  41         1226  
3 41     41   185 use warnings;
  41         69  
  41         853  
4              
5 41     41   204 use B::Deparse ();
  41         64  
  41         49438  
6              
7             sub new {
8 225     225 0 320 my ($class, $sub) = @_;
9 225         530 my $self = {
10             sub => $sub,
11             };
12 225         954 bless $self, $class;
13             }
14              
15             sub execute {
16 308     308 0 423 my ($self, $exceptions) = @_;
17 308 100       1812 return 1 if !$self->{sub};
18              
19 304         498 my $rv = eval {
20 304         1387 $self->{sub}->($exceptions);
21             };
22 304 100       6575 if ($@) {
23 20         735 warn $@;
24 20         54 $rv = '';
25             }
26 304         6511 return $rv;
27             }
28              
29             our $deparser = B::Deparse->new('-l');
30             sub _decompile {
31 195     195   337 my ($self) = @_;
32 195 100       701 unless ( exists $self->{code} ) {
33 135         200418 my @code = split( /\n/, $deparser->coderef2text($self->{sub}) );
34 135 50       1205 @code = (@code > 1) ? @code[1..$#code-1] : ();
35 135         647 $self->{code} = \@code;
36             }
37 195         569 return $self->{code};
38             }
39              
40             sub name {
41 133     133 0 268 my ($self) = @_;
42 133 100       437 return '' if !$self->{sub};
43              
44 129         202 my @code = grep { !/^ *(?:package|use|no|#line) / } @{ $self->_decompile() };
  549         2090  
  129         558  
45 129         696 my ($line) = _clean_code( $code[$#code] );
46 129         559 $line =~ s/;$//;
47 129         1061 return $line;
48             }
49              
50             sub message {
51 66     66 0 97 my ($self) = @_;
52 66 50       207 return '' if !$self->{sub};
53              
54 66         107 my @lines = @{ $self->_decompile() };
  66         157  
55 66         125 my @code = _clean_code(grep { !/^ *(?:package|use|no|#line) / } @lines);
  296         1053  
56 66         109 my ($line_number) = grep { /^ *#line / } @lines;
  296         670  
57              
58 66         188 my $msg = $self->type() . ": $line_number\n " . join("\n ", @code);
59              
60 66 100       218 if ( my ($left, $cmp, $right) = _split_expression( $code[$#code] ) ) {
61 48         83 my @package = grep { /^ *package / } @lines;
  216         408  
62 48 100       139 @package = 'package main;' unless @package;
63              
64 48         70 my @use = grep { /^ *(?:use|no) / } @lines;
  216         618  
65 48         96 push @use, "no warnings 'all';";
66              
67 48         111 my $left_value = _eval_in_context(@package, @use, $left);
68 48         110 my $right_value = _eval_in_context(@package, @use, $right);
69              
70 48 100 66     341 unless ($left_value =~ /
71 40         270 $msg .= "\n $left_value\t<- $left\n $right_value\t<- $right";
72             }
73             }
74 66         465 return $msg;
75             }
76              
77             sub _split_expression {
78 66     66   692 return $_[0] =~ /^\s*(?:return\s+)?(.*) ([!=<>]=|[<>]|<=>|eq|ne|cmp|[lg][te]|[!=]~) (.*?)\s*;?$/;
79             }
80              
81             sub _eval_in_context {
82 96     96   7405 my $result = eval( join("\n", @_) );
  8     8   60  
  8     8   13  
  8     8   311  
  8     8   42  
  8     8   12  
  8     8   292  
  8     8   36  
  8     8   11  
  8     8   373  
  8     8   41  
  8     8   14  
  8     8   185  
  8     8   39  
  8     8   14  
  8     8   257  
  8     8   36  
  8     8   11  
  8     8   283  
  8     8   53  
  8     8   15  
  8     8   265  
  8     8   40  
  8     8   15  
  8     8   299  
  8     8   37  
  8     8   15  
  8     8   348  
  8     8   41  
  8     8   16  
  8     8   196  
  8     8   36  
  8     8   16  
  8     8   272  
  8     8   37  
  8     8   15  
  8     8   241  
  8         46  
  8         16  
  8         228  
  8         37  
  8         16  
  8         227  
  8         36  
  8         12  
  8         197  
  8         35  
  8         16  
  8         187  
  8         37  
  8         12  
  8         192  
  8         41  
  8         14  
  8         275  
  8         45  
  8         17  
  8         192  
  8         33  
  8         14  
  8         237  
  8         34  
  8         12  
  8         300  
  8         37  
  8         14  
  8         148  
  8         31  
  8         15  
  8         215  
  8         29  
  8         16  
  8         207  
  8         46  
  8         15  
  8         268  
  8         39  
  8         10  
  8         276  
  8         45  
  8         16  
  8         203  
  8         42  
  8         12  
  8         204  
  8         37  
  8         13  
  8         208  
  8         36  
  8         71  
  8         165  
  8         47  
  8         15  
  8         202  
  8         35  
  8         12  
  8         276  
  8         36  
  8         43  
  8         269  
  8         38  
  8         14  
  8         169  
  8         37  
  8         10  
  8         242  
  8         38  
  8         24  
  8         177  
83              
84 96 100       304 if ($@) {
85 24         43 $result = $@;
86 24         55 $result =~ s/ at \(eval \d+\) line \d+.*\n?//;
87 24         65 $result = "";
88             }
89              
90 96 100       190 $result = '' unless defined $result;
91 96         187 return $result;
92             }
93              
94             # convert $$var to $var->
95             sub _clean_code {
96 195         1407 map {
97 195     195   329 s/\$(\$.*?)([\{\[])/$1->$2/g;
98 195         883 s/^ //;
99 195         636 $_;
100             } @_;
101             }
102              
103             package Test::Given::Invariant;
104 41     41   34845 use parent 'Test::Given::Check';
  41         13139  
  41         207  
105 5     5   26 sub type { 'Invariant' }
106              
107             package Test::Given::Then;
108 41     41   3564 use parent 'Test::Given::Check';
  41         85  
  41         204  
109 59     59   249 sub type { 'Then' }
110              
111             package Test::Given::And;
112 41     41   2477 use parent 'Test::Given::Check';
  41         77  
  41         149  
113 2     2   10 sub type { 'And' }
114              
115             package Test::Given::Test;
116              
117 41     41   26649 use Test::Given::Builder;
  41         134  
  41         423  
118             my $TEST_CLASS = 'Test::Given::Builder';
119              
120             sub new {
121 137     137   235 my ($class, $sub) = @_;
122 137         872 my $self = {
123             checks => [ Test::Given::Then->new($sub) ],
124             };
125 137         709 bless $self, $class;
126             }
127             sub add_check {
128 52     52   88 my ($self) = shift;
129 52         64 push @{ $self->{checks} }, Test::Given::And->new(@_);
  52         275  
130             }
131             sub execute {
132 135     135   339 my ($self, $context) = @_;
133 135         571 $context->reset();
134 135         1586 $context->apply_givens();
135 133         723 $context->apply_whens();
136 133         499 my $exceptions = $context->exceptions();
137 133         249 my @failed = grep { not $_->execute($exceptions) } @{ $self->{checks} };
  185         1344  
  133         572  
138 133         605 push @failed, $context->apply_invariants($exceptions);
139 133         258 my $passed = not @failed;
140 133         533 ok($passed, name($self->{checks}));
141 133 100       547 diag(message(\@failed)) unless $passed;
142 133         8455 return $passed;
143             }
144              
145             sub name {
146 133     133   226 my ($checks) = @_;
147 133         660 return $checks->[0]->name();
148             }
149              
150             sub message {
151 66     66   107 my ($failed) = @_;
152 66         139 return join("\n\n", map { $_->message() } @$failed);
  66         276  
153             }
154              
155             1;