Completed
Pull Request — master (#134)
by no
67:54 queued 59:59
created

IsoTimestampParserTest::getParserClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace ValueParsers\Test;
4
5
use DataValues\TimeValue;
6
use ValueParsers\IsoTimestampParser;
7
use ValueParsers\ParserOptions;
8
9
/**
10
 * @covers ValueParsers\IsoTimestampParser
11
 *
12
 * @group DataValue
13
 * @group DataValueExtensions
14
 *
15
 * @author Addshore
16
 * @author Thiemo Mättig
17
 */
18
class IsoTimestampParserTest extends ValueParserTestBase {
19
20
	/**
21
	 * @see ValueParserTestBase::getInstance
22
	 *
23
	 * @return IsoTimestampParser
24
	 */
25
	protected function getInstance() {
26
		return new IsoTimestampParser();
27
	}
28
29
	/**
30
	 * @see ValueParserTestBase::validInputProvider
31
	 */
32
	public function validInputProvider() {
33
		$gregorian = 'http://www.wikidata.org/entity/Q1985727';
34
		$julian = 'http://www.wikidata.org/entity/Q1985786';
35
36
		$julianOpts = new ParserOptions();
37
		$julianOpts->setOption( IsoTimestampParser::OPT_CALENDAR, $julian );
38
39
		$gregorianOpts = new ParserOptions();
40
		$gregorianOpts->setOption( IsoTimestampParser::OPT_CALENDAR, $gregorian );
41
42
		$prec10aOpts = new ParserOptions();
43
		$prec10aOpts->setOption( IsoTimestampParser::OPT_PRECISION, TimeValue::PRECISION_YEAR10 );
44
45
		$precDayOpts = new ParserOptions();
46
		$precDayOpts->setOption( IsoTimestampParser::OPT_PRECISION, TimeValue::PRECISION_DAY );
47
48
		$precSecondOpts = new ParserOptions();
49
		$precSecondOpts->setOption( IsoTimestampParser::OPT_PRECISION, TimeValue::PRECISION_SECOND );
50
51
		$valid = array(
52
			// Whitespace
53
			"+0000000000002013-07-16T00:00:00Z\n" => array(
54
				'+2013-07-16T00:00:00Z',
55
				TimeValue::PRECISION_DAY,
56
			),
57
			' +0000000000002013-07-00T00:00:00Z ' => array(
58
				'+2013-07-00T00:00:00Z',
59
				TimeValue::PRECISION_MONTH,
60
			),
61
62
			// Empty options tests
63
			'+0000000000002013-00-00T00:00:00Z' => array(
64
				'+2013-00-00T00:00:00Z',
65
				TimeValue::PRECISION_YEAR,
66
			),
67
			'+0000000000000000-00-00T00:00:00Z' => array(
68
				'+0000-00-00T00:00:00Z',
69
				TimeValue::PRECISION_YEAR,
70
				$julian
71
			),
72
			'+0000000000002000-00-00T00:00:00Z' => array(
73
				'+2000-00-00T00:00:00Z',
74
				TimeValue::PRECISION_YEAR,
75
			),
76
			'+0000000000008000-00-00T00:00:00Z' => array(
77
				'+8000-00-00T00:00:00Z',
78
				TimeValue::PRECISION_YEAR1K,
79
			),
80
			'+0000000000020000-00-00T00:00:00Z' => array(
81
				'+20000-00-00T00:00:00Z',
82
				TimeValue::PRECISION_YEAR10K,
83
			),
84
			'+0000000000200000-00-00T00:00:00Z' => array(
85
				'+200000-00-00T00:00:00Z',
86
				TimeValue::PRECISION_YEAR100K,
87
			),
88
			'+0000000002000000-00-00T00:00:00Z' => array(
89
				'+2000000-00-00T00:00:00Z',
90
				TimeValue::PRECISION_YEAR1M,
91
			),
92
			'+0000000020000000-00-00T00:00:00Z' => array(
93
				'+20000000-00-00T00:00:00Z',
94
				TimeValue::PRECISION_YEAR10M,
95
			),
96
			'+0000000200000000-00-00T00:00:00Z' => array(
97
				'+200000000-00-00T00:00:00Z',
98
				TimeValue::PRECISION_YEAR100M,
99
			),
100
			'+0000002000000000-00-00T00:00:00Z' => array(
101
				'+2000000000-00-00T00:00:00Z',
102
				TimeValue::PRECISION_YEAR1G,
103
			),
104
			'+2000000000000000-00-00T00:00:00Z' => array(
105
				'+2000000000000000-00-00T00:00:00Z',
106
				TimeValue::PRECISION_YEAR1G,
107
			),
108
			'-2000000000000000-00-00T00:00:00Z' => array(
109
				'-2000000000000000-00-00T00:00:00Z',
110
				TimeValue::PRECISION_YEAR1G,
111
				$julian
112
			),
113
			'+0000000000002013-07-16T00:00:00Z (Gregorian)' => array(
114
				'+2013-07-16T00:00:00Z',
115
				TimeValue::PRECISION_DAY,
116
			),
117
			'+0000000000000000-01-01T00:00:00Z (Gregorian)' => array(
118
				'+0000-01-01T00:00:00Z',
119
				TimeValue::PRECISION_DAY,
120
121
			),
122
			'+0000000000002001-01-14T00:00:00Z (Julian)' => array(
123
				'+2001-01-14T00:00:00Z',
124
				TimeValue::PRECISION_DAY,
125
				$julian,
126
			),
127
			'+0000000000010000-01-01T00:00:00Z (Gregorian)' => array(
128
				'+10000-01-01T00:00:00Z',
129
				TimeValue::PRECISION_DAY,
130
			),
131
			'-0000000000000001-01-01T00:00:00Z (Gregorian)' => array(
132
				'-0001-01-01T00:00:00Z',
133
				TimeValue::PRECISION_DAY,
134
				$gregorian
135
			),
136
			'-00000000001-01-01T00:00:00Z (Gregorian)' => array(
137
				'-0001-01-01T00:00:00Z',
138
				TimeValue::PRECISION_DAY,
139
				$gregorian,
140
				$julianOpts // overridden by explicit calendar in input string
141
			),
142
			'-00000000001-01-01T00:00:00Z (Julian)' => array(
143
				'-0001-01-01T00:00:00Z',
144
				TimeValue::PRECISION_DAY,
145
				$julian,
146
				$gregorianOpts // overridden by explicit calendar in input string
147
			),
148
			'-000001-01-01T00:00:00Z (Gregorian)' => array(
149
				'-0001-01-01T00:00:00Z',
150
				TimeValue::PRECISION_DAY,
151
				$gregorian
152
			),
153
			'-1-01-01T00:00:00Z (Gregorian)' => array(
154
				'-0001-01-01T00:00:00Z',
155
				TimeValue::PRECISION_DAY,
156
				$gregorian
157
			),
158
159
			// Tests with different options
160
			'-1-01-02T00:00:00Z' => array(
161
				'-0001-01-02T00:00:00Z',
162
				TimeValue::PRECISION_DAY,
163
				$gregorian,
164
				$gregorianOpts,
165
			),
166
			'+2001-01-03T00:00:00Z' => array(
167
				'+2001-01-03T00:00:00Z',
168
				TimeValue::PRECISION_DAY,
169
				$julian,
170
				$julianOpts,
171
			),
172
			'-1-01-04T00:00:00Z' => array(
173
				'-0001-01-04T00:00:00Z',
174
				TimeValue::PRECISION_YEAR10,
175
				$julian,
176
				$prec10aOpts,
177
			),
178
			'-1-01-05T00:00:00Z' => array(
179
				'-0001-01-05T00:00:00Z',
180
				TimeValue::PRECISION_DAY,
181
				$julian,
182
			),
183
184
			'+1999-00-00T00:00:00Z' => array(
185
				'+1999-00-00T00:00:00Z',
186
				TimeValue::PRECISION_YEAR,
187
			),
188
			'+2000-00-00T00:00:00Z' => array(
189
				'+2000-00-00T00:00:00Z',
190
				TimeValue::PRECISION_YEAR,
191
			),
192
			'+2010-00-00T00:00:00Z' => array(
193
				'+2010-00-00T00:00:00Z',
194
				TimeValue::PRECISION_YEAR,
195
			),
196
197
			// Optional sign character
198
			'2015-01-01T00:00:00Z' => array(
199
				'+2015-01-01T00:00:00Z',
200
				TimeValue::PRECISION_DAY,
201
			),
202
203
			// Optional time zone
204
			'2015-01-01T00:00:00' => array(
205
				'+2015-01-01T00:00:00Z',
206
				TimeValue::PRECISION_DAY,
207
			),
208
209
			// Actual minus character from Unicode; roundtrip with TimeDetailsFormatter
210
			"\xE2\x88\x922015-01-01T00:00:00" => array(
211
				'-2015-01-01T00:00:00Z',
212
				TimeValue::PRECISION_DAY,
213
				$julian
214
			),
215
216
			// Optional colons
217
			'2015-01-01T161718' => array(
218
				'+0000000000002015-01-01T16:17:18Z',
219
				TimeValue::PRECISION_SECOND,
220
			),
221
			'2015-01-01T1617' => array(
222
				'+0000000000002015-01-01T16:17:00Z',
223
				TimeValue::PRECISION_MINUTE,
224
			),
225
226
			// Optional second
227
			'2015-01-01T00:00' => array(
228
				'+2015-01-01T00:00:00Z',
229
				TimeValue::PRECISION_DAY,
230
			),
231
232
			// Optional hour and minute
233
			'2015-01-01' => array(
234
				'+2015-01-01T00:00:00Z',
235
				TimeValue::PRECISION_DAY,
236
			),
237
			'60-01-01' => array(
238
				'+0060-01-01T00:00:00Z',
239
				TimeValue::PRECISION_DAY,
240
				$julian
241
			),
242
			// 32 can not be confused with anything. Can't be day or month. Can't be minute or
243
			// second because a time can not start with minute or second.
244
			'32-01-01' => array(
245
				'+0032-01-01T00:00:00Z',
246
				TimeValue::PRECISION_DAY,
247
				$julian
248
			),
249
250
			// Years <= 31 require either the time part or a year with more than 2 digits
251
			'1-01-01T00:00' => array(
252
				'+0001-01-01T00:00:00Z',
253
				TimeValue::PRECISION_DAY,
254
				$julian
255
			),
256
			'001-01-01' => array(
257
				'+0001-01-01T00:00:00Z',
258
				TimeValue::PRECISION_DAY,
259
				$julian
260
			),
261
			// 00-00-00 to 24-00-00 can be confused with a time, but not if it is signed.
262
			'-01-02-03' => array(
263
				'-0001-02-03T00:00:00Z',
264
				TimeValue::PRECISION_DAY,
265
				$julian
266
			),
267
			'+01-02-03' => array(
268
				'+0001-02-03T00:00:00Z',
269
				TimeValue::PRECISION_DAY,
270
				$julian
271
			),
272
273
			// Day zero
274
			'2015-01-00' => array(
275
				'+2015-01-00T00:00:00Z',
276
				TimeValue::PRECISION_MONTH,
277
			),
278
279
			// Month zero
280
			'2015-00-00' => array(
281
				'+2015-00-00T00:00:00Z',
282
				TimeValue::PRECISION_YEAR,
283
			),
284
285
			// Leap seconds are a valid concept
286
			'+2015-01-01T00:00:61Z' => array(
287
				'+2015-01-01T00:00:61Z',
288
				TimeValue::PRECISION_SECOND,
289
			),
290
291
			// Tests for correct precision when a bad precision is passed through the opts
292
			// @see https://bugzilla.wikimedia.org/show_bug.cgi?id=62730
293
			'+0000000000000012-12-00T00:00:00Z' => array(
294
				'+0012-12-00T00:00:00Z',
295
				TimeValue::PRECISION_MONTH,
296
				$julian,
297
				$precDayOpts,
298
			),
299
			'+2015-01-01T00:00:00Z' => array(
300
				'+2015-01-01T00:00:00Z',
301
				TimeValue::PRECISION_SECOND,
302
				$gregorian,
303
				$precSecondOpts,
304
			),
305
306
			// Test Julian/Gregorian switch in October 1582.
307
			'1583-01-01' => array(
308
				'+1583-01-01T00:00:00Z',
309
				TimeValue::PRECISION_DAY,
310
				$gregorian
311
			),
312
313
			// Test Julian/Gregorian switch in October 1582.
314
			'1582-08-01' => array(
315
				'+1582-08-01T00:00:00Z',
316
				TimeValue::PRECISION_DAY,
317
				$julian
318
			),
319
		);
320
321
		$argLists = array();
322
323
		foreach ( $valid as $key => $value ) {
324
			$timestamp = $value[0];
325
			$precision = isset( $value[1] ) ? $value[1] : TimeValue::PRECISION_DAY;
326
			$calendarModel = isset( $value[2] ) ? $value[2] : $gregorian;
327
			$options = isset( $value[3] ) ? $value[3] : null;
328
329
			$argLists[] = array(
330
				// Because PHP magically turns numeric keys into ints/floats
331
				(string)$key,
332
				new TimeValue( $timestamp, 0, 0, 0, $precision, $calendarModel ),
0 ignored issues
show
Bug introduced by
It seems like $timestamp defined by $value[0] on line 324 can also be of type object<ValueParsers\ParserOptions>; however, DataValues\TimeValue::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like $calendarModel defined by isset($value[2]) ? $value[2] : $gregorian on line 326 can also be of type object<ValueParsers\ParserOptions>; however, DataValues\TimeValue::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
333
				new IsoTimestampParser( null, $options )
0 ignored issues
show
Bug introduced by
It seems like $options defined by isset($value[3]) ? $value[3] : null on line 327 can also be of type string; however, ValueParsers\IsoTimestampParser::__construct() does only seem to accept null|object<ValueParsers\ParserOptions>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
334
			);
335
		}
336
337
		return $argLists;
338
	}
339
340
	/**
341
	 * @see ValueParserTestBase::invalidInputProvider
342
	 */
343
	public function invalidInputProvider() {
344
		$argLists = array();
345
346
		$invalid = array(
347
			// Stuff that's not even a string
348
			true,
349
			false,
350
			null,
351
			array(),
352
			'foooooooooo',
353
			'1 June 2014',
354
			// Can be confused with a time (HMS), DMY or MDY
355
			'00-00-00',
356
			'12-01-01',
357
			'24-00-00',
358
			'31-01-01',
359
			// Month and day must be two digits
360
			'2015-12-1',
361
			'2015-1-31',
362
			// Time and seconds are optional, but hour without minutes is not allowed
363
			'+2015-12-31T23',
364
			'+2015-12-31T23Z',
365
			// Elements out of allowed bounds
366
			'+2015-00-01T00:00:00Z',
367
			'+2015-01-00T01:00:00Z',
368
			'+2015-01-00T00:01:00Z',
369
			'+2015-01-00T00:00:01Z',
370
			'+2015-13-01T00:00:00Z',
371
			'+2015-01-32T00:00:00Z',
372
			'+2015-01-01T24:00:00Z',
373
			'+2015-01-01T00:60:00Z',
374
			'+2015-01-01T00:00:62Z',
375
			// This parser should not replace the year parser
376
			'1234567890873',
377
			2134567890
378
		);
379
380
		foreach ( $invalid as $value ) {
381
			$argLists[] = array( $value );
382
		}
383
384
		return $argLists;
385
	}
386
387
	/**
388
	 * @dataProvider optionsProvider
389
	 */
390
	public function testOptions(
391
		$value,
392
		array $options,
393
		$timestamp,
394
		$calendarModel,
395
		$precision = TimeValue::PRECISION_DAY
396
	) {
397
		$parser = new IsoTimestampParser( null, new ParserOptions( $options ) );
398
		$this->assertEquals(
399
			new TimeValue( $timestamp, 0, 0, 0, $precision, $calendarModel ),
400
			$parser->parse( $value )
401
		);
402
	}
403
404
	public function optionsProvider() {
405
		$gregorian = 'http://www.wikidata.org/entity/Q1985727';
406
		$julian = 'http://www.wikidata.org/entity/Q1985786';
407
408
		return array(
409
			'Auto-detected Gregorian' => array(
410
				'1583-01-31',
411
				array(),
412
				'+1583-01-31T00:00:00Z',
413
				$gregorian
414
			),
415
			'Option overrides auto-detected Gregorian' => array(
416
				'1583-01-31',
417
				array( IsoTimestampParser::OPT_CALENDAR => $julian ),
418
				'+1583-01-31T00:00:00Z',
419
				$julian
420
			),
421
			'Auto-detected Julian' => array(
422
				'1582-01-31',
423
				array(),
424
				'+1582-01-31T00:00:00Z',
425
				$julian
426
			),
427
			'Option overrides auto-detected Julian' => array(
428
				'1582-01-31',
429
				array( IsoTimestampParser::OPT_CALENDAR => $gregorian ),
430
				'+1582-01-31T00:00:00Z',
431
				$gregorian
432
			),
433
			'Option can decrease precision' => array(
434
				'2016-01-31',
435
				array( IsoTimestampParser::OPT_PRECISION => TimeValue::PRECISION_MONTH ),
436
				'+2016-01-31T00:00:00Z',
437
				$gregorian,
438
				TimeValue::PRECISION_MONTH
439
			),
440
			'Option can set minimal precision' => array(
441
				'2016-01-31',
442
				array( IsoTimestampParser::OPT_PRECISION => TimeValue::PRECISION_YEAR1G ),
443
				'+2016-01-31T00:00:00Z',
444
				$gregorian,
445
				TimeValue::PRECISION_YEAR1G
446
			),
447
			'Option can not increase year precision' => array(
448
				'2016-00-00',
449
				array( IsoTimestampParser::OPT_PRECISION => TimeValue::PRECISION_MONTH ),
450
				'+2016-00-00T00:00:00Z',
451
				$gregorian,
452
				TimeValue::PRECISION_YEAR
453
			),
454
			'Option can not increase month precision' => array(
455
				'2016-01-00',
456
				array( IsoTimestampParser::OPT_PRECISION => TimeValue::PRECISION_DAY ),
457
				'+2016-01-00T00:00:00Z',
458
				$gregorian,
459
				TimeValue::PRECISION_MONTH
460
			),
461
			'Option can increase day precision' => array(
462
				'2016-01-31',
463
				array( IsoTimestampParser::OPT_PRECISION => TimeValue::PRECISION_HOUR ),
464
				'+2016-01-31T00:00:00Z',
465
				$gregorian,
466
				TimeValue::PRECISION_HOUR
467
			),
468
			'Precision option accepts strings' => array(
469
				'2016-01-31',
470
				array( IsoTimestampParser::OPT_PRECISION => '10' ),
471
				'+2016-01-31T00:00:00Z',
472
				$gregorian,
473
				TimeValue::PRECISION_MONTH
474
			),
475
		);
476
	}
477
478
	/**
479
	 * @dataProvider invalidOptionsProvider
480
	 */
481
	public function testInvalidOptions( array $options ) {
482
		$parser = new IsoTimestampParser( null, new ParserOptions( $options ) );
483
		$this->setExpectedException( 'ValueParsers\ParseException' );
484
		$parser->parse( '2016-01-31' );
485
	}
486
487
	public function invalidOptionsProvider() {
488
		return array(
489
			array( array( IsoTimestampParser::OPT_PRECISION => -1 ) ),
490
			array( array( IsoTimestampParser::OPT_PRECISION => 1.5 ) ),
491
			array( array( IsoTimestampParser::OPT_PRECISION => 1000 ) ),
492
			array( array( IsoTimestampParser::OPT_PRECISION => 'invalid' ) ),
493
		);
494
	}
495
496
}
497