AbstractNumberFormatterTest.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Intl\Tests\NumberFormatter;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\Intl\Globals\IntlGlobals;
  13. use Symfony\Component\Intl\NumberFormatter\NumberFormatter;
  14. use Symfony\Component\Intl\Util\IntlTestHelper;
  15. /**
  16. * Note that there are some values written like -2147483647 - 1. This is the lower 32bit int max and is a known
  17. * behavior of PHP.
  18. */
  19. abstract class AbstractNumberFormatterTest extends TestCase
  20. {
  21. /**
  22. * @dataProvider formatCurrencyWithDecimalStyleProvider
  23. */
  24. public function testFormatCurrencyWithDecimalStyle($value, $currency, $expected)
  25. {
  26. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  27. $this->assertEquals($expected, $formatter->formatCurrency($value, $currency));
  28. }
  29. public function formatCurrencyWithDecimalStyleProvider()
  30. {
  31. return array(
  32. array(100, 'ALL', '100'),
  33. array(100, 'BRL', '100.00'),
  34. array(100, 'CRC', '100'),
  35. array(100, 'JPY', '100'),
  36. array(100, 'CHF', '100'),
  37. array(-100, 'ALL', '-100'),
  38. array(-100, 'BRL', '-100'),
  39. array(-100, 'CRC', '-100'),
  40. array(-100, 'JPY', '-100'),
  41. array(-100, 'CHF', '-100'),
  42. array(1000.12, 'ALL', '1,000.12'),
  43. array(1000.12, 'BRL', '1,000.12'),
  44. array(1000.12, 'CRC', '1,000.12'),
  45. array(1000.12, 'JPY', '1,000.12'),
  46. array(1000.12, 'CHF', '1,000.12'),
  47. );
  48. }
  49. /**
  50. * @dataProvider formatCurrencyWithCurrencyStyleProvider
  51. */
  52. public function testFormatCurrencyWithCurrencyStyle($value, $currency, $expected)
  53. {
  54. $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
  55. $this->assertEquals($expected, $formatter->formatCurrency($value, $currency));
  56. }
  57. public function formatCurrencyWithCurrencyStyleProvider()
  58. {
  59. return array(
  60. array(100, 'ALL', 'ALL100'),
  61. array(-100, 'ALL', '-ALL100'),
  62. array(1000.12, 'ALL', 'ALL1,000'),
  63. array(100, 'JPY', '¥100'),
  64. array(-100, 'JPY', '-¥100'),
  65. array(1000.12, 'JPY', '¥1,000'),
  66. array(100, 'EUR', '€100.00'),
  67. array(-100, 'EUR', '-€100.00'),
  68. array(1000.12, 'EUR', '€1,000.12'),
  69. );
  70. }
  71. /**
  72. * @dataProvider formatCurrencyWithCurrencyStyleCostaRicanColonsRoundingProvider
  73. */
  74. public function testFormatCurrencyWithCurrencyStyleCostaRicanColonsRounding($value, $currency, $symbol, $expected)
  75. {
  76. IntlTestHelper::requireIntl($this, '58.1');
  77. $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
  78. $this->assertEquals(sprintf($expected, $symbol), $formatter->formatCurrency($value, $currency));
  79. }
  80. public function formatCurrencyWithCurrencyStyleCostaRicanColonsRoundingProvider()
  81. {
  82. return array(
  83. array(100, 'CRC', 'CRC', '%s100.00'),
  84. array(-100, 'CRC', 'CRC', '-%s100.00'),
  85. array(1000.12, 'CRC', 'CRC', '%s1,000.12'),
  86. );
  87. }
  88. /**
  89. * @dataProvider formatCurrencyWithCurrencyStyleBrazilianRealRoundingProvider
  90. */
  91. public function testFormatCurrencyWithCurrencyStyleBrazilianRealRounding($value, $currency, $symbol, $expected)
  92. {
  93. $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
  94. $this->assertEquals(sprintf($expected, $symbol), $formatter->formatCurrency($value, $currency));
  95. }
  96. public function formatCurrencyWithCurrencyStyleBrazilianRealRoundingProvider()
  97. {
  98. return array(
  99. array(100, 'BRL', 'R', '%s$100.00'),
  100. array(-100, 'BRL', 'R', '-%s$100.00'),
  101. array(1000.12, 'BRL', 'R', '%s$1,000.12'),
  102. // Rounding checks
  103. array(1000.121, 'BRL', 'R', '%s$1,000.12'),
  104. array(1000.123, 'BRL', 'R', '%s$1,000.12'),
  105. array(1000.125, 'BRL', 'R', '%s$1,000.12'),
  106. array(1000.127, 'BRL', 'R', '%s$1,000.13'),
  107. array(1000.129, 'BRL', 'R', '%s$1,000.13'),
  108. array(11.50999, 'BRL', 'R', '%s$11.51'),
  109. array(11.9999464, 'BRL', 'R', '%s$12.00'),
  110. );
  111. }
  112. /**
  113. * @dataProvider formatCurrencyWithCurrencyStyleSwissRoundingProvider
  114. */
  115. public function testFormatCurrencyWithCurrencyStyleSwissRounding($value, $currency, $symbol, $expected)
  116. {
  117. $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
  118. $this->assertEquals(sprintf($expected, $symbol), $formatter->formatCurrency($value, $currency));
  119. }
  120. public function formatCurrencyWithCurrencyStyleSwissRoundingProvider()
  121. {
  122. return array(
  123. array(100, 'CHF', 'CHF', '%s100.00'),
  124. array(-100, 'CHF', 'CHF', '-%s100.00'),
  125. array(1000.12, 'CHF', 'CHF', '%s1,000.12'),
  126. array('1000.12', 'CHF', 'CHF', '%s1,000.12'),
  127. // Rounding checks
  128. array(1000.121, 'CHF', 'CHF', '%s1,000.12'),
  129. array(1000.123, 'CHF', 'CHF', '%s1,000.12'),
  130. array(1000.125, 'CHF', 'CHF', '%s1,000.12'),
  131. array(1000.127, 'CHF', 'CHF', '%s1,000.13'),
  132. array(1000.129, 'CHF', 'CHF', '%s1,000.13'),
  133. array(1200000.00, 'CHF', 'CHF', '%s1,200,000.00'),
  134. array(1200000.1, 'CHF', 'CHF', '%s1,200,000.10'),
  135. array(1200000.10, 'CHF', 'CHF', '%s1,200,000.10'),
  136. array(1200000.101, 'CHF', 'CHF', '%s1,200,000.10'),
  137. );
  138. }
  139. public function testFormat()
  140. {
  141. $errorCode = IntlGlobals::U_ZERO_ERROR;
  142. $errorMessage = 'U_ZERO_ERROR';
  143. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  144. $this->assertSame('9.555', $formatter->format(9.555));
  145. $this->assertSame($errorMessage, $this->getIntlErrorMessage());
  146. $this->assertSame($errorCode, $this->getIntlErrorCode());
  147. $this->assertFalse($this->isIntlFailure($this->getIntlErrorCode()));
  148. $this->assertSame($errorMessage, $formatter->getErrorMessage());
  149. $this->assertSame($errorCode, $formatter->getErrorCode());
  150. $this->assertFalse($this->isIntlFailure($formatter->getErrorCode()));
  151. }
  152. public function testFormatWithCurrencyStyle()
  153. {
  154. $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
  155. $this->assertEquals('¤1.00', $formatter->format(1));
  156. }
  157. /**
  158. * @dataProvider formatTypeInt32Provider
  159. */
  160. public function testFormatTypeInt32($formatter, $value, $expected, $message = '')
  161. {
  162. $formattedValue = $formatter->format($value, NumberFormatter::TYPE_INT32);
  163. $this->assertEquals($expected, $formattedValue, $message);
  164. }
  165. public function formatTypeInt32Provider()
  166. {
  167. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  168. $message = '->format() TYPE_INT32 formats inconsistently an integer if out of the 32 bit range.';
  169. return array(
  170. array($formatter, 1, '1'),
  171. array($formatter, 1.1, '1'),
  172. array($formatter, 2147483648, '-2,147,483,648', $message),
  173. array($formatter, -2147483649, '2,147,483,647', $message),
  174. );
  175. }
  176. /**
  177. * @dataProvider formatTypeInt32WithCurrencyStyleProvider
  178. */
  179. public function testFormatTypeInt32WithCurrencyStyle($formatter, $value, $expected, $message = '')
  180. {
  181. $formattedValue = $formatter->format($value, NumberFormatter::TYPE_INT32);
  182. $this->assertEquals($expected, $formattedValue, $message);
  183. }
  184. public function formatTypeInt32WithCurrencyStyleProvider()
  185. {
  186. $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
  187. $message = '->format() TYPE_INT32 formats inconsistently an integer if out of the 32 bit range.';
  188. return array(
  189. array($formatter, 1, '¤1.00'),
  190. array($formatter, 1.1, '¤1.00'),
  191. array($formatter, 2147483648, '-¤2,147,483,648.00', $message),
  192. array($formatter, -2147483649, '¤2,147,483,647.00', $message),
  193. );
  194. }
  195. /**
  196. * The parse() method works differently with integer out of the 32 bit range. format() works fine.
  197. *
  198. * @dataProvider formatTypeInt64Provider
  199. */
  200. public function testFormatTypeInt64($formatter, $value, $expected)
  201. {
  202. $formattedValue = $formatter->format($value, NumberFormatter::TYPE_INT64);
  203. $this->assertEquals($expected, $formattedValue);
  204. }
  205. public function formatTypeInt64Provider()
  206. {
  207. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  208. return array(
  209. array($formatter, 1, '1'),
  210. array($formatter, 1.1, '1'),
  211. array($formatter, 2147483648, '2,147,483,648'),
  212. array($formatter, -2147483649, '-2,147,483,649'),
  213. );
  214. }
  215. /**
  216. * @dataProvider formatTypeInt64WithCurrencyStyleProvider
  217. */
  218. public function testFormatTypeInt64WithCurrencyStyle($formatter, $value, $expected)
  219. {
  220. $formattedValue = $formatter->format($value, NumberFormatter::TYPE_INT64);
  221. $this->assertEquals($expected, $formattedValue);
  222. }
  223. public function formatTypeInt64WithCurrencyStyleProvider()
  224. {
  225. $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
  226. return array(
  227. array($formatter, 1, '¤1.00'),
  228. array($formatter, 1.1, '¤1.00'),
  229. array($formatter, 2147483648, '¤2,147,483,648.00'),
  230. array($formatter, -2147483649, '-¤2,147,483,649.00'),
  231. );
  232. }
  233. /**
  234. * @dataProvider formatTypeDoubleProvider
  235. */
  236. public function testFormatTypeDouble($formatter, $value, $expected)
  237. {
  238. $formattedValue = $formatter->format($value, NumberFormatter::TYPE_DOUBLE);
  239. $this->assertEquals($expected, $formattedValue);
  240. }
  241. public function formatTypeDoubleProvider()
  242. {
  243. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  244. return array(
  245. array($formatter, 1, '1'),
  246. array($formatter, 1.1, '1.1'),
  247. );
  248. }
  249. /**
  250. * @dataProvider formatTypeDoubleWithCurrencyStyleProvider
  251. */
  252. public function testFormatTypeDoubleWithCurrencyStyle($formatter, $value, $expected)
  253. {
  254. $formattedValue = $formatter->format($value, NumberFormatter::TYPE_DOUBLE);
  255. $this->assertEquals($expected, $formattedValue);
  256. }
  257. public function formatTypeDoubleWithCurrencyStyleProvider()
  258. {
  259. $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
  260. return array(
  261. array($formatter, 1, '¤1.00'),
  262. array($formatter, 1.1, '¤1.10'),
  263. );
  264. }
  265. /**
  266. * @dataProvider formatTypeCurrencyProvider
  267. */
  268. public function testFormatTypeCurrency($formatter, $value)
  269. {
  270. $exceptionCode = 'PHPUnit\Framework\Error\Warning';
  271. if (class_exists('PHPUnit_Framework_Error_Warning')) {
  272. $exceptionCode = 'PHPUnit_Framework_Error_Warning';
  273. }
  274. $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}($exceptionCode);
  275. $formatter->format($value, NumberFormatter::TYPE_CURRENCY);
  276. }
  277. /**
  278. * @dataProvider formatTypeCurrencyProvider
  279. */
  280. public function testFormatTypeCurrencyReturn($formatter, $value)
  281. {
  282. $this->assertFalse(@$formatter->format($value, NumberFormatter::TYPE_CURRENCY));
  283. }
  284. public function formatTypeCurrencyProvider()
  285. {
  286. $df = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  287. $cf = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
  288. return array(
  289. array($df, 1),
  290. array($cf, 1),
  291. );
  292. }
  293. /**
  294. * @dataProvider formatFractionDigitsProvider
  295. */
  296. public function testFormatFractionDigits($value, $expected, $fractionDigits = null, $expectedFractionDigits = 1)
  297. {
  298. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  299. $attributeRet = null;
  300. if (null !== $fractionDigits) {
  301. $attributeRet = $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, $fractionDigits);
  302. }
  303. $formattedValue = $formatter->format($value);
  304. $this->assertSame($expected, $formattedValue);
  305. $this->assertSame($expectedFractionDigits, $formatter->getAttribute(NumberFormatter::FRACTION_DIGITS));
  306. if (null !== $attributeRet) {
  307. $this->assertTrue($attributeRet);
  308. }
  309. }
  310. public function formatFractionDigitsProvider()
  311. {
  312. return array(
  313. array(1.123, '1.123', null, 0),
  314. array(1.123, '1', 0, 0),
  315. array(1.123, '1.1', 1, 1),
  316. array(1.123, '1.12', 2, 2),
  317. array(1.123, '1', -1, 0),
  318. array(1.123, '1', 'abc', 0),
  319. );
  320. }
  321. /**
  322. * @dataProvider formatGroupingUsedProvider
  323. */
  324. public function testFormatGroupingUsed($value, $expected, $groupingUsed = null, $expectedGroupingUsed = 1)
  325. {
  326. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  327. $attributeRet = null;
  328. if (null !== $groupingUsed) {
  329. $attributeRet = $formatter->setAttribute(NumberFormatter::GROUPING_USED, $groupingUsed);
  330. }
  331. $formattedValue = $formatter->format($value);
  332. $this->assertSame($expected, $formattedValue);
  333. $this->assertSame($expectedGroupingUsed, $formatter->getAttribute(NumberFormatter::GROUPING_USED));
  334. if (null !== $attributeRet) {
  335. $this->assertTrue($attributeRet);
  336. }
  337. }
  338. public function formatGroupingUsedProvider()
  339. {
  340. return array(
  341. array(1000, '1,000', null, 1),
  342. array(1000, '1000', 0, 0),
  343. array(1000, '1,000', 1, 1),
  344. array(1000, '1,000', 2, 1),
  345. array(1000, '1000', 'abc', 0),
  346. array(1000, '1,000', -1, 1),
  347. );
  348. }
  349. /**
  350. * @dataProvider formatRoundingModeRoundHalfUpProvider
  351. */
  352. public function testFormatRoundingModeHalfUp($value, $expected)
  353. {
  354. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  355. $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
  356. $formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_HALFUP);
  357. $this->assertSame($expected, $formatter->format($value), '->format() with ROUND_HALFUP rounding mode.');
  358. }
  359. public function formatRoundingModeRoundHalfUpProvider()
  360. {
  361. // The commented value is differently rounded by intl's NumberFormatter in 32 and 64 bit architectures
  362. return array(
  363. array(1.121, '1.12'),
  364. array(1.123, '1.12'),
  365. // array(1.125, '1.13'),
  366. array(1.127, '1.13'),
  367. array(1.129, '1.13'),
  368. );
  369. }
  370. /**
  371. * @dataProvider formatRoundingModeRoundHalfDownProvider
  372. */
  373. public function testFormatRoundingModeHalfDown($value, $expected)
  374. {
  375. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  376. $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
  377. $formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_HALFDOWN);
  378. $this->assertSame($expected, $formatter->format($value), '->format() with ROUND_HALFDOWN rounding mode.');
  379. }
  380. public function formatRoundingModeRoundHalfDownProvider()
  381. {
  382. return array(
  383. array(1.121, '1.12'),
  384. array(1.123, '1.12'),
  385. array(1.125, '1.12'),
  386. array(1.127, '1.13'),
  387. array(1.129, '1.13'),
  388. );
  389. }
  390. /**
  391. * @dataProvider formatRoundingModeRoundHalfEvenProvider
  392. */
  393. public function testFormatRoundingModeHalfEven($value, $expected)
  394. {
  395. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  396. $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
  397. $formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_HALFEVEN);
  398. $this->assertSame($expected, $formatter->format($value), '->format() with ROUND_HALFEVEN rounding mode.');
  399. }
  400. public function formatRoundingModeRoundHalfEvenProvider()
  401. {
  402. return array(
  403. array(1.121, '1.12'),
  404. array(1.123, '1.12'),
  405. array(1.125, '1.12'),
  406. array(1.127, '1.13'),
  407. array(1.129, '1.13'),
  408. );
  409. }
  410. /**
  411. * @dataProvider formatRoundingModeRoundCeilingProvider
  412. */
  413. public function testFormatRoundingModeCeiling($value, $expected)
  414. {
  415. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  416. $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
  417. $formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_CEILING);
  418. $this->assertSame($expected, $formatter->format($value), '->format() with ROUND_CEILING rounding mode.');
  419. }
  420. public function formatRoundingModeRoundCeilingProvider()
  421. {
  422. return array(
  423. array(1.123, '1.13'),
  424. array(1.125, '1.13'),
  425. array(1.127, '1.13'),
  426. array(-1.123, '-1.12'),
  427. array(-1.125, '-1.12'),
  428. array(-1.127, '-1.12'),
  429. );
  430. }
  431. /**
  432. * @dataProvider formatRoundingModeRoundFloorProvider
  433. */
  434. public function testFormatRoundingModeFloor($value, $expected)
  435. {
  436. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  437. $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
  438. $formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_FLOOR);
  439. $this->assertSame($expected, $formatter->format($value), '->format() with ROUND_FLOOR rounding mode.');
  440. }
  441. public function formatRoundingModeRoundFloorProvider()
  442. {
  443. return array(
  444. array(1.123, '1.12'),
  445. array(1.125, '1.12'),
  446. array(1.127, '1.12'),
  447. array(-1.123, '-1.13'),
  448. array(-1.125, '-1.13'),
  449. array(-1.127, '-1.13'),
  450. );
  451. }
  452. /**
  453. * @dataProvider formatRoundingModeRoundDownProvider
  454. */
  455. public function testFormatRoundingModeDown($value, $expected)
  456. {
  457. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  458. $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
  459. $formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_DOWN);
  460. $this->assertSame($expected, $formatter->format($value), '->format() with ROUND_DOWN rounding mode.');
  461. }
  462. public function formatRoundingModeRoundDownProvider()
  463. {
  464. return array(
  465. array(1.123, '1.12'),
  466. array(1.125, '1.12'),
  467. array(1.127, '1.12'),
  468. array(-1.123, '-1.12'),
  469. array(-1.125, '-1.12'),
  470. array(-1.127, '-1.12'),
  471. );
  472. }
  473. /**
  474. * @dataProvider formatRoundingModeRoundUpProvider
  475. */
  476. public function testFormatRoundingModeUp($value, $expected)
  477. {
  478. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  479. $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
  480. $formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_UP);
  481. $this->assertSame($expected, $formatter->format($value), '->format() with ROUND_UP rounding mode.');
  482. }
  483. public function formatRoundingModeRoundUpProvider()
  484. {
  485. return array(
  486. array(1.123, '1.13'),
  487. array(1.125, '1.13'),
  488. array(1.127, '1.13'),
  489. array(-1.123, '-1.13'),
  490. array(-1.125, '-1.13'),
  491. array(-1.127, '-1.13'),
  492. );
  493. }
  494. public function testGetLocale()
  495. {
  496. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  497. $this->assertEquals('en', $formatter->getLocale());
  498. }
  499. public function testGetSymbol()
  500. {
  501. $decimalFormatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  502. $currencyFormatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
  503. $r = new \ReflectionProperty('Symfony\Component\Intl\NumberFormatter\NumberFormatter', 'enSymbols');
  504. $r->setAccessible(true);
  505. $expected = $r->getValue('Symfony\Component\Intl\NumberFormatter\NumberFormatter');
  506. for ($i = 0; $i <= 17; ++$i) {
  507. $this->assertSame($expected[1][$i], $decimalFormatter->getSymbol($i));
  508. $this->assertSame($expected[2][$i], $currencyFormatter->getSymbol($i));
  509. }
  510. }
  511. public function testGetTextAttribute()
  512. {
  513. $decimalFormatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  514. $currencyFormatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
  515. $r = new \ReflectionProperty('Symfony\Component\Intl\NumberFormatter\NumberFormatter', 'enTextAttributes');
  516. $r->setAccessible(true);
  517. $expected = $r->getValue('Symfony\Component\Intl\NumberFormatter\NumberFormatter');
  518. for ($i = 0; $i <= 5; ++$i) {
  519. $this->assertSame($expected[1][$i], $decimalFormatter->getTextAttribute($i));
  520. $this->assertSame($expected[2][$i], $currencyFormatter->getTextAttribute($i));
  521. }
  522. }
  523. /**
  524. * @dataProvider parseProvider
  525. */
  526. public function testParse($value, $expected, $message, $expectedPosition, $groupingUsed = true)
  527. {
  528. $position = 0;
  529. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  530. $formatter->setAttribute(NumberFormatter::GROUPING_USED, $groupingUsed);
  531. $parsedValue = $formatter->parse($value, NumberFormatter::TYPE_DOUBLE, $position);
  532. $this->assertSame($expected, $parsedValue, $message);
  533. $this->assertSame($expectedPosition, $position, $message);
  534. if ($expected === false) {
  535. $errorCode = IntlGlobals::U_PARSE_ERROR;
  536. $errorMessage = 'Number parsing failed: U_PARSE_ERROR';
  537. } else {
  538. $errorCode = IntlGlobals::U_ZERO_ERROR;
  539. $errorMessage = 'U_ZERO_ERROR';
  540. }
  541. $this->assertSame($errorMessage, $this->getIntlErrorMessage());
  542. $this->assertSame($errorCode, $this->getIntlErrorCode());
  543. $this->assertSame($errorCode !== 0, $this->isIntlFailure($this->getIntlErrorCode()));
  544. $this->assertSame($errorMessage, $formatter->getErrorMessage());
  545. $this->assertSame($errorCode, $formatter->getErrorCode());
  546. $this->assertSame($errorCode !== 0, $this->isIntlFailure($formatter->getErrorCode()));
  547. }
  548. public function parseProvider()
  549. {
  550. return array(
  551. array('prefix1', false, '->parse() does not parse a number with a string prefix.', 0),
  552. array('1.4suffix', (float) 1.4, '->parse() parses a number with a string suffix.', 3),
  553. array('-.4suffix', (float) -0.4, '->parse() parses a negative dot float with suffix.', 3),
  554. array('-123,4', false, '->parse() does not parse when invalid grouping used.', 6),
  555. array('-1234,567', false, '->parse() does not parse when invalid grouping used.', 5),
  556. array('-123,,456', false, '->parse() does not parse when invalid grouping used.', 4),
  557. array('-123,,456', -123.0, '->parse() parses when grouping is disabled.', 4, false),
  558. );
  559. }
  560. public function testParseTypeDefault()
  561. {
  562. $exceptionCode = 'PHPUnit\Framework\Error\Warning';
  563. if (class_exists('PHPUnit_Framework_Error_Warning')) {
  564. $exceptionCode = 'PHPUnit_Framework_Error_Warning';
  565. }
  566. $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}($exceptionCode);
  567. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  568. $formatter->parse('1', NumberFormatter::TYPE_DEFAULT);
  569. }
  570. /**
  571. * @dataProvider parseTypeInt32Provider
  572. */
  573. public function testParseTypeInt32($value, $expected, $message = '')
  574. {
  575. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  576. $parsedValue = $formatter->parse($value, NumberFormatter::TYPE_INT32);
  577. $this->assertSame($expected, $parsedValue, $message);
  578. }
  579. public function parseTypeInt32Provider()
  580. {
  581. return array(
  582. array('1', 1),
  583. array('1.1', 1),
  584. array('.1', 0),
  585. array('2,147,483,647', 2147483647),
  586. array('-2,147,483,648', -2147483647 - 1),
  587. array('2,147,483,648', false, '->parse() TYPE_INT32 returns false when the number is greater than the integer positive range.'),
  588. array('-2,147,483,649', false, '->parse() TYPE_INT32 returns false when the number is greater than the integer negative range.'),
  589. );
  590. }
  591. public function testParseTypeInt64With32BitIntegerInPhp32Bit()
  592. {
  593. IntlTestHelper::require32Bit($this);
  594. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  595. $parsedValue = $formatter->parse('2,147,483,647', NumberFormatter::TYPE_INT64);
  596. $this->assertInternalType('integer', $parsedValue);
  597. $this->assertEquals(2147483647, $parsedValue);
  598. $parsedValue = $formatter->parse('-2,147,483,648', NumberFormatter::TYPE_INT64);
  599. $this->assertInternalType('int', $parsedValue);
  600. $this->assertEquals(-2147483648, $parsedValue);
  601. }
  602. public function testParseTypeInt64With32BitIntegerInPhp64Bit()
  603. {
  604. IntlTestHelper::require64Bit($this);
  605. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  606. $parsedValue = $formatter->parse('2,147,483,647', NumberFormatter::TYPE_INT64);
  607. $this->assertInternalType('integer', $parsedValue);
  608. $this->assertEquals(2147483647, $parsedValue);
  609. $parsedValue = $formatter->parse('-2,147,483,648', NumberFormatter::TYPE_INT64);
  610. $this->assertInternalType('integer', $parsedValue);
  611. $this->assertEquals(-2147483647 - 1, $parsedValue);
  612. }
  613. /**
  614. * If PHP is compiled in 32bit mode, the returned value for a 64bit integer are float numbers.
  615. */
  616. public function testParseTypeInt64With64BitIntegerInPhp32Bit()
  617. {
  618. IntlTestHelper::require32Bit($this);
  619. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  620. // int 64 using only 32 bit range strangeness
  621. $parsedValue = $formatter->parse('2,147,483,648', NumberFormatter::TYPE_INT64);
  622. $this->assertInternalType('float', $parsedValue);
  623. $this->assertEquals(2147483648, $parsedValue, '->parse() TYPE_INT64 does not use true 64 bit integers, using only the 32 bit range.');
  624. $parsedValue = $formatter->parse('-2,147,483,649', NumberFormatter::TYPE_INT64);
  625. $this->assertInternalType('float', $parsedValue);
  626. $this->assertEquals(-2147483649, $parsedValue, '->parse() TYPE_INT64 does not use true 64 bit integers, using only the 32 bit range.');
  627. }
  628. /**
  629. * If PHP is compiled in 64bit mode, the returned value for a 64bit integer are 32bit integer numbers.
  630. */
  631. public function testParseTypeInt64With64BitIntegerInPhp64Bit()
  632. {
  633. IntlTestHelper::require64Bit($this);
  634. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  635. $parsedValue = $formatter->parse('2,147,483,648', NumberFormatter::TYPE_INT64);
  636. $this->assertInternalType('integer', $parsedValue);
  637. $this->assertEquals(2147483648, $parsedValue, '->parse() TYPE_INT64 uses true 64 bit integers (PHP >= 5.3.14 and PHP >= 5.4.4).');
  638. $parsedValue = $formatter->parse('-2,147,483,649', NumberFormatter::TYPE_INT64);
  639. $this->assertInternalType('integer', $parsedValue);
  640. $this->assertEquals(-2147483649, $parsedValue, '->parse() TYPE_INT64 uses true 64 bit integers (PHP >= 5.3.14 and PHP >= 5.4.4).');
  641. }
  642. /**
  643. * @dataProvider parseTypeDoubleProvider
  644. */
  645. public function testParseTypeDouble($value, $expectedValue)
  646. {
  647. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  648. $parsedValue = $formatter->parse($value, NumberFormatter::TYPE_DOUBLE);
  649. $this->assertEquals($expectedValue, $parsedValue, '', 0.001);
  650. }
  651. public function parseTypeDoubleProvider()
  652. {
  653. return array(
  654. array('1', (float) 1),
  655. array('1.1', 1.1),
  656. array('9,223,372,036,854,775,808', 9223372036854775808),
  657. array('-9,223,372,036,854,775,809', -9223372036854775809),
  658. );
  659. }
  660. public function testParseTypeCurrency()
  661. {
  662. $exceptionCode = 'PHPUnit\Framework\Error\Warning';
  663. if (class_exists('PHPUnit_Framework_Error_Warning')) {
  664. $exceptionCode = 'PHPUnit_Framework_Error_Warning';
  665. }
  666. $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}($exceptionCode);
  667. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  668. $formatter->parse('1', NumberFormatter::TYPE_CURRENCY);
  669. }
  670. public function testParseWithNotNullPositionValue()
  671. {
  672. $position = 1;
  673. $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
  674. $formatter->parse('123', NumberFormatter::TYPE_DOUBLE, $position);
  675. $this->assertEquals(3, $position);
  676. }
  677. /**
  678. * @param string $locale
  679. * @param null $style
  680. * @param null $pattern
  681. *
  682. * @return \NumberFormatter
  683. */
  684. abstract protected function getNumberFormatter($locale = 'en', $style = null, $pattern = null);
  685. /**
  686. * @return string
  687. */
  688. abstract protected function getIntlErrorMessage();
  689. /**
  690. * @return int
  691. */
  692. abstract protected function getIntlErrorCode();
  693. /**
  694. * @param int $errorCode
  695. *
  696. * @return bool
  697. */
  698. abstract protected function isIntlFailure($errorCode);
  699. }