1<?php
2
3require_once('test_util.php');
4
5use Google\Protobuf\Internal\RepeatedField;
6use Google\Protobuf\Internal\GPBType;
7use Foo\TestMessage;
8use Foo\TestMessage\Sub;
9
10class RepeatedFieldTest extends \PHPUnit\Framework\TestCase
11{
12
13    #########################################################
14    # Test int32 field.
15    #########################################################
16
17    public function testInt32()
18    {
19        $arr = new RepeatedField(GPBType::INT32);
20
21        // Test append.
22        $arr[] = MAX_INT32;
23        $this->assertSame(MAX_INT32, $arr[0]);
24        $arr[] = MIN_INT32;
25        $this->assertSame(MIN_INT32, $arr[1]);
26
27        $arr[] = 1.1;
28        $this->assertSame(1, $arr[2]);
29        $arr[] = MAX_INT32_FLOAT;
30        $this->assertSame(MAX_INT32, $arr[3]);
31        $arr[] = MAX_INT32_FLOAT;
32        $this->assertSame(MAX_INT32, $arr[4]);
33
34        $arr[] = '2';
35        $this->assertSame(2, $arr[5]);
36        $arr[] = '3.1';
37        $this->assertSame(3, $arr[6]);
38        $arr[] = MAX_INT32_STRING;
39        $this->assertSame(MAX_INT32, $arr[7]);
40
41        $this->assertEquals(8, count($arr));
42
43        for ($i = 0; $i < count($arr); $i++) {
44            $arr[$i] = 0;
45            $this->assertSame(0, $arr[$i]);
46        }
47
48        // Test set.
49        $arr[0] = MAX_INT32;
50        $this->assertSame(MAX_INT32, $arr[0]);
51        $arr[1] = MIN_INT32;
52        $this->assertSame(MIN_INT32, $arr[1]);
53
54        $arr[2] = 1.1;
55        $this->assertSame(1, $arr[2]);
56        $arr[3] = MAX_INT32_FLOAT;
57        $this->assertSame(MAX_INT32, $arr[3]);
58        $arr[4] = MAX_INT32_FLOAT;
59        $this->assertSame(MAX_INT32, $arr[4]);
60
61        $arr[5] = '2';
62        $this->assertSame(2, $arr[5]);
63        $arr[6] = '3.1';
64        $this->assertSame(3, $arr[6]);
65        $arr[7] = MAX_INT32_STRING;
66        $this->assertSame(MAX_INT32, $arr[7]);
67
68        // Test foreach.
69        $arr = new RepeatedField(GPBType::INT32);
70        for ($i = 0; $i < 3; $i++) {
71          $arr[] = $i;
72        }
73        $i = 0;
74        foreach ($arr as $val) {
75          $this->assertSame($i++, $val);
76        }
77        $this->assertSame(3, $i);
78    }
79
80    #########################################################
81    # Test uint32 field.
82    #########################################################
83
84    public function testUint32()
85    {
86        $arr = new RepeatedField(GPBType::UINT32);
87
88        // Test append.
89        $arr[] = MAX_UINT32;
90        $this->assertSame(-1, $arr[0]);
91        $arr[] = -1;
92        $this->assertSame(-1, $arr[1]);
93        $arr[] = MIN_UINT32;
94        $this->assertSame(MIN_UINT32, $arr[2]);
95
96        $arr[] = 1.1;
97        $this->assertSame(1, $arr[3]);
98        $arr[] = MAX_UINT32_FLOAT;
99        $this->assertSame(-1, $arr[4]);
100        $arr[] = -1.0;
101        $this->assertSame(-1, $arr[5]);
102        $arr[] = MIN_UINT32_FLOAT;
103        $this->assertSame(MIN_UINT32, $arr[6]);
104
105        $arr[] = '2';
106        $this->assertSame(2, $arr[7]);
107        $arr[] = '3.1';
108        $this->assertSame(3, $arr[8]);
109        $arr[] = MAX_UINT32_STRING;
110        $this->assertSame(-1, $arr[9]);
111        $arr[] = '-1.0';
112        $this->assertSame(-1, $arr[10]);
113        $arr[] = MIN_UINT32_STRING;
114        $this->assertSame(MIN_UINT32, $arr[11]);
115
116        $this->assertEquals(12, count($arr));
117
118        for ($i = 0; $i < count($arr); $i++) {
119            $arr[$i] = 0;
120            $this->assertSame(0, $arr[$i]);
121        }
122
123        // Test set.
124        $arr[0] = MAX_UINT32;
125        $this->assertSame(-1, $arr[0]);
126        $arr[1] = -1;
127        $this->assertSame(-1, $arr[1]);
128        $arr[2] = MIN_UINT32;
129        $this->assertSame(MIN_UINT32, $arr[2]);
130
131        $arr[3] = 1.1;
132        $this->assertSame(1, $arr[3]);
133        $arr[4] = MAX_UINT32_FLOAT;
134        $this->assertSame(-1, $arr[4]);
135        $arr[5] = -1.0;
136        $this->assertSame(-1, $arr[5]);
137        $arr[6] = MIN_UINT32_FLOAT;
138        $this->assertSame(MIN_UINT32, $arr[6]);
139
140        $arr[7] = '2';
141        $this->assertSame(2, $arr[7]);
142        $arr[8] = '3.1';
143        $this->assertSame(3, $arr[8]);
144        $arr[9] = MAX_UINT32_STRING;
145        $this->assertSame(-1, $arr[9]);
146        $arr[10] = '-1.0';
147        $this->assertSame(-1, $arr[10]);
148        $arr[11] = MIN_UINT32_STRING;
149        $this->assertSame(MIN_UINT32, $arr[11]);
150    }
151
152    #########################################################
153    # Test int64 field.
154    #########################################################
155
156    public function testInt64()
157    {
158        $arr = new RepeatedField(GPBType::INT64);
159
160        // Test append.
161        $arr[] = MAX_INT64;
162        $arr[] = MIN_INT64;
163        $arr[] = 1.1;
164        $arr[] = '2';
165        $arr[] = '3.1';
166        $arr[] = MAX_INT64_STRING;
167        $arr[] = MIN_INT64_STRING;
168        if (PHP_INT_SIZE == 4) {
169            $this->assertSame(MAX_INT64, $arr[0]);
170            $this->assertSame(MIN_INT64, $arr[1]);
171            $this->assertSame('1', $arr[2]);
172            $this->assertSame('2', $arr[3]);
173            $this->assertSame('3', $arr[4]);
174            $this->assertSame(MAX_INT64_STRING, $arr[5]);
175            $this->assertSame(MIN_INT64_STRING, $arr[6]);
176        } else {
177            $this->assertSame(MAX_INT64, $arr[0]);
178            $this->assertSame(MIN_INT64, $arr[1]);
179            $this->assertSame(1, $arr[2]);
180            $this->assertSame(2, $arr[3]);
181            $this->assertSame(3, $arr[4]);
182            $this->assertSame(MAX_INT64, $arr[5]);
183            $this->assertSame(MIN_INT64, $arr[6]);
184        }
185
186
187        $this->assertEquals(7, count($arr));
188
189        for ($i = 0; $i < count($arr); $i++) {
190            $arr[$i] = 0;
191            if (PHP_INT_SIZE == 4) {
192                $this->assertSame('0', $arr[$i]);
193            } else {
194                $this->assertSame(0, $arr[$i]);
195            }
196        }
197
198        // Test set.
199        $arr[0] = MAX_INT64;
200        $arr[1] = MIN_INT64;
201        $arr[2] = 1.1;
202        $arr[3] = '2';
203        $arr[4] = '3.1';
204        $arr[5] = MAX_INT64_STRING;
205        $arr[6] = MIN_INT64_STRING;
206
207        if (PHP_INT_SIZE == 4) {
208            $this->assertSame(MAX_INT64_STRING, $arr[0]);
209            $this->assertSame(MIN_INT64_STRING, $arr[1]);
210            $this->assertSame('1', $arr[2]);
211            $this->assertSame('2', $arr[3]);
212            $this->assertSame('3', $arr[4]);
213            $this->assertSame(MAX_INT64_STRING, $arr[5]);
214            $this->assertEquals(MIN_INT64_STRING, $arr[6]);
215        } else {
216            $this->assertSame(MAX_INT64, $arr[0]);
217            $this->assertSame(MIN_INT64, $arr[1]);
218            $this->assertSame(1, $arr[2]);
219            $this->assertSame(2, $arr[3]);
220            $this->assertSame(3, $arr[4]);
221            $this->assertSame(MAX_INT64, $arr[5]);
222            $this->assertEquals(MIN_INT64, $arr[6]);
223        }
224    }
225
226    #########################################################
227    # Test uint64 field.
228    #########################################################
229
230    public function testUint64()
231    {
232        $arr = new RepeatedField(GPBType::UINT64);
233
234        // Test append.
235        $arr[] = MAX_UINT64;
236        $arr[] = 1.1;
237        $arr[] = '2';
238        $arr[] = '3.1';
239        $arr[] = MAX_UINT64_STRING;
240
241        if (PHP_INT_SIZE == 4) {
242            $this->assertSame(MAX_UINT64_STRING, $arr[0]);
243            $this->assertSame('1', $arr[1]);
244            $this->assertSame('2', $arr[2]);
245            $this->assertSame('3', $arr[3]);
246            $this->assertSame(MAX_UINT64_STRING, $arr[4]);
247        } else {
248            $this->assertSame(MAX_UINT64, $arr[0]);
249            $this->assertSame(1, $arr[1]);
250            $this->assertSame(2, $arr[2]);
251            $this->assertSame(3, $arr[3]);
252            $this->assertSame(MAX_UINT64, $arr[4]);
253            $this->assertSame(5, count($arr));
254        }
255
256        $this->assertSame(5, count($arr));
257
258        for ($i = 0; $i < count($arr); $i++) {
259            $arr[$i] = 0;
260            if (PHP_INT_SIZE == 4) {
261                $this->assertSame('0', $arr[$i]);
262            } else {
263                $this->assertSame(0, $arr[$i]);
264            }
265        }
266
267        // Test set.
268        $arr[0] = MAX_UINT64;
269        $arr[1] = 1.1;
270        $arr[2] = '2';
271        $arr[3] = '3.1';
272        $arr[4] = MAX_UINT64_STRING;
273
274        if (PHP_INT_SIZE == 4) {
275            $this->assertSame(MAX_UINT64_STRING, $arr[0]);
276            $this->assertSame('1', $arr[1]);
277            $this->assertSame('2', $arr[2]);
278            $this->assertSame('3', $arr[3]);
279            $this->assertSame(MAX_UINT64_STRING, $arr[4]);
280        } else {
281            $this->assertSame(MAX_UINT64, $arr[0]);
282            $this->assertSame(1, $arr[1]);
283            $this->assertSame(2, $arr[2]);
284            $this->assertSame(3, $arr[3]);
285            $this->assertSame(MAX_UINT64, $arr[4]);
286        }
287    }
288
289    #########################################################
290    # Test float field.
291    #########################################################
292
293    public function testFloat()
294    {
295        $arr = new RepeatedField(GPBType::FLOAT);
296
297        // Test append.
298        $arr[] = 1;
299        $this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
300
301        $arr[] = 1.1;
302        $this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
303
304        $arr[] = '2';
305        $this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
306        $arr[] = '3.1';
307        $this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
308
309        $this->assertEquals(4, count($arr));
310
311        for ($i = 0; $i < count($arr); $i++) {
312            $arr[$i] = 0;
313            $this->assertSame(0.0, $arr[$i]);
314        }
315
316        // Test set.
317        $arr[0] = 1;
318        $this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
319
320        $arr[1] = 1.1;
321        $this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
322
323        $arr[2] = '2';
324        $this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
325        $arr[3] = '3.1';
326        $this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
327    }
328
329    #########################################################
330    # Test double field.
331    #########################################################
332
333    public function testDouble()
334    {
335        $arr = new RepeatedField(GPBType::DOUBLE);
336
337        // Test append.
338        $arr[] = 1;
339        $this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
340
341        $arr[] = 1.1;
342        $this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
343
344        $arr[] = '2';
345        $this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
346        $arr[] = '3.1';
347        $this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
348
349        $this->assertEquals(4, count($arr));
350
351        for ($i = 0; $i < count($arr); $i++) {
352            $arr[$i] = 0;
353            $this->assertSame(0.0, $arr[$i]);
354        }
355
356        // Test set.
357        $arr[0] = 1;
358        $this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
359
360        $arr[1] = 1.1;
361        $this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
362
363        $arr[2] = '2';
364        $this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
365        $arr[3] = '3.1';
366        $this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
367    }
368
369    #########################################################
370    # Test bool field.
371    #########################################################
372
373    public function testBool()
374    {
375        $arr = new RepeatedField(GPBType::BOOL);
376
377        // Test append.
378        $arr[] = true;
379        $this->assertSame(true, $arr[0]);
380
381        $arr[] = -1;
382        $this->assertSame(true, $arr[1]);
383
384        $arr[] = 1.1;
385        $this->assertSame(true, $arr[2]);
386
387        $arr[] = '';
388        $this->assertSame(false, $arr[3]);
389
390        $this->assertEquals(4, count($arr));
391
392        for ($i = 0; $i < count($arr); $i++) {
393            $arr[$i] = 0;
394            $this->assertSame(false, $arr[$i]);
395        }
396
397        // Test set.
398        $arr[0] = true;
399        $this->assertSame(true, $arr[0]);
400
401        $arr[1] = -1;
402        $this->assertSame(true, $arr[1]);
403
404        $arr[2] = 1.1;
405        $this->assertSame(true, $arr[2]);
406
407        $arr[3] = '';
408        $this->assertSame(false, $arr[3]);
409    }
410
411    #########################################################
412    # Test string field.
413    #########################################################
414
415    public function testString()
416    {
417        $arr = new RepeatedField(GPBType::STRING);
418
419        // Test append.
420        $arr[] = 'abc';
421        $this->assertSame('abc', $arr[0]);
422
423        $arr[] = 1;
424        $this->assertSame('1', $arr[1]);
425
426        $arr[] = 1.1;
427        $this->assertSame('1.1', $arr[2]);
428
429        $arr[] = true;
430        $this->assertSame('1', $arr[3]);
431
432        $this->assertEquals(4, count($arr));
433
434        for ($i = 0; $i < count($arr); $i++) {
435            $arr[$i] = '';
436            $this->assertSame('', $arr[$i]);
437        }
438
439        // Test set.
440        $arr[0] = 'abc';
441        $this->assertSame('abc', $arr[0]);
442
443        $arr[1] = 1;
444        $this->assertSame('1', $arr[1]);
445
446        $arr[2] = 1.1;
447        $this->assertSame('1.1', $arr[2]);
448
449        $arr[3] = true;
450        $this->assertSame('1', $arr[3]);
451    }
452
453    #########################################################
454    # Test message field.
455    #########################################################
456
457    public function testMessage()
458    {
459        $arr = new RepeatedField(GPBType::MESSAGE, Sub::class);
460
461        // Test append.
462        $sub_m = new Sub();
463        $sub_m->setA(1);
464        $arr[] = $sub_m;
465        $this->assertSame(1, $arr[0]->getA());
466
467        $this->assertEquals(1, count($arr));
468
469        // Test set.
470        $sub_m = new Sub();
471        $sub_m->setA(2);
472        $arr[0] = $sub_m;
473        $this->assertSame(2, $arr[0]->getA());
474
475        // Test foreach.
476        $arr = new RepeatedField(GPBType::MESSAGE, Sub::class);
477        for ($i = 0; $i < 3; $i++) {
478          $arr[] = new Sub();
479          $arr[$i]->setA($i);
480        }
481        $i = 0;
482        foreach ($arr as $val) {
483          $this->assertSame($i++, $val->getA());
484        }
485        $this->assertSame(3, $i);
486    }
487
488    #########################################################
489    # Test offset type
490    #########################################################
491
492    public function testOffset()
493    {
494        $arr = new RepeatedField(GPBType::INT32);
495        $arr[] = 0;
496
497        $arr[0] = 1;
498        $this->assertSame(1, $arr[0]);
499        $this->assertSame(1, count($arr));
500
501        $arr['0'] = 2;
502        $this->assertSame(2, $arr['0']);
503        $this->assertSame(2, $arr[0]);
504        $this->assertSame(1, count($arr));
505
506        $arr[0.0] = 3;
507        $this->assertSame(3, $arr[0.0]);
508        $this->assertSame(1, count($arr));
509    }
510
511    public function testInsertRemoval()
512    {
513        $arr = new RepeatedField(GPBType::INT32);
514
515        $arr[] = 0;
516        $arr[] = 1;
517        $arr[] = 2;
518        $this->assertSame(3, count($arr));
519
520        unset($arr[2]);
521        $this->assertSame(2, count($arr));
522        $this->assertSame(0, $arr[0]);
523        $this->assertSame(1, $arr[1]);
524
525        $arr[] = 3;
526        $this->assertSame(3, count($arr));
527        $this->assertSame(0, $arr[0]);
528        $this->assertSame(1, $arr[1]);
529        $this->assertSame(3, $arr[2]);
530    }
531
532    #########################################################
533    # Test reference in array
534    #########################################################
535
536    public function testArrayElementIsReferenceInSetters()
537    {
538        // Bool elements
539        $values = [true];
540        array_walk($values, function (&$value) {});
541        $m = new TestMessage();
542        $m->setRepeatedBool($values);
543
544        // Int32 elements
545        $values = [1];
546        array_walk($values, function (&$value) {});
547        $m = new TestMessage();
548        $m->setRepeatedInt32($values);
549
550        // Double elements
551        $values = [1.0];
552        array_walk($values, function (&$value) {});
553        $m = new TestMessage();
554        $m->setRepeatedDouble($values);
555
556        // String elements
557        $values = ['a'];
558        array_walk($values, function (&$value) {});
559        $m = new TestMessage();
560        $m->setRepeatedString($values);
561
562        // Message elements
563        $m = new TestMessage();
564        $subs = [1, 2];
565        foreach ($subs as &$sub) {
566            $sub = new Sub(['a' => $sub]);
567        }
568        $m->setRepeatedMessage($subs);
569    }
570
571    #########################################################
572    # Test memory leak
573    #########################################################
574
575    public function testCycleLeak()
576    {
577        gc_collect_cycles();
578        $arr = new RepeatedField(GPBType::MESSAGE, TestMessage::class);
579        $arr[] = new TestMessage;
580        $arr[0]->SetRepeatedRecursive($arr);
581
582        // Clean up memory before test.
583        gc_collect_cycles();
584        $start = memory_get_usage();
585        unset($arr);
586
587        // Explicitly trigger garbage collection.
588        gc_collect_cycles();
589
590        $end = memory_get_usage();
591        $this->assertLessThan($start, $end);
592    }
593}
594