File Coverage

web/cgi-bin/yatt.lib/YATT/Test.pm
Criterion Covered Total %
statement 198 208 95.1
branch 50 70 71.4
condition 17 29 58.6
subroutine 36 38 94.7
pod 0 13 0.0
total 301 358 84.0


line stmt bran cond sub pod time code
1             # -*- mode: perl; coding: utf-8 -*-
2             package YATT::Test;
3 10     10   38346 use strict;
  10         23  
  10         303  
4 10     10   273 use warnings qw(FATAL all NONFATAL misc);
  10         21  
  10         464  
5 10     10   51 use base qw(Test::More);
  10         16  
  10         8787  
6 10     10   155598 BEGIN {$INC{'YATT/Test.pm'} = __FILE__}
7              
8 10     10   94 use File::Basename;
  10         21  
  10         741  
9 10     10   52 use Cwd;
  10         20  
  10         616  
10              
11 10     10   9089 use Data::Dumper;
  10         91124  
  10         665  
12 10     10   68 use Carp;
  10         18  
  10         610  
13              
14 10     10   8766 use Time::HiRes qw(usleep);
  10         15436  
  10         58  
15              
16 10     10   6646 use YATT;
  10         27  
  10         344  
17 10         694 use YATT::Util qw(rootname catch checked_eval default defined_fmt
18             require_and
19 10     10   74 );
  10         17  
20 10     10   5135 use YATT::Util::Symbol;
  10         21  
  10         993  
21 10     10   66 use YATT::Util::Finalizer;
  10         15  
  10         543  
22 10     10   5767 use YATT::Util::DirTreeBuilder qw(tmpbuilder);
  10         28  
  10         533  
23 10     10   5603 use YATT::Util::DictOrder;
  10         26  
  10         9542  
24              
25             #========================================
26              
27             our @EXPORT = qw(ok is isnt like is_deeply skip fail plan
28             require_ok isa_ok
29             basename
30              
31             wait_for_time
32              
33             is_rendered raises is_can run
34             capture rootname checked_eval default defined_fmt
35             tmpbuilder
36             dumper
37              
38             xhf_test
39             *TRANS
40             );
41             foreach my $name (@EXPORT) {
42             my $glob = globref(__PACKAGE__, $name);
43             unless (*{$glob}{CODE}) {
44             *$glob = \&{globref("Test::More", $name)};
45             }
46             }
47              
48             *eq_or_diff = do {
49             if (catch {require Test::Differences} \ my $error) {
50             \&Test::More::is;
51             } else {
52             \&Test::Differences::eq_or_diff;
53             }
54             };
55              
56             push @EXPORT, qw(eq_or_diff);
57              
58             our @EXPORT_OK = @EXPORT;
59              
60             #========================================
61              
62             sub run {
63 1     1 0 2 my ($testname, $sub) = @_;
64 1         3 my $res = eval { $sub->() };
  1         4  
65 1         976 Test::More::is $@, '', "$testname doesn't raise error";
66 1         585 $res
67             }
68              
69             sub is_can ($$$) {
70 2     2 0 588 my ($desc, $cmp, $title) = @_;
71 2         6 my ($obj, $method, @args) = @$desc;
72 2         32 my $sub = $obj->can($method);
73 2         12 Test::More::ok defined $sub, "$title - can";
74 2 50       1360 if ($sub) {
75 2         880 Test::More::is scalar($sub->($obj, @args)), $cmp, $title;
76             } else {
77 0         0 Test::More::fail "skipped because method '$method' not found.";
78             }
79             }
80              
81             sub is_rendered ($$$) {
82 130     130 0 4672 my ($desc, $cmp, $title) = @_;
83 130         310 my ($trans, $path, @args) = @$desc;
84 130         166 my $error;
85 130 0   0   858 local $SIG{__DIE__} = sub {$error = @_ > 1 ? [@_] : shift};
  0         0  
86 130 0   0   816 local $SIG{__WARN__} = sub {$error = @_ > 1 ? [@_] : shift};
  0         0  
87 130         275 my ($sub, $pkg) = eval {
88 130         503 &YATT::break_translator;
89 130         655 $trans->get_handler_to(render => @$path)
90             };
91 130         830 Test::More::is $error, undef, "$title - compiled.";
92 130         103248 eval {
93 130 50       410 if ($sub) {
    0          
94             my $out = capture {
95 130     130   466 &YATT::break_handler;
96 130         5118 $sub->($pkg, @args);
97 130         1328 };
98 130 50       734 $out =~ s{\r}{}g if defined $out;
99 130         675 eq_or_diff($out, $cmp, $title);
100             } elsif ($error) {
101 0         0 Test::More::fail "skipped, because of previous compile error for [$title]: $error";
102             }
103             };
104 130 50       134858 if ($@) {
105 0         0 Test::More::fail "$title: runtime error: $@";
106             }
107             }
108              
109             sub raises ($$$) {
110 32     32 0 76 my ($desc, $cmp, $title) = @_;
111 32         90 my ($trans, $method, @args) = @$desc;
112 32     32   67 my $result = eval {capture {$trans->$method(@args)}};
  32         306  
  32         179  
113 32         403 Test::More::like $@, $cmp, $title;
114 32         24900 $result;
115             }
116              
117             #----------------------------------------
118              
119             sub dumper {
120             join "\n", map {
121 86     86 0 4587 Data::Dumper->new([$_])->Terse(1)->Indent(0)->Dump;
  140         3772  
122             } @_;
123             }
124              
125             #----------------------------------------
126 10     10   64 use base qw(YATT::Class::Configurable);
  10         18  
  10         1162  
