Failed Conditions
Push — master ( 9327e7...7f99bf )
by Vladimir
04:21
created

SyncPromiseTest::testPendingPromise()   A

Complexity

Conditions 4
Paths 15

Size

Total Lines 60
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 41
dl 0
loc 60
rs 9.264
c 0
b 0
f 0
cc 4
nc 15
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Tests\Executor\Promise;
6
7
use GraphQL\Executor\Promise\Adapter\SyncPromise;
8
use PHPUnit\Framework\Error\Error;
9
use PHPUnit\Framework\TestCase;
10
use function uniqid;
11
12
class SyncPromiseTest extends TestCase
13
{
14
    public function getFulfilledPromiseResolveData()
15
    {
16
        $onFulfilledReturnsNull = function () {
17
            return null;
18
        };
19
20
        $onFulfilledReturnsSameValue = function ($value) {
21
            return $value;
22
        };
23
24
        $onFulfilledReturnsOtherValue = function ($value) {
25
            return 'other-' . $value;
26
        };
27
28
        $onFulfilledThrows = function ($value) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

28
        $onFulfilledThrows = function (/** @scrutinizer ignore-unused */ $value) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
29
            throw new \Exception('onFulfilled throws this!');
30
        };
31
32
        return [
33
            // $resolvedValue, $onFulfilled, $expectedNextValue, $expectedNextReason, $expectedNextState
34
            ['test-value', null, 'test-value', null, SyncPromise::FULFILLED],
35
            [uniqid(), $onFulfilledReturnsNull, null, null, SyncPromise::FULFILLED],
36
            ['test-value', $onFulfilledReturnsSameValue, 'test-value', null, SyncPromise::FULFILLED],
37
            ['test-value-2', $onFulfilledReturnsOtherValue, 'other-test-value-2', null, SyncPromise::FULFILLED],
38
            ['test-value-3', $onFulfilledThrows, null, 'onFulfilled throws this!', SyncPromise::REJECTED],
39
        ];
40
    }
41
42
    /**
43
     * @dataProvider getFulfilledPromiseResolveData
44
     */
45
    public function testFulfilledPromiseCannotChangeValue(
46
        $resolvedValue,
47
        $onFulfilled,
0 ignored issues
show
Unused Code introduced by
The parameter $onFulfilled is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

47
        /** @scrutinizer ignore-unused */ $onFulfilled,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
48
        $expectedNextValue,
0 ignored issues
show
Unused Code introduced by
The parameter $expectedNextValue is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

48
        /** @scrutinizer ignore-unused */ $expectedNextValue,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
49
        $expectedNextReason,
0 ignored issues
show
Unused Code introduced by
The parameter $expectedNextReason is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

49
        /** @scrutinizer ignore-unused */ $expectedNextReason,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
50
        $expectedNextState
0 ignored issues
show
Unused Code introduced by
The parameter $expectedNextState is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

50
        /** @scrutinizer ignore-unused */ $expectedNextState

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
51
    ) {
52
        $promise = new SyncPromise();
53
        $this->assertEquals(SyncPromise::PENDING, $promise->state);
54
55
        $promise->resolve($resolvedValue);
56
        $this->assertEquals(SyncPromise::FULFILLED, $promise->state);
57
58
        $this->expectException(\Throwable::class);
59
        $this->expectExceptionMessage('Cannot change value of fulfilled promise');
60
        $promise->resolve($resolvedValue . '-other-value');
61
    }
62
63
    /**
64
     * @dataProvider getFulfilledPromiseResolveData
65
     */
