File Coverage

blib/lib/Test/Proto/Common.pm
Criterion Covered Total %
statement 61 78 78.2
branch 30 38 78.9
condition 6 12 50.0
subroutine 12 16 75.0
pod 6 6 100.0
total 115 150 76.6


line stmt bran cond sub pod time code
1             package Test::Proto::Common;
2 15     15   20832 use 5.008;
  15         53  
  15         589  
3 15     15   83 use strict;
  15         26  
  15         436  
4 15     15   87 use warnings;
  15         36  
  15         371  
5 15     15   7101 use Sub::Name;
  15         5631  
  15         897  
6 15     15   119 use Exporter 'import';
  15         29  
  15         530  
7 15     15   90 use Scalar::Util qw(blessed looks_like_number);
  15         34  
  15         2772  
8             our @EXPORT = qw(define_test define_simple_test simple_test upgrade upgrade_comparison);
9              
10             our $TEST_PREFIX = '_TEST_'; #~ this is used when creating internal methods.
11              
12             =head1 NAME
13              
14             Test::Proto::Common - Provides common functions for Test::Proto development
15              
16             =head1 SYNOPSIS
17              
18             use Test::Proto::Common; # exports all functions automatically
19              
20             Provides functions used to build a Prototype class.
21              
22             =cut
23              
24             =head1 FUNCTIONS
25              
26             All these functions are for use in prototype classes, not in scripts.
27              
28             =head3 define_test
29              
30             define_test 'is_uppercase', sub {
31             my ($self, $data, $reason) = @_; # self is the runner, NOT the prototype
32             if ($self->subject =~ !/[a-z]/){
33             return $self->pass;
34             }
35             return $self->fail;
36             };
37              
38             Adds a test definition to the class. This allows you to create user-facing test methods which interact with the test definition. The name you provide is the name of the test definition, which usually matches the test method (but is not required to).
39              
40             Optionally, you can set the package to which this method is to be added as a third argument.
41              
42             =cut
43              
44             sub define_test {
45 814     814 1 2525 my ( $testName, $testSub, $customPackage ) = @_;
46 814         3306 my ( $package, $filename, $line ) = caller;
47 814 50       2433 $package = $customPackage if defined $customPackage;
48             {
49 15     15   93 no strict 'refs';
  15         39  
  15         3905  
  814         1124  
50 814         2177 my $fullName = $package . '::' . $TEST_PREFIX . $testName;
51 814         13375 *$fullName = subname( $TEST_PREFIX . $testName, $testSub ); #~ Consider Sub::Install here, per Khisanth on irc.freenode.net#perl
52             }
53              
54             #~ return value of this not specified
55             }
56              
57             =head3 define_simple_test
58              
59             Adds a test definition to the class. In this case, the subroutine passed evaluates the subject against the expected data.
60              
61             =cut
62              
63             sub define_simple_test {
64 0     0 1 0 my ( $testName, $testSub, $customPackage ) = @_;
65 0         0 my ( $package, $filename, $line ) = caller;
66 0 0       0 $package = $customPackage if defined $customPackage;
67             define_test(
68             $testName,
69             sub {
70 0     0   0 my ( $self, $data, $reason ) = ( shift, shift, shift ); # self is the runner, NOT the prototype
71 0 0       0 if ( $testSub->( $self->subject, $data->{expected} ) ) {
72 0         0 return $self->pass;
73             }
74             else {
75 0         0 return $self->fail;
76             }
77             },
78 0         0 $package
79             );
80             }
81              
82             =head3 simple_test
83              
84             simple_test 'lc_eq', sub {
85             return lc ($_[0]) eq $_[1];
86             };
87            
88             ...
89            
90             p->lc_eq('yes')->ok('Yes');
91              
92             Adds a test method to the class. The first argument is the name of that method, the second argument is the code to be executed - however, the code should return only a true or false value, and is passed only the test subject and the expected value, not the runner or full data.
93              
94             The test method itself takes one argument, the expected value.
95              
96             =cut
97              
98             sub simple_test {
99 0     0 1 0 my ( $testName, $testSub ) = @_;
100 0         0 my ( $package, $filename, $line ) = caller;
101             {
102 15     15   97 no strict 'refs';
  15         28  
  15         11100  
  0         0  
103             {
104             #package $package;
105 0         0 define_simple_test( $testName, $testSub, $package );
  0         0  
106             }
107 0         0 my $fullName = $package . '::' . $testName;
108             *$fullName = subname(
109             $testName,
110             sub {
111 0     0   0 my ( $self, $expected, $reason ) = ( shift, shift, shift );
112 0         0 $self->add_test( $testName, { expected => $expected }, $reason );
113             }
114 0         0 ); # Consider Sub::Install here, per Khisanth on irc.freenode.net#perl
115             }
116             }
117              
118             =head3 upgrade
119              
120             upgrade('NONE'); # returns Test::Proto::Base->new()->eq('NONE')
121             upgrade(1); # returns Test::Proto::Base->new()->num_eq(1)
122             upgrade(['foo']); # returns Test::Proto::ArrayRef->new()->array_eq(['foo'])
123             upgrade({'foo'=>'bar'}); # returns Test::Proto::HashRef->new()->hash_of({'foo'=>'bar'})
124             upgrade(sub {return $_ * 2 == 4}); Test::Proto::Base->new()->try(...)
125              
126             Returns a Prototype which corresponds to the data in the first argument.
127              
128             If the first argument is already a prototype, this does nothing.
129              
130             Use this when you have a parameter and want to validate data against it, but you do not know if it is a prototype or 'natural data'.
131              
132             =cut
133              
134             sub upgrade {
135 2242     2242 1 4591 my ( $expected, $noref ) = @_;
136             {
137 2242         2753 require Test::Proto::Base;
  2242         15220  
138 2242         17160 require Test::Proto::HashRef;
139 2242         13577 require Test::Proto::ArrayRef;
140              
141 2242 50       8124 if ( defined ref $expected ) {
142 2242 100       10010 if ( blessed $expected) {
143 1185 50 33     29226 return Test::Proto::ArrayRef->new()->array->contains_only($expected)
      33        
144             if $expected->isa('Test::Proto::Series')
145             or $expected->isa('Test::Proto::Repeatable')
146             or $expected->isa('Test::Proto::Alternation');
147 1185 100       34462 return $expected if $expected->isa('Test::Proto::Base');
148             }
149 1082 100       7035 return Test::Proto::ArrayRef->new()->array->array_eq($expected) if ref $expected eq 'ARRAY';
150 974 100       2972 return Test::Proto::HashRef->new()->hash->superhash_of($expected) if ref $expected eq 'HASH';
151 971 100       3289 return Test::Proto::Base->new()->like($expected) if ref $expected eq 'Regexp';
152 946 100       4280 return Test::Proto::Base->new()->try($expected) if ref $expected eq 'CODE';
153             }
154 913 100       14333 return Test::Proto::Base->new()->scalar->num_eq($expected) if Scalar::Util::looks_like_number($expected);
155              
156             #return Test::Proto::Base->new()->eq($expected) if $noref;
157 530         14308 return Test::Proto::Base->new()->scalar->eq($expected);
158             }
159             }
160              
161             =head3 upgrade_comparison
162              
163             upgrade_comparison(sub {lc shift cmp lc shift}, 'Lowercase Comparison');
164              
165             This creates a Test::Proto::Compare object using the code provided in the first argument. The second argument, if present, is used as the summary.
166              
167             If the first argument is either of the strings 'cmp' or '<=>', it will return the appropriate string or numeric comparison, as these are special-cased.
168              
169             =cut
170              
171             sub upgrade_comparison {
172 60     60 1 2828 require Test::Proto::Compare;
173 60         2532 require Test::Proto::Compare::Numeric;
174 60         156 my ( $comparison, $summary ) = @_;
175 60 100       225 $summary = 'Unknown comparison' unless defined $summary; #:5.8
176 60 100 66     636 if ( ref $comparison eq 'CODE' ) {
    100 66        
    100          
177 27         717 return Test::Proto::Compare->new($comparison)->summary($summary);
178             }
179             elsif ( blessed $comparison and $comparison->isa('Test::Proto::Compare') ) {
180 3         13 return $comparison;
181             }
182             elsif ( defined $comparison and !ref $comparison ) {
183 2 100       40 return Test::Proto::Compare->new if $comparison eq 'cmp';
184 1 50       31 return Test::Proto::Compare::Numeric->new if $comparison eq '<=>';
185             }
186 28         738 return Test::Proto::Compare->new;
187             }
188              
189             =head3 chainable
190              
191             around 'attr', 'other_attr', \&Test::Proto::Common::chainable;
192              
193             ...
194              
195             $object->attr(2)->some_method;
196              
197             Use this to make a Moo attribute chainable.
198            
199             =cut
200              
201             sub chainable {
202 185869     185869 1 4587000 my $orig = shift;
203 185869         237636 my $self = shift;
204 185869 100       366020 if ( exists $_[0] ) {
205              
206             #~ when setting, return self
207 18118         48042 $orig->( $self, @_ );
208 18118         53007 return $self;
209             }
210             else {
211             #~ when getting, return value
212 167751         2321785 return $orig->( $self, @_ );
213             }
214             }
215              
216             1;