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   788254 use strict;
  5         12  
  5         115  
3 5     5   24 use warnings;
  5         8  
  5         109  
4              
5 5     5   25 use Test::Stream::Plugin;
  5         8  
  5         30  
6              
7 5     5   4394 use Devel::Declare;
  5         185966  
  5         26  
8 5     5   4267 use B::Hooks::EndOfScope;
  5         64030  
  5         41  
9              
10 5     5   3404 use PadWalker qw/peek_my peek_our/;
  5         3345  
  5         327  
11 5     5   28 use Carp qw/confess croak/;
  5         9  
  5         5926  
12              
13             # Do not declare any variables here!!!!
14              
15             my %META;
16             sub _metahash {
17 36     36   7871 my $string = "";
18 36         48 my $vars = { %{peek_our(1)}, %{peek_my(1)} };
  36         211  
  36         158  
19              
20             {
21 36         74 my $id = shift;
  36         50  
22 36         264 my @caller = caller(0);
23 36   100     286 my $meta = $META{$id} || return {};
24              
25 17         25 my $var_string = "";
26 17         44 for my $var (keys %$vars) {
27 36         53 my $end = "\$vars->{'$var'}";
28 36 50       89 if ($var =~ m/^([\@\%\$])/) {
29 36         70 $end = "${1}{$end}";
30             }
31             else {
32 0         0 next;
33             }
34 36         78 $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         2040 my $hash = eval $string;
52 17 50       284 die $@ unless $hash;
53              
54 17         134 return $hash;
55             }
56              
57             # Now we can define some variables.
58             my $ID = 1;
59             our $DEBUG = 0;
60             our $VERSION = "0.000002";
61              
62             sub load_ts_plugin {
63 5     5 0 115 my $class = shift;
64 5         10 my $caller = shift;
65              
66             Devel::Declare->setup_for(
67             $caller->[0],
68             {
69 5         23 map { $_ => { const => \&parser } } grep { $caller->[0]->can($_) } qw{
  70         228  
  70         247  
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   1297 my $line = Devel::Declare::get_linestr();
84 36         78 my $offset = Devel::Declare::get_linestr_offset();
85 36         62 substr($line, $offset, 0) = ';';
86 36         98 Devel::Declare::set_linestr($line);
87 36 50       113 print STDERR "FINAL: |$line|\n" if $DEBUG;
88             }
89 36     36   2033 }
90              
91             sub parser {
92 41     41 0 2941 my ($dec, $offset) = @_;
93 41         54 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         56 my $id = $ID++;
98              
99             # This is used to back out all changes if a parsing error occurs.
100 41         51 my @restore;
101             my $restore = sub {
102 5     5   15 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         9 my ($offset, $len, $val) = @$set;
106 4         22 substr($line, $offset, $len) = $val;
107             }
108 5         11 Devel::Declare::set_linestr($line);
109 5 50       16 print "FIXED: |$line|\n" if $DEBUG;
110 5         51 return 0;
111 41         178 };
112              
113             # Skip the initial boring stuff
114 41         108 $offset += Devel::Declare::toke_move_past_token($offset);
115 41         80 $offset += Devel::Declare::toke_skipspace($offset);
116 41         93 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         93 my $post_name = " => Test::Stream::Plugin::SpecDeclare::_metahash($id), (";
122              
123             # Get the block name
124 41         68 my $start = substr($line, $offset, 1);
125 41 100 100     269 if ($start eq '"' || $start eq "'") {
    100          
126             # Quoted name
127 11         64 my $len = Devel::Declare::toke_scan_str($offset);
128 11         32 $name = Devel::Declare::get_lex_stuff();
129 11         22 Devel::Declare::clear_lex_stuff();
130 11         12 $offset += $len;
131 11         28 my $new = $post_name;
132 11         21 substr($line, $offset, 0) = $new;
133 11         25 Devel::Declare::set_linestr($line);
134 11         25 push @restore => [$offset, length($new), ""];
135 11         20 $offset += length($new);
136             }
137             elsif (my $nlen = Devel::Declare::toke_scan_word($offset, 1)) {
138             # Bareword name
139 29         52 $name = substr($line, $offset, $nlen);
140 29         51 my $new = qq|"${name}"${post_name}|;
141 29         51 substr($line, $offset, $nlen) = $new;
142 29         59 Devel::Declare::set_linestr($line);
143 29         71 push @restore => [$offset, length($new), $name];
144 29         46 $offset += length($new);
145             }
146              
147             # Back out if we failed to get a name
148 41 100       98 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         94 $line = Devel::Declare::get_linestr();
154 40         67 $start = substr($line, $offset, 1);
155 40 100       123 if ($start eq '(') {
156 17         60 my $len = Devel::Declare::toke_scan_str($offset);
157 17         49 $meta = Devel::Declare::get_lex_stuff();
158 17         31 Devel::Declare::clear_lex_stuff();
159 17         37 $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         42 $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         45 my @newlines = $meta =~ /(\n)/g;
170 17         30 my $new = join '' => @newlines;
171 17         39 substr($line, $offset, $len) = $new;
172 17         35 Devel::Declare::set_linestr($line);
173              
174             # This is how to back it out later
175 17         49 push @restore => [$offset, length($new), "($meta)"];
176              
177             # Advance the offset
178 17         30 $offset += length($new);
179             }
180              
181             # Move to the start of the block
182 40         82 $offset += Devel::Declare::toke_skipspace($offset);
183 40         82 $line = Devel::Declare::get_linestr();
184 40         58 $start = substr($line, $offset, 1);
185 40 100       85 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         46 my $new = "), sub { BEGIN { Test::Stream::Plugin::SpecDeclare::_inject_scope(); }; ";
190 36         63 substr($line, $offset, 1) = $new;
191 36         71 Devel::Declare::set_linestr($line);
192 36         39 $offset += length($new);
193 36 50       397 print STDERR "PREFIN: |$line|\n" if $DEBUG;
194             }
195              
196             1;
197              
198             __END__