66
    public function testFulfilledPromiseCannotBeRejected(
67
        $resolvedValue,
68
        $onFulfilled,
0 ignored issues
show
Unused Code introduced by
The parameter $onFulfilled is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

68
        /** @scrutinizer ignore-unused */ $onFulfilled,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
69
        $expectedNextValue,
0 ignored issues
show
Unused Code introduced by
The parameter $expectedNextValue is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

69
        /** @scrutinizer ignore-unused */ $expectedNextValue,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
70
        $expectedNextReason,
0 ignored issues
show
Unused Code introduced by
The parameter $expectedNextReason is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

70
        /** @scrutinizer ignore-unused */ $expectedNextReason,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
71
        $expectedNextState
0 ignored issues
show
Unused Code introduced by
The parameter $expectedNextState is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

71
        /** @scrutinizer ignore-unused */ $expectedNextState

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
72
    ) {
73
        $promise = new SyncPromise();
74
        $this->assertEquals(SyncPromise::PENDING, $promise->state);
75
76
        $promise->resolve($resolvedValue);
77
        $this->assertEquals(SyncPromise::FULFILLED, $promise->state);
78
79
        $this->expectException(\Throwable::class);
80
        $this->expectExceptionMessage('Cannot reject fulfilled promise');
81
        $promise->reject(new \Exception('anything'));
82
    }
83
84
    /**
85
     * @dataProvider getFulfilledPromiseResolveData
86
     */
87
    public function testFulfilledPromise(
88
        $resolvedValue,
89
        $onFulfilled,
90
        $expectedNextValue,
91
        $expectedNextReason,
92
        $expectedNextState
93
    ) {
94
        $promise = new SyncPromise();
95
        $this->assertEquals(SyncPromise::PENDING, $promise->state);
96
97
        $promise->resolve($resolvedValue);
98
        $this->assertEquals(SyncPromise::FULFILLED, $promise->state);
99
100
        $nextPromise = $promise->then(
101
            null,
102
            function () {
103
            }
104
        );
105
        $this->assertSame($promise, $nextPromise);
106
107
        $onRejectedCalled = false;
108
        $nextPromise      = $promise->then(
109
            $onFulfilled,
110
            function () use (&$onRejectedCalled) {
111
                $onRejectedCalled = true;
112
            }
113
        );
114
115
        if ($onFulfilled) {
116
            $this->assertNotSame($promise, $nextPromise);
117
            $this->assertEquals(SyncPromise::PENDING, $nextPromise->state);
118
        } else {
119
            $this->assertEquals(SyncPromise::FULFILLED, $nextPromise->state);
120
        }
121
        $this->assertEquals(false, $onRejectedCalled);
122
123
        $this->assertValidPromise($nextPromise, $expectedNextReason, $expectedNextValue, $expectedNextState);
124
125
        $nextPromise2 = $promise->then($onFulfilled);
126
        $nextPromise3 = $promise->then($onFulfilled);
127
128
        if ($onFulfilled) {
129
            $this->assertNotSame($nextPromise, $nextPromise2);
130
        }
131
132
        SyncPromise::runQueue();
133
134
        $this->assertValidPromise($nextPromise2, $expectedNextReason, $expectedNextValue, $expectedNextState);
135
        $this->assertValidPromise($nextPromise3, $expectedNextReason, $expectedNextValue, $expectedNextState);
136
    }
137
138
    private function assertValidPromise(
139
        SyncPromise $promise,
140
        $expectedNextReason,
141
        $expectedNextValue,
142
        $expectedNextState
143
    ) {
144
        $actualNextValue   = null;
145
        $actualNextReason  = null;
146
        $onFulfilledCalled = false;
147
        $onRejectedCalled  = false;
148
149
        $promise->then(
150
            function ($nextValue) use (&$actualNextValue, &$onFulfilledCalled) {
151
                $onFulfilledCalled = true;
152
                $actualNextValue   = $nextValue;
153
            },
154
            function (\Throwable $reason) use (&$actualNextReason, &$onRejectedCalled) {
155
                $onRejectedCalled = true;
156
                $actualNextReason = $reason->getMessage();
157
            }
158
        );
159
160
        $this->assertEquals($onFulfilledCalled, false);
161
        $this->assertEquals($onRejectedCalled, false);
162
163
        SyncPromise::runQueue();
164
165
        $this->assertEquals(! $expectedNextReason, $onFulfilledCalled);
166
        $this->assertEquals(! ! $expectedNextReason, $onRejectedCalled);
167
168
        $this->assertEquals($expectedNextValue, $actualNextValue);
169
        $this->assertEquals($expectedNextReason, $actualNextReason);
170
        $this->assertEquals($expectedNextState, $promise->state);
171
    }
