File Coverage

blib/lib/XT/Manager/API.pm
Criterion Covered Total %
statement 97 221 43.8
branch 4 68 5.8
condition 0 27 0.0
subroutine 31 52 59.6
pod 0 14 0.0
total 132 382 34.5


line stmt bran cond sub pod time code
1             package XT::Manager::API;
2              
3 1     1   6 use strict;
  1         2  
  1         72  
4              
5             BEGIN {
6 1     1   3 $XT::Manager::API::AUTHORITY = 'cpan:TOBYINK';
7 1         35 $XT::Manager::API::VERSION = '0.006';
8             };
9              
10             BEGIN {
11             package XT::Manager::API::Types;
12 1     1   1137 no thanks;
  1         244  
  1         9  
13 1     1   1265 use Path::Tiny ();
  1         22821  
  1         59  
14 1         17 use Type::Library -base,
15 1     1   813 -declare => qw( Path AbsPath File AbsFile Dir AbsDir XtTest XtTestSet XtComparison );
  1         43941  
16 1     1   3070 use Type::Utils;
  1         5216  
  1         11  
17 1     1   3537 use Types::Standard qw( Str ArrayRef );
  1         52654  
  1         22  
18 1     1   1211 use Types::TypeTiny 0.004 StringLike => { -as => "Stringable" };
  1         39  
  1         9  
19            
20 1     1   1063 class_type Path, { class => "Path::Tiny" };
21            
22             declare AbsPath,
23 0         0 as Path, where { $_->is_absolute },
24 3         7668 inline_as { $_[0]->parent->inline_check($_) . "&& ${_}->is_absolute" },
25             message {
26 0 0       0 is_Path($_) ? "Path '$_' is not absolute" : Path->get_message($_);
27 1         8337 };
28            
29             declare File,
30 0         0 as Path, where { $_->is_file },
31 2         1200 inline_as { $_[0]->parent->inline_check($_) . "&& (-f $_)" },
32             message {
33 0 0       0 is_Path($_) ? "File '$_' does not exist" : Path->get_message($_);
34 1         535 };
35            
36             declare Dir,
37 0         0 as Path, where { $_->is_dir },
38 2         1218 inline_as { $_[0]->parent->inline_check($_) . "&& (-d $_)" },
39             message {
40 0 0       0 is_Path($_) ? "Directory '$_' does not exist" : Path->get_message($_);
41 1         644 };
42            
43             declare AbsFile,
44             as intersection([AbsPath, File]),
45             message {
46 0 0       0 is_AbsPath($_) ? File->get_message($_) : AbsPath->get_message($_);
47 1         551 };
48            
49             declare AbsDir,
50             as intersection([AbsPath, Dir]),
51             message {
52 0 0       0 is_AbsPath($_) ? Dir->get_message($_) : AbsPath->get_message($_);
53 1         762 };
54            
55 1         593 for my $type ( Path, File, Dir ) {
56 3         782 coerce(
57             $type,
58             from Str() => q{ Path::Tiny::path($_) },
59             from Stringable() => q{ Path::Tiny::path($_) },
60             from ArrayRef() => q{ Path::Tiny::path(@$_) },
61             );
62             }
63            
64 1         390 for my $type ( AbsPath, AbsFile, AbsDir ) {
65 3         737 coerce(
66             $type,
67             from Path => q{ $_->absolute },
68             from Str() => q{ Path::Tiny::path($_)->absolute },
69             from Stringable() => q{ Path::Tiny::path($_)->absolute },
70             from ArrayRef() => q{ Path::Tiny::path(@$_)->absolute },
71             );
72             }
73            
74 1         462 class_type XtTest, { class => "XT::Manager::Test" };
75 1         1288 class_type XtTestSet, { class => "XT::Manager::TestSet" };
76 1         857 class_type XtComparison, { class => "XT::Manager::Comparison" };
77             };
78              
79 1     1   730 BEGIN {
80             package XT::Manager::API::Syntax;
81 1     1   952 no thanks;
  1         2  
  1         9  
82 1     1   2816 use Moo ();
  1         13362  
  1         28  
83 1     1   1061 use Moo::Role ();
  1         25638  
  1         26  
84 1     1   7 use Import::Into ();
  1         2  
  1         23  
85 1         9 use Syntax::Collector -collect => q{
86             use Types::Standard 0 -types;
87             use XT::Manager::API::Types 0 -types;
88             use match::smart 0.004 qw(M);
89             use constant 0 { true => !!1, false => !!0 };
90             use constant 0 { read_only => 'ro', read_write => 'rw', lazy_build => 'lazy' };
91             no thanks 0.001;
92             use strict 0;
93             use warnings 0;
94 1     1   920 };
  1         1250  
95             sub _exporter_validate_opts
96             {
97 7     7   468 my $me = shift;
98 7         15 my ($opts) = @_;
99 7 100       43 'Moo::Role'->import::into($opts->{into}) if $opts->{role};
100 7 100       1551 'Moo'->import::into($opts->{into}) if $opts->{class};
101 7         5076 $me->SUPER::_exporter_validate_opts(@_);
102             }
103             }
104              
105             BEGIN {
106             package XT::Manager::Exception::FileNotFound;
107 1     1   6 use XT::Manager::API::Syntax -class;
  1         4  
  1         23  
108 1     1   14686 with qw(Throwable)
109             };
110              
111             BEGIN {
112             package XT::Manager::Test;
113 1     1   27483 use XT::Manager::API::Syntax -class;
  1         4  
  1         13  
114            
115 1     1   7348 has t_file => (
116             is => read_only,
117             isa => File,
118             required => true,
119             coerce => File->coercion,
120             handles => { name => "basename" },
121             );
122            
123 1         988 has config_file => (
124             is => lazy_build,
125             isa => File,
126             required => false,
127             coerce => File->coercion,
128             predicate => "has_config_file",
129             );
130            
131             sub BUILDARGS
132             {
133 0     0 0   my $class = shift;
134 0           my $params = $class->SUPER::BUILDARGS(@_);
135 0 0         delete $params->{config_file} unless defined $params->{config_file};
136 0           return $params;
137             }
138            
139             sub _build_file
140             {
141 0     0     my ($self, $extension) = @_;
142 0           my $abs = $self->t_file->absolute;
143 0           $abs =~ s/\.\Kt$/$extension/;
144 0 0         return unless -f $abs;
145 0           return $abs;
146             }
147            
148             sub _build_config_file
149             {
150 0     0     shift->_build_file('config');
151             }
152             };
153              
154             BEGIN {
155             package XT::Manager::TestSet;
156 1     1   809 use XT::Manager::API::Syntax -role;
  1         2  
  1         8  
157            
158 1     1   12688 requires qw(
159             add_test
160             remove_test
161             _build_tests
162             _build_disposable_config_files
163             );
164            
165 1         24 has tests => (
166             is => lazy_build,
167             isa => ArrayRef[XtTest],
168             predicate => "has_tests",
169             );
170            
171 1         3939 has disposable_config_files => (
172             is => lazy_build,
173             isa => Bool,
174             predicate => "has_disposable_config_files",
175             );
176            
177 0     0 0   sub is_ignored { +return }
178            
179             sub test
180             {
181 0     0 0   my ($self, $name) = @_;
182 0           my @results = grep { $_->name eq $name } @{ $self->tests };
  0            
  0            
183 0 0         wantarray ? @results : $results[0];
184             }
185             };
186              
187             BEGIN {
188             package XT::Manager::FileSystemTestSet;
189 1     1   772 use XT::Manager::API::Syntax -role;
  1         31  
  1         11  
190 1     1   8499 with qw(XT::Manager::TestSet);
191            
192 1         1252 has dir => (
193             is => read_only,
194             isa => Dir,
195             required => true,
196             coerce => Dir->coercion,
197             );
198            
199             sub _build_tests
200             {
201 0     0     my $self = shift;
202 0 0         $self->dir->mkpath unless -d $self->dir;
203            
204             [
205 0 0         map { XtTest->new(t_file => $_) }
  0            
206 0           grep { !$_->is_dir and $_ =~ /\.t$/ }
207             $self->dir->children
208             ]
209             }
210            
211 0     0     sub _build_disposable_config_files { true }
212            
213             sub compare
214             {
215 0     0 0   my ($self, $other) = @_;
216 0           my %results;
217 0           foreach my $t (@{ $self->tests })
  0            
218             {
219 0           $results{ $t->name }{L} = [ $t->t_file->stat->mtime ];
220             }
221 0           foreach my $t (@{ $other->tests })
  0            
222             {
223 0           $results{ $t->name }{R} = [ $t->t_file->stat->mtime ];
224             }
225            
226             XtComparison->new(
227 0           left => $self,
228             right => $other,
229             data => \%results,
230             );
231             }
232            
233             sub add_test
234             {
235 0     0 0   my ($self, $t) = @_;
236 0           my $o = $t;
237 0 0         $t = $self->test($t) unless ref $t;
238            
239 0 0         "XT::Manager::Exception::FileNotFound"->throw(
240             message => "$o not found in ".$self->dir
241             ) unless ref $t;
242            
243 0           my $dir = $self->dir;
244 0           my ($t_file, $config_file);
245             my $dump = sub {
246 0     0     my ($old, $new) = @_;
247 0           my $fh = $new->openw;
248 0           print $fh $old->slurp;
249 0           close $fh;
250 0           utime $old->stat->mtime, $old->stat->mtime, "$new";
251 0           };
252            
253 0           $t_file = File->coercion->([ "$dir", $t->t_file->basename ]);
254 0           $dump->($t->t_file, $t_file);
255            
256 0 0         if ($t->has_config_file)
257             {
258 0           $config_file = File->coercion->([ "$dir", $t->config_file->basename ]);
259 0 0 0       $dump->($t->config_file, $config_file) if $self->disposable_config_files || !(-e $config_file);
260             }
261            
262 0           my $object = XtTest->new(
263             t_file => $t_file,
264             config_file => $config_file,
265             );
266 0           push @{ $self->tests }, $object;
  0            
267            
268 0           return $object;
269             }
270            
271             sub remove_test
272             {
273 0     0 0   my ($self, $t) = @_;
274 0           my $o = $t;
275 0 0         $t = $self->test($t) unless ref $t;
276            
277 0 0         "XT::Manager::Exception::FileNotFound"->throw(
278             "$o not found in ".$self->dir
279             ) unless ref $t;
280            
281 0           $t->t_file->remove;
282 0 0         if ($t->has_config_file)
283             {
284 0 0 0       $t->config_file->remove if $self->disposable_config_files || !(-e $t->config_file);
285             }
286            
287 0           $self->tests([ grep { $_->name ne $t->name } @{ $self->tests } ]);
  0            
  0            
288 0           return $self;
289             }
290             };
291              
292             BEGIN {
293             package XT::Manager::Repository;
294 1     1   452 use XT::Manager::API::Syntax -class;
  1         1  
  1         7  
295 1     1   7158 with qw(XT::Manager::FileSystemTestSet);
296             }
297              
298             BEGIN {
299             package XT::Manager::XTdir;
300 1     1   1611 use XT::Manager::API::Syntax -class;
  1         3  
  1         7  
301 1     1   7194 with qw(XT::Manager::FileSystemTestSet);
302            
303 1         1201 has ignore_list => (
304             is => lazy_build,
305             isa => Any,
306             predicate => "has_ignore_list",
307             );
308            
309 0     0     sub _build_disposable_config_files { false }
310            
311             sub _build_ignore_list
312             {
313 0     0     my $self = shift;
314 0 0         $self->dir->mkpath unless -d $self->dir;
315            
316 0           my $file = File->coercion->([ $self->dir, '.xt-ignore' ]);
317 0 0         return unless -f "$file";
318 0           my @ignore =
319 0           map { qr{$_} }
320 0           map { chomp; $_ }
  0            
321             $file->slurp;
322 0           return \@ignore;
323             }
324            
325             sub is_ignored
326             {
327 0     0 0   my ($self, $name) = @_;
328 0 0         return true if $name |M| $self->ignore_list;
329 0           return;
330             }
331            
332             sub add_ignore
333             {
334 0     0 0   my ($self, $string) = @_;
335 0 0         $self->dir->mkpath unless -d $self->dir;
336            
337 0           my $file = File->coercion->([ $self->dir, '.xt-ignore' ]);
338 0           open my $fh, '>>', "$file";
339 0           print $fh quotemeta($string);
340 0           close $fh;
341 0           push @{ $self->ignore_list }, qr{ \Q $string \E }x;
  0            
342             }
343             };
344              
345             BEGIN {
346             package XT::Manager::Comparison;
347 1     1   929 use XT::Manager::API::Syntax -class;
  1         2  
  1         7  
348            
349             use constant {
350 1         581 LEFT_ONLY => '+ ',
351             RIGHT_ONLY => ' ? ',
352             LEFT_NEWER => 'U ',
353             RIGHT_NEWER => ' M ',
354 1     1   7154 };
  1         2  
355            
356 1     1   5 has data => (
357             is => read_only,
358             isa => HashRef,
359             required => true,
360             );
361            
362 1         771 has [qw/left right/] => (
363             is => read_only,
364             does => XtTestSet,
365             required => true,
366             );
367            
368             sub test_names
369             {
370 0     0 0   my $self = shift;
371 0           sort keys %{ $self->data };
  0            
372             }
373            
374             sub left_has
375             {
376 0     0 0   my ($self, $name) = @_;
377 0           return $self->data->{$name}{L};
378             }
379            
380             sub right_has
381             {
382 0     0 0   my ($self, $name) = @_;
383 0           return $self->data->{$name}{R};
384             }
385            
386             sub status
387             {
388 0     0 0   my ($self, $name) = @_;
389 0           my $L = $self->left_has($name);
390 0           my $R = $self->right_has($name);
391            
392 0 0 0       return LEFT_ONLY if ( $L and !$R );
393 0 0 0       return RIGHT_ONLY if ( !$L and $R );
394 0 0 0       return LEFT_NEWER if ( $L and $R and $L->[0] > $R->[0] );
      0        
395 0 0 0       return RIGHT_NEWER if ( $L and $R and $L->[0] < $R->[0] );
      0        
396 0           return;
397             }
398            
399             sub show
400             {
401 0     0 0   my ($self, $verbose) = @_;
402            
403 0           my $str = '';
404 0           foreach my $t ($self->test_names)
405             {
406 0 0         next if $self->right->is_ignored($t);
407            
408 0           my $status = $self->status($t);
409 0 0 0       if (defined $status and length $status)
    0          
410             {
411 0           $str .= sprintf("%s %s\n", $status, $t);
412             }
413             elsif ($verbose)
414             {
415 0           $str .= " $t\n";
416             }
417             }
418 0           return $str;
419             }
420            
421             sub should_pull
422             {
423 0     0 0   my $self = shift;
424 0           grep
425             {
426 0           my $f = $_;
427 0 0         if ($self->right->is_ignored($f))
428             {
429 0           0;
430             }
431             else
432             {
433 0           my $st = $self->status($f);
434 0 0         $st = "" unless defined $st;
435 0 0         $st eq LEFT_ONLY || $st eq LEFT_NEWER;
436             }
437             } $self->test_names;
438             }
439             };
440              
441             __FILE__
442             __END__