testGivenOneEuro_getEuroStringReturnsOnePointZeroZero()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace WMDE\Euro\Tests\Unit;
6
7
use InvalidArgumentException;
8
use PHPUnit\Framework\TestCase;
9
use WMDE\Euro\Euro;
10
11
/**
12
 * @covers \WMDE\Euro\Euro
13
 *
14
 * @license GPL-2.0-or-later
15
 * @author Jeroen De Dauw < [email protected] >
16
 */
17
class EuroTest extends TestCase {
18
19
	/**
20
	 * @dataProvider unsignedIntegerProvider
21
	 */
22
	public function testGetCentsReturnsConstructorArgument( int $unsignedInteger ) {
23
		$amount = Euro::newFromCents( $unsignedInteger );
24
		$this->assertSame( $unsignedInteger, $amount->getEuroCents() );
25
	}
26
27
	public function unsignedIntegerProvider() {
28
		return [
29
			[ 0 ], [ 1 ], [ 2 ], [ 9 ], [ 10 ], [ 11 ],
30
			[ 99 ], [ 100 ], [ 101 ], [ 999 ], [ 1000 ], [ 1001 ],
31
		];
32
	}
33
34
	public function testGivenZero_getEuroFloatReturnsZeroFloat() {
35
		$amount = Euro::newFromCents( 0 );
36
		$this->assertExactFloat( 0.0, $amount->getEuroFloat() );
37
		$this->assertNotSame( 0, $amount->getEuroFloat() );
38
	}
39
40
	private function assertExactFloat( $expected, $actual ) {
41
		$this->assertIsFloat( $actual );
42
		$this->assertEquals( $expected, $actual, '', 0 );
0 ignored issues
show
Unused Code introduced by
The call to PHPUnit\Framework\Assert::assertEquals() has too many arguments starting with 0. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

42
		$this->/** @scrutinizer ignore-call */ 
43
         assertEquals( $expected, $actual, '', 0 );

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
43
	}
44
45
	public function testGivenOneEuro_getEuroFloatReturnsOne() {
46
		$amount = Euro::newFromCents( 100 );
47
		$this->assertExactFloat( 1.0, $amount->getEuroFloat() );
48
	}
49
50
	public function testGivenOneCent_getEuroFloatReturnsPointZeroOne() {
51
		$amount = Euro::newFromCents( 1 );
52
		$this->assertExactFloat( 0.01, $amount->getEuroFloat() );
53
	}
54
55
	public function testGiven33cents_getEuroFloatReturnsPointThreeThree() {
56
		$amount = Euro::newFromCents( 33 );
57
		$this->assertExactFloat( 0.33, $amount->getEuroFloat() );
58
	}
59
60
	/**
61
	 * @dataProvider getEurosDataProvider
62
	 */
63
	public function testGetEurosReturnsCorrectValues( int $cents, int $expectedEuros ) {
64
		$amount = Euro::newFromCents( $cents );
65
		$this->assertEquals( $expectedEuros, $amount->getEuros() );
66
	}
67
68
	public function getEurosDataProvider(): array {
69
		return [
70
			[ 0, 0 ],
71
			[ 3, 0 ],
72
			[ 102, 1 ],
73
			[ 149, 1 ],
74
			[ 150, 1 ],
75
			[ 151, 1 ],
76
			[ 199, 1 ],
77
			[ 555, 5 ],
78
			[ 1033, 10 ],
79
			[ 9999, 99 ],
80
		];
81
	}
82
83
	public function testGivenNegativeAmount_constructorThrowsException() {
84
		$this->expectException( \InvalidArgumentException::class );
85
		Euro::newFromCents( -1 );
86
	}
87
88
	public function testGivenZero_getEuroStringReturnsZeroString() {
89
		$amount = Euro::newFromCents( 0 );
90
		$this->assertSame( '0.00', $amount->getEuroString() );
91
	}
92
93
	public function testGivenOneEuro_getEuroStringReturnsOnePointZeroZero() {
94
		$amount = Euro::newFromCents( 100 );
95
		$this->assertSame( '1.00', $amount->getEuroString() );
96
	}
97
98
	public function testGivenTwoEuros_getEuroStringReturnsTwoPointZeroZero() {
99
		$amount = Euro::newFromCents( 200 );
100
		$this->assertSame( '2.00', $amount->getEuroString() );
101
	}
102
103
	public function testGivenOneCent_getEuroStringReturnsZeroPointZeroOne() {
104
		$amount = Euro::newFromCents( 1 );
105
		$this->assertSame( '0.01', $amount->getEuroString() );
106
	}
107
108
	public function testGivenTenCents_getEuroStringReturnsZeroPointOneZero() {
109
		$amount = Euro::newFromCents( 10 );
110
		$this->assertSame( '0.10', $amount->getEuroString() );
111
	}
112
113
	public function testGiven1234Cents_getEuroStringReturns12euro34() {
114
		$amount = Euro::newFromCents( 1234 );
115
		$this->assertSame( '12.34', $amount->getEuroString() );
116
	}
117
118
	public function testGiven9876Cents_stringCastingReturns98euro76() {
119
		$amount = Euro::newFromCents( 9876 );
120
		$this->assertSame( '98.76', (string)$amount );
121
	}
122
123
	public function testGivenEuroAmount_jsonEncodeWillEncodeProperly() {
124
		$amount = Euro::newFromCents( 9876 );
125
		$this->assertSame( '"98.76"', json_encode( $amount ) );
126
	}
127
128
	public function testOneEuroString_getsTurnedInto100cents() {
129
		$this->assertSame( 100, Euro::newFromString( '1.00' )->getEuroCents() );
130
	}
131
132
	public function testOneCentString_getsTurnedInto1cents() {
133
		$this->assertSame( 1, Euro::newFromString( '0.01' )->getEuroCents() );
134
	}
135
136
	public function testTenCentString_getsTurnedInto10cents() {
137
		$this->assertSame( 10, Euro::newFromString( '0.10' )->getEuroCents() );
138
	}
139
140
	public function testShortTenCentString_getsTurnedInto10cents() {
141
		$this->assertSame( 10, Euro::newFromString( '0.1' )->getEuroCents() );
142
	}
143
144
	public function testShortOneEuroString_getsTurnedInto100cents() {
145
		$this->assertSame( 100, Euro::newFromString( '1' )->getEuroCents() );
146
	}
147
148
	public function testOneDecimalOneEuroString_getsTurnedInto100cents() {
149
		$this->assertSame( 100, Euro::newFromString( '1.0' )->getEuroCents() );
150
	}
151
152
	public function testMultiDecimalOneEuroString_getsTurnedInto100cents() {
153
		$this->assertSame( 100, Euro::newFromString( '1.00000' )->getEuroCents() );
154
	}
155
156
	public function testHandlingOfLargeEuroString() {
157
		$this->assertSame( 3133742, Euro::newFromString( '31337.42' )->getEuroCents() );
158
	}
159
160
	public function testEuroStringThatCausedRoundingError_doesNotCauseRoundingError() {
161
		// Regression test for https://phabricator.wikimedia.org/T183481
162
		$this->assertSame( 870, Euro::newFromString( '8.70' )->getEuroCents() );
163
		$this->assertSame( 920, Euro::newFromString( '9.20' )->getEuroCents() );
164
	}
165
166
	public function testEuroStringWithRoundingError_getsRoundedAppropriately() {
167
		$this->assertSame( 101, Euro::newFromString( '1.0100000001' )->getEuroCents() );
168
		$this->assertSame( 101, Euro::newFromString( '1.010000009999' )->getEuroCents() );
169
		$this->assertSame( 101, Euro::newFromString( '1.011' )->getEuroCents() );
170
		$this->assertSame( 101, Euro::newFromString( '1.014' )->getEuroCents() );
171
		$this->assertSame( 101, Euro::newFromString( '1.0149' )->getEuroCents() );
172
		$this->assertSame( 102, Euro::newFromString( '1.015' )->getEuroCents() );
173
		$this->assertSame( 102, Euro::newFromString( '1.019' )->getEuroCents() );
174
		$this->assertSame( 102, Euro::newFromString( '1.0199999' )->getEuroCents() );
175
		$this->assertSame( 870, Euro::newFromString( '8.701' )->getEuroCents() );
176
		$this->assertSame( 870, Euro::newFromString( '8.70499' )->getEuroCents() );
177
		$this->assertSame( 871, Euro::newFromString( '8.705' )->getEuroCents() );
178
		$this->assertSame( 871, Euro::newFromString( '8.705000' )->getEuroCents() );
179
		$this->assertSame( 871, Euro::newFromString( '8.705001' )->getEuroCents() );
180
		$this->assertSame( 871, Euro::newFromString( '8.709999' )->getEuroCents() );
181
	}
182
183
	public function testGivenNegativeAmountString_exceptionIsThrown() {
184
		$this->expectException( \InvalidArgumentException::class );
185
		Euro::newFromString( '-1.00' );
186
	}
187
188
	public function testGivenStringWithComma_exceptionIsThrown() {
189
		$this->expectException( \InvalidArgumentException::class );
190
		Euro::newFromString( '1,00' );
191
	}
192
193
	public function testGivenStringWithMultipleDots_ExceptionIsThrown() {
194
		$this->expectException( \InvalidArgumentException::class );
195
		Euro::newFromString( '1.0.0' );
196
	}
197
198
	public function testGivenNonNumber_exceptionIsThrown() {
199
		$this->expectException( \InvalidArgumentException::class );
200
		Euro::newFromString( '1.00abc' );
201
	}
202
203
	public function testGivenNegativeFloatAmount_exceptionIsThrown() {
204
		$this->expectException( \InvalidArgumentException::class );
205
		Euro::newFromFloat( -1.00 );
206
	}
207
208
	public function testOneEuroFloat_getsTurnedInto100cents() {
209
		$this->assertSame( 100, Euro::newFromFloat( 1.0 )->getEuroCents() );
210
	}
211
212
	public function testOneCentFloat_getsTurnedInto1cent() {
213
		$this->assertSame( 1, Euro::newFromFloat( 0.01 )->getEuroCents() );
214
	}
215
216
	public function testTenCentFloat_getsTurnedInto10cents() {
217
		$this->assertSame( 10, Euro::newFromFloat( 0.1 )->getEuroCents() );
218
	}
219
220
	public function testHandlingOfLargeEuroFloat() {
221
		$this->assertSame( 3133742, Euro::newFromFloat( 31337.42 )->getEuroCents() );
222
	}
223
224
	public function testFloatWithRoundingError_getsRoundedAppropriately() {
225
		$this->assertSame( 101, Euro::newFromFloat( 1.0100000001 )->getEuroCents() );
226
		$this->assertSame( 101, Euro::newFromFloat( 1.010000009999 )->getEuroCents() );
227
		$this->assertSame( 101, Euro::newFromFloat( 1.011 )->getEuroCents() );
228
		$this->assertSame( 101, Euro::newFromFloat( 1.014 )->getEuroCents() );
229
		$this->assertSame( 101, Euro::newFromFloat( 1.0149 )->getEuroCents() );
230
		$this->assertSame( 102, Euro::newFromFloat( 1.015 )->getEuroCents() );
231
		$this->assertSame( 102, Euro::newFromFloat( 1.019 )->getEuroCents() );
232
		$this->assertSame( 102, Euro::newFromFloat( 1.0199999 )->getEuroCents() );
233
		$this->assertSame( 870, Euro::newFromFloat( 8.70 )->getEuroCents() );
234
	}
235
236
	public function testZeroEuroIntegers_isZeroCents() {
237
		$this->assertSame( 0, Euro::newFromInt( 0 )->getEuroCents() );
238
	}
239
240
	public function testOneEuroIntegers_is100Cents() {
241
		$this->assertSame( 100, Euro::newFromInt( 1 )->getEuroCents() );
242
	}
243
244
	// phpcs:ignore MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName
245
	public function test1337EuroIntegers_is133700Cents() {
246
		$this->assertSame( 133700, Euro::newFromInt( 1337 )->getEuroCents() );
247
	}
248
249
	public function testGivenNegativeIntegerAmount_exceptionIsThrown() {
250
		$this->expectException( \InvalidArgumentException::class );
251
		Euro::newFromInt( -1 );
252
	}
253
254
	/**
255
	 * @dataProvider euroProvider
256
	 * @param Euro $euro
257
	 */
258
	public function testEuroEqualsItself( Euro $euro ) {
259
		$this->assertTrue( $euro->equals( clone $euro ) );
260
	}
261
262
	public function euroProvider() {
263
		return [
264
			[ Euro::newFromCents( 0 ) ],
265
			[ Euro::newFromCents( 1 ) ],
266
			[ Euro::newFromCents( 99 ) ],
267
			[ Euro::newFromCents( 100 ) ],
268
			[ Euro::newFromCents( 9999 ) ],
269
		];
270
	}
271
272
	public function testOneCentDoesNotEqualOneEuro() {
273
		$this->assertFalse( Euro::newFromCents( 1 )->equals( Euro::newFromInt( 1 ) ) );
274
	}
275
276
	public function testOneCentDoesNotEqualTwoCents() {
277
		$this->assertFalse( Euro::newFromCents( 1 )->equals( Euro::newFromCents( 2 ) ) );
278
	}
279
280
	public function testOneCentDoesNotEqualOneEuroAndOneCent() {
281
		$this->assertFalse( Euro::newFromCents( 1 )->equals( Euro::newFromCents( 101 ) ) );
282
	}
283
284
	public function test9001centsDoesNotEqual9000cents() {
285
		$this->assertFalse( Euro::newFromCents( 9001 )->equals( Euro::newFromCents( 9000 ) ) );
286
	}
287
288
	/**
289
	 * @dataProvider tooLongStringProvider
290
	 */
291
	public function testNewFromStringThrowsExceptionWhenStringIsTooLong( string $string ) {
292
		$this->expectException( InvalidArgumentException::class );
293
		$this->expectExceptionMessage( 'Number is too big' );
294
295
		Euro::newFromString( $string );
296
	}
297
298
	public function tooLongStringProvider() {
299
		yield [ '1111111111111111111111111111111' ];
300
		yield [ (string)PHP_INT_MAX ];
301
		yield [ substr( (string)PHP_INT_MAX, 0, -2 ) ];
302
	}
303
304
	public function testNewFromStringHandlesLongStrings() {
305
		Euro::newFromString( substr( (string)PHP_INT_MAX, 0, -3 ) );
306
		$this->assertTrue( true );
307
	}
308
309
	/**
310
	 * @dataProvider tooHighNumberProvider
311
	 */
312
	public function testNewFromIntThrowsExceptionWhenIntegerIsTooHigh( int $int ) {
313
		$this->expectException( InvalidArgumentException::class );
314
		$this->expectExceptionMessage( 'Number is too big' );
315
		Euro::newFromInt( $int );
316
	}
317
318
	public function tooHighNumberProvider() {
319
		yield [ PHP_INT_MAX ];
320
		yield [ (int)floor( PHP_INT_MAX / 10 ) ];
321
		yield [ (int)floor( PHP_INT_MAX / 100 ) ];
322
	}
323
324
	/**
325
	 * @dataProvider tooHighNumberProvider
326
	 */
327
	public function testNewFromFloatThrowsExceptionWhenFloatIsTooHigh( int $int ) {
328
		$this->expectException( InvalidArgumentException::class );
329
		$this->expectExceptionMessage( 'Number is too big' );
330
		Euro::newFromFloat( (float)$int );
331
	}
332
333
	public function testNewFromIntHandlesBigIntegers() {
334
		// Edge case test for the highest allowed value (Euro::CENTS_PER_EURO +1 )
335
		// 100 (Euro::CENTS_PER_EURO) does not work due to rounding
336
		$number = (int)floor( PHP_INT_MAX / 101 );
337
338
		$this->assertSame(
339
			$number * 100,
340
			Euro::newFromInt( $number )->getEuroCents()
341
		);
342
	}
343
344
}
345