aa4a72cfd9effc0532ecb51aa6d8bde9237e6a81
[gitmo/MooseX-ClassAttribute.git] / t / lib / SharedTests.pm
1 package SharedTests;
2
3 use strict;
4 use warnings;
5
6 use Scalar::Util qw( isweak );
7 use Test::More;
8
9 use vars qw($Lazy);
10
11 our %Attrs = (
12     ObjectCount => {
13         is      => 'rw',
14         isa     => 'Int',
15         default => 0,
16     },
17     WeakAttribute => {
18         is       => 'rw',
19         isa      => 'Object',
20         weak_ref => 1,
21     },
22     LazyAttribute => {
23         is   => 'rw',
24         isa  => 'Int',
25         lazy => 1,
26         # The side effect is used to test that this was called
27         # lazily.
28         default => sub { $Lazy = 1 },
29     },
30     ReadOnlyAttribute => {
31         is      => 'ro',
32         isa     => 'Int',
33         default => 10,
34     },
35     ManyNames => {
36         is        => 'rw',
37         isa       => 'Int',
38         reader    => 'M',
39         writer    => 'SetM',
40         clearer   => 'ClearM',
41         predicate => 'HasM',
42     },
43     Delegatee => {
44         is      => 'rw',
45         isa     => 'Delegatee',
46         handles => [ 'units', 'color' ],
47         # if it's not lazy it makes a new object before we define
48         # Delegatee's attributes.
49         lazy    => 1,
50         default => sub { Delegatee->new() },
51     },
52     Mapping => {
53         traits  => ['Hash'],
54         is      => 'rw',
55         isa     => 'HashRef[Str]',
56         default => sub { {} },
57         handles => {
58             'ExistsInMapping' => 'exists',
59             'IdsInMapping'    => 'keys',
60             'GetMapping'      => 'get',
61             'SetMapping'      => 'set',
62         },
63     },
64     Built => {
65         is      => 'ro',
66         builder => '_BuildIt',
67     },
68     LazyBuilt => {
69         is      => 'ro',
70         lazy    => 1,
71         builder => '_BuildIt',
72     },
73     Triggerish => {
74         is      => 'rw',
75         trigger => sub { shift->_CallTrigger(@_) },
76     },
77     TriggerRecord => {
78         is      => 'ro',
79         default => sub { [] },
80     },
81 );
82
83 {
84     package HasClassAttribute;
85
86     use Moose qw( has );
87     use MooseX::ClassAttribute;
88
89     while ( my ( $name, $def ) = each %SharedTests::Attrs ) {
90         class_has $name => %{$def};
91     }
92
93     has 'size' => (
94         is      => 'rw',
95         isa     => 'Int',
96         default => 5,
97     );
98
99     no Moose;
100
101     sub BUILD {
102         my $self = shift;
103
104         $self->ObjectCount( $self->ObjectCount() + 1 );
105     }
106
107     sub _BuildIt {42}
108
109     sub _CallTrigger {
110         push @{ $_[0]->TriggerRecord() }, [@_];
111     }
112
113     sub make_immutable {
114         my $class = shift;
115
116         $class->meta()->make_immutable();
117         Delegatee->meta()->make_immutable();
118     }
119 }
120
121 {
122     package Delegatee;
123
124     use Moose;
125
126     has 'units' => (
127         is      => 'ro',
128         default => 5,
129     );
130
131     has 'color' => (
132         is      => 'ro',
133         default => 'blue',
134     );
135
136     no Moose;
137 }
138
139 {
140     package Child;
141
142     use Moose;
143     use MooseX::ClassAttribute;
144
145     extends 'HasClassAttribute';
146
147     class_has '+ReadOnlyAttribute' => ( default => 30 );
148
149     class_has 'YetAnotherAttribute' => (
150         is      => 'ro',
151         default => 'thing',
152     );
153
154     no Moose;
155 }
156
157 sub run_tests {
158     my $thing = shift || 'HasClassAttribute';
159
160     local $Test::Builder::Level = $Test::Builder::Level + 1;
161
162     $Lazy = 0;
163
164     my $count = ref $thing ? 1 : 0;
165
166     {
167         is(
168             $thing->ObjectCount(), $count,
169             'ObjectCount() is 0'
170         );
171
172         unless ( ref $thing ) {
173             my $hca1 = $thing->new();
174             is(
175                 $hca1->size(), 5,
176                 'size is 5 - object attribute works as expected'
177             );
178             is(
179                 $thing->ObjectCount(), 1,
180                 'ObjectCount() is 1'
181             );
182
183             my $hca2 = $thing->new( size => 10 );
184             is(
185                 $hca2->size(), 10,
186                 'size is 10 - object attribute can be set via constructor'
187             );
188             is(
189                 $thing->ObjectCount(), 2,
190                 'ObjectCount() is 2'
191             );
192             is(
193                 $hca2->ObjectCount(), 2,
194                 'ObjectCount() is 2 - can call class attribute accessor on object'
195             );
196         }
197     }
198
199     unless ( ref $thing ) {
200         my $hca3 = $thing->new( ObjectCount => 20 );
201         is(
202             $hca3->ObjectCount(), 3,
203             'class attributes passed to the constructor do not get set in the object'
204         );
205         is(
206             $thing->ObjectCount(), 3,
207             'class attributes are not affected by constructor params'
208         );
209     }
210
211     {
212         my $object = bless {}, 'Thing';
213
214         $thing->WeakAttribute($object);
215
216         undef $object;
217
218         ok(
219             !defined $thing->WeakAttribute(),
220             'weak class attributes are weak'
221         );
222     }
223
224     {
225         is(
226             $SharedTests::Lazy, 0,
227             '$SharedTests::Lazy is 0'
228         );
229
230         is(
231             $thing->LazyAttribute(), 1,
232             '$thing->LazyAttribute() is 1'
233         );
234
235         is(
236             $SharedTests::Lazy, 1,
237             '$SharedTests::Lazy is 1 after calling LazyAttribute'
238         );
239     }
240
241     {
242         eval { $thing->ReadOnlyAttribute(20) };
243         like(
244             $@, qr/\QCannot assign a value to a read-only accessor/,
245             'cannot set read-only class attribute'
246         );
247     }
248
249     {
250         is(
251             Child->ReadOnlyAttribute(), 30,
252             q{Child class can extend parent's class attribute}
253         );
254     }
255
256     {
257         ok(
258             !$thing->HasM(),
259             'HasM() returns false before M is set'
260         );
261
262         $thing->SetM(22);
263
264         ok(
265             $thing->HasM(),
266             'HasM() returns true after M is set'
267         );
268         is(
269             $thing->M(), 22,
270             'M() returns 22'
271         );
272
273         $thing->ClearM();
274
275         ok(
276             !$thing->HasM(),
277             'HasM() returns false after M is cleared'
278         );
279     }
280
281     {
282         isa_ok(
283             $thing->Delegatee(), 'Delegatee',
284             'has a Delegetee object'
285         );
286         is(
287             $thing->units(), 5,
288             'units() delegates to Delegatee and returns 5'
289         );
290     }
291
292     {
293         my @ids = $thing->IdsInMapping();
294         is(
295             scalar @ids, 0,
296             'there are no keys in the mapping yet'
297         );
298
299         ok(
300             !$thing->ExistsInMapping('a'),
301             'key does not exist in mapping'
302         );
303
304         $thing->SetMapping( a => 20 );
305
306         ok(
307             $thing->ExistsInMapping('a'),
308             'key does exist in mapping'
309         );
310
311         is(
312             $thing->GetMapping('a'), 20,
313             'value for a in mapping is 20'
314         );
315     }
316
317     {
318         is(
319             $thing->Built(), 42,
320             'attribute with builder works'
321         );
322
323         is(
324             $thing->LazyBuilt(), 42,
325             'attribute with lazy builder works'
326         );
327     }
328
329     {
330         $thing->Triggerish(42);
331
332         is( scalar @{ $thing->TriggerRecord() }, 1, 'trigger was called' );
333         is( $thing->Triggerish(), 42, 'Triggerish is now 42' );
334
335         $thing->Triggerish(84);
336         is( $thing->Triggerish(), 84, 'Triggerish is now 84' );
337
338         is_deeply(
339             $thing->TriggerRecord(),
340             [
341                 [ $thing, qw( 42 ) ],
342                 [ $thing, qw( 84 42 ) ],
343             ],
344             'trigger passes old value correctly'
345         );
346     }
347 }
348
349 1;