172
173
    public function getRejectedPromiseData()
174
    {
175
        $onRejectedReturnsNull = function () {
176
            return null;
177
        };
178
179
        $onRejectedReturnsSomeValue = function ($reason) {
0 ignored issues
show
Unused Code introduced by
The parameter $reason is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

179
        $onRejectedReturnsSomeValue = function (/** @scrutinizer ignore-unused */ $reason) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
180
            return 'some-value';
181
        };
182
183
        $onRejectedThrowsSameReason = function ($reason) {
184
            throw $reason;
185
        };
186
187
        $onRejectedThrowsOtherReason = function ($value) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

187
        $onRejectedThrowsOtherReason = function (/** @scrutinizer ignore-unused */ $value) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
188
            throw new \Exception('onRejected throws other!');
189
        };
190
191
        return [
192
            // $rejectedReason, $onRejected, $expectedNextValue, $expectedNextReason, $expectedNextState
193
            [new \Exception('test-reason'), null, null, 'test-reason', SyncPromise::REJECTED],
194
            [new \Exception('test-reason-2'), $onRejectedReturnsNull, null, null, SyncPromise::FULFILLED],
195
            [new \Exception('test-reason-3'), $onRejectedReturnsSomeValue, 'some-value', null, SyncPromise::FULFILLED],
196
            [new \Exception('test-reason-4'), $onRejectedThrowsSameReason, null, 'test-reason-4', SyncPromise::REJECTED],
197
            [new \Exception('test-reason-5'), $onRejectedThrowsOtherReason, null, 'onRejected throws other!', SyncPromise::REJECTED],
198
        ];
199
    }
200
201
    /**
202
     * @dataProvider getRejectedPromiseData
203
     */
204
    public function testRejectedPromiseCannotChangeReason(
205
        $rejectedReason,
206
        $onRejected,
0 ignored issues
show
Unused Code introduced by
The parameter $onRejected is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

206
        /** @scrutinizer ignore-unused */ $onRejected,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
207
        $expectedNextValue,
0 ignored issues
show
Unused Code introduced by
The parameter $expectedNextValue is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

207
        /** @scrutinizer ignore-unused */ $expectedNextValue,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
208
        $expectedNextReason,
0 ignored issues
show
Unused Code introduced by
The parameter $expectedNextReason is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

208
        /** @scrutinizer ignore-unused */ $expectedNextReason,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
209
        $expectedNextState
0 ignored issues
show
Unused Code introduced by
The parameter $expectedNextState is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

209
        /** @scrutinizer ignore-unused */ $expectedNextState

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
210
    ) {
211
        $promise = new SyncPromise();
212
        $this->assertEquals(SyncPromise::PENDING, $promise->state);
213
214
        $promise->reject($rejectedReason);
215
        $this->assertEquals(SyncPromise::REJECTED, $promise->state);
216
217
        $this->expectException(\Throwable::class);
218
        $this->expectExceptionMessage('Cannot change rejection reason');
219
        $promise->reject(new \Exception('other-reason'));
220
    }
221
222
    /**
223
     * @dataProvider getRejectedPromiseData
224
     */
225
    public function testRejectedPromiseCannotBeResolved(
226
        $rejectedReason,
227
        $onRejected,
0 ignored issues
show
Unused Code introduced by
The parameter $onRejected is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

227
        /** @scrutinizer ignore-unused */ $onRejected,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
228
        $expectedNextValue,
0 ignored issues
show
Unused Code introduced by
The parameter $expectedNextValue is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

228
        /** @scrutinizer ignore-unused */ $expectedNextValue,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
229
        $expectedNextReason,
0 ignored issues
show
Unused Code introduced by
The parameter $expectedNextReason is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

229
        /** @scrutinizer ignore-unused */ $expectedNextReason,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
230
        $expectedNextState
0 ignored issues
show
Unused Code introduced by
The parameter $expectedNextState is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

230
        /** @scrutinizer ignore-unused */ $expectedNextState

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
231
    ) {
232
        $promise = new SyncPromise();
233
        $this->assertEquals(SyncPromise::PENDING, $promise->state);
234
235
        $promise->reject($rejectedReason);
236
        $this->assertEquals(SyncPromise::REJECTED, $promise->state);
237
238
        $this->expectException(\Throwable::class);
239
        $this->expectExceptionMessage('Cannot resolve rejected promise');
240
        $promise->resolve('anything');
241
    }
