File Coverage

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


line stmt bran cond sub pod time code
1             # -*- mode: perl; coding: utf-8 -*-
2             package YATT::Test;
3 10     10   23764 use strict;
  10         16  
  10         350  
4 10     10   182 use warnings FATAL => qw(all);
  10         12  
  10         307  
5 10     10   35 use base qw(Test::More);
  10         9  
  10         4618  
6 10     10   96800 BEGIN {$INC{'YATT/Test.pm'} = __FILE__}
7              
8 10     10   52 use File::Basename;
  10         9  
  10         526  
9 10     10   34 use Cwd;
  10         14  
  10         401  
10              
11 10     10   4607 use Data::Dumper;
  10         56569  
  10         538  
12 10     10   61 use Carp;
  10         12  
  10         470  
13              
14 10     10   4631 use Time::HiRes qw(usleep);
  10         11177  
  10         40  
15              
16 10     10   3934 use YATT;
  10         15  
  10         220  
17 10         414 use YATT::Util qw(rootname catch checked_eval default defined_fmt
18             require_and
19 10     10   47 );
  10         10  
20 10     10   2518 use YATT::Util::Symbol;
  10         15  
  10         624  
21 10     10   50 use YATT::Util::Finalizer;
  10         14  
  10         390  
22 10     10   2953 use YATT::Util::DirTreeBuilder qw(tmpbuilder);
  10         18  
  10         439  
23 10     10   3069 use YATT::Util::DictOrder;
  10         13  
  10         6829  
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         1 my $res = eval { $sub->() };
  1         2  
