1<?php
2/*
3 *
4 * Copyright 2018 gRPC authors.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20class ChannelTest extends \PHPUnit\Framework\TestCase
21{
22    public function setUp(): void
23    {
24    }
25
26    public function tearDown(): void
27    {
28        if (!empty($this->channel)) {
29            $this->channel->close();
30        }
31    }
32
33    public function testInsecureCredentials()
34    {
35        $this->channel = new Grpc\Channel('localhost:50000',
36            ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
37        $this->assertSame('Grpc\Channel', get_class($this->channel));
38    }
39
40    public function testConstructorCreateSsl()
41    {
42        $channel = new Grpc\Channel('localhost:50033',
43            ['credentials' => \Grpc\ChannelCredentials::createSsl()]);
44        $this->assertNotNull($channel);
45    }
46
47    public function testCreateXdsWithSsl()
48    {
49        $xdsCreds = \Grpc\ChannelCredentials::createXds(
50            \Grpc\ChannelCredentials::createSsl()
51        );
52        $this->assertNotNull($xdsCreds);
53    }
54
55    public function disabled_testCreateXdsWithInsecure() {
56        $xdsCreds = \Grpc\ChannelCredentials::createXds(
57            \Grpc\ChannelCredentials::createInsecure()
58        );
59        $this->assertNotNull($xdsCreds);
60    }
61
62    public function testCreateXdsWithNull() {
63        $this->expectException(\InvalidArgumentException::class);
64        $xdsCreds = \Grpc\ChannelCredentials::createXds(null);
65    }
66
67    public function testCreateXdsWithInvalidType()
68    {
69        $expected = $this->logicalOr(
70            // PHP8
71            new \PHPUnit\Framework\Constraint\Exception(\InvalidArgumentException::class),
72            // PHP7
73            new \PHPUnit\Framework\Constraint\Exception(\TypeError::class)
74        );
75        try {
76            $xdsCreds = \Grpc\ChannelCredentials::createXds("invalid-type");
77        } catch (\Throwable $exception) {
78            $this->assertThat($exception, $expected);
79            return;
80        }
81        $this->assertThat(null, $expected);
82    }
83
84    public function testGetConnectivityState()
85    {
86        $this->channel = new Grpc\Channel('localhost:50001',
87             ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
88        $state = $this->channel->getConnectivityState();
89        $this->assertEquals(0, $state);
90    }
91
92    public function testGetConnectivityStateWithInt()
93    {
94        $this->channel = new Grpc\Channel('localhost:50002',
95             ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
96        $state = $this->channel->getConnectivityState(123);
97        $this->assertEquals(0, $state);
98    }
99
100    public function testGetConnectivityStateWithString()
101    {
102        $this->channel = new Grpc\Channel('localhost:50003',
103             ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
104        $state = $this->channel->getConnectivityState('hello');
105        $this->assertEquals(0, $state);
106    }
107
108    public function testGetConnectivityStateWithBool()
109    {
110        $this->channel = new Grpc\Channel('localhost:50004',
111             ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
112        $state = $this->channel->getConnectivityState(true);
113        $this->assertEquals(0, $state);
114    }
115
116    public function testGetTarget()
117    {
118        $this->channel = new Grpc\Channel('localhost:50005',
119             ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
120        $target = $this->channel->getTarget();
121        $this->assertTrue(is_string($target));
122    }
123
124    public function testWatchConnectivityState()
125    {
126        $this->channel = new Grpc\Channel('localhost:50006',
127             ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
128        $now = Grpc\Timeval::now();
129        $deadline = $now->add(new Grpc\Timeval(100*1000));  // 100ms
130        // we act as if 'CONNECTING'(=1) was the last state
131        // we saw, so the default state of 'IDLE' should be delivered instantly
132        $state = $this->channel->watchConnectivityState(1, $deadline);
133        $this->assertTrue($state);
134        unset($now);
135        unset($deadline);
136    }
137
138    public function testClose()
139    {
140        $this->channel = new Grpc\Channel('localhost:50007',
141             ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
142        $this->assertNotNull($this->channel);
143        $this->channel->close();
144    }
145
146    public function testInvalidConstructorWithNull()
147    {
148        $this->expectException(\InvalidArgumentException::class);
149        $this->channel = new Grpc\Channel();
150        $this->assertNull($this->channel);
151    }
152
153    public function testInvalidConstructorWith()
154    {
155        $this->expectException(\InvalidArgumentException::class);
156        $this->channel = new Grpc\Channel('localhost:50008', 'invalid');
157        $this->assertNull($this->channel);
158    }
159
160    public function testInvalidCredentials()
161    {
162        $this->expectException(\InvalidArgumentException::class);
163        $this->channel = new Grpc\Channel('localhost:50009',
164            ['credentials' => new Grpc\Timeval(100)]);
165    }
166
167    public function testInvalidOptionsArray()
168    {
169        $this->expectException(\InvalidArgumentException::class);
170        $this->channel = new Grpc\Channel('localhost:50010',
171            ['abc' => []]);
172    }
173
174    public function testInvalidGetConnectivityStateWithArray()
175    {
176        $this->expectException(\InvalidArgumentException::class);
177        $this->channel = new Grpc\Channel('localhost:50011',
178            ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
179        $this->channel->getConnectivityState([]);
180    }
181
182    public function testInvalidWatchConnectivityState()
183    {
184        $this->expectException(\InvalidArgumentException::class);
185        $this->channel = new Grpc\Channel('localhost:50012',
186            ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
187        $this->channel->watchConnectivityState([]);
188    }
189
190    public function testInvalidWatchConnectivityState2()
191    {
192        $this->expectException(\InvalidArgumentException::class);
193        $this->channel = new Grpc\Channel('localhost:50013',
194            ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
195        $this->channel->watchConnectivityState(1, 'hi');
196    }
197
198
199    public function assertConnecting($state) {
200      $this->assertTrue($state == GRPC\CHANNEL_CONNECTING ||
201                        $state == GRPC\CHANNEL_TRANSIENT_FAILURE);
202    }
203
204    public function waitUntilNotIdle($channel) {
205        for ($i = 0; $i < 10; $i++) {
206            $now = Grpc\Timeval::now();
207            $deadline = $now->add(new Grpc\Timeval(1000));
208            if ($channel->watchConnectivityState(GRPC\CHANNEL_IDLE,
209                                                 $deadline)) {
210                return true;
211            }
212        }
213        $this->assertTrue(false);
214    }
215
216    public function testPersistentChannelSameHost()
217    {
218        $this->channel1 = new Grpc\Channel('localhost:50014', [
219            "grpc_target_persist_bound" => 3,
220        ]);
221        // the underlying grpc channel is the same by default
222        // when connecting to the same host
223        $this->channel2 = new Grpc\Channel('localhost:50014', []);
224
225        // both channels should be IDLE
226        $state = $this->channel1->getConnectivityState();
227        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
228        $state = $this->channel2->getConnectivityState();
229        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
230
231        // try to connect on channel1
232        $state = $this->channel1->getConnectivityState(true);
233        $this->waitUntilNotIdle($this->channel1);
234
235        // both channels should now be in the CONNECTING state
236        $state = $this->channel1->getConnectivityState();
237        $this->assertConnecting($state);
238        $state = $this->channel2->getConnectivityState();
239        $this->assertConnecting($state);
240
241        $this->channel1->close();
242        $this->channel2->close();
243    }
244
245    public function testPersistentChannelDifferentHost()
246    {
247        // two different underlying channels because different hostname
248        $this->channel1 = new Grpc\Channel('localhost:50015', [
249            "grpc_target_persist_bound" => 3,
250        ]);
251        $this->channel2 = new Grpc\Channel('localhost:50016', []);
252
253        // both channels should be IDLE
254        $state = $this->channel1->getConnectivityState();
255        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
256        $state = $this->channel2->getConnectivityState();
257        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
258
259        // try to connect on channel1
260        $state = $this->channel1->getConnectivityState(true);
261        $this->waitUntilNotIdle($this->channel1);
262
263        // channel1 should now be in the CONNECTING state
264        $state = $this->channel1->getConnectivityState();
265        $this->assertConnecting($state);
266        // channel2 should still be in the IDLE state
267        $state = $this->channel2->getConnectivityState();
268        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
269
270        $this->channel1->close();
271        $this->channel2->close();
272    }
273
274    public function testPersistentChannelSameArgs()
275    {
276        $this->channel1 = new Grpc\Channel('localhost:50017', [
277          "grpc_target_persist_bound" => 3,
278          "abc" => "def",
279          ]);
280        $this->channel2 = new Grpc\Channel('localhost:50017', ["abc" => "def"]);
281
282        // try to connect on channel1
283        $state = $this->channel1->getConnectivityState(true);
284        $this->waitUntilNotIdle($this->channel1);
285
286        $state = $this->channel1->getConnectivityState();
287        $this->assertConnecting($state);
288        $state = $this->channel2->getConnectivityState();
289        $this->assertConnecting($state);
290
291        $this->channel1->close();
292        $this->channel2->close();
293    }
294
295    public function testPersistentChannelDifferentArgs()
296    {
297        $this->channel1 = new Grpc\Channel('localhost:50018', [
298            "grpc_target_persist_bound" => 3,
299          ]);
300        $this->channel2 = new Grpc\Channel('localhost:50018', ["abc" => "def"]);
301
302        // try to connect on channel1
303        $state = $this->channel1->getConnectivityState(true);
304        $this->waitUntilNotIdle($this->channel1);
305
306        $state = $this->channel1->getConnectivityState();
307        $this->assertConnecting($state);
308        $state = $this->channel2->getConnectivityState();
309        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
310
311        $this->channel1->close();
312        $this->channel2->close();
313    }
314
315    public function persistentChannelSameChannelCredentialsProvider(): array
316    {
317        return [
318            [
319                Grpc\ChannelCredentials::createSsl(),
320                Grpc\ChannelCredentials::createSsl(),
321                50301,
322            ],
323            [
324                Grpc\ChannelCredentials::createSsl(
325                    file_get_contents(dirname(__FILE__) . '/../data/ca.pem')
326                ),
327                Grpc\ChannelCredentials::createSsl(
328                    file_get_contents(dirname(__FILE__) . '/../data/ca.pem')
329                ),
330                50302,
331            ],
332            [
333                Grpc\ChannelCredentials::createInSecure(),
334                Grpc\ChannelCredentials::createInSecure(),
335                50303,
336            ],
337            [
338                \Grpc\ChannelCredentials::createXds(
339                    \Grpc\ChannelCredentials::createSsl()
340                ),
341                \Grpc\ChannelCredentials::createXds(
342                    \Grpc\ChannelCredentials::createSsl()
343                ),
344                50304,
345            ],
346            [
347                \Grpc\ChannelCredentials::createXds(
348                    \Grpc\ChannelCredentials::createSsl()
349                ),
350                \Grpc\ChannelCredentials::createXds(
351                    \Grpc\ChannelCredentials::createSsl()
352                ),
353                50305,
354            ],
355            [
356                \Grpc\ChannelCredentials::createXds(
357                    \Grpc\ChannelCredentials::createSsl(
358                        file_get_contents(dirname(__FILE__) . '/../data/ca.pem')
359                    )
360                ),
361                \Grpc\ChannelCredentials::createXds(
362                    \Grpc\ChannelCredentials::createSsl(
363                        file_get_contents(dirname(__FILE__) . '/../data/ca.pem')
364                    )
365                ),
366                50306,
367            ],
368            /*
369            [
370                \Grpc\ChannelCredentials::createXds(
371                    \Grpc\ChannelCredentials::createInSecure()
372                ),
373                \Grpc\ChannelCredentials::createXds(
374                    \Grpc\ChannelCredentials::createInSecure()
375                ),
376                50307,
377            ],
378            */
379        ];
380    }
381
382    /**
383     * @dataProvider persistentChannelSameChannelCredentialsProvider
384     */
385    public function testPersistentChannelSameChannelCredentials(
386        $creds1,
387        $creds2,
388        $port
389    ) {
390        $this->channel1 = new Grpc\Channel(
391            'localhost:' . $port,
392            [
393                "credentials" => $creds1,
394                "grpc_target_persist_bound" => 3,
395            ]
396        );
397        $this->channel2 = new Grpc\Channel(
398            'localhost:' . $port,
399            ["credentials" => $creds2]
400        );
401
402        // try to connect on channel1
403        $state = $this->channel1->getConnectivityState(true);
404        $this->waitUntilNotIdle($this->channel1);
405
406        $state = $this->channel1->getConnectivityState();
407        $this->assertConnecting($state);
408        $state = $this->channel2->getConnectivityState();
409        $this->assertConnecting($state);
410
411        $this->channel1->close();
412        $this->channel2->close();
413    }
414
415    public function persistentChannelDifferentChannelCredentialsProvider(): array
416    {
417        return [
418            [
419                Grpc\ChannelCredentials::createSsl(),
420                Grpc\ChannelCredentials::createSsl(
421                    file_get_contents(dirname(__FILE__) . '/../data/ca.pem')
422                ),
423                50351,
424            ],
425            [
426                Grpc\ChannelCredentials::createSsl(),
427                Grpc\ChannelCredentials::createInsecure(),
428                50352,
429            ],
430            [
431                \Grpc\ChannelCredentials::createXds(
432                    \Grpc\ChannelCredentials::createSsl()
433                ),
434                \Grpc\ChannelCredentials::createXds(
435                    \Grpc\ChannelCredentials::createSsl(
436                        file_get_contents(dirname(__FILE__) . '/../data/ca.pem')
437                    )
438                ),
439                50353,
440            ],
441            /*
442            [
443                \Grpc\ChannelCredentials::createXds(
444                    \Grpc\ChannelCredentials::createSsl()
445                ),
446                \Grpc\ChannelCredentials::createXds(
447                    \Grpc\ChannelCredentials::createInsecure()
448                ),
449                50354,
450            ],
451            [
452                \Grpc\ChannelCredentials::createInsecure(),
453                \Grpc\ChannelCredentials::createXds(
454                    \Grpc\ChannelCredentials::createInsecure()
455                ),
456                50355,
457            ],
458            */
459            [
460                \Grpc\ChannelCredentials::createSsl(),
461                \Grpc\ChannelCredentials::createXds(
462                    \Grpc\ChannelCredentials::createSsl()
463                ),
464                50356,
465            ],
466        ];
467    }
468
469    /**
470     * @dataProvider persistentChannelDifferentChannelCredentialsProvider
471     */
472    public function testPersistentChannelDifferentChannelCredentials(
473        $creds1,
474        $creds2,
475        $port
476    ) {
477
478        $this->channel1 = new Grpc\Channel(
479            'localhost:' . $port,
480            [
481                "credentials" => $creds1,
482                "grpc_target_persist_bound" => 3,
483            ]
484        );
485        $this->channel2 = new Grpc\Channel(
486            'localhost:' . $port,
487            ["credentials" => $creds2]
488        );
489
490        // try to connect on channel1
491        $state = $this->channel1->getConnectivityState(true);
492        $this->waitUntilNotIdle($this->channel1);
493
494        $state = $this->channel1->getConnectivityState();
495        $this->assertConnecting($state);
496        $state = $this->channel2->getConnectivityState();
497        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
498
499        $this->channel1->close();
500        $this->channel2->close();
501    }
502
503    public function testPersistentChannelSharedChannelClose1()
504    {
505        // same underlying channel
506        $this->channel1 = new Grpc\Channel('localhost:50123', [
507            "grpc_target_persist_bound" => 3,
508        ]);
509        $this->channel2 = new Grpc\Channel('localhost:50123', []);
510
511        // close channel1
512        $this->channel1->close();
513
514        // channel2 can still be use. We need to exclude the possible that
515        // in testPersistentChannelSharedChannelClose2, the exception is thrown
516        // by channel1.
517        $state = $this->channel2->getConnectivityState();
518        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
519    }
520
521    public function testPersistentChannelSharedChannelClose2()
522    {
523        $this->expectException(\RuntimeException::class);
524        // same underlying channel
525        $this->channel1 = new Grpc\Channel('localhost:50223', [
526            "grpc_target_persist_bound" => 3,
527        ]);
528        $this->channel2 = new Grpc\Channel('localhost:50223', []);
529
530        // close channel1
531        $this->channel1->close();
532
533        // channel2 can still be use
534        $state = $this->channel2->getConnectivityState();
535        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
536
537        // channel 1 is closed
538        $state = $this->channel1->getConnectivityState();
539    }
540
541    public function testPersistentChannelCreateAfterClose()
542    {
543        $this->channel1 = new Grpc\Channel('localhost:50024', [
544            "grpc_target_persist_bound" => 3,
545        ]);
546
547        $this->channel1->close();
548
549        $this->channel2 = new Grpc\Channel('localhost:50024', []);
550        $state = $this->channel2->getConnectivityState();
551        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
552
553        $this->channel2->close();
554    }
555
556    public function testPersistentChannelSharedMoreThanTwo()
557    {
558        $this->channel1 = new Grpc\Channel('localhost:50025', [
559            "grpc_target_persist_bound" => 3,
560        ]);
561        $this->channel2 = new Grpc\Channel('localhost:50025', []);
562        $this->channel3 = new Grpc\Channel('localhost:50025', []);
563
564        // try to connect on channel1
565        $state = $this->channel1->getConnectivityState(true);
566        $this->waitUntilNotIdle($this->channel1);
567
568        // all 3 channels should be in CONNECTING state
569        $state = $this->channel1->getConnectivityState();
570        $this->assertConnecting($state);
571        $state = $this->channel2->getConnectivityState();
572        $this->assertConnecting($state);
573        $state = $this->channel3->getConnectivityState();
574        $this->assertConnecting($state);
575
576        $this->channel1->close();
577    }
578
579    public function callbackFunc($context)
580    {
581        return [];
582    }
583
584    public function callbackFunc2($context)
585    {
586        return ["k1" => "v1"];
587    }
588
589    public function testPersistentChannelWithCallCredentials()
590    {
591        $creds = Grpc\ChannelCredentials::createSsl();
592        $callCreds = Grpc\CallCredentials::createFromPlugin(
593            [$this, 'callbackFunc']);
594        $credsWithCallCreds = Grpc\ChannelCredentials::createComposite(
595            $creds, $callCreds);
596
597        // If a ChannelCredentials object is composed with a
598        // CallCredentials object, the underlying grpc channel will
599        // always be created new and NOT persisted.
600        $this->channel1 = new Grpc\Channel('localhost:50026',
601                                           ["credentials" =>
602                                            $credsWithCallCreds,
603                                            "grpc_target_persist_bound" => 3,
604                                            ]);
605        $this->channel2 = new Grpc\Channel('localhost:50026',
606                                           ["credentials" =>
607                                            $credsWithCallCreds]);
608
609        // try to connect on channel1
610        $state = $this->channel1->getConnectivityState(true);
611        $this->waitUntilNotIdle($this->channel1);
612
613        $state = $this->channel1->getConnectivityState();
614        $this->assertConnecting($state);
615        $state = $this->channel2->getConnectivityState();
616        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
617
618        $this->channel1->close();
619        $this->channel2->close();
620    }
621
622    public function testPersistentChannelWithDifferentCallCredentials()
623    {
624        $callCreds1 = Grpc\CallCredentials::createFromPlugin(
625            [$this, 'callbackFunc']);
626        $callCreds2 = Grpc\CallCredentials::createFromPlugin(
627            [$this, 'callbackFunc2']);
628
629        $creds1 = Grpc\ChannelCredentials::createSsl();
630        $creds2 = Grpc\ChannelCredentials::createComposite(
631            $creds1, $callCreds1);
632        $creds3 = Grpc\ChannelCredentials::createComposite(
633            $creds1, $callCreds2);
634
635        // Similar to the test above, anytime a ChannelCredentials
636        // object is composed with a CallCredentials object, the
637        // underlying grpc channel will always be separate and not
638        // persisted
639        $this->channel1 = new Grpc\Channel('localhost:50027',
640                                           ["credentials" => $creds1,
641                                            "grpc_target_persist_bound" => 3,
642                                            ]);
643        $this->channel2 = new Grpc\Channel('localhost:50027',
644                                           ["credentials" => $creds2]);
645        $this->channel3 = new Grpc\Channel('localhost:50027',
646                                           ["credentials" => $creds3]);
647
648        // try to connect on channel1
649        $state = $this->channel1->getConnectivityState(true);
650        $this->waitUntilNotIdle($this->channel1);
651
652        $state = $this->channel1->getConnectivityState();
653        $this->assertConnecting($state);
654        $state = $this->channel2->getConnectivityState();
655        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
656        $state = $this->channel3->getConnectivityState();
657        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
658
659        $this->channel1->close();
660        $this->channel2->close();
661        $this->channel3->close();
662    }
663
664    public function testPersistentChannelForceNew()
665    {
666        $this->channel1 = new Grpc\Channel('localhost:50028', [
667            "grpc_target_persist_bound" => 2,
668        ]);
669        // even though all the channel params are the same, channel2
670        // has a new and different underlying channel
671        $this->channel2 = new Grpc\Channel('localhost:50028',
672                                           ["force_new" => true]);
673
674        // try to connect on channel1
675        $state = $this->channel1->getConnectivityState(true);
676        $this->waitUntilNotIdle($this->channel1);
677
678        $state = $this->channel1->getConnectivityState();
679        $this->assertConnecting($state);
680        $state = $this->channel2->getConnectivityState();
681        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
682
683        $this->channel1->close();
684        $this->channel2->close();
685    }
686
687    public function testPersistentChannelForceNewOldChannelIdle1()
688    {
689
690        $this->channel1 = new Grpc\Channel('localhost:50029', [
691            "grpc_target_persist_bound" => 2,
692        ]);
693        $this->channel2 = new Grpc\Channel('localhost:50029',
694                                           ["force_new" => true]);
695        // channel3 shares with channel1
696        $this->channel3 = new Grpc\Channel('localhost:50029', []);
697
698        // try to connect on channel2
699        $state = $this->channel2->getConnectivityState(true);
700        $this->waitUntilNotIdle($this->channel2);
701        $state = $this->channel1->getConnectivityState();
702        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
703        $state = $this->channel2->getConnectivityState();
704        $this->assertConnecting($state);
705        $state = $this->channel3->getConnectivityState();
706        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
707
708        $this->channel1->close();
709        $this->channel2->close();
710    }
711
712    public function testPersistentChannelForceNewOldChannelIdle2()
713    {
714
715        $this->channel1 = new Grpc\Channel('localhost:50032', [
716            "grpc_target_persist_bound" => 2,
717        ]);
718        $this->channel2 = new Grpc\Channel('localhost:50032', []);
719
720        // try to connect on channel2
721        $state = $this->channel1->getConnectivityState(true);
722        $this->waitUntilNotIdle($this->channel2);
723        $state = $this->channel1->getConnectivityState();
724        $this->assertConnecting($state);
725        $state = $this->channel2->getConnectivityState();
726        $this->assertConnecting($state);
727
728        $this->channel1->close();
729        $this->channel2->close();
730    }
731
732    public function testPersistentChannelForceNewOldChannelClose1()
733    {
734
735        $this->channel1 = new Grpc\Channel('localhost:50130', [
736            "grpc_target_persist_bound" => 2,
737        ]);
738        $this->channel2 = new Grpc\Channel('localhost:50130',
739                                           ["force_new" => true]);
740        // channel3 shares with channel1
741        $this->channel3 = new Grpc\Channel('localhost:50130', []);
742
743        $this->channel1->close();
744
745        $state = $this->channel2->getConnectivityState();
746        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
747
748        // channel3 is still usable. We need to exclude the possibility that in
749        // testPersistentChannelForceNewOldChannelClose2, the exception is thrown
750        // by channel1 and channel2.
751        $state = $this->channel3->getConnectivityState();
752        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
753    }
754
755    public function testPersistentChannelForceNewOldChannelClose2()
756    {
757        $this->expectException(\RuntimeException::class);
758        $this->channel1 = new Grpc\Channel('localhost:50230', [
759            "grpc_target_persist_bound" => 2,
760        ]);
761        $this->channel2 = new Grpc\Channel('localhost:50230',
762          ["force_new" => true]);
763        // channel3 shares with channel1
764        $this->channel3 = new Grpc\Channel('localhost:50230', []);
765
766        $this->channel1->close();
767
768        $state = $this->channel2->getConnectivityState();
769        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
770
771        // channel3 is still usable
772        $state = $this->channel3->getConnectivityState();
773        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
774
775        // channel 1 is closed
776        $this->channel1->getConnectivityState();
777    }
778
779    public function testPersistentChannelForceNewNewChannelClose()
780    {
781
782        $this->channel1 = new Grpc\Channel('localhost:50031', [
783            "grpc_target_persist_bound" => 2,
784        ]);
785        $this->channel2 = new Grpc\Channel('localhost:50031',
786                                           ["force_new" => true]);
787        $this->channel3 = new Grpc\Channel('localhost:50031', []);
788
789        $this->channel2->close();
790
791        $state = $this->channel1->getConnectivityState();
792        $this->assertEquals(GRPC\CHANNEL_IDLE, $state);
793
794        // can still connect on channel1
795        $state = $this->channel1->getConnectivityState(true);
796        $this->waitUntilNotIdle($this->channel1);
797
798        $state = $this->channel1->getConnectivityState();
799        $this->assertConnecting($state);
800
801        $this->channel1->close();
802    }
803}
804