127 10         141 use YATT::Types -base => __PACKAGE__
128             , [TestDesc => [qw(cf_FILE realfile
129             ntests
130             cf_TITLE num cf_TAG
131             cf_BREAK
132             cf_SKIP
133             cf_WIDGET
134             cf_RANDOM
135             cf_IN cf_PARAM cf_OUT cf_ERROR)]]
136             , [Config => [['^cf_translator' => 'YATT::Translator::Perl']
137             , '^cf_toplevel'
138             , '^TMPDIR', 'gen'
139             ]]
140             , [Toplevel => []]
141 10     10   5113 ;
  10         23  
142              
143 1 50   1   2 Config->define(target => sub { my $self = shift; $self->toplevel
  1         6  
144             || $self->translator });
145              
146             Config->define(new_translator => sub {
147             ;#
148 13     13   101 (my Config $global, my ($loader, @opts)) = @_;
149 13         77 require_and($global->translator => new => loader => $loader, @opts);
150             });
151              
152             Config->define(configure_DIR => sub {
153             ;#
154 1     1   3 (my Config $global, my ($dir)) = @_;
155 1         8 $global->{TMPDIR} = tmpbuilder($dir);
156             });
157              
158             sub ntests {
159 1     1 0 2 my $ntests = 0;
160 1         3 foreach my $section (@_) {
161 13         20 foreach my TestDesc $test (@{$section}[1 .. $#$section]) {
  13         27  
162 184         301 $ntests += $test->{ntests};
163             }
164             }
165 1         8 $ntests;
166             }
167              
168             sub xhf_test {
169 1     1 0 15 my Config $global = do {
170 1         31 shift->Config->new(DIR => shift);
171             };
172              
173 1 50 33     27 if (@_ == 1 and -d $_[0]) {
174 1         2 my $srcdir = shift;
175 1         305 @_ = dict_sort <$srcdir/*.xhf>;
176             }
177              
178 1 50       10 croak "Source is missing." unless @_;
179 1         9 my @sections = $global->xhf_load_sections(@_);
180              
181 1         6 Test::More::plan(tests => 1 + ntests(@sections));
182              
183 1         172 require_ok($global->target);
184              
185 1         397 $global->xhf_do_sections(@sections);
186             }
187              
188             sub xhf_load_sections {
189 1     1 0 3 my Config $global = shift;
190              
191 1         716 require YATT::XHF;
192              
193 1         3 my @sections;
194 1         4 foreach my $testfile (@_) {
195 13         70 my $parser = new YATT::XHF(filename => $testfile);
196 13         15 my TestDesc $prev;
197 13         25 my ($n, @test, %uniq) = (0);
198 13         35 while (my $rec = $parser->read_as_hash) {
199 184 50       401 if ($rec->{global}) {
200 0         0 $global->configure(%{$rec->{global}});
  0         0  
201 0         0 next;
202             }
203 184         1037 push @test, my TestDesc $test = $global->TestDesc->new(%$rec);
204 184         438 $test->{ntests} = $global->ntests_in_desc($test);
205             $test->{cf_FILE} ||= $prev && $prev->{cf_FILE}
206 184 100 66     1403 && $prev->{cf_FILE} =~ m{%d} ? $prev->{cf_FILE} : undef;
      100        
207              
208 184 100       412 if ($test->{cf_IN}) {
209 10     10   5516 use YATT::Util::redundant_sprintf;
  10         23  
  10         77  
210 159   100     693 $test->{realfile} = sprintf($test->{cf_FILE} ||= "doc/f%d.html", $n);
211 159   66     362 $test->{cf_WIDGET} ||= do {
212 159         250 my $widget = $test->{realfile};
213 159         437 $widget =~ s{^doc/}{};
214 159         528 $widget =~ s{\.\w+$}{};
215 159         264 $widget =~ s{/}{:}g;
216 159         509 $widget;
217             };
218             }
219              
220 184 100       427 if ($test->{cf_OUT}) {
221 130   33     346 $test->{cf_WIDGET} ||= $prev && $prev->{cf_WIDGET};
      66        
222 130 100 66     393 if (not $test->{cf_TITLE} and $prev) {
223 23         81 $test->{num} = default($prev->{num}) + 1;
224 23         58 $test->{cf_TITLE} = $prev->{cf_TITLE};
225             }
226             }
227 184         280 $prev = $test;
228             } continue {
229 184         804 $n++;
230             }
231              
232 13         203 push @sections, [$testfile => @test];
233             }
234              
235 1         7 @sections;
236             }
237              
238             sub xhf_is_runnable {
239 184     184 0 355 (my Config $global, my TestDesc $test) = @_;
240 184 100       1292 $test->{cf_OUT} || $test->{cf_ERROR};
241             }
242              
243             sub xhf_do_sections {
244 1     1 0 5 (my Config $global, my @sections) = @_;
245              
246 1         1 my $SECTION = 0;
247 1         4 foreach my $section (@sections) {
248 13         109 my ($testfile, @all) = @$section;
249 13         104 my $builder = $global->{TMPDIR}->as_sub;
250 13         95 my $DIR = $builder->([DIR => "doc"]);
251              
252 13         36 my @test;
253 13         32 foreach my TestDesc $test (@all) {
254 184 100       646 if ($test->{cf_IN}) {
255 159 50       1176 die "Conflicting FILE: $test->{realfile}!\n" if -e $test->{realfile};
256             $builder->($global->{TMPDIR}->path2desc
257 159         734 ($test->{realfile}, $test->{cf_IN}));
258             }
259 184 100       943 push @test, $test if $global->xhf_is_runnable($test);
260             }
261              
262 13         66 my @loader = (DIR => "$DIR/doc");
263 13         29 push @loader, LIB => do {
264 13 100       319 if (-d "$DIR/lib") {
265 1         5 my $libdir = "$DIR/lib";
266 1         40 chmod 0755, $libdir;
267 1         5 $libdir;
268             } else {
269 12         106 getcwd;
270             }
271             };
272              
273 13         29 my %config;
274 13 100       319 if (-r (my $fn = "$DIR/doc/.htyattroot")) {
275 2         23 %config = YATT::XHF->new(filename => $fn)->read_as('pairlist');
276             }
277              
278 13         79 &YATT::break_translator;
279             $global->{gen} = ($global->toplevel || $global)->new_translator
280             (\@loader
281             , app_prefix => "MyApp$SECTION"
282             , debug_translator => $ENV{DEBUG}
283 13   33     67 , no_lineinfo => YATT::Util::no_lineinfo()
284             , %config
285             );
286              
287 13         5592 foreach my TestDesc $test (@test) {
288 162 50       770 my @widget_path; @widget_path = split /:/, $test->{cf_WIDGET} if $test->{cf_WIDGET};
  162         1129  
289 162 50       274 my ($param); ($param) = map {ref $_ ? $_ : 'main'->checked_eval($_)}
  90         417  
290 162 100       629 $test->{cf_PARAM} if $test->{cf_PARAM};
291              
292             SKIP: {
293 162         237 $global->xhf_runtest_desc($test, $testfile, \@widget_path, $param);
  162         666  
294             }
295             }
296             } continue {
297 13         1406 $SECTION++;
298             }
299             }
300              
301             sub xhf_runtest_desc {
302 162     162 0 459 (my Config $global, my TestDesc $test
303             , my ($testfile, $widget_path, $param)) = @_;
304              
305 162 50       673 unless (defined $test->{cf_TITLE}) {
306 0         0 die "test title is not defined!" . dumper($test);
307             }
308             my $title = join("", '[', basename($testfile), '] ', $test->{cf_TITLE}
309 162         8104 , defined_fmt(' (%d)', $test->{num}, ''));
310              
311 162         853 my $toplevel = $global->toplevel;
312 162 100       567 if ($test->{cf_OUT}) {
    50          
313             Test::More::skip("($test->{cf_SKIP}) $title", 2)
314 130 100       447 if $test->{cf_SKIP};
315              
316 129 50 33     526 if ($toplevel
317             and my $sub = $toplevel->can("set_random_list")) {
318 0         0 $sub->($global, $test->{cf_RANDOM});
319             }
320              
321 129 100       410 &YATT::breakpoint if $test->{cf_BREAK};
322             is_rendered [$global->{gen}, $widget_path, $param]
323 129         605 , $test->{cf_OUT}, $title;
324             } elsif ($test->{cf_ERROR}) {
325             Test::More::skip("($test->{cf_SKIP}) $title", 1)
326 32 100       111 if $test->{cf_SKIP};
327 31 100       97 &YATT::breakpoint if $test->{cf_BREAK};
328 31         957 raises [$global->{gen}, call_handler => render => $widget_path, $param]
329             , qr{$test->{cf_ERROR}}s, $title;
330             }
331             }
332              
333             sub ntests_in_desc {
334 184     184 0 288 (my $this, my TestDesc $test) = @_;
335 184 100       430 if ($test->{cf_OUT}) {
    100          
336 130         295 2
337             } elsif ($test->{cf_ERROR}) {
338 32         65 1
339             } else {
340 22         42 0
341             }
342             }
343              
344             #
345             sub wait_for_time {
346 4     4 0 992 my ($time) = @_;
347 4         17 my $now = Time::HiRes::time;
348 4         11 my $diff = $time - $now;
349 4 100       17 return if $diff <= 0;
350 2         1911691 usleep(int($diff * 1000 * 1000));
351 2         38 $diff;
352             }
353              
354             1;