Completed
Pull Request — master (#66)
by Daniel
05:21 queued 02:25
created

QuantityValueTest   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 357
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 5

Importance

Changes 9
Bugs 1 Features 2
Metric Value
wmc 24
c 9
b 1
f 2
lcom 0
cbo 5
dl 0
loc 357
rs 10

24 Methods

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