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