xref: /openbsd-src/gnu/usr.bin/perl/t/class/field.t (revision 5486feefcc8cb79b19e014ab332cc5dfd05b3b33)
1f2a19305Safresh1#!./perl
2f2a19305Safresh1
3f2a19305Safresh1BEGIN {
4f2a19305Safresh1    chdir 't' if -d 't';
5f2a19305Safresh1    require './test.pl';
6f2a19305Safresh1    set_up_inc('../lib');
7f2a19305Safresh1    require Config;
8f2a19305Safresh1}
9f2a19305Safresh1
10f2a19305Safresh1use v5.36;
11f2a19305Safresh1use feature 'class';
12f2a19305Safresh1no warnings 'experimental::class';
13f2a19305Safresh1
14f2a19305Safresh1# We can't test fields in isolation without having at least one method to
15f2a19305Safresh1# use them from. We'll try to keep most of the heavy testing of method
16f2a19305Safresh1# abilities to t/class/method.t
17f2a19305Safresh1
18f2a19305Safresh1# field in method
19f2a19305Safresh1{
20*5486feefSafresh1    class Testcase1 {
21f2a19305Safresh1        field $f;
22f2a19305Safresh1        method incr { return ++$f; }
23f2a19305Safresh1    }
24f2a19305Safresh1
25*5486feefSafresh1    my $obj = Testcase1->new;
26f2a19305Safresh1    $obj->incr;
27f2a19305Safresh1    is($obj->incr, 2, 'Field $f incremented twice');
28f2a19305Safresh1
29*5486feefSafresh1    my $obj2 = Testcase1->new;
30f2a19305Safresh1    is($obj2->incr, 1, 'Fields are distinct between instances');
31f2a19305Safresh1}
32f2a19305Safresh1
33f2a19305Safresh1# fields are distinct
34f2a19305Safresh1{
35*5486feefSafresh1    class Testcase2 {
36f2a19305Safresh1        field $x;
37f2a19305Safresh1        field $y;
38f2a19305Safresh1
39f2a19305Safresh1        method setpos { $x = $_[0]; $y = $_[1] }
40f2a19305Safresh1        method x      { return $x; }
41f2a19305Safresh1        method y      { return $y; }
42f2a19305Safresh1    }
43f2a19305Safresh1
44*5486feefSafresh1    my $obj = Testcase2->new;
45f2a19305Safresh1    $obj->setpos(10, 20);
46f2a19305Safresh1    is($obj->x, 10, '$pos->x');
47f2a19305Safresh1    is($obj->y, 20, '$pos->y');
48f2a19305Safresh1}
49f2a19305Safresh1
50f2a19305Safresh1# fields of all variable types
51f2a19305Safresh1{
52*5486feefSafresh1    class Testcase3 {
53f2a19305Safresh1        field $s;
54f2a19305Safresh1        field @a;
55f2a19305Safresh1        field %h;
56f2a19305Safresh1
57f2a19305Safresh1        method setup {
58f2a19305Safresh1            $s = "scalar";
59f2a19305Safresh1            @a = ( "array" );
60f2a19305Safresh1            %h = ( key => "hash" );
61f2a19305Safresh1            return $self; # test chaining
62f2a19305Safresh1        }
63f2a19305Safresh1        method test {
64f2a19305Safresh1            ::is($s,      "scalar", 'scalar storage');
65f2a19305Safresh1            ::is($a[0],   "array",  'array storage');
66f2a19305Safresh1            ::is($h{key}, "hash",   'hash storage');
67f2a19305Safresh1        }
68f2a19305Safresh1    }
69f2a19305Safresh1
70*5486feefSafresh1    Testcase3->new->setup->test;
71f2a19305Safresh1}
72f2a19305Safresh1
73f2a19305Safresh1# fields can be captured by anon subs
74f2a19305Safresh1{
75*5486feefSafresh1    class Testcase4 {
76f2a19305Safresh1        field $count;
77f2a19305Safresh1
78f2a19305Safresh1        method make_incrsub {
79f2a19305Safresh1            return sub { $count++ };
80f2a19305Safresh1        }
81f2a19305Safresh1
82f2a19305Safresh1        method count { return $count }
83f2a19305Safresh1    }
84f2a19305Safresh1
85*5486feefSafresh1    my $obj = Testcase4->new;
86f2a19305Safresh1    my $incr = $obj->make_incrsub;
87f2a19305Safresh1
88f2a19305Safresh1    $incr->();
89f2a19305Safresh1    $incr->();
90f2a19305Safresh1    $incr->();
91f2a19305Safresh1
92f2a19305Safresh1    is($obj->count, 3, '$obj->count after invoking closure x 3');
93f2a19305Safresh1}
94f2a19305Safresh1
95f2a19305Safresh1# fields can be captured by anon methods
96f2a19305Safresh1{
97*5486feefSafresh1    class Testcase5 {
98f2a19305Safresh1        field $count;
99f2a19305Safresh1
100f2a19305Safresh1        method make_incrmeth {
101f2a19305Safresh1            return method { $count++ };
102f2a19305Safresh1        }
103f2a19305Safresh1
104f2a19305Safresh1        method count { return $count }
105f2a19305Safresh1    }
106f2a19305Safresh1
107*5486feefSafresh1    my $obj = Testcase5->new;
108f2a19305Safresh1    my $incr = $obj->make_incrmeth;
109f2a19305Safresh1
110f2a19305Safresh1    $obj->$incr;
111f2a19305Safresh1    $obj->$incr;
112f2a19305Safresh1    $obj->$incr;
113f2a19305Safresh1
114f2a19305Safresh1    is($obj->count, 3, '$obj->count after invoking method-closure x 3');
115f2a19305Safresh1}
116f2a19305Safresh1
117f2a19305Safresh1# fields of multiple unit classes are distinct
118f2a19305Safresh1{
119*5486feefSafresh1    class Testcase6::A;
120f2a19305Safresh1    field $x = "A";
121f2a19305Safresh1    method m { return "unit-$x" }
122f2a19305Safresh1
123*5486feefSafresh1    class Testcase6::B;
124f2a19305Safresh1    field $x = "B";
125f2a19305Safresh1    method m { return "unit-$x" }
126f2a19305Safresh1
127f2a19305Safresh1    package main;
128*5486feefSafresh1    ok(eq_array([Testcase6::A->new->m, Testcase6::B->new->m], ["unit-A", "unit-B"]),
129f2a19305Safresh1        'Fields of multiple unit classes remain distinct');
130f2a19305Safresh1}
131f2a19305Safresh1
132f2a19305Safresh1# fields can be initialised with constant expressions
133f2a19305Safresh1{
134*5486feefSafresh1    class Testcase7 {
135f2a19305Safresh1        field $scalar = 123;
136f2a19305Safresh1        method scalar { return $scalar; }
137f2a19305Safresh1
138f2a19305Safresh1        field @array = (4, 5, 6);
139f2a19305Safresh1        method array { return @array; }
140f2a19305Safresh1
141f2a19305Safresh1        field %hash  = (7 => 89);
142f2a19305Safresh1        method hash { return %hash; }
143f2a19305Safresh1    }
144f2a19305Safresh1
145*5486feefSafresh1    my $obj = Testcase7->new;
146f2a19305Safresh1
147f2a19305Safresh1    is($obj->scalar, 123, 'Scalar field can be constant initialised');
148f2a19305Safresh1
149f2a19305Safresh1    ok(eq_array([$obj->array], [4, 5, 6]), 'Array field can be constant initialised');
150f2a19305Safresh1
151f2a19305Safresh1    ok(eq_hash({$obj->hash}, {7 => 89}), 'Hash field can be constant initialised');
152f2a19305Safresh1}
153f2a19305Safresh1
154f2a19305Safresh1# field initialiser expressions are evaluated within the constructor of each
155f2a19305Safresh1# instance
156f2a19305Safresh1{
157f2a19305Safresh1    my $next_x = 1;
158f2a19305Safresh1    my @items;
159f2a19305Safresh1    my %mappings;
160f2a19305Safresh1
161*5486feefSafresh1    class Testcase8 {
162f2a19305Safresh1        field $x = $next_x++;
163f2a19305Safresh1        method x { return $x; }
164f2a19305Safresh1
165f2a19305Safresh1        field @y = ("more", @items);
166f2a19305Safresh1        method y { return @y; }
167f2a19305Safresh1
168f2a19305Safresh1        field %z = (first => "value", %mappings);
169f2a19305Safresh1        method z { return %z; }
170f2a19305Safresh1    }
171f2a19305Safresh1
172f2a19305Safresh1    is($next_x, 1, '$next_x before any objects');
173f2a19305Safresh1
174f2a19305Safresh1    @items = ("values");
175f2a19305Safresh1    $mappings{second} = "here";
176f2a19305Safresh1
177*5486feefSafresh1    my $obj1 = Testcase8->new;
178*5486feefSafresh1    my $obj2 = Testcase8->new;
179f2a19305Safresh1
180f2a19305Safresh1    is($obj1->x, 1, 'Object 1 has x == 1');
181f2a19305Safresh1    is($obj2->x, 2, 'Object 2 has x == 2');
182f2a19305Safresh1
183f2a19305Safresh1    is($next_x, 3, '$next_x after constructing two');
184f2a19305Safresh1
185f2a19305Safresh1    ok(eq_array([$obj1->y], ["more", "values"]),
186f2a19305Safresh1        'Object 1 has correct array field');
187f2a19305Safresh1    ok(eq_hash({$obj1->z}, {first => "value", second => "here"}),
188f2a19305Safresh1        'Object 1 has correct hash field');
189f2a19305Safresh1}
190f2a19305Safresh1
191f2a19305Safresh1# fields are visible during initialiser expressions of later fields
192f2a19305Safresh1{
193*5486feefSafresh1    class Testcase9 {
194f2a19305Safresh1        field $one   = 1;
195f2a19305Safresh1        field $two   = $one + 1;
196f2a19305Safresh1        field $three = $two + 1;
197f2a19305Safresh1
198f2a19305Safresh1        field @four = $one;
199f2a19305Safresh1        field @five = (@four, $two, $three);
200f2a19305Safresh1        field @six  = grep { $_ > 1 } @five;
201f2a19305Safresh1
202f2a19305Safresh1        method three { return $three; }
203f2a19305Safresh1
204f2a19305Safresh1        method six { return @six; }
205f2a19305Safresh1    }
206f2a19305Safresh1
207*5486feefSafresh1    my $obj = Testcase9->new;
208f2a19305Safresh1    is($obj->three, 3, 'Scalar fields initialised from earlier fields');
209f2a19305Safresh1    ok(eq_array([$obj->six], [2, 3]), 'Array fields initialised from earlier fields');
210f2a19305Safresh1}
211f2a19305Safresh1
212f2a19305Safresh1# fields can take :param attributes to consume constructor parameters
213f2a19305Safresh1{
214f2a19305Safresh1    my $next_gamma = 4;
215f2a19305Safresh1
216*5486feefSafresh1    class Testcase10 {
217f2a19305Safresh1        field $alpha :param        = undef;
218f2a19305Safresh1        field $beta  :param        = 123;
219f2a19305Safresh1        field $gamma :param(delta) = $next_gamma++;
220f2a19305Safresh1
221f2a19305Safresh1        method values { return ($alpha, $beta, $gamma); }
222f2a19305Safresh1    }
223f2a19305Safresh1
224*5486feefSafresh1    my $obj = Testcase10->new(
225f2a19305Safresh1        alpha => "A",
226f2a19305Safresh1        beta  => "B",
227f2a19305Safresh1        delta => "G",
228f2a19305Safresh1    );
229f2a19305Safresh1    ok(eq_array([$obj->values], [qw(A B G)]),
230f2a19305Safresh1        'Field initialised by :params');
231f2a19305Safresh1    is($next_gamma, 4, 'Defaulting expression not evaluated for passed value');
232f2a19305Safresh1
233*5486feefSafresh1    $obj = Testcase10->new();
234f2a19305Safresh1    ok(eq_array([$obj->values], [undef, 123, 4]),
235f2a19305Safresh1        'Field initialised by defaulting expressions');
236f2a19305Safresh1    is($next_gamma, 5, 'Defaulting expression evaluated for missing value');
237f2a19305Safresh1}
238f2a19305Safresh1
239f2a19305Safresh1# fields can be made non-optional
240f2a19305Safresh1{
241*5486feefSafresh1    class Testcase11 {
242f2a19305Safresh1        field $x :param;
243f2a19305Safresh1        field $y :param;
244f2a19305Safresh1    }
245f2a19305Safresh1
246*5486feefSafresh1    Testcase11->new(x => 1, y => 1);
247f2a19305Safresh1
248*5486feefSafresh1    ok(!eval { Testcase11->new(x => 2) },
249f2a19305Safresh1        'Constructor fails without y');
250*5486feefSafresh1    like($@, qr/^Required parameter 'y' is missing for "Testcase11" constructor at /,
251f2a19305Safresh1        'Failure from missing y argument');
252f2a19305Safresh1}
253f2a19305Safresh1
254f2a19305Safresh1# field assignment expressions on :param can use //= and ||=
255f2a19305Safresh1{
256*5486feefSafresh1    class Testcase12 {
257f2a19305Safresh1        field $if_exists  :param(e)   = "DEF";
258f2a19305Safresh1        field $if_defined :param(d) //= "DEF";
259f2a19305Safresh1        field $if_true    :param(t) ||= "DEF";
260f2a19305Safresh1
261f2a19305Safresh1        method values { return ($if_exists, $if_defined, $if_true); }
262f2a19305Safresh1    }
263f2a19305Safresh1
264f2a19305Safresh1    ok(eq_array(
265*5486feefSafresh1        [Testcase12->new(e => "yes", d => "yes", t => "yes")->values],
266f2a19305Safresh1        ["yes", "yes", "yes"]),
267f2a19305Safresh1        'Values for "yes"');
268f2a19305Safresh1
269f2a19305Safresh1    ok(eq_array(
270*5486feefSafresh1        [Testcase12->new(e => 0, d => 0, t => 0)->values],
271f2a19305Safresh1        [0, 0, "DEF"]),
272f2a19305Safresh1        'Values for 0');
273f2a19305Safresh1
274f2a19305Safresh1    ok(eq_array(
275*5486feefSafresh1        [Testcase12->new(e => undef, d => undef, t => undef)->values],
276f2a19305Safresh1        [undef, "DEF", "DEF"]),
277f2a19305Safresh1        'Values for undef');
278f2a19305Safresh1
279f2a19305Safresh1    ok(eq_array(
280*5486feefSafresh1        [Testcase12->new()->values],
281f2a19305Safresh1        ["DEF", "DEF", "DEF"]),
282f2a19305Safresh1        'Values for missing');
283f2a19305Safresh1}
284f2a19305Safresh1
285f2a19305Safresh1# field initialiser expressions permit `goto` in do {} blocks
286f2a19305Safresh1{
287*5486feefSafresh1    class Testcase13 {
288f2a19305Safresh1        field $forwards = do { goto HERE; HERE: 1 };
289f2a19305Safresh1        field $backwards = do { my $x; HERE: ; goto HERE if !$x++; 2 };
290f2a19305Safresh1
291f2a19305Safresh1        method values { return ($forwards, $backwards) }
292f2a19305Safresh1    }
293f2a19305Safresh1
294f2a19305Safresh1    ok(eq_array(
295*5486feefSafresh1        [Testcase13->new->values],
296f2a19305Safresh1        [1, 2],
297f2a19305Safresh1        'Values for goto inside do {} blocks in field initialisers'));
298f2a19305Safresh1}
299f2a19305Safresh1
300*5486feefSafresh1# field initialiser expressions permit a __CLASS__
301*5486feefSafresh1{
302*5486feefSafresh1    class Testcase14 {
303*5486feefSafresh1        field $classname = __CLASS__;
304*5486feefSafresh1
305*5486feefSafresh1        method classname { return $classname }
306*5486feefSafresh1    }
307*5486feefSafresh1
308*5486feefSafresh1    is(Testcase14->new->classname, "Testcase14", '__CLASS__ in field initialisers');
309*5486feefSafresh1}
310*5486feefSafresh1
311f2a19305Safresh1done_testing;
312