65 1         329 Test::More::is $@, '', "$testname doesn't raise error";
66 1         161 $res
67             }
68              
69             sub is_can ($$$) {
70 2     2 0 171 my ($desc, $cmp, $title) = @_;
71 2         3 my ($obj, $method, @args) = @$desc;
72 2         15 my $sub = $obj->can($method);
73 2         7 Test::More::ok defined $sub, "$title - can";
74 2 50       313 if ($sub) {
75 2         378 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 1787 my ($desc, $cmp, $title) = @_;
83 130         321 my ($trans, $path, @args) = @$desc;
84 130         153 my $error;
85 130 0   0   703 local $SIG{__DIE__} = sub {$error = @_ > 1 ? [@_] : shift};
  0         0  
86 130 0   0   516 local $SIG{__WARN__} = sub {$error = @_ > 1 ? [@_] : shift};
  0         0  
87 130         242 my ($sub, $pkg) = eval {
88 130         493 &YATT::break_translator;
89 130         464 $trans->get_handler_to(render => @$path)
90             };
91 130         676 Test::More::is $error, undef, "$title - compiled.";
92 130         40350 eval {
93 130 50       343 if ($sub) {
    0          
94             my $out = capture {
95 130     130   380 &YATT::break_handler;
96 130         3565 $sub->($pkg, @args);
97 130         1096 };
98 130 50       570 $out =~ s{\r}{}g if defined $out;
99 130         560 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       69733 if ($@) {
105 0         0 Test::More::fail "$title: runtime error: $@";
106             }
107             }
108              
109             sub raises ($$$) {
110 32     32 0 62 my ($desc, $cmp, $title) = @_;
111 32         100 my ($trans, $method, @args) = @$desc;
112 32     32   48 my $result = eval {capture {$trans->$method(@args)}};
  32         228  
  32         151  
113 32         336 Test::More::like $@, $cmp, $title;
114 32         9214 $result;
115             }
116              
117             #----------------------------------------
118              
119             sub dumper {
120 140         2071 join "\n", map {
121 86     86 0 2315 Data::Dumper->new([$_])->Terse(1)->Indent(0)->Dump;
122             } @_;
123             }
124              
125             #----------------------------------------
126 10     10   40 use base qw(YATT::Class::Configurable);
  10         11  
  10         1311  
127 10         82 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   2562 ;
  10         12  
142              
143 1 50   1   3 Config->define(target => sub { my $self = shift; $self->toplevel
  1         5  
144             || $self->translator });
145              
146             Config->define(new_translator => sub {
147             ;#
148 13     13   72 (my Config $global, my ($loader, @opts)) = @_;
149 13         55 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         6 $global->{TMPDIR} = tmpbuilder($dir);
156             });
157              
158             sub ntests {
159 1     1 0 2 my $ntests = 0;
160 1         2 foreach my $section (@_) {
161 13         13 foreach my TestDesc $test (@{$section}[1 .. $#$section]) {
  13         18  
162 184         209 $ntests += $test->{ntests};
163             }
164             }
165 1         7 $ntests;
166             }
167              
168             sub xhf_test {
169 1     1 0 10 my Config $global = do {
170 1         27 shift->Config->new(DIR => shift);
171             };
172              
173 1 50 33     23 if (@_ == 1 and -d $_[0]) {
174 1         2 my $srcdir = shift;
175 1         248 @_ = dict_sort <$srcdir/*.xhf>;
176             }
177              
178 1 50       6 croak "Source is missing." unless @_;
179 1         8 my @sections = $global->xhf_load_sections(@_);
180              
181 1         5 Test::More::plan(tests => 1 + ntests(@sections));
182              
183 1         161 require_ok($global->target);
184              
185 1         354 $global->xhf_do_sections(@sections);
186             }
187              
188             sub xhf_load_sections {
189 1     1 0 1 my Config $global = shift;
190              
191 1         469 require YATT::XHF;
192              
193 1         2 my @sections;
194 1         4 foreach my $testfile (@_) {
195 13         69 my $parser = new YATT::XHF(filename => $testfile);
196 13         16 my TestDesc $prev;
197 13         17 my ($n, @test, %uniq) = (0);
198 13         25 while (my $rec = $parser->read_as_hash) {
199 184 50       280 if ($rec->{global}) {
200 0         0 $global->configure(%{$rec->{global}});
  0         0  
201 0         0 next;
202             }
203 184         757 push @test, my TestDesc $test = $global->TestDesc->new(%$rec);
204 184         318 $test->{ntests} = $global->ntests_in_desc($test);
205 184 100 100     1038 $test->{cf_FILE} ||= $prev && $prev->{cf_FILE}
      100        
206             && $prev->{cf_FILE} =~ m{%d} ? $prev->{cf_FILE} : undef;
207              
208 184 100       260 if ($test->{cf_IN}) {
209 10     10   2872 use YATT::Util::redundant_sprintf;
  10         12  
  10         44  
210 159   100     502 $test->{realfile} = sprintf($test->{cf_FILE} ||= "doc/f%d.html", $n);
211 159   66     334 $test->{cf_WIDGET} ||= do {
212 159         157 my $widget = $test->{realfile};
213 159         363 $widget =~ s{^doc/}{};
214 159         401 $widget =~ s{\.\w+$}{};
215 159         148 $widget =~ s{/}{:}g;
216 159         326 $widget;
217             };
218             }
219              
220 184 100       281 if ($test->{cf_OUT}) {
221 130   33     238 $test->{cf_WIDGET} ||= $prev && $prev->{cf_WIDGET};
      66        
222 130 100 66     257 if (not $test->{cf_TITLE} and $prev) {
223 23         65 $test->{num} = default($prev->{num}) + 1;
224 23         42 $test->{cf_TITLE} = $prev->{cf_TITLE};
225             }
226             }
227 184         180 $prev = $test;
228             } continue {
229 184         573 $n++;
230             }
231              
232 13         554 push @sections, [$testfile => @test];
233             }
234              
235 1         6 @sections;
236             }
237              
238             sub xhf_is_runnable {
239 184     184 0 208 (my Config $global, my TestDesc $test) = @_;
240 184 100       769 $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         3 foreach my $section (@sections) {
248 13         74 my ($testfile, @all) = @$section;
249 13         87 my $builder = $global->{TMPDIR}->as_sub;
250 13         55 my $DIR = $builder->([DIR => "doc"]);
251              
252 13         16 my @test;
253 13         22 foreach my TestDesc $test (@all) {
254 184 100       363 if ($test->{cf_IN}) {
255 159 50       754 die "Conflicting FILE: $test->{realfile}!\n" if -e $test->{realfile};
256 159         392 $builder->($global->{TMPDIR}->path2desc
257             ($test->{realfile}, $test->{cf_IN}));
258             }
259 184 100       511 push @test, $test if $global->xhf_is_runnable($test);
260             }
261              
262 13         49 my @loader = (DIR => "$DIR/doc");
263 13         18 push @loader, LIB => do {
264 13 100       184 if (-d "$DIR/lib") {
265 1         3 my $libdir = "$DIR/lib";
266 1         30 chmod 0755, $libdir;
267 1         3 $libdir;
268             } else {
269 12         69 getcwd;
270             }
271             };
272              
273 13         25 my %config;
274 13 100       161 if (-r (my $fn = "$DIR/doc/.htyattroot")) {
275 2         14 %config = YATT::XHF->new(filename => $fn)->read_as('pairlist');
276             }
277              
278 13         58 &YATT::break_translator;
279 13   33     52 $global->{gen} = ($global->toplevel || $global)->new_translator
280             (\@loader
281             , app_prefix => "MyApp$SECTION"
282             , debug_translator => $ENV{DEBUG}
283             , no_lineinfo => YATT::Util::no_lineinfo()
284             , %config
285             );
286              
287 13         5233 foreach my TestDesc $test (@test) {
288 162 50       348 my @widget_path; @widget_path = split /:/, $test->{cf_WIDGET} if $test->{cf_WIDGET};
  162         965  
289 162 50       256 my ($param); ($param) = map {ref $_ ? $_ : 'main'->checked_eval($_)}
  162 100       534  
  90         310  
290             $test->{cf_PARAM} if $test->{cf_PARAM};
291              
292 162         596 SKIP: {
293 162         199 $global->xhf_runtest_desc($test, $testfile, \@widget_path, $param);
294             }
295             }
296             } continue {
297 13         580 $SECTION++;
298             }
299             }
300              
301             sub xhf_runtest_desc {
302 162     162 0 393 (my Config $global, my TestDesc $test
303             , my ($testfile, $widget_path, $param)) = @_;
304              
305 162 50       553 unless (defined $test->{cf_TITLE}) {
306 0         0 die "test title is not defined!" . dumper($test);
307             }
308 162         7049 my $title = join("", '[', basename($testfile), '] ', $test->{cf_TITLE}
309             , defined_fmt(' (%d)', $test->{num}, ''));
310              
311 162         671 my $toplevel = $global->toplevel;
312 162 100       485 if ($test->{cf_OUT}) {
    50          
313 130 100       415 Test::More::skip("($test->{cf_SKIP}) $title", 2)
314             if $test->{cf_SKIP};
315              
316 129 50 33     333 if ($toplevel
317             and my $sub = $toplevel->can("set_random_list")) {
318 0         0 $sub->($global, $test->{cf_RANDOM});
319             }
320              
321 129 100       283 &YATT::breakpoint if $test->{cf_BREAK};
322 129         517 is_rendered [$global->{gen}, $widget_path, $param]
323             , $test->{cf_OUT}, $title;
324             } elsif ($test->{cf_ERROR}) {
325 32 100       94 Test::More::skip("($test->{cf_SKIP}) $title", 1)
326             if $test->{cf_SKIP};
327 31 100       70 &YATT::breakpoint if $test->{cf_BREAK};
328 31         806 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 162 (my $this, my TestDesc $test) = @_;
335 184 100       289 if ($test->{cf_OUT}) {
    100          
336 130         193 2
337             } elsif ($test->{cf_ERROR}) {
338 32         53 1
339             } else {
340 22         37 0
341             }
342             }
343              
344             #
345             sub wait_for_time {
346 4     4 0 662 my ($time) = @_;
347 4         10 my $now = Time::HiRes::time;
348 4         9 my $diff = $time - $now;
349 4 100       14 return if $diff <= 0;
350 2         1536203 usleep(int($diff * 1000 * 1000));
351 2         27 $diff;
352             }
353              
354             1;