242
243
    /**
244
     * @dataProvider getRejectedPromiseData
245
     */
246
    public function testRejectedPromise(
247
        $rejectedReason,
248
        $onRejected,
249
        $expectedNextValue,
250
        $expectedNextReason,
251
        $expectedNextState
252
    ) {
253
        $promise = new SyncPromise();
254
        $this->assertEquals(SyncPromise::PENDING, $promise->state);
255
256
        $promise->reject($rejectedReason);
257
        $this->assertEquals(SyncPromise::REJECTED, $promise->state);
258
259
        try {
260
            $promise->reject(new \Exception('other-reason'));
261
            $this->fail('Expected exception not thrown');
262
        } catch (\Throwable $e) {
263
            $this->assertEquals('Cannot change rejection reason', $e->getMessage());
264
        }
265
266
        try {
267
            $promise->resolve('anything');
268
            $this->fail('Expected exception not thrown');
269
        } catch (\Throwable $e) {
270
            $this->assertEquals('Cannot resolve rejected promise', $e->getMessage());
271
        }
272
273
        $nextPromise = $promise->then(
274
            function () {
275
            },
276
            null
277
        );
278
        $this->assertSame($promise, $nextPromise);
279
280
        $onFulfilledCalled = false;
281
        $nextPromise       = $promise->then(
282
            function () use (&$onFulfilledCalled) {
283
                $onFulfilledCalled = true;
284
            },
285
            $onRejected
286
        );
287
288
        if ($onRejected) {
289
            $this->assertNotSame($promise, $nextPromise);
290
            $this->assertEquals(SyncPromise::PENDING, $nextPromise->state);
291
        } else {
292
            $this->assertEquals(SyncPromise::REJECTED, $nextPromise->state);
293
        }
294
        $this->assertEquals(false, $onFulfilledCalled);
295
        $this->assertValidPromise($nextPromise, $expectedNextReason, $expectedNextValue, $expectedNextState);
296
297
        $nextPromise2 = $promise->then(null, $onRejected);
298
        $nextPromise3 = $promise->then(null, $onRejected);
299
300
        if ($onRejected) {
301
            $this->assertNotSame($nextPromise, $nextPromise2);
302
        }
303
304
        SyncPromise::runQueue();
305
306
        $this->assertValidPromise($nextPromise2, $expectedNextReason, $expectedNextValue, $expectedNextState);
307
        $this->assertValidPromise($nextPromise3, $expectedNextReason, $expectedNextValue, $expectedNextState);
308
    }
309
310
    public function testPendingPromise() : void
