Completed
Pull Request — master (#93)
by no
10:10 queued 08:05
created

testInvalidMonthNameParsing()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 2
1
<?php
2
3
namespace ValueParsers\Test;
4
5
use DataValues\TimeValue;
6
use ValueParsers\IsoTimestampParser;
7
use ValueParsers\ParseException;
8
use ValueParsers\ParserOptions;
9
use ValueParsers\YearMonthDayTimeParser;
10
11
/**
12
 * @covers ValueParsers\YearMonthDayTimeParser
13
 *
14
 * @group DataValue
15
 * @group DataValueExtensions
16
 * @group TimeParsers
17
 * @group ValueParsers
18
 *
19
 * @license GPL-2.0+
20
 * @author Thiemo Kreuz
21
 */
22
class YearMonthDayTimeParserTest extends StringValueParserTest {
23
24
	/**
25
	 * @deprecated since DataValues Common 0.3, just use getInstance.
26
	 */
27
	protected function getParserClass() {
28
		throw new \LogicException( 'Should not be called, use getInstance' );
29
	}
30
31
	/**
32
	 * @see ValueParserTestBase::getInstance
33
	 *
34
	 * @return YearMonthDayTimeParser
35
	 */
36
	protected function getInstance() {
37
		return $this->getYearMonthDayTimeParser();
38
	}
39
40
	/**
41
	 * @param int[] $months
42
	 *
43
	 * @return YearMonthDayTimeParser
44
	 */
45
	private function getYearMonthDayTimeParser( array $months = [] ) {
46
		return new YearMonthDayTimeParser( null, $months );
47
	}
48
49
	/**
50
	 * @see ValueParserTestBase::validInputProvider
51
	 */
52
	public function validInputProvider() {
53
		$gregorian = 'http://www.wikidata.org/entity/Q1985727';
54
		$julian = 'http://www.wikidata.org/entity/Q1985786';
55
56
		$valid = array(
57
			// Whitespace
58
			"2016-01-01\n" => array( '+2016-01-01T00:00:00Z' ),
59
			' 2016-01-01 ' => array( '+2016-01-01T00:00:00Z' ),
60
61
			// YMD, typically used in ISO 8601
62
			'2015-12-31' => array( '+2015-12-31T00:00:00Z' ),
63
			'2015 12 31' => array( '+2015-12-31T00:00:00Z' ),
64
			'2015 1 13' => array( '+2015-01-13T00:00:00Z' ),
65
66
			// DMY
67
			'31.12.2015' => array( '+2015-12-31T00:00:00Z' ),
68
			'31. 12. 2015' => array( '+2015-12-31T00:00:00Z' ),
69
			'31/12/2015' => array( '+2015-12-31T00:00:00Z' ),
70
			'31 12 2015' => array( '+2015-12-31T00:00:00Z' ),
71
			'31th 12th 2015' => array( '+2015-12-31T00:00:00Z' ),
72
			'day 31, month 12, year 2015' => array( '+2015-12-31T00:00:00Z' ),
73
74
			// MDY, almost exclusively used in the United States
75
			'12/31/2015' => array( '+2015-12-31T00:00:00Z' ),
76
			'12-31-2015' => array( '+2015-12-31T00:00:00Z' ),
77
			'12 31 2015' => array( '+2015-12-31T00:00:00Z' ),
78
79
			// YDM, exclusively used in Kazakhstan
80
			// https://en.wikipedia.org/wiki/Calendar_date#Gregorian.2C_year-day-month_.28YDM.29
81
			'2015.31.12' => array( '+2015-12-31T00:00:00Z' ),
82
			'2015 13 1' => array( '+2015-01-13T00:00:00Z' ),
83
84
			// Month and day are the same, does not matter if DMY or MDY
85
			'01 1 2015' => array( '+2015-01-01T00:00:00Z' ),
86
			'12 12 2015' => array( '+2015-12-12T00:00:00Z' ),
87
88
			// Month and day are the same, does not matter if YMD or YDM
89
			'2015 01 1' => array( '+2015-01-01T00:00:00Z' ),
90
			'2015 12 12' => array( '+2015-12-12T00:00:00Z' ),
91
92
			// Julian
93
			'32-12-31' => array( '+0032-12-31T00:00:00Z', $julian ),
94
			'31.12.32' => array( '+0032-12-31T00:00:00Z', $julian ),
95
			'12/31/60' => array( '+0060-12-31T00:00:00Z', $julian ),
96
97
			// Negative years
98
			'-2015-12-31' => array( '-2015-12-31T00:00:00Z', $julian ),
99
			'year -2015-12-31' => array( '-2015-12-31T00:00:00Z', $julian ),
100
			'31 12 -2015' => array( '-2015-12-31T00:00:00Z', $julian ),
101
			'12/31/-2015' => array( '-2015-12-31T00:00:00Z', $julian ),
102
			'2015-12-31 BC' => array( '-2015-12-31T00:00:00Z', $julian ),
103
			'31 12 2015 BC' => array( '-2015-12-31T00:00:00Z', $julian ),
104
			'12/31/2015 BC' => array( '-2015-12-31T00:00:00Z', $julian ),
105
106
			// A negative number must be the year.
107
			'year -3-2-13' => array( '-0003-02-13T00:00:00Z', $julian ),
108
			'13. 2. -3' => array( '-0003-02-13T00:00:00Z', $julian ),
109
			'23:12:-59' => array( '-0059-12-23T00:00:00Z', $julian ),
110
		);
111
112
		$cases = array();
113
114
		foreach ( $valid as $value => $args ) {
115
			$timestamp = $args[0];
116
			$calendarModel = isset( $args[1] ) ? $args[1] : $gregorian;
117
118
			$cases[] = array(
119
				// Because PHP magically turns numeric keys into ints/floats
120
				(string)$value,
121
				new TimeValue( $timestamp, 0, 0, 0, TimeValue::PRECISION_DAY, $calendarModel )
122
			);
123
		}
124
125
		return $cases;
126
	}
127
128
	/**
129
	 * @see StringValueParserTest::invalidInputProvider
130
	 */
131
	public function invalidInputProvider() {
132
		$invalid = array(
133
			// This parser can only parse strings that contain exactly three numbers.
134
			'2015',
135
			'12.2015',
136
			'May 1 2015',
137
			'1. May 2015',
138
			'1 2015-12-31',
139
			'31.12.2015 23',
140
			'31.12.2015 23:59',
141
			'+2015-12-31T00:00:00Z',
142
143
			// Can be confused with a time (HMS)
144
			'12:31:59',
145
			'12:59:59',
146
			'23:12:59',
147
			'23:12:31',
148
			'-23:12:31',
149
			'-24:00:00',
150
151
			// No year can be identified if all numbers are smaller than 32.
152
			'12 12 12',
153
			'31 12 12',
154
			'12 31 12',
155
			'31 31 12',
156
			'12 12 31',
157
			'31 12 31',
158
			'12 31 31',
159
			'31 31 31',
160
161
			// Two or more candidates for the year.
162
			'32 32 12',
163
			'32 12 32',
164
			'12 32 32',
165
			'32 32 32',
166
167
			// Year can be identified, but month and day can not be distinguished.
168
			'32 2 1',
169
			'2015-12-11',
170
			'1 2 32',
171
			'11.12.2015',
172
173
			// Formats DYM and MYD do not exist and should not be parsed.
174
			'12 -1 12',
175
			'12 32 12',
176
			'12 2015 31',
177
			'31 2015 12',
178
179
			// Duplicate era.
180
			'year -2015-12-31 BC',
181
			'31.12.-2015 BC',
182
183
			// Zeros.
184
			'-2015-00-00',
185
			'0000-00-00',
186
			'2015-00-00',
187
			'2015-12-00',
188
			'0. 0. -2015',
189
			'0. 0. 0',
190
			'0. 0. 2015',
191
			'0. 12. 2015',
192
193
			// To long.
194
			'2015-12-031',
195
			'2015-012-31',
196
		);
197
198
		$cases = parent::invalidInputProvider();
199
200
		foreach ( $invalid as $value ) {
201
			$cases[] = array( $value );
202
		}
203
204
		return $cases;
205
	}
206
207
	/**
208
	 * @dataProvider monthNamesProvider
209
	 */
210
	public function testMonthNameParsing( $value, array $months, TimeValue $expected ) {
211
		$parser = $this->getYearMonthDayTimeParser( $months );
212
		$this->assertTrue( $expected->equals( $parser->parse( $value ) ) );
213
	}
214
215
	public function monthNamesProvider() {
216
		$gregorian = 'http://www.wikidata.org/entity/Q1985727';
217
218
		$valid = [
219
			'13.12.1999' => [
220
				[],
221
				'+1999-12-13T00:00:00Z',
222
			],
223
			'13. February 1999' => [
224
				[ 'February' => 2 ],
225
				'+1999-02-13T00:00:00Z',
226
			],
227
		];
228
229
		$cases = [];
230
231
		foreach ( $valid as $value => $args ) {
232
			$months = $args[0];
233
			$timestamp = $args[1];
234
			$calendarModel = isset( $args[2] ) ? $args[2] : $gregorian;
235
236
			$cases[] = [
237
				(string)$value,
238
				$months,
239
				new TimeValue( $timestamp, 0, 0, 0, TimeValue::PRECISION_DAY, $calendarModel )
0 ignored issues
show
Bug introduced by
It seems like $calendarModel defined by isset($args[2]) ? $args[2] : $gregorian on line 234 can also be of type array; 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...
240
			];
241
		}
242
243
		return $cases;
244
	}
245
246
	/**
247
	 * @dataProvider invalidMonthNamesProvider
248
	 */
249
	public function testInvalidMonthNameParsing( $value, array $months ) {
250
		$parser = $this->getYearMonthDayTimeParser( $months );
251
		$this->setExpectedException( 'ValueParsers\ParseException' );
252
		$parser->parse( $value );
253
	}
254
255
	public function invalidMonthNamesProvider() {
256
		return [
257
			[ '13. February 1999', [] ],
258
			[ 'February. February 1999', [ 'February' => 2 ] ],
259
			[ '13. Feb 1999', [ 'February' => 2 ] ],
260
		];
261
	}
262
263
	/**
264
	 * @dataProvider optionsProvider
265
	 */
266
	public function testOptions(
267
		$value,
268
		array $options,
269
		$timestamp,
270
		$calendarModel,
271
		$precision = TimeValue::PRECISION_DAY
272
	) {
273
		$parser = new YearMonthDayTimeParser( null, [], new ParserOptions( $options ) );
274
		$this->assertEquals(
275
			new TimeValue( $timestamp, 0, 0, 0, $precision, $calendarModel ),
276
			$parser->parse( $value )
277
		);
278
	}
279
280
	public function optionsProvider() {
281
		$gregorian = 'http://www.wikidata.org/entity/Q1985727';
282
		$julian = 'http://www.wikidata.org/entity/Q1985786';
283
284
		return array(
285
			'Auto-detected Gregorian' => array(
286
				'1583-01-31',
287
				array(),
288
				'+1583-01-31T00:00:00Z',
289
				$gregorian
290
			),
291
			'Option overrides auto-detected Gregorian' => array(
292
				'1583-01-31',
293
				array( IsoTimestampParser::OPT_CALENDAR => $julian ),
294
				'+1583-01-31T00:00:00Z',
295
				$julian
296
			),
297
			'Auto-detected Julian' => array(
298
				'1582-01-31',
299
				array(),
300
				'+1582-01-31T00:00:00Z',
301
				$julian
302
			),
303
			'Option overrides auto-detected Julian' => array(
304
				'1582-01-31',
305
				array( IsoTimestampParser::OPT_CALENDAR => $gregorian ),
306
				'+1582-01-31T00:00:00Z',
307
				$gregorian
308
			),
309
			'Option can decrease precision' => array(
310
				'2016-01-31',
311
				array( IsoTimestampParser::OPT_PRECISION => TimeValue::PRECISION_MONTH ),
312
				'+2016-01-31T00:00:00Z',
313
				$gregorian,
314
				TimeValue::PRECISION_MONTH
315
			),
316
			'Option can set minimal precision' => array(
317
				'2016-01-31',
318
				array( IsoTimestampParser::OPT_PRECISION => TimeValue::PRECISION_YEAR1G ),
319
				'+2016-01-31T00:00:00Z',
320
				$gregorian,
321
				TimeValue::PRECISION_YEAR1G
322
			),
323
			'Option can increase day precision' => array(
324
				'2016-01-31',
325
				array( IsoTimestampParser::OPT_PRECISION => TimeValue::PRECISION_HOUR ),
326
				'+2016-01-31T00:00:00Z',
327
				$gregorian,
328
				TimeValue::PRECISION_HOUR
329
			),
330
			'Precision option accepts strings' => array(
331
				'2016-01-31',
332
				array( IsoTimestampParser::OPT_PRECISION => '10' ),
333
				'+2016-01-31T00:00:00Z',
334
				$gregorian,
335
				TimeValue::PRECISION_MONTH
336
			),
337
		);
338
	}
339
340
	/**
341
	 * @dataProvider invalidOptionsProvider
342
	 */
343
	public function testInvalidOptions( array $options ) {
344
		$parser = new YearMonthDayTimeParser( null, [], new ParserOptions( $options ) );
345
		$this->setExpectedException( ParseException::class );
346
		$parser->parse( '2016-01-31' );
347
	}
348
349
	public function invalidOptionsProvider() {
350
		return array(
351
			array( array( IsoTimestampParser::OPT_PRECISION => -1 ) ),
352
			array( array( IsoTimestampParser::OPT_PRECISION => 1.5 ) ),
353
			array( array( IsoTimestampParser::OPT_PRECISION => 1000 ) ),
354
			array( array( IsoTimestampParser::OPT_PRECISION => 'invalid' ) ),
355
		);
356
	}
357
358
}
359