File: type_check.t

package info (click to toggle)
libmethod-signatures-perl 20170211-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 672 kB
  • sloc: perl: 3,860; makefile: 2
file content (149 lines) | stat: -r--r--r-- 6,620 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/perl

use strict;
use warnings;

use Test::More;
use Test::Warn;
use Test::Exception;

use Method::Signatures;


{ package Foo::Bar; sub new { bless {}, __PACKAGE__; } }
{ package Foo::Baz; sub new { bless {}, __PACKAGE__; } }

our $foobar = Foo::Bar->new;
our $foobaz = Foo::Baz->new;


# types to check below
# the test name needs to be interpolated into a method name, so it must be a valid identifier
# either good value or bad value can be an array reference:
#   *   if it is, it is taken to be multiple values to try
#   *   if you want to pass an array reference, you have to put it inside another array reference
#   *   so, [ 42, undef ] makes two calls: one with 42, and one with undef
#   *   but [[ 42, undef ]] makes one call, passing [ 42, undef ]
our @TYPES =
(
##  Test Name       =>  Type                =>  Good Value                      =>  Bad Value
    int             =>  'Int'               =>  42                              =>  'foo'                               ,
    bool            =>  'Bool'              =>  0                               =>  'fool'                              ,
    aref            =>  'ArrayRef',         =>  [[ 42, undef ]]                 =>  42                                  ,
    class           =>  'Foo::Bar'          =>  $foobar                         =>  $foobaz                             ,
    maybe_int       =>  'Maybe[Int]'        =>  [ 42, undef ]                   =>  'foo'                               ,
    paramized_aref  =>  'ArrayRef[Num]'     =>  [[ 6.5, 42, 1e23 ]]             =>  [[ 6.5, 42, 'thing' ]]              ,
    paramized_href  =>  'HashRef[Num]'      =>  { a => 6.5, b => 2, c => 1e23 } =>  { a => 6.5, b => 42, c => 'thing' } ,
    paramized_nested=>  'HashRef[ArrayRef[Int]]'
                                            =>  { foo=>[1..3], bar=>[1] }       =>  { foo=>['a'] }                               ,
##  ScalarRef[X] not implemented in Mouse, so this test is moved to typeload_moose.t
##  if Mouse starts supporting it, the test could be restored here
#   paramized_sref  =>  'ScalarRef[Num]'    =>  \42                             =>  \'thing'                            ,
    int_or_aref     =>  'Int|ArrayRef[Int]' =>  [ 42 , [42 ] ]                  =>  'foo'                               ,
    int_or_aref_or_undef
                    =>  'Int|ArrayRef[Int]|Undef'
                                            =>  [ 42 , [42 ], undef ]           =>  'foo'                               ,
);


our $tester;
{
    package TypeCheck::Class;

    use strict;
    use warnings;

    use Test::More;
    use Test::Warn;
    use Test::Exception;

    use lib 't/lib';
    use GenErrorRegex qw< badval_error badtype_error >;

    use Method::Signatures;

    method new ($class:) { bless {}, $class; }

    sub _list { return ref $_[0] eq 'ARRAY' ? @{$_[0]} : ( $_[0] ); }


    $tester = __PACKAGE__->new;
    while (@TYPES)
    {
        my ($name, $type, $goodval, $badval) = splice @TYPES, 0, 4;
        note "name/type/goodval/badval $name/$type/$goodval/$badval";
        my $method = "check_$name";
        no strict 'refs';

        # make sure the declaration of the method doesn't throw a warning
        warning_is { eval qq{ method $method ($type \$bar) {} } } undef, "no warnings from declaring $name param";

        # positive test--can we call it with a good value?
        my @vals = _list($goodval);
        my $count = 1;
        foreach (@vals)
        {
            my $tag = @vals ? ' (alternative ' . $count++ . ')' : '';
            lives_ok { $tester->$method($_) } "call with good value for $name passes" . $tag;
        }

        # negative test--does calling it with a bad value throw an exception?
        @vals = _list($badval);
        $count = 1;
        foreach (@vals)
        {
            my $tag = @vals ? ' (#' . $count++ . ')' : '';
            throws_ok { $tester->$method($_) } badval_error($tester, bar => $type, $_, $method),
                    "call with bad value for $name dies";
        }
    }


    # try some mixed (i.e. some with a type, some without) and multiples

    my $method = 'check_mixed_type_first';
    warning_is { eval qq{ method $method (Int \$bar, \$baz) {} } } undef, 'no warnings (type, notype)';
    lives_ok { $tester->$method(0, 'thing') } 'call with good values (type, notype) passes';
    throws_ok { $tester->$method('thing1', 'thing2') } badval_error($tester, bar => Int => thing1 => $method),
            'call with bad values (type, notype) dies';

    $method = 'check_mixed_type_second';
    warning_is { eval qq{ method $method (\$bar, Int \$baz) {} } } undef, 'no warnings (notype, type)';
    lives_ok { $tester->$method('thing', 1) } 'call with good values (notype, type) passes';
    throws_ok { $tester->$method('thing1', 'thing2') } badval_error($tester, baz => Int => thing2 => $method),
            'call with bad values (notype, type) dies';

    $method = 'check_multiple_types';
    warning_is { eval qq{ method $method (Int \$bar, Int \$baz) {} } } undef, 'no warnings when type loaded';
    lives_ok { $tester->$method(1, 1) } 'call with good values (type, type) passes';
    # with two types, and bad values for both, they should fail in order of declaration
    throws_ok { $tester->$method('thing1', 'thing2') } badval_error($tester, bar => Int => thing1 => $method),
            'call with bad values (type, type) dies';

    # want to try one with undef as well to make sure we don't get an uninitialized warning

    warning_is { eval { $tester->check_int(undef) } } undef, 'no warning for undef value in type checking';
    like $@, badval_error($tester, bar => Int => undef, 'check_int'),
            'call with undefined Int arg is okay';


    # finally, some types that shouldn't be recognized
    my $type;

    $method = 'unknown_type';
    $type = 'Bmoogle';
    warning_is { eval qq{ method $method ($type \$bar) {} } } undef, 'no warnings when weird type loaded';
    throws_ok { $tester->$method(42) } badtype_error($tester, $type, "perhaps you forgot to load it?", $method),
            'call with unrecognized type dies';

    # this one is a bit specialer in that it involved an unrecognized parameterization
    $method = 'unknown_paramized_type';
    $type = 'Bmoogle[Int]';
    warning_is { eval qq{ method $method ($type \$bar) {} } } undef, 'no warnings when weird paramized type loaded';
    throws_ok { $tester->$method(42) } badtype_error($tester, $type, "looks like it doesn't parse correctly", $method),
            'call with unrecognized paramized type dies';

}


done_testing;