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