GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 53bcfa...6873de )
by SignpostMarv
03:59
created

DaftTestObjectTest   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 572
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 572
rs 8.3157
c 0
b 0
f 0
wmc 43

8 Methods

Rating   Name   Duplication   Size   Complexity  
A testThrowsException() 0 20 4
B testRetrievePropertyValueFromDataNotNullableException() 0 40 4
F testGood() 0 181 23
A DefinesOwnUntypedIdInterfaceProvider() 0 14 4
B testDefinesOwnUntypedIdInterface() 0 29 5
B GoodDataProvider() 0 189 1
A ThrowsExceptionProvider() 0 56 1
A RetrievePropertyValueFromDataNotNullableExceptionDataProvider() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like DaftTestObjectTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DaftTestObjectTest, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
* @author SignpostMarv
4
*/
5
declare(strict_types=1);
6
7
namespace SignpostMarv\DaftObject\Tests;
8
9
use ReflectionMethod;
10
use SignpostMarv\DaftObject\ClassDoesNotImplementClassException;
11
use SignpostMarv\DaftObject\ClassMethodReturnHasZeroArrayCountException;
12
use SignpostMarv\DaftObject\ClassMethodReturnIsNotArrayOfStringsException;
13
use SignpostMarv\DaftObject\DaftObject;
14
use SignpostMarv\DaftObject\DaftObjectWorm;
15
use SignpostMarv\DaftObject\DefinesOwnIdPropertiesInterface;
16
use SignpostMarv\DaftObject\DefinesOwnIntegerIdInterface;
17
use SignpostMarv\DaftObject\DefinesOwnStringIdInterface;
18
use SignpostMarv\DaftObject\DefinesOwnUntypedIdInterface;
19
use SignpostMarv\DaftObject\PropertyNotNullableException;
20
use SignpostMarv\DaftObject\PropertyNotRewriteableException;
21
use SignpostMarv\DaftObject\ReadOnly;
22
use SignpostMarv\DaftObject\ReadOnlyBad;
23
use SignpostMarv\DaftObject\ReadOnlyBadDefinesOwnId;
24
use SignpostMarv\DaftObject\ReadOnlyInsuficientIdProperties;
25
use SignpostMarv\DaftObject\ReadOnlyTwoColumnPrimaryKey;
26
use SignpostMarv\DaftObject\ReadWrite;
27
use SignpostMarv\DaftObject\ReadWriteTwoColumnPrimaryKey;
28
use SignpostMarv\DaftObject\ReadWriteWorm;
29
use SignpostMarv\DaftObject\UndefinedPropertyException;
30
use SignpostMarv\DaftObject\WriteOnly;
31
use SignpostMarv\DaftObject\WriteOnlyWorm;
32
33
class DaftTestObjectTest extends TestCase
34
{
35
    public function GoodDataProvider() : array
36
    {
37
        return [
38
            [
39
                ReadOnly::class,
40
                [
41
                    'Foo' => 'Foo',
42
                    'Bar' => 1.0,
43
                    'Baz' => 2,
44
                    'Bat' => true,
45
                ],
46
                true,
47
                false,
48
            ],
49
            [
50
                ReadOnly::class,
51
                [
52
                    'Foo' => 'Foo',
53
                    'Bar' => 2.0,
54
                    'Baz' => 3,
55
                    'Bat' => false,
56
                ],
57
                true,
58
                false,
59
            ],
60
            [
61
                ReadOnly::class,
62
                [
63
                    'Foo' => 'Foo',
64
                    'Bar' => 3.0,
65
                    'Baz' => 4,
66
                    'Bat' => null,
67
                ],
68
                true,
69
                false,
70
            ],
71
            [
72
                ReadOnlyTwoColumnPrimaryKey::class,
73
                [
74
                    'Foo' => 'Foo',
75
                    'Bar' => 1.0,
76
                    'Baz' => 2,
77
                    'Bat' => true,
78
                ],
79
                true,
80
                false,
81
            ],
82
            [
83
                ReadOnlyTwoColumnPrimaryKey::class,
84
                [
85
                    'Foo' => 'Foo',
86
                    'Bar' => 2.0,
87
                    'Baz' => 3,
88
                    'Bat' => false,
89
                ],
90
                true,
91
                false,
92
            ],
93
            [
94
                ReadOnlyTwoColumnPrimaryKey::class,
95
                [
96
                    'Foo' => 'Foo',
97
                    'Bar' => 3.0,
98
                    'Baz' => 4,
99
                    'Bat' => null,
100
                ],
101
                true,
102
                false,
103
            ],
104
            [
105
                ReadWriteTwoColumnPrimaryKey::class,
106
                [
107
                    'Foo' => 'Foo',
108
                    'Bar' => 1.0,
109
                    'Baz' => 2,
110
                    'Bat' => true,
111
                ],
112
                true,
113
                false,
114
            ],
115
            [
116
                ReadWriteTwoColumnPrimaryKey::class,
117
                [
118
                    'Foo' => 'Foo',
119
                    'Bar' => 2.0,
120
                    'Baz' => 3,
121
                    'Bat' => false,
122
                ],
123
                true,
124
                false,
125
            ],
126
            [
127
                ReadWriteTwoColumnPrimaryKey::class,
128
                [
129
                    'Foo' => 'Foo',
130
                    'Bar' => 3.0,
131
                    'Baz' => 4,
132
                    'Bat' => null,
133
                ],
134
                true,
135
                false,
136
            ],
137
            [
138
                WriteOnly::class,
139
                [
140
                    'Foo' => 'Foo',
141
                    'Bar' => 1.0,
142
                    'Baz' => 2,
143
                    'Bat' => true,
144
                ],
145
                false,
146
                true,
147
            ],
148
            [
149
                WriteOnly::class,
150
                [
151
                    'Foo' => 'Foo',
152
                    'Bar' => 2.0,
153
                    'Baz' => 3,
154
                    'Bat' => false,
155
                ],
156
                false,
157
                true,
158
            ],
159
            [
160
                WriteOnly::class,
161
                [
162
                    'Foo' => 'Foo',
163
                    'Bar' => 3.0,
164
                    'Baz' => 4,
165
                    'Bat' => null,
166
                ],
167
                false,
168
                true,
169
            ],
170
            [
171
                ReadWrite::class,
172
                [
173
                    'Foo' => 'Foo',
174
                    'Bar' => 1.0,
175
                    'Baz' => 2,
176
                    'Bat' => true,
177
                ],
178
                true,
179
                true,
180
            ],
181
            [
182
                ReadWrite::class,
183
                [
184
                    'Foo' => 'Foo',
185
                    'Bar' => 2.0,
186
                    'Baz' => 3,
187
                    'Bat' => false,
188
                ],
189
                true,
190
                true,
191
            ],
192
            [
193
                ReadWrite::class,
194
                [
195
                    'Foo' => 'Foo',
196
                    'Bar' => 3.0,
197
                    'Baz' => 4,
198
                    'Bat' => null,
199
                ],
200
                true,
201
                true,
202
            ],
203
            [
204
                ReadWriteWorm::class,
205
                [
206
                    'Foo' => 'Foo',
207
                    'Bar' => 3.0,
208
                    'Baz' => 4,
209
                    'Bat' => null,
210
                ],
211
                true,
212
                true,
213
            ],
214
            [
215
                WriteOnlyWorm::class,
216
                [
217
                    'Foo' => 'Foo',
218
                    'Bar' => 3.0,
219
                    'Baz' => 4,
220
                    'Bat' => null,
221
                ],
222
                false,
223
                true,
224
            ],
225
        ];
226
    }
227
228
    public function ThrowsExceptionProvider() : array
229
    {
230
        return [
231
            [
232
                WriteOnly::class,
233
                UndefinedPropertyException::class,
234
                (
235
                    'Property not defined: ' .
236
                    WriteOnly::class .
237
                    '::$NotFoo'
238
                ),
239
                [
240
                    'NotFoo' => 1,
241
                ],
242
                false,
243
                true,
244
            ],
245
            [
246
                ReadOnlyBad::class,
247
                ClassMethodReturnIsNotArrayOfStringsException::class,
248
                (
249
                    ReadOnlyBad::class .
250
                    '::DaftObjectIdProperties() does not return string[]'
251
                ),
252
                [
253
                    'Foo' => 'Bar',
254
                ],
255
                true,
256
                false,
257
            ],
258
            [
259
                ReadOnlyBadDefinesOwnId::class,
260
                ClassDoesNotImplementClassException::class,
261
                (
262
                    ReadOnlyBadDefinesOwnId::class .
263
                    ' does not implement ' .
264
                    DefinesOwnIdPropertiesInterface::class
265
                ),
266
                [
267
                    'Foo' => 'Bar',
268
                ],
269
                true,
270
                false,
271
            ],
272
            [
273
                ReadOnlyInsuficientIdProperties::class,
274
                ClassMethodReturnHasZeroArrayCountException::class,
275
                (
276
                    ReadOnlyInsuficientIdProperties::class .
277
                    '::DaftObjectIdProperties() must return at least one property'
278
                ),
279
                [
280
                    'Foo' => 'Bar',
281
                ],
282
                true,
283
                false,
284
            ],
285
        ];
286
    }
287
288
    public function DefinesOwnUntypedIdInterfaceProvider() : array
289
    {
290
        $out = [];
291
292
        foreach ($this->GoodDataProvider() as $args) {
293
            if (
294
                is_a($args[0], DefinesOwnUntypedIdInterface::class, true) &&
295
                true === $args[2]
296
            ) {
297
                $out[] = [$args[0], $args[1]];
298
            }
299
        }
300
301
        return $out;
302
    }
303
304
    /**
305
    * @dataProvider GoodDataProvider
306
    * @psalm-suppress ForbiddenCode
307
    */
308
    public function testGood(
309
        string $implementation,
310
        array $params,
311
        bool $readable = false,
312
        bool $writeable = false
313
    ) : void {
314
        /**
315
        * @var DaftObject $obj
316
        */
317
        $obj = new $implementation($params, $writeable);
318
319
        if (true === $readable) {
320
            $this->assertSame(
321
                ($writeable ? count($params) : 0),
322
                count($obj->ChangedProperties())
323
            );
324
325
            foreach ($params as $k => $v) {
326
                $getterMethod = 'Get' . ucfirst($k);
327
328
                $this->assertSame(
329
                    $params[$k],
330
                    $obj->$getterMethod(),
331
                    (
332
                        $implementation .
333
                        '::' .
334
                        $getterMethod .
335
                        '() does not match supplied $params'
336
                    )
337
                );
338
                $this->assertSame(
339
                    $params[$k],
340
                    $obj->$k,
341
                    (
342
                        $implementation .
343
                        '::$' .
344
                        $k .
345
                        ' does not match supplied $params'
346
                    )
347
                );
348
349
                $this->assertSame(
350
                    (is_null($params[$k]) ? false : true),
351
                    isset($obj->$k),
352
                    (
353
                        $implementation .
354
                        '::$' .
355
                        $k .
356
                        ' was not found as ' .
357
                        (is_null($params[$k]) ? 'not set' : 'set')
358
                    )
359
                );
360
            }
361
        }
362
363
        foreach (array_keys($params) as $property) {
364
            $this->assertSame(
365
                $writeable,
366
                $obj->HasPropertyChanged($property),
367
                (
368
                    $implementation .
369
                    '::$' .
370
                    $property .
371
                    ' was' .
372
                    ($writeable ? ' ' : ' not ') .
373
                    'writeable, property should' .
374
                    ($writeable ? ' ' : ' not ') .
375
                    'be changed'
376
                )
377
            );
378
379
            if ($writeable) {
380
                $obj->MakePropertiesUnchanged($property);
381
                $this->assertFalse($obj->HasPropertyChanged($property));
382
383
                if (
384
                    in_array(
385
                        $property,
386
                        $implementation::DaftObjectNullableProperties(),
387
                        true
388
                    )
389
                ) {
390
                    if ($obj instanceof DaftObjectWorm) {
391
                        $this->expectException(
392
                            PropertyNotRewriteableException::class
393
                        );
394
                        $this->expectExceptionMessage(
395
                            sprintf(
396
                                'Property not rewriteable: %s::$%s',
397
                                $implementation,
398
                                $property
399
                            )
400
                        );
401
                    }
402
                    unset($obj->$property);
403
                    $obj->$property = null;
404
                }
405
            }
406
        }
407
408
        $obj->MakePropertiesUnchanged(...array_keys($params));
409
410
        ob_start();
411
        var_dump($obj);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($obj) looks like debug code. Are you sure you do not want to remove it?
Loading history...
412
413
        $debugInfo = ob_get_clean();
414
415
        $props = [];
416
417
        foreach ($obj::DaftObjectExportableProperties() as $prop) {
418
            $expectedMethod = 'Get' . ucfirst($prop);
419
            if (
420
                $obj->__isset($prop) &&
421
                method_exists($obj, $expectedMethod) &&
422
                (
423
                    new ReflectionMethod($obj, $expectedMethod)
424
                )->isPublic()
425
            ) {
426
                $props[$prop] = $obj->$expectedMethod();
427
            }
428
        }
429
430
        $regex =
431
            '/(?:class |object\()' .
432
            preg_quote(get_class($obj), '/') .
433
            '[\)]{0,1}#' .
434
            '\d+ \(' .
435
            preg_quote((string) count($props), '/') .
436
            '\) \{.+';
437
438
        foreach ($props as $prop => $val) {
439
            $regex .=
440
                ' (?:public ' .
441
                preg_quote('$' . $prop, '/') .
442
                '|' .
443
                preg_quote('["' . $prop . '"]', '/') .
444
                ')[ ]{0,1}' .
445
                preg_quote('=', '/') .
446
                '>.+' .
447
                (
448
                    is_int($val)
449
                        ? 'int'
450
                        : (
451
                            is_bool($val)
452
                                ? 'bool'
453
                                : (
454
                                    is_float($val)
455
                                        ? '(?:float|double)'
456
                                        : preg_quote(gettype($val), '/')
457
                                )
458
                        )
459
                ) .
460
                preg_quote(
461
                    (
462
                        '(' .
463
                        (
464
                            is_string($val)
465
                                ? mb_strlen($val, '8bit')
466
                                : (
467
                                    is_numeric($val)
468
                                        ? (string) $val
469
                                        : var_export($val, true)
470
                                )
471
                        ) .
472
                        ')' .
473
                        (
474
                            is_string($val)
475
                                ? (' "' . $val . '"')
476
                                : ''
477
                        )
478
                    ),
479
                    '/'
480
                ) .
481
                '.+';
482
        }
483
484
        $regex .= '\}.+$/s';
485
486
        $this->assertRegExp(
487
            $regex,
488
            str_replace("\n", ' ', (string) $debugInfo)
489
        );
490
    }
491
492
    /**
493
    * @dataProvider ThrowsExceptionProvider
494
    */
495
    public function testThrowsException(
496
        string $implementation,
497
        string $expectedExceptionType,
498
        string $expectedExceptionMessage,
499
        array $params,
500
        bool $readable,
501
        bool $writeable
502
    ) : void {
503
        if ($readable) {
504
            $this->expectException($expectedExceptionType);
505
            $this->expectExceptionMessage($expectedExceptionMessage);
506
            $obj = new $implementation($params, $writeable);
507
508
            foreach (array_keys($params) as $property) {
509
                $var = $obj->$property;
0 ignored issues
show
Unused Code introduced by
The assignment to $var is dead and can be removed.
Loading history...
510
            }
511
        } elseif ($writeable) {
512
            $this->expectException($expectedExceptionType);
513
            $this->expectExceptionMessage($expectedExceptionMessage);
514
            $obj = new $implementation($params, $writeable);
0 ignored issues
show
Unused Code introduced by
The assignment to $obj is dead and can be removed.
Loading history...
515
        }
516
    }
517
518
    /**
519
    * @dataProvider DefinesOwnUntypedIdInterfaceProvider
520
    */
521
    public function testDefinesOwnUntypedIdInterface(
522
        string $implementation,
523
        array $params
524
    ) : void {
525
        $obj = new $implementation($params, false);
526
        $val = $obj->id;
527
528
        /**
529
        * @var DefinesOwnIdPropertiesInterface $implementation
530
        */
531
        $implementation = $implementation;
532
533
        $keys = $implementation::DaftObjectIdProperties();
534
535
        if (count($keys) < 2) {
536
            $key = $keys[0];
537
            $this->assertSame($val, $obj->$key);
538
        } else {
539
            $this->assertInternalType('array', $val);
540
            $keyVals = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $keyVals is dead and can be removed.
Loading history...
541
            foreach ($keys as $i => $key) {
542
                $this->assertSame($val[$i], $obj->$key);
543
            }
544
        }
545
546
        if ($obj instanceof DefinesOwnStringIdInterface) {
547
            $this->assertInternalType('string', $val);
548
        } elseif ($obj instanceof DefinesOwnIntegerIdInterface) {
549
            $this->assertInternalType('int', $val);
550
        }
551
    }
552
553
    public function RetrievePropertyValueFromDataNotNullableExceptionDataProvider() : array
554
    {
555
        return [
556
            [
557
                ReadOnly::class,
558
            ],
559
        ];
560
    }
561
562
    /**
563
    * @dataProvider RetrievePropertyValueFromDataNotNullableExceptionDataProvider
564
    */
565
    public function testRetrievePropertyValueFromDataNotNullableException(
566
        string $implementation
567
    ) : void {
568
        $obj = new $implementation();
569
570
        /**
571
        * @var DaftObject $implementation
572
        */
573
        $implementation = $implementation;
574
575
        $props = $implementation::DaftObjectProperties();
576
        $nullables = $implementation::DaftObjectNullableProperties();
577
578
        $allNullable = true;
579
580
        foreach ($props as $prop) {
581
            if (false === in_array($prop, $nullables, true)) {
582
                $allNullable = false;
583
                break;
584
            }
585
        }
586
587
        if ($allNullable) {
588
            $this->markTestSkipped(
589
                'Cannot test for not nullable exception if all properties are nullable'
590
            );
591
        }
592
593
        $prop = $props[0];
594
595
        $this->expectException(PropertyNotNullableException::class);
596
        $this->expectExceptionMessage(
597
            sprintf(
598
                'Property not nullable: %s::$%s',
599
                $implementation,
0 ignored issues
show
Bug introduced by
$implementation of type SignpostMarv\DaftObject\DaftObject is incompatible with the type string expected by parameter $args of sprintf(). ( Ignorable by Annotation )

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

599
                /** @scrutinizer ignore-type */ $implementation,
Loading history...
600
                $prop
601
            )
602
        );
603
604
        $obj->$prop;
605
    }
606
}
607