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