File Coverage

blib/lib/Test/Stream/Plugin/SpecDeclare.pm
Criterion Covered Total %
statement 109 110 99.0
branch 16 22 72.7
condition 5 5 100.0
subroutine 13 13 100.0
pod 0 2 0.0
total 143 152 94.0


line stmt bran cond sub pod time code
1             package Test::Stream::Plugin::SpecDeclare;
2 5     5   1071139 use strict;
  5         13  
  5         213  
3 5     5   27 use warnings;
  5         10  
  5         140  
4              
5 5     5   28 use Test::Stream::Plugin;
  5         10  
  5         102  
6              
7 5     5   4582 use Devel::Declare;
  5         25526  
  5         23  
8 5     5   4174 use B::Hooks::EndOfScope;
  5         60635  
  5         39  
9              
10 5     5   3742 use PadWalker qw/peek_my peek_our/;
  5         3688  
  5         378  
11 5     5   45 use Carp qw/confess croak/;
  5         11  
  5         6803  
12              
13             # Do not declare any variables here!!!!
14              
15             my %META;
16             sub _metahash {
17 36     36   2226 my $string = "";
18 36         47 my $vars = { %{peek_our(1)}, %{peek_my(1)} };
  36         239  
  36         157  
19              
20             {
21 36         81 my $id = shift;
  36         59  
22 36         276 my @caller = caller(0);
23 36   100     291 my $meta = $META{$id} || return {};
24              
25 17         24 my $var_string = "";
26 17         47 for my $var (keys %$vars) {
27 36         59 my $end = "\$vars->{'$var'}";
28 36 50       93 if ($var =~ m/^([\@\%\$])/) {
29 36         72 $end = "${1}{$end}";
30             }
31             else {
32 0         0 next;
33             }
34 36         76 $var_string .= "my $var = $end;\n";
35             }
36              
37 17         90 $string = <<" EOT";
38             package $caller[0];
39             $var_string
40              
41             # This is cut off access to these variables so they can not be modified in the
42             # eval.
43             my \$vars;
44             my \$string;
45             my \%META;
46             # line $caller[2] "$caller[1] (SpecDeclare eval)"
47             my \$h = {$meta};
48             EOT
49             }
50              
51 17         1989 my $hash = eval $string;
52 17 50       304 die $@ unless $hash;
53              
54 17         129 return $hash;
55             }
56              
57             # Now we can define some variables.
58             my $ID = 1;
59             our $DEBUG = 0;
60             our $VERSION = "0.000003";
61              
62             sub load_ts_plugin {
63 5     5 0 122 my $class = shift;
64 5         10 my $caller = shift;
65              
66             Devel::Declare->setup_for(
67             $caller->[0],
68             {
69 5         28 map { $_ => { const => \&parser } } grep { $caller->[0]->can($_) } qw{
  70         238  
  70         269  
70             describe cases
71             tests it
72             case
73             before_all after_all around_all
74             before_case after_case around_case
75             before_each after_each around_each
76             }
77             },
78             );
79             }
80              
81             sub _inject_scope {
82             on_scope_end {
83 36     36   1304 my $line = Devel::Declare::get_linestr();
84 36         81 my $offset = Devel::Declare::get_linestr_offset();
85 36         79 substr($line, $offset, 0) = ', __LINE__;';
86 36         88 Devel::Declare::set_linestr($line);
87 36 50       133 print STDERR "FINAL: |$line|\n" if $DEBUG;
88             }
89 36     36   2166 }
90              
91             sub parser {
92 41     41 0 3222 my ($dec, $offset) = @_;
93 41         50 my ($name, $meta);
94              
95             # Due to parsing strangeness we need to grab the meta-data and get it back
96             # later. This ID is used to fetch the data later.
97 41         58 my $id = $ID++;
98              
99             # This is used to back out all changes if a parsing error occurs.
100 41         49 my @restore;
101             my $restore = sub {
102 5     5   14 my $line = Devel::Declare::get_linestr();
103 5 50       14 print "MANGLE: |$line|\n" if $DEBUG;
104 5         10 for my $set (reverse @restore) {
105 4         10 my ($offset, $len, $val) = @$set;
106 4         13 substr($line, $offset, $len) = $val;
107             }
108 5         11 Devel::Declare::set_linestr($line);
109 5 50       10 print "FIXED: |$line|\n" if $DEBUG;
110 5         53 return 0;
111 41         168 };
112              
113             # Skip the initial boring stuff
114 41         114 $offset += Devel::Declare::toke_move_past_token($offset);
115 41         87 $offset += Devel::Declare::toke_skipspace($offset);
116 41         105 my $line = Devel::Declare::get_linestr();
117              
118             # After the name we use a fat comma, then get the meta hash by id, then add
119             # an opening paren, which strangely works around some parser issues, we
120             # will close it later
121 41         80 my $post_name = " => __LINE__, Test::Stream::Plugin::SpecDeclare::_metahash($id), (";
122              
123             # Get the block name
124 41         75 my $start = substr($line, $offset, 1);
125 41 100 100     308 if ($start eq '"' || $start eq "'") {
    100          
126             # Quoted name
127 11         61 my $len = Devel::Declare::toke_scan_str($offset);
128 11         31 $name = Devel::Declare::get_lex_stuff();
129 11         23 Devel::Declare::clear_lex_stuff();
130 11         12 $offset += $len;
131 11         33 my $new = $post_name;
132 11         18 substr($line, $offset, 0) = $new;
133 11         24 Devel::Declare::set_linestr($line);
134 11         30 push @restore => [$offset, length($new), ""];
135 11         21 $offset += length($new);
136             }
137             elsif (my $nlen = Devel::Declare::toke_scan_word($offset, 1)) {
138             # Bareword name
139 29         51 $name = substr($line, $offset, $nlen);
140 29         50 my $new = qq|"${name}"${post_name}|;
141 29         55 substr($line, $offset, $nlen) = $new;
142 29         59 Devel::Declare::set_linestr($line);
143 29         77 push @restore => [$offset, length($new), $name];
144 29         52 $offset += length($new);
145             }
146              
147             # Back out if we failed to get a name
148 41 100       109 return $restore->() unless defined $name;
149              
150 40         86 $offset += Devel::Declare::toke_skipspace($offset);
151              
152             # See if there is any meta stuff listed.
153 40         91 $line = Devel::Declare::get_linestr();
154 40         105 $start = substr($line, $offset, 1);
155 40 100       96 if ($start eq '(') {
156 17         68 my $len = Devel::Declare::toke_scan_str($offset);
157 17         44 $meta = Devel::Declare::get_lex_stuff();
158 17         33 Devel::Declare::clear_lex_stuff();
159 17         34 $line = Devel::Declare::get_linestr();
160              
161             # Stash the meta stuff to get later, in perls older than 5.20 we can't
162             # leave it here as it messes up the parser
163 17         40 $META{$id} = $meta;
164              
165             # Replace meta with nothing except the newlines (to preserve line
166             # numbers)
167             # For some reason putting anything here other than whitespace causes
168             # problems.
169 17         44 my @newlines = $meta =~ /(\n)/g;
170 17         30 my $new = join '' => @newlines;
171 17         42 substr($line, $offset, $len) = $new;
172 17         39 Devel::Declare::set_linestr($line);
173              
174             # This is how to back it out later
175 17         52 push @restore => [$offset, length($new), "($meta)"];
176              
177             # Advance the offset
178 17         31 $offset += length($new);
179             }
180              
181             # Move to the start of the block
182 40         78 $offset += Devel::Declare::toke_skipspace($offset);
183 40         85 $line = Devel::Declare::get_linestr();
184 40         59 $start = substr($line, $offset, 1);
185 40 100       96 return $restore->() unless $start eq '{';
186              
187             # Close the paren we opened above, then inject the sub keyword and the
188             # inject scope call which gets us the trailing semicolon.
189 36         40 my $new = "), sub { BEGIN { Test::Stream::Plugin::SpecDeclare::_inject_scope(); }; ";
190 36         57 substr($line, $offset, 1) = $new;
191 36         72 Devel::Declare::set_linestr($line);
192 36         45 $offset += length($new);
193 36 50       464 print STDERR "PREFIN: |$line|\n" if $DEBUG;
194             }
195              
196             1;
197              
198             __END__