|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace ValueFormatters\Test; |
|
4
|
|
|
|
|
5
|
|
|
use DataValues\QuantityValue; |
|
6
|
|
|
use DataValues\UnboundedQuantityValue; |
|
7
|
|
|
use PHPUnit\Framework\TestCase; |
|
8
|
|
|
use ValueFormatters\DecimalFormatter; |
|
9
|
|
|
use ValueFormatters\FormatterOptions; |
|
10
|
|
|
use ValueFormatters\QuantityFormatter; |
|
11
|
|
|
use ValueFormatters\ValueFormatter; |
|
12
|
|
|
|
|
13
|
|
|
/** |
|
14
|
|
|
* @covers \ValueFormatters\QuantityFormatter |
|
15
|
|
|
* |
|
16
|
|
|
* @group ValueFormatters |
|
17
|
|
|
* @group DataValueExtensions |
|
18
|
|
|
* |
|
19
|
|
|
* @license GPL-2.0-or-later |
|
20
|
|
|
* @author Daniel Kinzler |
|
21
|
|
|
*/ |
|
22
|
|
|
class QuantityFormatterTest extends TestCase { |
|
23
|
|
|
|
|
24
|
|
|
public function setUp() : void { |
|
25
|
|
|
if ( !\extension_loaded( 'bcmath' ) ) { |
|
26
|
|
|
$this->markTestSkipped( 'bcmath extension not loaded' ); |
|
27
|
|
|
} |
|
28
|
|
|
} |
|
29
|
|
|
|
|
30
|
|
|
/** |
|
31
|
|
|
* @see ValueFormatterTestBase::getInstance |
|
32
|
|
|
* |
|
33
|
|
|
* @param FormatterOptions|null $options |
|
34
|
|
|
* |
|
35
|
|
|
* @return QuantityFormatter |
|
36
|
|
|
*/ |
|
37
|
|
|
protected function getInstance( FormatterOptions $options = null ) { |
|
38
|
|
|
return $this->getQuantityFormatter( $options ); |
|
39
|
|
|
} |
|
40
|
|
|
|
|
41
|
|
|
/** |
|
42
|
|
|
* @param FormatterOptions|null $options |
|
43
|
|
|
* @param string|null $quantityWithUnitFormat |
|
44
|
|
|
* |
|
45
|
|
|
* @return QuantityFormatter |
|
46
|
|
|
*/ |
|
47
|
|
|
private function getQuantityFormatter( |
|
48
|
|
|
FormatterOptions $options = null, |
|
49
|
|
|
$quantityWithUnitFormat = null |
|
50
|
|
|
) { |
|
51
|
|
|
$vocabularyUriFormatter = $this->createMock( ValueFormatter::class ); |
|
52
|
|
|
$vocabularyUriFormatter->expects( $this->any() ) |
|
53
|
|
|
->method( 'format' ) |
|
54
|
|
|
->will( $this->returnCallback( function ( $unit ) { |
|
55
|
|
|
return $unit === '1' ? null : $unit; |
|
56
|
|
|
} ) ); |
|
57
|
|
|
|
|
58
|
|
|
return new QuantityFormatter( |
|
59
|
|
|
$options, |
|
60
|
|
|
new DecimalFormatter( $options ), |
|
61
|
|
|
$vocabularyUriFormatter, |
|
|
|
|
|
|
62
|
|
|
$quantityWithUnitFormat |
|
63
|
|
|
); |
|
64
|
|
|
} |
|
65
|
|
|
|
|
66
|
|
|
/** |
|
67
|
|
|
* @see ValueFormatterTestBase::validProvider |
|
68
|
|
|
*/ |
|
69
|
|
|
public function validProvider() { |
|
70
|
|
|
$noMargin = new FormatterOptions( [ |
|
71
|
|
|
QuantityFormatter::OPT_SHOW_UNCERTAINTY_MARGIN => false |
|
72
|
|
|
] ); |
|
73
|
|
|
|
|
74
|
|
|
$withMargin = new FormatterOptions( [ |
|
75
|
|
|
QuantityFormatter::OPT_SHOW_UNCERTAINTY_MARGIN => true |
|
76
|
|
|
] ); |
|
77
|
|
|
|
|
78
|
|
|
$noRounding = new FormatterOptions( [ |
|
79
|
|
|
QuantityFormatter::OPT_SHOW_UNCERTAINTY_MARGIN => true, |
|
80
|
|
|
QuantityFormatter::OPT_APPLY_ROUNDING => false |
|
81
|
|
|
] ); |
|
82
|
|
|
|
|
83
|
|
|
$exactRounding = new FormatterOptions( [ |
|
84
|
|
|
QuantityFormatter::OPT_SHOW_UNCERTAINTY_MARGIN => false, |
|
85
|
|
|
QuantityFormatter::OPT_APPLY_ROUNDING => -2 |
|
86
|
|
|
] ); |
|
87
|
|
|
|
|
88
|
|
|
$forceSign = new FormatterOptions( [ |
|
89
|
|
|
QuantityFormatter::OPT_SHOW_UNCERTAINTY_MARGIN => false, |
|
90
|
|
|
DecimalFormatter::OPT_FORCE_SIGN => true, |
|
91
|
|
|
] ); |
|
92
|
|
|
|
|
93
|
|
|
$noUnit = new FormatterOptions( [ |
|
94
|
|
|
QuantityFormatter::OPT_APPLY_UNIT => false, |
|
95
|
|
|
] ); |
|
96
|
|
|
|
|
97
|
|
|
return [ |
|
98
|
|
|
'+0/nm' => [ QuantityValue::newFromNumber( '+0', '1', '+0', '+0' ), '0', $noMargin ], |
|
99
|
|
|
'+0/wm' => [ QuantityValue::newFromNumber( '+0', '1', '+0', '+0' ), '0±0', $withMargin ], |
|
100
|
|
|
|
|
101
|
|
|
'+0.0/nm' => [ QuantityValue::newFromNumber( '+0.0', '°', '+0.1', '-0.1' ), '0.0 °', $noMargin ], |
|
102
|
|
|
'+0.0/wm' => [ QuantityValue::newFromNumber( '+0.0', '°', '+0.1', '-0.1' ), '0±0.1 °', $withMargin ], |
|
103
|
|
|
'+0.0/xr' => [ QuantityValue::newFromNumber( '+0.0', '°', '+0.1', '-0.1' ), '0.0 °', $exactRounding ], |
|
104
|
|
|
|
|
105
|
|
|
'-1205/nm' => [ QuantityValue::newFromNumber( '-1205', 'm', '-1105', '-1305' ), '-1200 m', $noMargin ], |
|
106
|
|
|
'-1205/wm' => [ |
|
107
|
|
|
QuantityValue::newFromNumber( '-1205', 'm', '-1105', '-1305' ), |
|
108
|
|
|
'-1205±100 m', |
|
109
|
|
|
$withMargin |
|
110
|
|
|
], |
|
111
|
|
|
'-1205/nr' => [ |
|
112
|
|
|
QuantityValue::newFromNumber( '-1205', 'm', '-1105', '-1305' ), |
|
113
|
|
|
'-1205±100 m', |
|
114
|
|
|
$noRounding |
|
115
|
|
|
], |
|
116
|
|
|
'-1205/xr' => [ |
|
117
|
|
|
QuantityValue::newFromNumber( '-1205', 'm', '-1105', '-1305' ), |
|
118
|
|
|
'-1205 m', |
|
119
|
|
|
$exactRounding |
|
120
|
|
|
], |
|
121
|
|
|
'-1205/nu' => [ QuantityValue::newFromNumber( '-1205', 'm', '-1105', '-1305' ), '-1205±100', $noUnit ], |
|
122
|
|
|
|
|
123
|
|
|
'+3.025/nm' => [ QuantityValue::newFromNumber( '+3.025', '1', '+3.02744', '+3.0211' ), '3.025', $noMargin ], |
|
124
|
|
|
'+3.025/wm' => [ |
|
125
|
|
|
QuantityValue::newFromNumber( '+3.025', '1', '+3.02744', '+3.0211' ), |
|
126
|
|
|
'3.025±0.0039', |
|
127
|
|
|
$withMargin |
|
128
|
|
|
], |
|
129
|
|
|
'+3.025/xr' => [ |
|
130
|
|
|
QuantityValue::newFromNumber( '+3.025', '1', '+3.02744', '+3.0211' ), |
|
131
|
|
|
'3.03', |
|
132
|
|
|
$exactRounding |
|
133
|
|
|
], |
|
134
|
|
|
'+3.125/nr' => [ |
|
135
|
|
|
QuantityValue::newFromNumber( '+3.125', '1', '+3.2', '+3.0' ), |
|
136
|
|
|
'3.125±0.125', |
|
137
|
|
|
$noRounding |
|
138
|
|
|
], |
|
139
|
|
|
'+3.125/xr' => [ QuantityValue::newFromNumber( '+3.125', '1', '+3.2', '+3.0' ), '3.13', $exactRounding ], |
|
140
|
|
|
|
|
141
|
|
|
'+3.125/fs' => [ QuantityValue::newFromNumber( '+3.125', '1', '+3.2', '+3.0' ), '+3.13', $forceSign ], |
|
142
|
|
|
|
|
143
|
|
|
// Unbounded quantities with different options |
|
144
|
|
|
'UB: +0.0/nm' => [ UnboundedQuantityValue::newFromNumber( '+0.0', '°' ), '0.0 °', $noMargin ], |
|
145
|
|
|
'UB: +0.0/wm' => [ UnboundedQuantityValue::newFromNumber( '+0.0', '°' ), '0.0 °', $withMargin ], |
|
146
|
|
|
'UB: +0.0/xr' => [ UnboundedQuantityValue::newFromNumber( '+0.0', '°' ), '0.0 °', $exactRounding ], |
|
147
|
|
|
'UB: +5.021/nm' => [ UnboundedQuantityValue::newFromNumber( '+5.021', '°' ), '5.021 °', $noMargin ], |
|
148
|
|
|
'UB: +5.021/wm' => [ UnboundedQuantityValue::newFromNumber( '+5.021', '°' ), '5.021 °', $withMargin ], |
|
149
|
|
|
'UB: +5.021/xr' => [ UnboundedQuantityValue::newFromNumber( '+5.021', '°' ), '5.02 °', $exactRounding ], |
|
150
|
|
|
'UB: +3.125/fs' => [ UnboundedQuantityValue::newFromNumber( '+3.125', '1' ), '+3.125', $forceSign ], |
|
151
|
|
|
|
|
152
|
|
|
// Unbounded quantities with enforced, exact rounding |
|
153
|
|
|
[ UnboundedQuantityValue::newFromNumber( '+0.00155', '1' ), '0.00', $exactRounding ], |
|
154
|
|
|
[ UnboundedQuantityValue::newFromNumber( '+0.0155', '1' ), '0.02', $exactRounding ], |
|
155
|
|
|
[ UnboundedQuantityValue::newFromNumber( '+0.155', '1' ), '0.16', $exactRounding ], |
|
156
|
|
|
[ UnboundedQuantityValue::newFromNumber( '+1.55', '1' ), '1.55', $exactRounding ], |
|
157
|
|
|
[ UnboundedQuantityValue::newFromNumber( '+15.5', '1' ), '15.5', $exactRounding ], |
|
158
|
|
|
[ UnboundedQuantityValue::newFromNumber( '+155', '1' ), '155', $exactRounding ], |
|
159
|
|
|
|
|
160
|
|
|
// Default options with different margins |
|
161
|
|
|
'24+-000.01' => [ QuantityValue::newFromNumber( '+24', '1', '+24.01', '+23.99' ), '24±0.01' ], |
|
162
|
|
|
'24+-000.10' => [ QuantityValue::newFromNumber( '+24', '1', '+24.1', '+23.9' ), '24±0.1' ], |
|
163
|
|
|
'24+-001.00' => [ QuantityValue::newFromNumber( '+24', '1', '+25', '+23' ), '24±1' ], |
|
164
|
|
|
'24+-010.00' => [ QuantityValue::newFromNumber( '+24', '1', '+34', '+14' ), '24±10' ], |
|
165
|
|
|
'24+-100.00' => [ QuantityValue::newFromNumber( '+24', '1', '+124', '-76' ), '24±100' ], |
|
166
|
|
|
|
|
167
|
|
|
// Rounding with a fixed +/-1 margin |
|
168
|
|
|
[ QuantityValue::newFromNumber( '+1.44', '1', '+2.44', '+0.44' ), '1', $noMargin ], |
|
169
|
|
|
[ QuantityValue::newFromNumber( '+1.45', '1', '+2.45', '+0.45' ), '1', $noMargin ], |
|
170
|
|
|
[ QuantityValue::newFromNumber( '+1.49', '1', '+2.49', '+0.49' ), '1', $noMargin ], |
|
171
|
|
|
[ QuantityValue::newFromNumber( '+1.50', '1', '+2.50', '+0.50' ), '2', $noMargin ], |
|
172
|
|
|
[ QuantityValue::newFromNumber( '+2.50', '1', '+3.50', '+1.50' ), '3', $noMargin ], |
|
173
|
|
|
|
|
174
|
|
|
// Rounding with different margins |
|
175
|
|
|
'1.55+/-0.09' => [ QuantityValue::newFromNumber( '+1.55', '1', '+1.64', '+1.46' ), '1.55', $noMargin ], |
|
176
|
|
|
'1.55+/-0.1' => [ QuantityValue::newFromNumber( '+1.55', '1', '+1.65', '+1.45' ), '1.6', $noMargin ], |
|
177
|
|
|
'1.55+/-0.49' => [ QuantityValue::newFromNumber( '+1.55', '1', '+2.04', '+1.06' ), '1.6', $noMargin ], |
|
178
|
|
|
'1.55+/-0.5' => [ QuantityValue::newFromNumber( '+1.55', '1', '+2.05', '+1.05' ), '1.6', $noMargin ], |
|
179
|
|
|
'1.55+/-0.99' => [ QuantityValue::newFromNumber( '+1.55', '1', '+2.54', '+0.56' ), '1.6', $noMargin ], |
|
180
|
|
|
'1.55+/-1' => [ QuantityValue::newFromNumber( '+1.55', '1', '+2.55', '+0.55' ), '2', $noMargin ], |
|
181
|
|
|
// FIXME: We should probably never round to zero as it is confusing. |
|
182
|
|
|
'1.55+/-10' => [ QuantityValue::newFromNumber( '+1.55', '1', '+11.55', '-8.45' ), '0', $noMargin ], |
|
183
|
|
|
|
|
184
|
|
|
// Do not mess with the value when the margin is rendered |
|
185
|
|
|
[ QuantityValue::newFromNumber( '+1500', '1', '+2500', '+500' ), '1500±1000' ], |
|
186
|
|
|
[ QuantityValue::newFromNumber( '+2', '1', '+2.005', '+1.995' ), '2±0.005' ], |
|
187
|
|
|
[ QuantityValue::newFromNumber( '+1.5', '1', '+2.5', '+0.5' ), '1.5±1' ], |
|
188
|
|
|
[ QuantityValue::newFromNumber( '+1.0005', '1', '+1.0015', '+0.9995' ), '1.0005±0.001' ], |
|
189
|
|
|
[ QuantityValue::newFromNumber( '+0.0015', '1', '+0.0025', '+0.0005' ), '0.0015±0.001' ], |
|
190
|
|
|
|
|
191
|
|
|
/** |
|
192
|
|
|
* Never mess with the margin |
|
193
|
|
|
* @see https://phabricator.wikimedia.org/T58892 |
|
194
|
|
|
*/ |
|
195
|
|
|
[ QuantityValue::newFromNumber( '+2', '1', '+3.5', '+0.5' ), '2±1.5' ], |
|
196
|
|
|
[ QuantityValue::newFromNumber( '+2', '1', '+2.016', '+1.984' ), '2±0.016' ], |
|
197
|
|
|
[ QuantityValue::newFromNumber( '+2', '1', '+2.0015', '+1.9985' ), '2±0.0015' ], |
|
198
|
|
|
[ QuantityValue::newFromNumber( '+0.0015', '1', '+0.003', '+0' ), '0.0015±0.0015' ], |
|
199
|
|
|
[ QuantityValue::newFromNumber( '+2.0011', '1', '+2.0022', '+2' ), '2.0011±0.0011' ], |
|
200
|
|
|
[ QuantityValue::newFromNumber( '+2.0099', '1', '+2.0198', '+2' ), '2.0099±0.0099' ], |
|
201
|
|
|
|
|
202
|
|
|
// IEEE edge cases |
|
203
|
|
|
[ |
|
204
|
|
|
QuantityValue::newFromNumber( |
|
205
|
|
|
'+1.00000000000000015', |
|
206
|
|
|
'1', |
|
207
|
|
|
'+1.00000000000000025', |
|
208
|
|
|
'+1.00000000000000005' |
|
209
|
|
|
), |
|
210
|
|
|
'1.00000000000000015±0.0000000000000001' |
|
211
|
|
|
], |
|
212
|
|
|
'0.2 / 3 * 3' => [ |
|
213
|
|
|
QuantityValue::newFromNumber( |
|
214
|
|
|
'+0.2000000000000000111', |
|
215
|
|
|
'1', |
|
216
|
|
|
'+0.2000000000000000111', |
|
217
|
|
|
'+0.2000000000000000111' |
|
218
|
|
|
), |
|
219
|
|
|
'0.2000000000000000111±0' |
|
220
|
|
|
], |
|
221
|
|
|
'8 - 6.4' => [ |
|
222
|
|
|
QuantityValue::newFromNumber( |
|
223
|
|
|
'+1.59999999999999964473', |
|
224
|
|
|
'1', |
|
225
|
|
|
'+1.59999999999999964473', |
|
226
|
|
|
'+1.59999999999999964473' |
|
227
|
|
|
), |
|
228
|
|
|
'1.59999999999999964473±0' |
|
229
|
|
|
], |
|
230
|
|
|
]; |
|
231
|
|
|
} |
|
232
|
|
|
|
|
233
|
|
|
public function testFormatWithFormatString() { |
|
234
|
|
|
$formatter = $this->getQuantityFormatter( null, '<$2>$1' ); |
|
235
|
|
|
$value = UnboundedQuantityValue::newFromNumber( '+5', 'USD' ); |
|
236
|
|
|
$formatted = $formatter->format( $value ); |
|
237
|
|
|
$this->assertSame( '<USD>5', $formatted ); |
|
238
|
|
|
} |
|
239
|
|
|
|
|
240
|
|
|
} |
|
241
|
|
|
|
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: