File Coverage

blib/lib/Test/CT.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Test::CT;
2             # ABSTRACT: *Mix* of Test::More + Test::Reuse + Test::Routine, with *template* system.
3 2     2   1582 use strict;
  2         4  
  2         142  
4             our $VERSION = '0.142'; # VERSION 0.142
5             our $VERSION = '0.142';
6             $VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval)
7 2     2   12 use strict;
  2         3  
  2         54  
8 2     2   1010 use MooseX::Singleton;
  0            
  0            
9             use Moose::Exporter;
10              
11             use Test::CT::TestFile;
12              
13             use Test::More qw//;
14              
15             Moose::Exporter->setup_import_methods(
16             as_is => [
17             qw /ok is isnt like unlike is_deeply diag note explain cmp_ok/,
18             \&Test::More::use_ok,
19             \&Test::More::require_ok,
20             \&Test::More::todo_skip,
21             \&Test::More::pass,
22             \&Test::More::fail,
23             \&Test::More::plan,
24             \&Test::More::done_testing,
25             \&Test::More::can_ok,
26             \&Test::More::isa_ok,
27             \&Test::More::new_ok,
28             \&Test::More::subtest,
29             \&Test::More::BAIL_OUT,
30             ],
31             also => 'Moose',
32             );
33              
34             has current_test => (is => 'rw', isa => 'Str', default => sub { 'test name not defined!' });
35             has stash => (is => 'rw', default => sub { {} });
36             has config => (is => 'rw', default => sub { {} });
37             has track => (is => 'rw', isa => 'Bool', default => sub { 1 });
38              
39             has tests => (
40             traits => ['Array'],
41             is => 'ro',
42             isa => 'ArrayRef[Test::CT::TestFile]',
43             default => sub { [] },
44             handles => {
45             all_tests => 'elements',
46             add_test => 'push',
47             map_tests => 'map',
48             find_test => 'first',
49             grep_test => 'grep',
50             count_tests => 'count',
51             },
52             );
53              
54              
55             # NOTE: copy from Test::More.pm
56             # Can't use Carp because it might cause use_ok() to accidentally succeed
57             # even though the module being used forgot to use Carp. Yes, this
58             # actually happened.
59             sub _carp {
60             my( $file, $line ) = ( caller(1) )[ 1, 2 ];
61             return warn @_, " at $file line $line\n";
62             }
63              
64             sub _croak {
65             my( $file, $line ) = ( caller(1) )[ 1, 2 ];
66             die(@_, " at $file line $line\n");
67             }
68             # END OF NOTE
69              
70              
71             around stash => sub {
72             my $orig = shift;
73             my $c = shift;
74             my $stash = $orig->($c);
75             if (@_) {
76             my $new_stash = @_ > 1 ? {@_} : $_[0];
77             croak('stash takes a hash or hashref') unless ref $new_stash;
78             foreach my $key ( keys %$new_stash ) {
79             $stash->{$key} = $new_stash->{$key};
80             }
81             }
82              
83             return $stash;
84             };
85              
86              
87             sub run {
88             my ($self, %param) = @_;
89              
90             if (exists $param{name} ){
91             my $item = $self->find_test( sub { $_->name eq $param{name} });
92             return fail("cant find test $param{name}...") unless defined $item;
93              
94             $self->_run_test(\%param, $item);
95             }
96              
97             if (exists $param{like} || exists $param{llike} ){
98             my $regexp = exists $param{like} ? qr/$param{like}/ : qr/^$param{llike}/;
99              
100             my @items = $self->grep_test( sub { $_->name =~ $regexp });
101             $self->_run_test(\%param, $_) for @items;
102             }
103              
104             return 1;
105             }
106              
107             sub _run_test {
108             my ($self, $param, $item) = @_;
109              
110             my $name = $item->name;
111             my $do_exec = !$item->has_run || $param->{force_exec};
112              
113             # NOTE tests don't ran more than one time, even if fail
114             $item->has_run(1);
115             if ($do_exec){
116             $self->current_test($name);
117             eval{
118             $item->coderef->();
119             };
120              
121             if ($@){
122             $item->error($@);
123             return fail("$name died with error $@");
124             }
125             }else{
126             diag("Test $name already run");
127             }
128             return 1;
129              
130             }
131              
132             sub ok {
133             my ($maybetrue, $test_name) = @_;
134             my $self = Test::CT->instance;
135              
136             my $res = Test::More::ok( $maybetrue, $test_name);
137             $self->push_log({
138             func => 'ok',
139             arguments => [$maybetrue],
140             result => $res,
141             name => $test_name
142             });
143             }
144              
145             sub is {
146             my ($got, $expt, $test_name) = @_;
147              
148             my $self = Test::CT->instance;
149             my $res = Test::More::is( $got, $expt, $test_name);
150             $self->push_log({
151             func => 'is',
152             arguments => [$got, $expt, $test_name],
153             result => $res,
154             name => $test_name
155             });
156              
157             }
158              
159             sub isnt {
160             my ($got, $expt, $test_name) = @_;
161              
162             my $self = Test::CT->instance;
163             my $res = Test::More::isnt( $got, $expt, $test_name);
164             $self->push_log({
165             func => 'isnt',
166             arguments => [$got, $expt, $test_name],
167             result => $res,
168             name => $test_name
169             });
170             }
171              
172              
173             sub like {
174             my ($got, $expt, $test_name) = @_;
175              
176             my $self = Test::CT->instance;
177             my $res = Test::More::like( $got, $expt, $test_name);
178             $self->push_log({
179             func => 'like',
180             arguments => [$got, $expt, $test_name],
181             result => $res,
182             name => $test_name
183             });
184             }
185              
186             sub unlike {
187             my ($got, $expt, $test_name) = @_;
188              
189             my $self = Test::CT->instance;
190             my $res = Test::More::unlike( $got, $expt, $test_name);
191             $self->push_log({
192             func => 'unlike',
193             arguments => [$got, $expt, $test_name],
194             result => $res,
195             name => $test_name
196             });
197             }
198              
199             sub is_deeply {
200             my ($got, $expt, $test_name) = @_;
201              
202             my $self = Test::CT->instance;
203             my $res = Test::More::is_deeply( $got, $expt, $test_name);
204             $self->push_log({
205             func => 'is_deeply',
206             arguments => [$got, $expt, $test_name],
207             result => $res,
208             name => $test_name
209             });
210             }
211              
212             sub cmp_ok {
213             my ($got, $op, $want, $test_name) = @_;
214              
215             my $self = Test::CT->instance;
216             my $res = Test::More::cmp_ok( $got, $op, $want, $test_name );
217             $self->push_log({
218             func => 'cmp_ok',
219             arguments => [$got, $op, $want, $test_name],
220             result => $res,
221             name => $test_name
222             });
223             }
224              
225             sub diag {
226             my ($a) = @_;
227              
228             Test::More::diag( $a );
229             Test::CT->instance->push_log({
230             func => 'diag',
231             message => $a
232             });
233             }
234              
235             sub explain {
236             my ($a) = @_;
237              
238             Test::More::explain( $a );
239             Test::CT->instance->push_log({
240             func => 'explain',
241             message => $a
242             });
243             }
244              
245             sub note {
246             my ($a) = @_;
247             Test::More::note( $a );
248             Test::CT->instance->push_log({
249             func => 'note',
250             message => $a
251             });
252             }
253              
254              
255             sub push_log {
256             my ($self, $param) = @_;
257              
258             $param->{name} = $self->current_test;
259             push @{$self->stash->{_log}}, $param;
260             }
261              
262             sub down_log_level {
263             print STDERR "NOT IMPLEMENTED!";
264             }
265              
266             sub up_log_level {
267             print STDERR "NOT IMPLEMENTED!";
268             }
269              
270             sub finalize {
271             my ($self, $param) = @_;
272              
273             if ($self->track && ref $self->config->{log_writer} eq 'ARRAY'){
274              
275             foreach my $writer_conf (@{$self->config->{log_writer}}){
276              
277             $writer_conf->{path} = $self->config->{log_writers}{default_path}
278             unless exists $writer_conf->{path};
279              
280             my $class = 'Test::CT::LogWriter::' . $writer_conf->{format};
281             eval("use $class;");
282             die $@ if $@;
283              
284             my $writer = $class->new( tester => $self );
285              
286             $writer->generate($writer_conf);
287             }
288              
289             }
290              
291             }
292              
293              
294             1;
295              
296             __END__
297              
298             =pod
299              
300             =head1 NAME
301              
302             Test::CT - *Mix* of Test::More + Test::Reuse + Test::Routine, with *template* system.
303              
304             =head1 VERSION
305              
306             version 0.142
307              
308             =head1 SYNOPSIS
309              
310             use Test::CT;
311              
312             # get test singleton object
313             my $tester = Test::CT->instance;
314              
315             # add your tests.. this may repeat sometimes in your file.
316             do {
317             my $ref = sub {
318             # your testing code goes here
319             my $user = { name => 'foo' };
320             ok($user->{name}, 'looks have a name!');
321             is($user->{name}, 'foo', 'user name is foo');
322             isnt(1, 0, '1 isnt 0');
323              
324             $tester->stash->{user} = $user;
325              
326             };
327             # add this code reference to tests list
328             $tester->add_test(
329             Test::CT::TestFile->new(
330             coderef => $ref,
331             name => 'ct/tests/001-name-you-give.t'
332             )
333             );
334             };
335              
336             # then you can add another test that use $tester->stash->{user}
337             # expecting it to be ok
338              
339             # run the tests!
340             $tester->run( name => 'ct/tests/001-name-you-give.t');
341              
342             # this will not ran the test again
343             $tester->run( name => 'ct/tests/001-name-you-give.t');
344              
345             # but you can force it
346             $tester->run( name => 'ct/tests/001-name-you-give.t', force_exec => 1);
347              
348             $tester->run( like => 'name-.+'); # all tests name =~ /name-.+/
349              
350             $tester->run( llike => 'ct/tests/'); # all tests name =~ /^name-.+/
351              
352             # TODO
353             $tester->run( like => qr/your regularexpr/);
354              
355             Please see more in README in https://github.com/renatoaware/Test-CT
356              
357             =head1 DESCRIPTION
358              
359             Test-CT is a different way to you write your tests files.
360              
361             Using commands of Test::More, writing separated tests files like Test::Aggregate::Nested
362             and using a stash to keep tracking of all tests for you write a simple (or not)
363             documentation for your project.
364              
365             =head1 METHODS
366              
367             =head2 run(%conf)
368              
369             Run the coderef of a Test::CT::TestFile
370              
371             ?name => 'string' # find test by name
372             ?like => 'string' # find test by /$like/
373             ?llike => 'string' # find test by /^$like/
374             ?force_exec => $boolean # true for execute tests even if already executed before
375              
376             =head2 stash
377              
378             It's like Catalyst stash. A simple hashref, so you can:
379              
380             $tester->stash( foo => 1, bar => 2)
381             $tester->stash({ abc => 2});
382             $tester->stash->{foo}++;
383              
384             =head2 finalize
385              
386             Instantiate all Test::CT::LogWriter::XXX from @{$self->config->{log_writer}} to generate documentation.
387              
388             Should be called after all tests run.
389              
390             =head1 CAVEATS
391              
392             This is alpha software. But the interface will be stable, and changes will
393             make effort to keep back compatibility, even though major revisions.
394              
395             =head1 SPONSORED BY
396              
397             Aware - L<http://www.aware.com.br>
398              
399             =head1 AUTHOR
400              
401             Renato Cron <rentocron@cpan.org>
402              
403             =head1 COPYRIGHT AND LICENSE
404              
405             This software is copyright (c) 2013 by Renato Cron.
406              
407             This is free software; you can redistribute it and/or modify it under
408             the same terms as the Perl 5 programming language system itself.
409              
410             =cut