Passed
Pull Request — master (#7)
by Alex
07:26
created

testDiffWillReturnDateInterval()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 10
c 1
b 1
f 0
dl 0
loc 17
rs 9.9332
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ArpTest\DateTime;
6
7
use Arp\DateTime\DateFactoryInterface;
8
use Arp\DateTime\DateIntervalFactoryInterface;
9
use Arp\DateTime\DateTimeFactory;
10
use Arp\DateTime\DateTimeFactoryInterface;
11
use Arp\DateTime\Exception\DateIntervalFactoryException;
12
use Arp\DateTime\Exception\DateTimeFactoryException;
13
use Cassandra\Date;
0 ignored issues
show
Bug introduced by
The type Cassandra\Date was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use PHPUnit\Framework\MockObject\MockObject;
15
use PHPUnit\Framework\TestCase;
16
17
/**
18
 * @author  Alex Patterson <[email protected]>
19
 * @package ArpTest\DateTime
20
 */
21
final class DateTimeFactoryTest extends TestCase
22
{
23
    /**
24
     * @var DateIntervalFactoryInterface|MockObject
25
     */
26
    private $dateIntervalFactory;
27
28
    /**
29
     * Prepare the test case dependencies
30
     */
31
    public function setUp(): void
32
    {
33
        $this->dateIntervalFactory = $this->getMockForAbstractClass(DateIntervalFactoryInterface::class);
34
    }
35
36
    /**
37
     * Ensure that the factory implements DateFactoryInterface.
38
     *
39
     * @covers \Arp\DateTime\DateTimeFactory
40
     */
41
    public function testImplementsDateFactoryInterface(): void
42
    {
43
        $factory = new DateTimeFactory($this->dateIntervalFactory);
44
45
        $this->assertInstanceOf(DateFactoryInterface::class, $factory);
46
    }
47
48
    /**
49
     * Ensure that the factory implements DateTimeFactoryInterface.
50
     *
51
     * @covers \Arp\DateTime\DateTimeFactory
52
     */
53
    public function testImplementsDateTimeFactoryInterface(): void
54
    {
55
        $factory = new DateTimeFactory($this->dateIntervalFactory);
56
57
        $this->assertInstanceOf(DateTimeFactoryInterface::class, $factory);
58
    }
59
60
    /**
61
     * Ensure that the factory implements DateIntervalFactoryInterface.
62
     *
63
     * @covers \Arp\DateTime\DateTimeFactory
64
     */
65
    public function testImplementsDateIntervalFactoryInterface(): void
66
    {
67
        $factory = new DateTimeFactory($this->dateIntervalFactory);
68
69
        $this->assertInstanceOf(DateIntervalFactoryInterface::class, $factory);
70
    }
71
72
    /**
73
     * Ensure that calls to createDateTime() will return the valid configured \DateTime instance.
74
     *
75
     * @param string                    $spec     The date and time specification.
76
     * @param \DateTimeZone|string|null $timeZone The optional date time zone to test.
77
     *
78
     * @dataProvider getCreateDateTimeData
79
     *
80
     * @covers       \Arp\DateTime\DateTimeFactory::createDateTime
81
     * @covers       \Arp\DateTime\DateTimeFactory::resolveDateTimeZone
82
     *
83
     * @throws DateTimeFactoryException
84
     */
85
    public function testCreateDateTime(string $spec, $timeZone = null): void
86
    {
87
        $factory = new DateTimeFactory($this->dateIntervalFactory);
88
89
        $dateTime = $factory->createDateTime($spec, $timeZone);
90
91
        $this->assertSame($spec, $dateTime->format('Y-m-d H:i:s'));
92
    }
93
94
    /**
95
     * @return array
96
     */
97
    public function getCreateDateTimeData(): array
98
    {
99
        return [
100
            [
101
                '2019-05-14 12:33:00',
102
            ],
103
104
            [
105
                '2019-08-14 17:34:55',
106
                'UTC',
107
            ],
108
109
            [
110
                '2020-08-22 14:43:12',
111
                null,
112
            ],
113
114
            [
115
                '2020-08-22 14:44:37',
116
                new \DateTimeZone('Europe/London'),
117
            ],
118
        ];
119
    }
120
121
    /**
122
     * Ensure that if the DateTime cannot be created because the provided $spec is invalid, a new
123
     * DateTimeFactoryException will be thrown.
124
     *
125
     * @covers \Arp\DateTime\DateTimeFactory::createDateTime
126
     *
127
     * @throws DateTimeFactoryException
128
     */
129
    public function testCreateDateTimeWillThrowDateTimeFactoryExceptionForInvalidDateTimeSpec(): void
130
    {
131
        $factory = new DateTimeFactory($this->dateIntervalFactory);
132
133
        $spec = 'foo'; // invalid argument
134
135
        $exceptionMessage = sprintf(
136
            'DateTime::__construct(): Failed to parse time string (%s) at position 0 (%s)',
137
            $spec,
138
            $spec[0]
139
        );
140
141
        $this->expectException(DateTimeFactoryException::class);
142
        $this->expectExceptionMessage(
143
            sprintf(
144
                'Failed to create a valid \DateTime instance using \'%s\': %s',
145
                $spec,
146
                $exceptionMessage
147
            )
148
        );
149
150
        $factory->createDateTime($spec);
151
    }
152
153
    /**
154
     * Assert that a DateTimeFactoryException will be thrown if providing an invalid $spec
155
     * argument to createFromFormat().
156
     *
157
     * @param string                    $spec
158
     * @param string                    $format
159
     * @param \DateTimeZone|string|null $timeZone
160
     *
161
     * @dataProvider getCreateFromFormatWillThrowDateTimeFactoryExceptionForInvalidDateTimeData
162
     *
163
     * @covers       \Arp\DateTime\DateTimeFactory::createFromFormat
164
     *
165
     * @throws DateTimeFactoryException
166
     */
167
    public function testCreateFromFormatWillThrowDateTimeFactoryExceptionForInvalidDateTimeSpec(
168
        string $spec,
169
        string $format,
170
        $timeZone = null
171
    ): void {
172
        $factory = new DateTimeFactory($this->dateIntervalFactory);
173
174
        $this->expectException(DateTimeFactoryException::class);
175
        $this->expectExceptionMessage(
176
            sprintf(
177
                'Failed to create a valid \DateTime instance using \'%s\' and format \'%s\'',
178
                $spec,
179
                $format
180
            )
181
        );
182
183
        $factory->createFromFormat($spec, $format, $timeZone);
184
    }
185
186
    /**
187
     * @return array
188
     */
189
    public function getCreateFromFormatWillThrowDateTimeFactoryExceptionForInvalidDateTimeData(): array
190
    {
191
        return [
192
            [
193
                'test',
194
                'Y-m-d',
195
            ],
196
        ];
197
    }
198
199
    /**
200
     * Assert that a DateTimeFactoryException will be thrown when providing a invalid \DateTimeZone object to
201
     * createDateTime().
202
     *
203
     * @covers \Arp\DateTime\DateTimeFactory::createDateTime
204
     * @covers \Arp\DateTime\DateTimeFactory::resolveDateTimeZone
205
     *
206
     * @throws DateTimeFactoryException
207
     */
208
    public function testCreateDateTimeWillThrowDateTimeFactoryExceptionForInvalidDateTimeZone(): void
209
    {
210
        $factory = new DateTimeFactory($this->dateIntervalFactory);
211
212
        $spec = 'now';
213
        $timeZone = new \stdClass();
214
215
        $errorMessage = sprintf(
216
            'The \'timeZone\' argument must be a \'string\''
217
            . 'or an object of type \'%s\'; \'%s\' provided in \'%s\'',
218
            \DateTimeZone::class,
219
            is_object($timeZone) ? get_class($timeZone) : gettype($timeZone),
220
            'resolveDateTimeZone'
221
        );
222
223
        $this->expectException(DateTimeFactoryException::class);
224
        $this->expectExceptionMessage($errorMessage);
225
226
        $factory->createDateTime($spec, $timeZone);
1 ignored issue
show
Bug introduced by
$timeZone of type stdClass is incompatible with the type DateTimeZone|null|string expected by parameter $timeZone of Arp\DateTime\DateTimeFactory::createDateTime(). ( Ignorable by Annotation )

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

226
        $factory->createDateTime($spec, /** @scrutinizer ignore-type */ $timeZone);
Loading history...
227
    }
228
229
    /**
230
     * Ensure that a \DateTime instance can be created from the provided format.
231
     *
232
     * @param string                    $spec
233
     * @param string                    $format
234
     * @param string|\DateTimeZone|null $timeZone
235
     *
236
     * @dataProvider getCreateFromFormatData
237
     *
238
     * @covers       \Arp\DateTime\DateTimeFactory::createFromFormat
239
     * @covers       \Arp\DateTime\DateTimeFactory::resolveDateTimeZone
240
     *
241
     * @throws DateTimeFactoryException
242
     */
243
    public function testCreateFromFormat(string $spec, string $format, $timeZone = null): void
244
    {
245
        $factory = new DateTimeFactory($this->dateIntervalFactory);
246
247
        $dateTime = $factory->createFromFormat($spec, $format, $timeZone);
248
249
        $this->assertSame($spec, $dateTime->format($format));
250
251
        if (null !== $timeZone) {
252
            $this->assertSame($timeZone, $dateTime->getTimezone()->getName());
253
        }
254
    }
255
256
    /**
257
     * @see https://www.php.net/manual/en/timezones.europe.php
258
     *
259
     * @return array
260
     */
261
    public function getCreateFromFormatData(): array
262
    {
263
        return [
264
            [
265
                '2019-04-01',
266
                'Y-m-d',
267
            ],
268
            [
269
                '1976/01/14',
270
                'Y/m/d',
271
            ],
272
            [
273
                '2019-08-14 17:34:55',
274
                'Y-m-d H:i:s',
275
                'UTC',
276
            ],
277
            [
278
                '2010-10-26 11:19:32',
279
                'Y-m-d H:i:s',
280
                'Europe/London',
281
            ],
282
        ];
283
    }
284
285
    /**
286
     * Ensure a \DateTimeZone instance is returned according to the provided $spec and $options.
287
     *
288
     * @param string $spec
289
     *
290
     * @dataProvider getCreateDateTimeZoneData
291
     *
292
     * @covers       \Arp\DateTime\DateTimeFactory::createDateTimeZone
293
     *
294
     * @throws DateTimeFactoryException
295
     */
296
    public function testCreateDateTimeZone(string $spec): void
297
    {
298
        $factory = new DateTimeFactory($this->dateIntervalFactory);
299
300
        $dateTimeZone = $factory->createDateTimeZone($spec);
301
302
        $this->assertSame($spec, $dateTimeZone->getName());
303
    }
304
305
    /**
306
     * @see https://www.php.net/manual/en/timezones.europe.php
307
     *
308
     * @return array
309
     */
310
    public function getCreateDateTimeZoneData(): array
311
    {
312
        return [
313
            [
314
                'Europe/London',
315
            ],
316
            [
317
                'Europe/Amsterdam',
318
            ],
319
            [
320
                'Europe/Rome',
321
            ],
322
            [
323
                'Atlantic/Bermuda',
324
            ],
325
            [
326
                'Atlantic/Azores',
327
            ],
328
            [
329
                'Antarctica/DumontDUrville',
330
            ],
331
        ];
332
    }
333
334
    /**
335
     * Ensure that if providing an invalid $spec argument to createDateTimeZone() a new DateTimeFactoryException
336
     * is thrown.
337
     *
338
     * @param string $spec The invalid timezone specification.
339
     *
340
     * @throws DateTimeFactoryException
341
     *
342
     * @dataProvider getCreateDateTimeZoneWillThrowDateTimeFactoryExceptionIfSpecIsInvalidData
343
     *
344
     * @covers       \Arp\DateTime\DateTimeFactory::createDateTimeZone
345
     */
346
    public function testCreateDateTimeZoneWillThrowDateTimeFactoryExceptionIfSpecIsInvalid(string $spec): void
347
    {
348
        $factory = new DateTimeFactory($this->dateIntervalFactory);
349
350
        $exceptionMessage = sprintf('DateTimeZone::__construct(): Unknown or bad timezone (%s)', $spec);
351
352
        $this->expectException(DateTimeFactoryException::class);
353
        $this->expectExceptionMessage(
354
            sprintf(
355
                'Failed to create a valid \DateTimeZone instance using \'%s\': %s',
356
                $spec,
357
                $exceptionMessage
358
            )
359
        );
360
361
        $factory->createDateTimeZone($spec);
362
    }
363
364
    /**
365
     * @return array
366
     */
367
    public function getCreateDateTimeZoneWillThrowDateTimeFactoryExceptionIfSpecIsInvalidData(): array
368
    {
369
        return [
370
            [
371
                'skjdvbnksd',
372
            ],
373
            [
374
                '2345234',
375
            ],
376
            [
377
                'Europe/MyEmpire',
378
            ],
379
        ];
380
    }
381
382
    /**
383
     * Assert that calls to creatDateInterval() will return the expected DateInterval instance.
384
     *
385
     * @param string $spec
386
     *
387
     * @covers       \Arp\DateTime\DateTimeFactory::createDateInterval
388
     * @dataProvider getCreateDateIntervalWillReturnANewDateIntervalToSpecData
389
     *
390
     * @throws DateTimeFactoryException
391
     */
392
    public function testCreateDateIntervalWillReturnANewDateIntervalToSpec(string $spec): void
393
    {
394
        $factory = new DateTimeFactory($this->dateIntervalFactory);
395
396
        /** @var \DateInterval|MockObject $dateInterval */
397
        $dateInterval = $this->createMock(\DateInterval::class);
398
399
        $this->dateIntervalFactory->expects($this->once())
400
            ->method('createDateInterval')
401
            ->willReturn($dateInterval);
402
403
        $this->assertSame($dateInterval, $factory->createDateInterval($spec));
404
    }
405
406
    /**
407
     * @return array
408
     */
409
    public function getCreateDateIntervalWillReturnANewDateIntervalToSpecData(): array
410
    {
411
        return [
412
            ['P100D'],
413
            ['P4Y1DT9H11M3S'],
414
            ['P2Y4DT6H8M'],
415
            ['P7Y8M'],
416
        ];
417
    }
418
419
    /**
420
     * Assert that an invalid $spec passed to createDateInterval() will raise a DateTimeFactoryException.
421
     *
422
     * @covers \Arp\DateTime\DateTimeFactory::createDateInterval
423
     *
424
     * @throws DateTimeFactoryException
425
     */
426
    public function testDateIntervalWillThrowDateTimeFactoryExceptionIfUnableToCreateADateInterval(): void
427
    {
428
        $spec = 'Hello';
429
430
        $factory = new DateTimeFactory($this->dateIntervalFactory);
431
432
        $exceptionCode = 123;
433
        $exceptionMessage = 'This is a test exception message';
434
        $exception = new DateIntervalFactoryException($exceptionMessage, $exceptionCode);
435
436
        $this->dateIntervalFactory->expects($this->once())
437
            ->method('createDateInterval')
438
            ->willThrowException($exception);
439
440
        $errorMessage = sprintf(
441
            'Failed to create date interval \'%s\': %s',
442
            $spec,
443
            $exceptionMessage
444
        );
445
446
        $this->expectDeprecationMessage(DateTimeFactoryException::class);
447
        $this->expectExceptionMessage($errorMessage);
448
        $this->expectExceptionCode($exceptionCode);
449
450
        $factory->createDateInterval($spec);
451
    }
452
453
    /**
454
     * Assert that the calls to diff will return a valid DateInterval.
455
     *
456
     * @covers \Arp\DateTime\DateTimeFactory::diff
457
     *
458
     * @throws DateTimeFactoryException
459
     */
460
    public function testDiffWillReturnDateInterval(): void
461
    {
462
        // @todo Data provider
463
        $origin = new \DateTime();
464
        $target = new \DateTime();
465
        $absolute = false;
466
467
        $dateInterval = $origin->diff($target);
468
469
        $factory = new DateTimeFactory($this->dateIntervalFactory);
470
471
        $this->dateIntervalFactory->expects($this->once())
472
            ->method('diff')
473
            ->with($origin, $target, $absolute)
474
            ->willReturn($dateInterval);
475
476
        $this->assertSame($dateInterval, $factory->diff($origin, $target, $absolute));
477
    }
478
479
    /**
480
     * Assert that a DateTimeFactoryException is thrown when unable to diff the provided dates.
481
     *
482
     * @covers \Arp\DateTime\DateTimeFactory::diff
483
     *
484
     * @throws DateTimeFactoryException
485
     */
486
    public function testDateTimeFactoryExceptionWillBeThrownIfDiffFails(): void
487
    {
488
        $factory = new DateTimeFactory($this->dateIntervalFactory);
489
490
        $origin = new \DateTime();
491
        $target = new \DateTime();
492
493
        $exceptionCode = 123;
494
        $exceptionMessage = 'This is a test exception message';
495
        $exception = new DateIntervalFactoryException($exceptionMessage, $exceptionCode);
496
497
        $this->dateIntervalFactory->expects($this->once())
498
            ->method('diff')
499
            ->with($origin, $target, false)
500
            ->willThrowException($exception);
501
502
        $this->expectException(DateTimeFactoryException::class);
503
        $this->expectExceptionMessage(sprintf('Failed to perform date diff: %s', $exceptionMessage));
504
        $this->expectExceptionCode($exceptionCode);
505
506
        $factory->diff($origin, $target);
507
    }
508
}
509