311
    {
312
        $promise = new SyncPromise();
313
        $this->assertEquals(SyncPromise::PENDING, $promise->state);
314
315
        try {
316
            $promise->resolve($promise);
317
            $this->fail('Expected exception not thrown');
318
        } catch (\Throwable $e) {
319
            $this->assertEquals('Cannot resolve promise with self', $e->getMessage());
320
            $this->assertEquals(SyncPromise::PENDING, $promise->state);
321
        }
322
323
        // Try to resolve with other promise (must resolve when other promise resolves)
324
        $otherPromise = new SyncPromise();
325
        $promise->resolve($otherPromise);
326
327
        $this->assertEquals(SyncPromise::PENDING, $promise->state);
328
        $this->assertEquals(SyncPromise::PENDING, $otherPromise->state);
329
330
        $otherPromise->resolve('the value');
331
        $this->assertEquals(SyncPromise::FULFILLED, $otherPromise->state);
332
        $this->assertEquals(SyncPromise::PENDING, $promise->state);
333
        $this->assertValidPromise($promise, null, 'the value', SyncPromise::FULFILLED);
334
335
        $promise = new SyncPromise();
336
        $promise->resolve('resolved!');
337
338
        $this->assertValidPromise($promise, null, 'resolved!', SyncPromise::FULFILLED);
339
340
        // Test rejections
341
        $promise = new SyncPromise();
342
        $this->assertEquals(SyncPromise::PENDING, $promise->state);
343
344
        try {
345
            $promise->reject('a');
346
            $this->fail('Expected exception not thrown');
347
        } catch (Error $e) {
348
            throw $e;
349
        } catch (\Throwable $e) {
350
            $this->assertEquals(SyncPromise::PENDING, $promise->state);
351
        }
352
353
        $promise->reject(new \Exception('Rejected Reason'));
354
        $this->assertValidPromise($promise, 'Rejected Reason', null, SyncPromise::REJECTED);
355
356
        $promise  = new SyncPromise();
357
        $promise2 = $promise->then(
358
            null,
359
            function () {
360
                return 'value';
361
            }
362
        );
363
        $promise->reject(new \Exception('Rejected Again'));
364
        $this->assertValidPromise($promise2, null, 'value', SyncPromise::FULFILLED);
365
366
        $promise  = new SyncPromise();
367
        $promise2 = $promise->then();
368
        $promise->reject(new \Exception('Rejected Once Again'));
369
        $this->assertValidPromise($promise2, 'Rejected Once Again', null, SyncPromise::REJECTED);
370
    }
371
372
    public function testPendingPromiseThen() : void
373
    {
374
        $promise = new SyncPromise();
375
        $this->assertEquals(SyncPromise::PENDING, $promise->state);
376
377
        $nextPromise = $promise->then();
378
        $this->assertNotSame($promise, $nextPromise);
379
        $this->assertEquals(SyncPromise::PENDING, $promise->state);
380
        $this->assertEquals(SyncPromise::PENDING, $nextPromise->state);
381
382
        // Make sure that it queues derivative promises until resolution:
383
        $onFulfilledCount = 0;
384
        $onRejectedCount  = 0;
385
        $onFulfilled      = function ($value) use (&$onFulfilledCount) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

385
        $onFulfilled      = function (/** @scrutinizer ignore-unused */ $value) use (&$onFulfilledCount) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
386
            $onFulfilledCount++;
387
388
            return $onFulfilledCount;
389
        };
390
391
        $onRejected = function ($reason) use (&$onRejectedCount) {
392
            $onRejectedCount++;
393
            throw $reason;
394
        };
395
396
        $nextPromise2 = $promise->then($onFulfilled, $onRejected);
397
        $nextPromise3 = $promise->then($onFulfilled, $onRejected);
398
        $nextPromise4 = $promise->then($onFulfilled, $onRejected);
399
400
        $this->assertEquals(SyncPromise::getQueue()->count(), 0);
401
        $this->assertEquals($onFulfilledCount, 0);
402
        $this->assertEquals($onRejectedCount, 0);
403
        $promise->resolve(1);
404
405
        $this->assertEquals(SyncPromise::getQueue()->count(), 4);
406
        $this->assertEquals($onFulfilledCount, 0);
407
        $this->assertEquals($onRejectedCount, 0);
408
        $this->assertEquals(SyncPromise::PENDING, $nextPromise->state);
409
        $this->assertEquals(SyncPromise::PENDING, $nextPromise2->state);
410
        $this->assertEquals(SyncPromise::PENDING, $nextPromise3->state);
411
        $this->assertEquals(SyncPromise::PENDING, $nextPromise4->state);
412
413
        SyncPromise::runQueue();
414
        $this->assertEquals(SyncPromise::getQueue()->count(), 0);
415
        $this->assertEquals($onFulfilledCount, 3);
416
        $this->assertEquals($onRejectedCount, 0);
417
        $this->assertValidPromise($nextPromise, null, 1, SyncPromise::FULFILLED);
418
        $this->assertValidPromise($nextPromise2, null, 1, SyncPromise::FULFILLED);
419
        $this->assertValidPromise($nextPromise3, null, 2, SyncPromise::FULFILLED);
420
        $this->assertValidPromise($nextPromise4, null, 3, SyncPromise::FULFILLED);
421
    }
422
}
423