001/*
002 * Copyright (c) 2012, 2013, Werner Keil, Credit Suisse (Anatole Tresch). Licensed under the Apache
003 * License, Version 2.0 (the "License"); you may not use this file except in compliance with the
004 * License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
005 * Unless required by applicable law or agreed to in writing, software distributed under the License
006 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
007 * or implied. See the License for the specific language governing permissions and limitations under
008 * the License. Contributors: Anatole Tresch - initial version.
009 */
010package org.javamoney.tck.tests;
011
012import org.javamoney.tck.TCKTestSetup;
013import org.javamoney.tck.tests.internal.TestAmount;
014import org.javamoney.tck.tests.internal.TestCurrencyUnit;
015import org.javamoney.tck.tests.internal.TestMonetaryAmountBuilder;
016import org.jboss.test.audit.annotations.SpecAssertion;
017import org.jboss.test.audit.annotations.SpecVersion;
018import org.testng.AssertJUnit;
019import org.testng.annotations.Test;
020
021import javax.money.*;
022import java.math.BigDecimal;
023import java.math.RoundingMode;
024import java.util.Collection;
025import java.util.Currency;
026import java.util.Locale;
027import java.util.Set;
028
029import static org.junit.Assert.assertNotNull;
030
031
032/**
033 * Created by Anatole on 10.03.14.
034 */
035@SpecVersion(spec = "JSR 354", version = "1.0.0")
036public class AccessingCurrenciesAmountsRoundingsTest {
037
038    // ****************** A. Accessing Currencies *******************
039
040    /**
041     * Test if MonetaryCurrencies provides all ISO related entries,
042     * similar to the JDK.
043     */
044    @Test(description = "4.2.7 Test if MonetaryCurrencies provides all ISO related entries similar to " +
045            "java.util.Currency.")
046    @SpecAssertion(section = "4.2.7", id = "427-A1")
047    public void testAllISOCurrenciesAvailable() {
048        for (Currency currency : Currency.getAvailableCurrencies()) {
049            AssertJUnit.assertTrue(
050                    "Section 4.2.7: Currency not available [MonetaryCurrencies#isCurrencyAvailable(String)] for JDK " +
051                            "currency code: " +
052                            currency.getCurrencyCode(),
053                    MonetaryCurrencies.isCurrencyAvailable(currency.getCurrencyCode()));
054            AssertJUnit.assertNotNull(
055                    "Section 4.2.7: Currency null [MonetaryCurrencies#igetCurrency(String)] for JDK currency code: " +
056                            currency.getCurrencyCode(), MonetaryCurrencies.getCurrency(currency.getCurrencyCode()));
057        }
058    }
059
060    /**
061     * Test if MonetaryCurrencies provides all Locale related
062     * entries, similar to the JDK (for all ISO countries).
063     */
064    @Test(description = "4.2.7 Test if MonetaryCurrencies provides all locale related entries similar to " +
065            "java.util.Currency.")
066    @SpecAssertion(section = "4.2.7", id = "427-A2")
067    public void testAllLocaleCurrenciesAvailable() {
068        for (String country : Locale.getISOCountries()) {
069            Locale locale = new Locale("", country);
070            if (Currency.getInstance(locale) != null) {
071                AssertJUnit.assertTrue(
072                        "Section 4.2.7: Currency not available [MonetaryCurrencies#isCurrencyAvailable(Locale)] for " +
073                                "locale: " +
074                                locale, MonetaryCurrencies.isCurrencyAvailable(locale));
075                AssertJUnit
076                        .assertNotNull("Currency null [MonetaryCurrencies#igetCurrency(Locale)] for locale: " + locale,
077                                MonetaryCurrencies.getCurrencies(locale));
078                Collection<CurrencyUnit> units = MonetaryCurrencies.getCurrencies(locale);
079                CurrencyUnit requiredCurrency = null;
080                for (CurrencyUnit cu : units) {
081                    if (Currency.getInstance(locale).getCurrencyCode().equals(cu.getCurrencyCode())) {
082                        requiredCurrency = cu;
083                        break;
084                    }
085                }
086                AssertJUnit.assertNotNull(
087                        "Section 4.2.7: No Currency returned from [MonetaryCurrencies#getCurrencies(Locale)] for " +
088                                "locale: " +
089                                locale, requiredCurrency);
090                AssertJUnit.assertEquals(
091                        "Section 4.2.7: Invalid Currency returned from [MonetaryCurrencies#getCurrencies(Locale)] for" +
092                                " " +
093                                "locale: " +
094                                locale +
095                                ", expected: " + Currency.getInstance(locale) + ", found: " + requiredCurrency,
096                        MonetaryCurrencies.getCurrency(Currency.getInstance(locale).getCurrencyCode()),
097                        requiredCurrency);
098            }
099        }
100    }
101
102    /**
103     * Test if MonetaryCurrencies provides correct instance with ISO
104     * codes.
105     */
106    @Test(description = "4.2.7 Test if MonetaryCurrencies provides correct ISO related entries similar to " +
107            "java.util.Currency.")
108    @SpecAssertion(section = "4.2.7", id = "427-A3")
109    public void testCorrectISOCodes() {
110        for (Currency currency : Currency.getAvailableCurrencies()) {
111            CurrencyUnit unit = MonetaryCurrencies.getCurrency(currency.getCurrencyCode());
112            AssertJUnit.assertEquals(
113                    "Section 4.2.7: Invalid Currency code returned from [MonetaryCurrencies#igetCurrency(String)] for" +
114                            " currency code:" +
115                            " " +
116                            currency.getCurrencyCode() + ", expected: " +
117                            Currency.getInstance(currency.getCurrencyCode()).getCurrencyCode() +
118                            ", found: " + unit.getCurrencyCode(),
119                    Currency.getInstance(currency.getCurrencyCode()).getCurrencyCode(), unit.getCurrencyCode());
120            AssertJUnit.assertEquals(
121                    "Section 4.2.7: Invalid numeric code returned from [MonetaryCurrencies#igetCurrency(String)] for " +
122                            "currency code: " +
123                            currency.getCurrencyCode() + ", expected: " +
124                            Currency.getInstance(currency.getCurrencyCode()).getNumericCode() +
125                            ", found: " + unit.getNumericCode(),
126                    Currency.getInstance(currency.getCurrencyCode()).getNumericCode(), unit.getNumericCode());
127            AssertJUnit.assertEquals(
128                    "Section 4.2.7: Invalid default fraction unit returned from [MonetaryCurrencies#igetCurrency" +
129                            "(String)] for " +
130                            "currency code: " +
131                            currency.getCurrencyCode() + ", expected: " +
132                            Currency.getInstance(currency.getCurrencyCode()).getDefaultFractionDigits() +
133                            ", found: " + unit.getDefaultFractionDigits(),
134                    Currency.getInstance(currency.getCurrencyCode()).getDefaultFractionDigits(),
135                    unit.getDefaultFractionDigits());
136        }
137    }
138
139    /**
140     * Test if MonetaryCurrencies provides correct instance with
141     * Locales.
142     */
143    @Test(description = "4.2.7 Test if MonetaryCurrencies provides correct locale related entries similar to " +
144            "java.util.Currency.")
145    @SpecAssertion(section = "4.2.7", id = "427-A4")
146    public void testCorrectLocales() {
147
148        MonetaryCurrencies.getCurrencies(new Locale("", "AD"));
149
150        for (String country : Locale.getISOCountries()) {
151            Locale locale = new Locale("", country);
152            if (Currency.getInstance(locale) == null) {
153                continue;
154            }
155            Set<CurrencyUnit> units = MonetaryCurrencies.getCurrencies(locale);
156            AssertJUnit.assertNotNull(
157                    "Section 4.2.7: Invalid Currencies (null) returned from [MonetaryCurrencies#igetCurrencies" +
158                            "(Locale)] for" +
159                            " locale: " +
160                            locale, units);
161            AssertJUnit.assertFalse(
162                    "Section 4.2.7: Empty Currencies returned from [MonetaryCurrencies#igetCurrencies(Locale)] for" +
163                            " locale: " +
164                            locale, units.isEmpty());
165            CurrencyUnit requiredCurrency = null;
166            for (CurrencyUnit cu : units) {
167                if (Currency.getInstance(locale).getCurrencyCode().equals(cu.getCurrencyCode())) {
168                    requiredCurrency = cu;
169                    break;
170                }
171            }
172            AssertJUnit.assertNotNull("Section 4.2.7: Required Currency missing in result returned from " +
173                    "[MonetaryCurrencies#igetCurrency(Locale)] for" +
174                    " locale: " +
175                    locale + ", expected: " + Currency.getInstance(locale).getCurrencyCode() +
176                    ", found: " + units, requiredCurrency);
177            AssertJUnit.assertEquals(
178                    "Section 4.2.7: Invalid numeric code returned from [MonetaryCurrencies#igetCurrency(Locale)] for " +
179                            "locale: " +
180                            locale + ", expected: " + Currency.getInstance(locale).getNumericCode() +
181                            ", found: " + requiredCurrency.getNumericCode(),
182                    Currency.getInstance(locale).getNumericCode(), requiredCurrency.getNumericCode());
183            AssertJUnit.assertEquals(
184                    "Section 4.2.7: Invalid default fraction unit returned from [MonetaryCurrencies#igetCurrency" +
185                            "(Locale)] for " +
186                            "locale: " +
187                            locale + ", expected: " + Currency.getInstance(locale).getDefaultFractionDigits() +
188                            ", found: " + requiredCurrency.getDefaultFractionDigits(),
189                    Currency.getInstance(locale).getDefaultFractionDigits(),
190                    requiredCurrency.getDefaultFractionDigits());
191        }
192    }
193
194    /**
195     * Test for custom MonetaryCurrencies provided, based on the TCK
196     * TestProvider.
197     */
198    @Test(description = "4.2.7 Test if MonetaryCurrencies provides customized locale identified currencies.")
199    @SpecAssertion(section = "4.2.7", id = "427-A5")
200    public void testCustomCurrencies() {
201        Locale testLocale = new Locale("lang", "count", "test");
202        Set<CurrencyUnit> cus = MonetaryCurrencies.getCurrencies(testLocale);
203        AssertJUnit.assertNotNull("Section 4.2.7: TestCurrency not returned for locale: " + testLocale, cus);
204        AssertJUnit.assertFalse("Section 4.2.7: TestCurrency not returned for locale: " + testLocale, cus.isEmpty());
205        AssertJUnit.assertEquals("Section 4.2.7: Unexpected CurrencyUnit class returned.", TestCurrencyUnit.class,
206                cus.iterator().next().getClass());
207        CurrencyUnit cu = MonetaryCurrencies.getCurrency("FOOLS_test");
208        AssertJUnit.assertNotNull("Section 4.2.7: TestCurrency not returned for currency code: FOOLS_test", cu);
209        AssertJUnit.assertEquals("Section 4.2.7: Unexpected CurrencyUnit class returned.", TestCurrencyUnit.class,
210                cu.getClass());
211    }
212
213    // ********************************* B. Accessing Monetary Amount Factories ***********************
214
215    /**
216     * Ensure amount factories are accessible for all types
217     * available,
218     * providing also the
219     * some test implementations with the
220     * TCK.
221     */
222    @Test(description = "4.2.7 Ensure amount classes to test are setup and registered/available in MonetaryAmounts.")
223    @SpecAssertion(section = "4.2.7", id = "427-B2")
224    public void testAmountTypesProvided() {
225        Collection<Class> amountClasses = TCKTestSetup.getTestConfiguration().getAmountClasses();
226        AssertJUnit.assertNotNull(amountClasses);
227        AssertJUnit.assertFalse(amountClasses.isEmpty());
228        Collection<Class<? extends MonetaryAmount>> providedClasses = MonetaryAmounts.getAmountTypes();
229        for (Class amountType : amountClasses) {
230            AssertJUnit.assertTrue("Section 4.2.7: Amount class not registered: " + amountType.getName(),
231                    providedClasses.contains(amountType));
232        }
233        AssertJUnit.assertTrue("Section 4.2.7: TCK Amount class not registered: " + TestAmount.class,
234                providedClasses.contains(TestAmount.class));
235    }
236
237    /**
238     * Ensure amount factories are accessible for all types
239     * available,
240     * providing also the
241     * some test implementations with the
242     * TCK,
243     * and that
244     * every factory accessed
245     * is a new instance.
246     */
247    @Test(description = "4.2.7 Ensure amount factories are accessible for all types available in MonetaryAmounts.")
248    @SpecAssertion(section = "4.2.7", id = "427-B3")
249    public void testAmountTypesInstantiatable() {
250        Collection<Class> amountClasses = TCKTestSetup.getTestConfiguration().getAmountClasses();
251        for (Class amountType : amountClasses) {
252            MonetaryAmountFactory<?> f = MonetaryAmounts.getAmountFactory(amountType);
253            AssertJUnit.assertNotNull("Section 4.2.7: MonetaryAmountFactory returned by MonetaryAmounts is null for " +
254                    amountType.getName(), f);
255            MonetaryAmountFactory<?> f2 = MonetaryAmounts.getAmountFactory(amountType);
256            AssertJUnit.assertNotNull("Section 4.2.7: MonetaryAmountFactory returned by MonetaryAmounts is null for " +
257                    amountType.getName(), f2);
258            AssertJUnit.assertNotSame("MonetaryAmountFactory instances are not distinct for " + amountType.getName(), f,
259                    f2);
260            TestCurrencyUnit tc = new TestCurrencyUnit();
261            MonetaryAmount m1 = f.setNumber(0L).setCurrency(tc).create();
262            AssertJUnit.assertNotNull(
263                    "Section 4.2.7: MonetaryAmountFactory creates null amounts for " + amountType.getName(), m1);
264            AssertJUnit.assertTrue(
265                    "Section 4.2.7: MonetaryAmountFactory creates non zero amounts for " + amountType.getName(),
266                    m1.isZero());
267            AssertJUnit.assertEquals(
268                    "Section 4.2.7: MonetaryAmountFactory creates non zero amounts for " + amountType.getName(), 0L,
269                    m1.getNumber().longValue());
270            AssertJUnit.assertTrue(
271                    "Section 4.2.7: MonetaryAmountFactory creates non assignable amounts instances for " +
272                            amountType.getName(), amountType.isAssignableFrom(m1.getClass()));
273        }
274        MonetaryAmountFactory<?> f = MonetaryAmounts.getAmountFactory(TestAmount.class);
275        AssertJUnit.assertNotNull("Section 4.2.7: MonetaryAmountFactory returned by MonetaryAmounts is null for " +
276                TestAmount.class.getName(), f);
277        AssertJUnit.assertEquals("MonetaryAmountFactory returned by MonetaryAmounts is obfuscated or proxied for " +
278                        TestMonetaryAmountBuilder.class.getName(), TestMonetaryAmountBuilder.class,
279                f.getClass());
280    }
281
282    /**
283     * Ensure correct query function implementations, providing also
284     * the some test implementations with the TCK.
285     */
286    @Test(description =
287            "4.2.7 Ensure correct query function, MonetaryAmounts.getAmountFactories should return factory" +
288                    "for explicit acquired amount types.")
289    @SpecAssertion(section = "4.2.7", id = "427-B4")
290    public void testAmountQueryType() {
291        MonetaryAmountFactoryQuery ctx = MonetaryAmountFactoryQueryBuilder.of().setTargetType(TestAmount.class).build();
292        Collection<MonetaryAmountFactory<?>> factories = MonetaryAmounts.getAmountFactories(ctx);
293        AssertJUnit.assertNotNull("Section 4.2.7: Amount factory query should return explicitly queried factories",
294                factories);
295        boolean found = false;
296        for (MonetaryAmountFactory<?> f : factories) {
297            if (f.getAmountType().equals(TestAmount.class)) {
298                found = true;
299                break;
300            }
301        }
302        AssertJUnit.assertTrue("Section 4.2.7: Amount type query should return same explicitly queried factory", found);
303        ctx = MonetaryAmountFactoryQueryBuilder.of().build();
304        MonetaryAmountFactory<?> factory = MonetaryAmounts.getAmountFactory(ctx);
305        AssertJUnit.assertNotNull("Section 4.2.7: Amount type must be provided", factory);
306    }
307
308    /**
309     * Ensure a default factory is returned. Test javamoney.config
310     * for  configuring default value.
311     */
312    @Test(description = "4.2.7 Ensure a default MonetaryAmountFactory is available.")
313    @SpecAssertion(section = "4.2.7", id = "427-B5")
314    public void testAmountDefaultType() {
315        AssertJUnit.assertNotNull("Section 4.2.7: No default MonetaryAmountFactory found.",
316                MonetaryAmounts.getDefaultAmountFactory());
317        // TODO check default configuration...
318    }
319
320    // ********************************* C. Accessing Roundings *****************************
321
322    /**
323     * Access roundings using all defined currencies, including TCK
324     * custom currencies.
325     */
326    @Test(description = "4.2.7 Ensure MonetaryRoundings instances are available, for all registered currencies.")
327    @SpecAssertion(section = "4.2.7", id = "427-C1")
328    public void testAccessRoundingsForCustomCurrencies_Default() {
329        // Using default roundings...
330        TestCurrencyUnit cu = new TestCurrencyUnit("ASDF", 3);
331        MonetaryRounding r = MonetaryRoundings.getDefaultRounding();
332        MonetaryAmount m =
333                new TestMonetaryAmountBuilder().setNumber(new BigDecimal("12.123456789101222232323")).setCurrency(cu)
334                        .create();
335        AssertJUnit.assertEquals(
336                "Section 4.2.7: expected ASDF 12.123 with default rounding from ASDF 12.123456789101222232323",
337                "ASDF 12.123", m.with(r).toString());
338        // should not throw an error!
339        for (Currency currency : Currency.getAvailableCurrencies()) {
340            m = new TestMonetaryAmountBuilder().setNumber(new BigDecimal("12.123456789101222232323"))
341                    .setCurrency(currency.getCurrencyCode()).create();
342            if (currency.getDefaultFractionDigits() >= 0) {
343                MonetaryAmount rounded = m.with(r); // should not throw an error
344                AssertJUnit.assertEquals(
345                        "Section 4.2.7: Returned amount class must be the same as the input class to the rounding " +
346                                "operator.", TestAmount.class, rounded.getClass());
347                AssertJUnit.assertEquals("Section 4.2.7: Rounding did change currency: " + rounded.getClass().getName(),
348                        currency.getCurrencyCode(), rounded.getCurrency().getCurrencyCode());
349                AssertJUnit.assertNotSame(
350                        "Section 4.2.7: Rounding did not have any effect, should use scale==2 as default.",
351                        m.getNumber().getScale(), rounded.getNumber().getScale());
352            }
353        }
354    }
355
356    /**
357     * Access roundings using all defined currencies, including TCK
358     * custom currencies.
359     */
360    @Test(description = "4.2.7 Ensure MonetaryRoundings instances are available, also for any custom currency " +
361            "(not registered).")
362    @SpecAssertion(section = "4.2.7", id = "427-C1")
363    public void testAccessRoundingsForCustomCurrencies_Explicit() {
364        // Using default roundings...
365        TestCurrencyUnit cu = new TestCurrencyUnit("ASDF", 3);
366        MonetaryOperator r = MonetaryRoundings.getRounding(cu);
367        MonetaryAmount m =
368                new TestMonetaryAmountBuilder().setNumber(new BigDecimal("12.123456789101222232323")).setCurrency(cu)
369                        .create();
370        AssertJUnit.assertEquals("Section 4.2.7: Expected ASDF 12.123 for custom rounding 12.123456789101222232323.",
371                "ASDF 12.123", m.with(r).toString());
372        // should not throw an error!
373        for (Currency currency : Currency.getAvailableCurrencies()) {
374            if (currency.getDefaultFractionDigits() >= 0) {
375                r = MonetaryRoundings.getRounding(cu);
376                m = m.with(r); // should not throw an error
377                AssertJUnit.assertEquals(
378                        "Section 4.2.7: Expected ASDF 12.123 for rounding for Currency" + cu.getCurrencyCode(),
379                        "ASDF 12.123", m.with(r).toString());
380            } else {
381                try {
382                    r = MonetaryRoundings.getRounding(cu);
383                    AssertJUnit.assertNotNull(r);
384                } catch (MonetaryException e) {
385                    // OK
386                }
387            }
388        }
389    }
390
391    /**
392     * Access roundings using all defined currencies, including TCK
393     * custom currencies.
394     */
395    @Test(expectedExceptions = NullPointerException.class,
396            description = "4.2.7: Expected NullPointerException accessing a rounding with " +
397                    "'MonetaryRoundings.getRounding(null)'.")
398    @SpecAssertion(section = "4.2.7", id = "427-C1")
399    public void testAccessRoundingsForCustomCurrencies_Explicit_Null() {
400        MonetaryRoundings.getRounding((CurrencyUnit) null);
401    }
402
403
404    /**
405     * Access roundings using a MonetaryContext. Use different
406     * MathContext/RoundingMode, as an attribute, when running
407     * on the JDK.
408     */
409    @Test(description = "4.2.7 Ensure correct MonetaryRounding returned for a mathematical RoundingQuery.")
410    @SpecAssertion(section = "4.2.7", id = "427-C2")
411    public void testAccessRoundingsWithRoundingContext() {
412        RoundingQuery ctx = RoundingQueryBuilder.of().setScale(1).set(RoundingMode.UP).build();
413        MonetaryOperator r = MonetaryRoundings.getRounding(ctx);
414        AssertJUnit.assertNotNull("Section 4.2.7: No rounding provided for RoundingQuery: " + ctx, r);
415        MonetaryAmount m =
416                new TestMonetaryAmountBuilder().setNumber(new BigDecimal("12.123456789101222232323")).setCurrency("CHF")
417                        .create();
418        AssertJUnit.assertEquals("Section 4.2.7: Invalid rounding provided for RoundingQuery: " + ctx, "CHF 12.2",
419                m.with(r).toString());
420    }
421
422    /**
423     * Access roundings using a RoundingContext, that is null.
424     */
425    @Test(expectedExceptions = NullPointerException.class,
426            description = "4.2.7: Ensure NullPointerException is thrown for " +
427                    "'MonetaryRoundings.getRounding((RoundingContext) null)'.")
428    @SpecAssertion(section = "4.2.7", id = "427-C2")
429    public void testAccessRoundingsWithMonetaryContext_Null() {
430        MonetaryRoundings.getRounding(null);
431    }
432
433    /**
434     * Access custom roundings and ensure TCK custom roundings are
435     * registered.
436     */
437    @Test(description = "4.2.7 Access named roundings and ensure TCK named roundings are " + "registered.")
438    @SpecAssertion(section = "4.2.7", id = "427-C3")
439    public void testAccessCustomRoundings() {
440        Set<String> ids = MonetaryRoundings.getRoundingNames();
441        AssertJUnit.assertNotNull("Section 4.2.7: Custom Rounding key are null", ids);
442        AssertJUnit
443                .assertTrue("Section 4.2.7: At least NOSCALE custom rounding must be present", ids.contains("NOSCALE"));
444    }
445
446    /**
447     * Test TCK custom roundings.
448     */
449    @Test(description = "4.2.7 Access custom roundings and ensure correct functionality.")
450    @SpecAssertion(section = "4.2.7", id = "427-C4")
451    public void testCustomRoundings() {
452        MonetaryOperator r = MonetaryRoundings.getRounding("NOSCALE");
453        AssertJUnit.assertNotNull(r);
454        MonetaryAmount m =
455                new TestMonetaryAmountBuilder().setNumber(new BigDecimal("12.123456789101222232323")).setCurrency("CHF")
456                        .create();
457        AssertJUnit.assertEquals("Section 4.2.7: Expected CHF 12 for NOSCALE operator on " + m, "CHF 12",
458                m.with(r).toString());
459    }
460
461    /**
462     * Test TCK custom roundings.
463     */
464    @Test(expectedExceptions = NullPointerException.class,
465            description = "4.2.7 Ensure NullPointerException is thrown for MonetaryRoundings.getRounding((String) null).")
466    @SpecAssertion(section = "4.2.7", id = "427-C4")
467    public void testCustomRoundings_Null() {
468        MonetaryRoundings.getRounding((String) null);
469    }
470
471    /**
472     * Test TCK custom roundings.
473     */
474    @Test(expectedExceptions = MonetaryException.class,
475            description = "4.2.7 Ensure MonetaryException is thrown for " + "accessing invalid named rounding.")
476    @SpecAssertion(section = "4.2.7", id = "427-C4")
477    public void testCustomRoundings_Foo() {
478        assertNotNull("Section 4.2.7: Expected custom rounding with name 'foo'.", MonetaryRoundings.getRounding("foo"));
479    }
480
481}