Completed
Push — master ( a25e2d...f3d5e6 )
by Daniel
24s
created

validConstructorArgumentsProvider()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 1
eloc 6
nc 1
nop 0
1
<?php
2
3
namespace DataValues\Tests;
4
5
use DataValues\DecimalValue;
6
use DataValues\QuantityValue;
7
use DataValues\UnboundedQuantityValue;
8
9
/**
10
 * @covers DataValues\QuantityValue
11
 *
12
 * @group DataValue
13
 * @group DataValueExtensions
14
 *
15
 * @license GPL-2.0+
16
 * @author Daniel Kinzler
17
 */
18
class QuantityValueTest extends DataValueTest {
19
20
	/**
21
	 * @see DataValueTest::getClass
22
	 *
23
	 * @return string
24
	 */
25
	public function getClass() {
26
		return 'DataValues\QuantityValue';
27
	}
28
29
	public function validConstructorArgumentsProvider() {
30
		$argLists = array();
31
32
		$argLists[] = array( new DecimalValue( '+42' ), '1', new DecimalValue( '+42' ), new DecimalValue( '+42' ) );
33
		$argLists[] = array( new DecimalValue( '+0.01' ), '1', new DecimalValue( '+0.02' ), new DecimalValue( '+0.0001' ) );
34
		$argLists[] = array( new DecimalValue( '-0.5' ), '1', new DecimalValue( '+0.02' ), new DecimalValue( '-0.7' ) );
35
36
		return $argLists;
37
	}
38
39
	public function invalidConstructorArgumentsProvider() {
40
		$argLists = array();
41
42
		$argLists[] = array( new DecimalValue( '+0' ), '', new DecimalValue( '+0' ), new DecimalValue( '+0' ) );
43
		$argLists[] = array( new DecimalValue( '+0' ), 1, new DecimalValue( '+0' ), new DecimalValue( '+0' ) );
44
45
		$argLists[] = array( new DecimalValue( '+0' ), '1', new DecimalValue( '-0.001' ), new DecimalValue( '-1' ) );
46
		$argLists[] = array( new DecimalValue( '+0' ), '1', new DecimalValue( '+1' ), new DecimalValue( '+0.001' ) );
47
48
		return $argLists;
49
	}
50
51
	/**
52
	 * @dataProvider instanceProvider
53
	 */
54
	public function testGetValue( QuantityValue $quantity, array $arguments ) {
55
		$this->assertSame( $quantity, $quantity->getValue() );
56
	}
57
58
	/**
59
	 * @dataProvider instanceProvider
60
	 */
61
	public function testGetAmount( QuantityValue $quantity, array $arguments ) {
62
		$this->assertSame( $arguments[0], $quantity->getAmount() );
63
	}
64
65
	/**
66
	 * @dataProvider instanceProvider
67
	 */
68
	public function testGetUnit( QuantityValue $quantity, array $arguments ) {
69
		$this->assertSame( $arguments[1], $quantity->getUnit() );
70
	}
71
72
	/**
73
	 * @dataProvider instanceProvider
74
	 */
75
	public function testGetUpperBound( QuantityValue $quantity, array $arguments ) {
76
		$this->assertSame( $arguments[2], $quantity->getUpperBound() );
77
	}
78
79
	/**
80
	 * @dataProvider instanceProvider
81
	 */
82
	public function testGetLowerBound( QuantityValue $quantity, array $arguments ) {
83
		$this->assertSame( $arguments[3], $quantity->getLowerBound() );
84
	}
85
86
	/**
87
	 * @dataProvider newFromNumberProvider
88
	 */
89
	public function testNewFromNumber( $amount, $unit, $upperBound, $lowerBound, QuantityValue $expected ) {
90
		$quantity = QuantityValue::newFromNumber( $amount, $unit, $upperBound, $lowerBound );
91
92
		$this->assertEquals( $expected->getAmount()->getValue(), $quantity->getAmount()->getValue() );
93
		$this->assertEquals( $expected->getUpperBound()->getValue(), $quantity->getUpperBound()->getValue() );
94
		$this->assertEquals( $expected->getLowerBound()->getValue(), $quantity->getLowerBound()->getValue() );
95
	}
96
97
	public function newFromNumberProvider() {
98
		return array(
99
			array(
100
				42, '1', null, null,
101
				new QuantityValue( new DecimalValue( '+42' ), '1', new DecimalValue( '+42' ), new DecimalValue( '+42' ) )
102
			),
103
			array(
104
				-0.05, '1', null, null,
105
				new QuantityValue( new DecimalValue( '-0.05' ), '1', new DecimalValue( '-0.05' ), new DecimalValue( '-0.05' ) )
106
			),
107
			array(
108
				0, 'm', 0.5, -0.5,
109
				new QuantityValue( new DecimalValue( '+0' ), 'm', new DecimalValue( '+0.5' ), new DecimalValue( '-0.5' ) )
110
			),
111
			array(
112
				'+23', '1', null, null,
113
				new QuantityValue( new DecimalValue( '+23' ), '1', new DecimalValue( '+23' ), new DecimalValue( '+23' ) )
114
			),
115
			array(
116
				'+42', '1', '+43', '+41',
117
				new QuantityValue( new DecimalValue( '+42' ), '1', new DecimalValue( '+43' ), new DecimalValue( '+41' ) )
118
			),
119
			array(
120
				'-0.05', 'm', '-0.04', '-0.06',
121
				new QuantityValue( new DecimalValue( '-0.05' ), 'm', new DecimalValue( '-0.04' ), new DecimalValue( '-0.06' ) )
122
			),
123
			array(
124
				new DecimalValue( '+42' ), '1', new DecimalValue( 43 ), new DecimalValue( 41.0 ),
125
				new QuantityValue( new DecimalValue( '+42' ), '1', new DecimalValue( 43 ), new DecimalValue( 41.0 ) )
126
			),
127
		);
128
	}
129
130
	/**
131
	 * @dataProvider validArraySerializationProvider
132
	 */
133
	public function testNewFromArray( $data, UnboundedQuantityValue $expected ) {
134
		$value = QuantityValue::newFromArray( $data );
135
		$this->assertTrue( $expected->equals( $value ), $value . ' should equal ' . $expected );
136
	}
137
138
	public function validArraySerializationProvider() {
139
		return array(
140
			'complete' => array(
141
				array(
142
					'amount' => '+2',
143
					'unit' => '1',
144
					'upperBound' => '+2.5',
145
					'lowerBound' => '+1.5',
146
				),
147
				QuantityValue::newFromNumber( '+2', '1', '+2.5', '+1.5' )
148
			),
149
			'unbounded' => array(
150
				array(
151
					'amount' => '+2',
152
					'unit' => '1',
153
				),
154
				UnboundedQuantityValue::newFromNumber( '+2', '1' )
155
			),
156
			'unbounded with existing array keys' => array(
157
				array(
158
					'amount' => '+2',
159
					'unit' => '1',
160
					'upperBound' => null,
161
					'lowerBound' => null,
162
				),
163
				UnboundedQuantityValue::newFromNumber( '+2', '1' )
164
			),
165
		);
166
	}
167
168
	/**
169
	 * @dataProvider invalidArraySerializationProvider
170
	 */
171
	public function testNewFromArray_failure( $data ) {
172
		$this->setExpectedException( 'DataValues\IllegalValueException' );
173
		QuantityValue::newFromArray( $data );
174
	}
175
176
	public function invalidArraySerializationProvider() {
177
		return array(
178
			'no-amount' => array(
179
				array(
180
					'unit' => '1',
181
					'upperBound' => '+2.5',
182
					'lowerBound' => '+1.5',
183
				)
184
			),
185
			'no-unit' => array(
186
				array(
187
					'amount' => '+2',
188
					'upperBound' => '+2.5',
189
					'lowerBound' => '+1.5',
190
				)
191
			),
192
			'no-upperBound' => array(
193
				array(
194
					'amount' => '+2',
195
					'unit' => '1',
196
					'lowerBound' => '+1.5',
197
				)
198
			),
199
			'no-lowerBound' => array(
200
				array(
201
					'amount' => '+2',
202
					'unit' => '1',
203
					'upperBound' => '+2.5',
204
				)
205
			),
206
			'bad-amount' => array(
207
				array(
208
					'amount' => 'x',
209
					'unit' => '1',
210
					'upperBound' => '+2.5',
211
					'lowerBound' => '+1.5',
212
				)
213
			),
214
			'bad-upperBound' => array(
215
				array(
216
					'amount' => '+2',
217
					'unit' => '1',
218
					'upperBound' => 'x',
219
					'lowerBound' => '+1.5',
220
				)
221
			),
222
			'bad-lowerBound' => array(
223
				array(
224
					'amount' => '+2',
225
					'unit' => '1',
226
					'upperBound' => '+2.5',
227
					'lowerBound' => 'x',
228
				)
229
			),
230
		);
231
	}
232
233
	/**
234
	 * @see https://phabricator.wikimedia.org/T110728
235
	 * @see http://www.regular-expressions.info/anchors.html#realend
236
	 */
237
	public function testTrailingNewlineRobustness() {
238
		$value = QuantityValue::newFromArray( array(
239
			'amount' => "-0.0\n",
240
			'unit' => "1\n",
241
			'upperBound' => "-0.0\n",
242
			'lowerBound' => "-0.0\n",
243
		) );
244
245
		$this->assertSame( array(
246
			'amount' => '+0.0',
247
			'unit' => "1\n",
248
			'upperBound' => '+0.0',
249
			'lowerBound' => '+0.0',
250
		), $value->getArrayValue() );
251
	}
252
253
	/**
254
	 * @dataProvider instanceProvider
255
	 */
256
	public function testGetSortKey( QuantityValue $quantity ) {
257
		$this->assertSame( $quantity->getAmount()->getValueFloat(), $quantity->getSortKey() );
258
	}
259
260
	/**
261
	 * @dataProvider getUncertaintyProvider
262
	 */
263
	public function testGetUncertainty( QuantityValue $quantity, $expected ) {
264
		$this->assertSame( $expected, $quantity->getUncertainty() );
265
	}
266
267
	public function getUncertaintyProvider() {
268
		return array(
269
			array( QuantityValue::newFromNumber( '+0', '1', '+0', '+0' ), 0.0 ),
270
271
			array( QuantityValue::newFromNumber( '+0', '1', '+1', '-1' ), 2.0 ),
272
			array( QuantityValue::newFromNumber( '+0.00', '1', '+0.01', '-0.01' ), 0.02 ),
273
			array( QuantityValue::newFromNumber( '+100', '1', '+101', '+99' ), 2.0 ),
274
			array( QuantityValue::newFromNumber( '+100.0', '1', '+100.1', '+99.9' ), 0.2 ),
275
			array( QuantityValue::newFromNumber( '+12.34', '1', '+12.35', '+12.33' ), 0.02 ),
276
277
			array( QuantityValue::newFromNumber( '+0', '1', '+0.2', '-0.6' ), 0.8 ),
278
			array( QuantityValue::newFromNumber( '+7.3', '1', '+7.7', '+5.2' ), 2.5 ),
279
		);
280
	}
281
282
	/**
283
	 * @dataProvider getUncertaintyMarginProvider
284
	 */
285
	public function testGetUncertaintyMargin( QuantityValue $quantity, $expected ) {
286
		$this->assertSame( $expected, $quantity->getUncertaintyMargin()->getValue() );
287
	}
288
289
	public function getUncertaintyMarginProvider() {
290
		return array(
291
			array( QuantityValue::newFromNumber( '+0', '1', '+1', '-1' ), '+1' ),
292
			array( QuantityValue::newFromNumber( '+0.00', '1', '+0.01', '-0.01' ), '+0.01' ),
293
294
			array( QuantityValue::newFromNumber( '-1', '1', '-1', '-1' ), '+0' ),
295
296
			array( QuantityValue::newFromNumber( '+0', '1', '+0.2', '-0.6' ), '+0.6' ),
297
			array( QuantityValue::newFromNumber( '+7.5', '1', '+7.5', '+5.5' ), '+2.0' ),
298
			array( QuantityValue::newFromNumber( '+11.5', '1', '+15', '+10.5' ), '+3.5' ),
299
		);
300
	}
301
302
	/**
303
	 * @dataProvider getOrderOfUncertaintyProvider
304
	 */
305
	public function testGetOrderOfUncertainty( QuantityValue $quantity, $expected ) {
306
		$this->assertSame( $expected, $quantity->getOrderOfUncertainty() );
307
	}
308
309
	public function getOrderOfUncertaintyProvider() {
310
		return array(
311
			0 => array( QuantityValue::newFromNumber( '+0' ), 0 ),
312
			1 => array( QuantityValue::newFromNumber( '-123' ), 0 ),
313
			2 => array( QuantityValue::newFromNumber( '-1.23' ), -2 ),
314
315
			10 => array( QuantityValue::newFromNumber( '-100', '1', '-99', '-101' ), 0 ),
316
			11 => array( QuantityValue::newFromNumber( '+0.00', '1', '+0.01', '-0.01' ), -2 ),
317
			12 => array( QuantityValue::newFromNumber( '-117.3', '1', '-117.2', '-117.4' ), -1 ),
318
319
			20 => array( QuantityValue::newFromNumber( '+100', '1', '+100.01', '+99.97' ), -2 ),
320
			21 => array( QuantityValue::newFromNumber( '-0.002', '1', '-0.001', '-0.004' ), -3 ),
321
			22 => array( QuantityValue::newFromNumber( '-0.002', '1', '+0.001', '-0.06' ), -3 ),
322
			23 => array( QuantityValue::newFromNumber( '-21', '1', '+1.1', '-120' ), 1 ),
323
			24 => array( QuantityValue::newFromNumber( '-2', '1', '+1.1', '-120' ), 0 ),
324
			25 => array( QuantityValue::newFromNumber( '+1000', '1', '+1100', '+900.03' ), 1 ),
325
			26 => array( QuantityValue::newFromNumber( '+1000', '1', '+1100', '+900' ), 2 ),
326
		);
327
	}
328
329
	/**
330
	 * @dataProvider transformProvider
331
	 */
332
	public function testTransform( QuantityValue $quantity, $transformation, QuantityValue $expected ) {
333
		$args = func_get_args();
334
		$extraArgs = array_slice( $args, 3 );
335
336
		$call = array( $quantity, 'transform' );
337
		$callArgs = array_merge( array( 'x', $transformation ), $extraArgs );
338
		$actual = call_user_func_array( $call, $callArgs );
339
340
		$this->assertSame( 'x', $actual->getUnit() );
341
		$this->assertEquals( $expected->getAmount()->getValue(), $actual->getAmount()->getValue(), 'value' );
342
		$this->assertEquals( $expected->getUpperBound()->getValue(), $actual->getUpperBound()->getValue(), 'upper bound' );
343
		$this->assertEquals( $expected->getLowerBound()->getValue(), $actual->getLowerBound()->getValue(), 'lower bound' );
344
	}
345
346
	public function transformProvider() {
347
		$identity = function ( DecimalValue $value ) {
348
			return $value;
349
		};
350
351
		$square = function ( DecimalValue $value ) {
352
			$v = $value->getValueFloat();
353
			return new DecimalValue( $v * $v * $v );
354
		};
355
356
		$scale = function ( DecimalValue $value, $factor ) {
357
			return new DecimalValue( $value->getValueFloat() * $factor );
358
		};
359
360
		return array(
361
			0 => array(
362
				QuantityValue::newFromNumber( '+10', '1', '+11', '+9' ),
363
				$identity,
364
				QuantityValue::newFromNumber( '+10', '?', '+11', '+9' )
365
			),
366
			1 => array(
367
				QuantityValue::newFromNumber( '-0.5', '1', '-0.4', '-0.6' ),
368
				$identity,
369
				QuantityValue::newFromNumber( '-0.5', '?', '-0.4', '-0.6' )
370
			),
371
			2 => array(
372
				QuantityValue::newFromNumber( '+0', '1', '+1', '-1' ),
373
				$square,
374
				QuantityValue::newFromNumber( '+0', '?', '+1', '-1' )
375
			),
376
			3 => array(
377
				QuantityValue::newFromNumber( '+10', '1', '+11', '+9' ),
378
				$square,
379
				// note how rounding applies to bounds
380
				QuantityValue::newFromNumber( '+1000', '?', '+1300', '+700' )
381
			),
382
			4 => array(
383
				QuantityValue::newFromNumber( '+0.5', '1', '+0.6', '+0.4' ),
384
				$scale,
385
				QuantityValue::newFromNumber( '+0.25', '?', '+0.30', '+0.20' ),
386
				0.5
387
			),
388
389
			// note: absolutely exact values require conversion with infinite precision!
390
			10 => array(
391
				QuantityValue::newFromNumber( '+100', '1', '+100', '+100' ),
392
				$scale,
393
				QuantityValue::newFromNumber( '+12825', '?', '+12825', '+12825' ),
394
				128.25
395
			),
396
397
			11 => array(
398
				QuantityValue::newFromNumber( '+100', '1', '+110', '+90' ),
399
				$scale,
400
				QuantityValue::newFromNumber( '+330', '?', '+370', '+300' ),
401
				3.3333
402
			),
403
			12 => array(
404
				QuantityValue::newFromNumber( '+100', '1', '+100.1', '+99.9' ),
405
				$scale,
406
				QuantityValue::newFromNumber( '+333.3', '?', '+333.7', '+333.0' ),
407
				3.3333
408
			),
409
			13 => array(
410
				QuantityValue::newFromNumber( '+100', '1', '+100.01', '+99.99' ),
411
				$scale,
412
				QuantityValue::newFromNumber( '+333.33', '?', '+333.36', '+333.30' ),
413
				3.3333
414
			),
415
		);
416
	}
417
418
}
419