diff --git a/test/jdk/java/text/Format/NumberFormat/BigDecimalCompatibilityTest.java b/test/jdk/java/text/Format/NumberFormat/BigDecimalCompatibilityTest.java index 2af23a4c82d..66fc44e368f 100644 --- a/test/jdk/java/text/Format/NumberFormat/BigDecimalCompatibilityTest.java +++ b/test/jdk/java/text/Format/NumberFormat/BigDecimalCompatibilityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,58 +24,85 @@ /* * @test * @bug 4018937 - * @summary Confirm that DecimalFormat.parse() parses BigDecimal and BigInteger as expected. + * @summary Confirm that DecimalFormat.parse() parses BigDecimal and BigInteger + * string values as expected. Specifically, ensure a ParseException is + * not thrown as well as the parsed value being numerically correct. + * Tests large String values with combinations of multipliers and exponents. + * @run junit BigDecimalCompatibilityTest */ -import java.math.*; -import java.text.*; -import java.util.*; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Locale; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; public class BigDecimalCompatibilityTest { - static boolean err = false; + private static DecimalFormat df = new DecimalFormat(); + // Save JVM default Locale + private static final Locale savedLocale = Locale.getDefault(); - static final String[] input_data = { - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" - }; - static final String[] exponents = { - "E-100", "E100", "E-900", "E900", "" - }; - static final int[] multipliers = { - -1, 1, -100, 100, -9999, 9999 + // ---- Used for the test data (start) ---- + + // Both ArrayList composed of Arguments(String longString, int multiplier) + private static final ArrayList bigIntegers = new ArrayList(); + private static final ArrayList bigDecimals = new ArrayList(); + + // Long string data to generate combinations of test values + private static final String[] inputData = { + "0".repeat(400), + "1234567890".repeat(40)}; + + // Variety of exponents to test parse() against + private static final String[] exponents = { + "E-100", "E100", "E-900", "E900", "" }; - public static void main(String[] args) throws Exception { - Locale loc = Locale.getDefault(); + // Variety of multipliers that DecimalFormat can apply + private static final int[] multipliers = { + -1, 1, -100, 100, -9999, 9999 + }; + // ---- Used for the test data (end) ---- + + // Set JVM default Locale to US and populate the test arrayLists + @BeforeAll + static void initAll() { Locale.setDefault(Locale.US); - - testBigDecimal(); - testBigInteger(); - - Locale.setDefault(loc); - - if (err) { - throw new RuntimeException("Error: Unexpected value"); - } + buildTestData(); } - static private void testBigDecimal() { - DecimalFormat df = new DecimalFormat(); - df.setParseBigDecimal(true); - df.setMaximumFractionDigits(Integer.MAX_VALUE); - - for (int i = 0; i < input_data.length; i++) { - for (int j = 0; j < input_data.length; j++) { - for (int k = 0; k < input_data.length; k++) { - for (int l = 0; l < input_data.length; l++) { - for (int m = 0; m < exponents.length; m++) { - String s = input_data[i] + input_data[j] + '.' + - input_data[k] + input_data[l] + - exponents[m]; - for (int n = 0; n < multipliers.length; n++) { - test(df, s, multipliers[n]); - test(df, '-'+s, multipliers[n]); + /* + * Uses inputData and exponents to build long string + * decimal and integer values and populate bigDecimals and bigIntegers + * accordingly. Attaches a multiplier value as well to the test data. + */ + private static void buildTestData() { + for (String longString1 : inputData) { + for (String longString2 : inputData) { + String bigInteger = longString1 + longString2; + for (int multiplier : multipliers) { + bigIntegers.add(Arguments.of(bigInteger, multiplier)); + bigIntegers.add(Arguments.of('-' + bigInteger, multiplier)); + } + for (String longString3 : inputData) { + for (String longString4 : inputData) { + for (String exponent : exponents) { + String bigDecimal = longString1 + longString2 + '.' + + longString3 + longString4 + exponent; + for (int multiplier : multipliers) { + bigDecimals.add(Arguments.of(bigDecimal, multiplier)); + bigDecimals.add(Arguments.of('-' + bigDecimal, multiplier)); } } } @@ -84,51 +111,70 @@ public class BigDecimalCompatibilityTest { } } - static private void testBigInteger() { - DecimalFormat df = new DecimalFormat(); - df.setParseBigDecimal(true); - df.setMaximumFractionDigits(Integer.MAX_VALUE); - - for (int i = 0; i < input_data.length; i++) { - for (int j = 0; j < input_data.length; j++) { - String s = input_data[i] + input_data[j]; - for (int k = 0; k < multipliers.length; k++) { - test(df, s, multipliers[k]); - test(df, '-'+s, multipliers[k]); - } - } - } + // Restore JVM default Locale + @AfterAll + static void tearDownAll() { + Locale.setDefault(savedLocale); } - static void test(DecimalFormat df, String s, int multiplier) { + // Tests strings with length 1600+. See test() for specific details. + @ParameterizedTest + @MethodSource("bigDecimalProvider") + public void bigDecimalParseTest(String longString, int multiplier) { + test(longString, multiplier); + } + + // Returns 960 arrangements of bigDecimal string values and multipliers + // In the form of (String, int). + private static Stream bigDecimalProvider() { + return bigDecimals.stream(); + } + + // Tests strings with length 800+. See test() for specific details. + @ParameterizedTest + @MethodSource("bigIntegerProvider") + public void bigIntegerParseTest(String longString, int multiplier) { + test(longString, multiplier); + } + + // Returns 48 arrangements of bigInteger string values and multipliers + // In the form of (String, int). + private static Stream bigIntegerProvider() { + return bigIntegers.stream(); + } + + /* + * Tests that parsing a large BigDecimal/BigInteger string value + * will not throw a ParseException with setParseBigDecimal as true. + * Parses with a variety of multiplier values. Then ensures that the parsed + * value is the expected number. + */ + private static void test(String longString, int multiplier) { + // Reset DecimalFormat for a clean test + df = new DecimalFormat(); + df.setParseBigDecimal(true); + // wide enough to support the long string test data + df.setMaximumFractionDigits(Integer.MAX_VALUE); df.setMultiplier(multiplier); - Number num = null; - try { - num = df.parse(s); - } - catch (ParseException e) { - err = true; - System.err.println("Failed: Exception occurred: " + e.getMessage()); - return; - } + // Check parse and returned value. This was originally intended to ensure + // a ParseException is not thrown + Number parsedValue = assertDoesNotThrow(()-> df.parse(longString), + "Should not throw an Exception"); + BigDecimal expectedValue = getExpected(longString, multiplier); + assertEquals(expectedValue, parsedValue, "With multiplier: " + multiplier); + } - BigDecimal bd = new BigDecimal(s); + // Utility to get a numerically correct value of a long string. + // Dependent on BigDecimal implementation + private static BigDecimal getExpected(String longString, int multiplier) { + BigDecimal expected = new BigDecimal(longString); try { - bd = bd.divide(new BigDecimal(multiplier)); + expected = expected.divide(new BigDecimal(multiplier)); } catch (ArithmeticException e) { - bd = bd.divide(new BigDecimal(multiplier), RoundingMode.HALF_EVEN); - } - check(num, bd, multiplier); - } - - static void check(Number got, BigDecimal expected, int multiplier) { - if (!got.equals(expected)) { - err = true; - System.err.println("Failed: got:" + got + - ", expected: " + expected + - ", multiplier=" + multiplier); + expected = expected.divide(new BigDecimal(multiplier), RoundingMode.HALF_EVEN); } + return expected; } } diff --git a/test/jdk/java/text/Format/NumberFormat/Bug4208135.java b/test/jdk/java/text/Format/NumberFormat/Bug4208135.java index 1d641226ab7..301b90c89f3 100644 --- a/test/jdk/java/text/Format/NumberFormat/Bug4208135.java +++ b/test/jdk/java/text/Format/NumberFormat/Bug4208135.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,105 +23,164 @@ /* * @test - * @summary Confirm that the decimal separator is shown when explicitly requested. + * @summary Confirm that the decimal separator is shown when explicitly requested + * (or not shown if not requested). Tests against double, long, BigDecimal, + * and BigInteger with a combination of different patterns. * @bug 4208135 + * @run junit Bug4208135 */ -import java.math.*; -import java.text.*; -import java.util.*; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.DecimalFormat; +import java.util.Locale; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class Bug4208135 { - static DecimalFormat df; + private static DecimalFormat df; + // Save JVM default Locale + private static final Locale savedLocale = Locale.getDefault(); - static boolean err = false; - - static public void main(String[] args){ - - Locale defaultLoc = Locale.getDefault(); + // Set JVM default locale to US + @BeforeAll + static void init() { Locale.setDefault(Locale.US); - - df = new DecimalFormat(); - - df.applyPattern("0.#E0"); - - df.setDecimalSeparatorAlwaysShown(true); - checkFormat(0.0, "0.E0"); - checkFormat(10.0, "1.E1"); - checkFormat(1000.0, "1.E3"); - checkFormat(0L, "0.E0"); - checkFormat(10L, "1.E1"); - checkFormat(1000L, "1.E3"); - checkFormat(new BigDecimal("0.0"), "0.E0"); - checkFormat(new BigDecimal("10.0"), "1.E1"); - checkFormat(new BigDecimal("1000.0"), "1.E3"); - checkFormat(new BigInteger("00"), "0.E0"); - checkFormat(new BigInteger("10"), "1.E1"); - checkFormat(new BigInteger("1000"), "1.E3"); - - df.setDecimalSeparatorAlwaysShown(false); - checkFormat(0.0, "0E0"); - checkFormat(10.0, "1E1"); - checkFormat(1000.0, "1E3"); - checkFormat(0L, "0E0"); - checkFormat(10L, "1E1"); - checkFormat(1000L, "1E3"); - checkFormat(new BigDecimal("0.0"), "0E0"); - checkFormat(new BigDecimal("10.0"), "1E1"); - checkFormat(new BigDecimal("1000.0"), "1E3"); - checkFormat(new BigInteger("0"), "0E0"); - checkFormat(new BigInteger("10"), "1E1"); - checkFormat(new BigInteger("1000"), "1E3"); - - df.applyPattern("0.###"); - - df.setDecimalSeparatorAlwaysShown(true); - checkFormat(0.0, "0."); - checkFormat(10.0, "10."); - checkFormat(1000.0, "1000."); - checkFormat(0L, "0."); - checkFormat(10L, "10."); - checkFormat(1000L, "1000."); - checkFormat(new BigDecimal("0.0"), "0."); - checkFormat(new BigDecimal("10.0"), "10."); - checkFormat(new BigDecimal("1000.0"), "1000."); - checkFormat(new BigInteger("0"), "0."); - checkFormat(new BigInteger("10"), "10."); - checkFormat(new BigInteger("1000"), "1000."); - - df.setDecimalSeparatorAlwaysShown(false); - checkFormat(0.0, "0"); - checkFormat(10.0, "10"); - checkFormat(1000.0, "1000"); - checkFormat(0L, "0"); - checkFormat(10L, "10"); - checkFormat(1000L, "1000"); - checkFormat(new BigDecimal("0.0"), "0"); - checkFormat(new BigDecimal("10.0"), "10"); - checkFormat(new BigDecimal("1000.0"), "1000"); - checkFormat(new BigInteger("0"), "0"); - checkFormat(new BigInteger("10"), "10"); - checkFormat(new BigInteger("1000"), "1000"); - - Locale.setDefault(defaultLoc); - - if (err) { - throw new RuntimeException("Wrong format/parse with DecimalFormat"); - } } - static void checkFormat(Number num, String expected) { - String got = df.format(num); - if (!got.equals(expected)) { - err = true; - System.err.println(" DecimalFormat format(" + - num.getClass().getName() + - ") error:" + - "\n\tnumber: " + num + - "\n\tSeparatorShown? : " + df.isDecimalSeparatorAlwaysShown() + - "\n\tgot: " + got + - "\n\texpected: " + expected); - } + // Restore JVM default locale + @AfterAll + static void tearDown() { + Locale.setDefault(savedLocale); + } + + // Confirm that decimal separator shown when formatting a number + @ParameterizedTest + @MethodSource("fractionalDigitsWithSeparatorProvider") + public void fractionalDigitsWithSeparatorTest(Number num, String expected) { + df = getDF("0.#E0", true); + String actualFormatted = df.format(num); + assertEquals(expected, actualFormatted, getErrMsg("0.#E0", true)); + } + + // Combination of numbers and a fractional exponent pattern with a separator + private static Stream fractionalDigitsWithSeparatorProvider() { + return Stream.of( + Arguments.of(0.0, "0.E0"), + Arguments.of(10.0, "1.E1"), + Arguments.of(1000.0, "1.E3"), + Arguments.of(0L, "0.E0"), + Arguments.of(10L, "1.E1"), + Arguments.of(1000L, "1.E3"), + Arguments.of(new BigDecimal("0.0"), "0.E0"), + Arguments.of(new BigDecimal("10.0"), "1.E1"), + Arguments.of(new BigDecimal("1000.0"), "1.E3"), + Arguments.of(new BigInteger("00"), "0.E0"), + Arguments.of(new BigInteger("10"), "1.E1"), + Arguments.of(new BigInteger("1000"), "1.E3") + ); + } + + // Confirm that decimal separator not shown when formatting a number + @ParameterizedTest + @MethodSource("fractionalDigitsNoSeparatorProvider") + public void fractionalDigitsNoSeparatorTest(Number num, String expected) { + df = getDF("0.#E0", false); + String actualFormatted = df.format(num); + assertEquals(expected, actualFormatted, getErrMsg("0.#E0", false)); + } + + // Combination of numbers and a fractional exponent pattern with no separator + private static Stream fractionalDigitsNoSeparatorProvider() { + return Stream.of( + Arguments.of(0.0, "0E0"), + Arguments.of(10.0, "1E1"), + Arguments.of(1000.0, "1E3"), + Arguments.of(0L, "0E0"), + Arguments.of(10L, "1E1"), + Arguments.of(1000L, "1E3"), + Arguments.of(new BigDecimal("0.0"), "0E0"), + Arguments.of(new BigDecimal("10.0"), "1E1"), + Arguments.of(new BigDecimal("1000.0"), "1E3"), + Arguments.of(new BigInteger("00"), "0E0"), + Arguments.of(new BigInteger("10"), "1E1"), + Arguments.of(new BigInteger("1000"), "1E3") + ); + } + + // Confirm that decimal separator shown when formatting a number + @ParameterizedTest + @MethodSource("noFractionalDigitsWithSeparatorProvider") + public void noFractionalDigitsWithSeparatorTest(Number num, String expected) { + df = getDF("0.###", true); + String actualFormatted = df.format(num); + assertEquals(expected, actualFormatted, getErrMsg("0.###", true)); + } + + // Combination of numbers and a non-fractional exponent pattern with a separator + private static Stream noFractionalDigitsWithSeparatorProvider() { + return Stream.of( + Arguments.of(0.0, "0."), + Arguments.of(10.0, "10."), + Arguments.of(1000.0, "1000."), + Arguments.of(0L, "0."), + Arguments.of(10L, "10."), + Arguments.of(1000L, "1000."), + Arguments.of(new BigDecimal("0.0"), "0."), + Arguments.of(new BigDecimal("10.0"), "10."), + Arguments.of(new BigDecimal("1000.0"), "1000."), + Arguments.of(new BigInteger("00"), "0."), + Arguments.of(new BigInteger("10"), "10."), + Arguments.of(new BigInteger("1000"), "1000.") + ); + } + + // Confirm that decimal separator not shown when formatting a number + @ParameterizedTest + @MethodSource("noFractionalDigitsNoSeparatorProvider") + public void noFractionalDigitsNoSeparatorTest(Number num, String expected) { + df = getDF("0.###", false); + String actualFormatted = df.format(num); + assertEquals(expected, actualFormatted, getErrMsg("0.###", false)); + } + + // Combination of numbers and a non-fractional exponent pattern with no separator + private static Stream noFractionalDigitsNoSeparatorProvider() { + return Stream.of( + Arguments.of(0.0, "0"), + Arguments.of(10.0, "10"), + Arguments.of(1000.0, "1000"), + Arguments.of(0L, "0"), + Arguments.of(10L, "10"), + Arguments.of(1000L, "1000"), + Arguments.of(new BigDecimal("0.0"), "0"), + Arguments.of(new BigDecimal("10.0"), "10"), + Arguments.of(new BigDecimal("1000.0"), "1000"), + Arguments.of(new BigInteger("00"), "0"), + Arguments.of(new BigInteger("10"), "10"), + Arguments.of(new BigInteger("1000"), "1000") + ); + } + + // Creates clean DF and sets the pattern and separatorShown value + private static DecimalFormat getDF(String pattern, boolean separatorShown) { + df = new DecimalFormat(); + df.applyPattern(pattern); + df.setDecimalSeparatorAlwaysShown(separatorShown); + return df; + } + + // Utility to get a helpful error message when values are not as expected + private static String getErrMsg(String pattern, boolean separatorShown) { + return String.format("Fails with pattern= %s, with separatorShown = %s", + pattern, separatorShown); } } diff --git a/test/jdk/java/text/Format/NumberFormat/Bug4838107.java b/test/jdk/java/text/Format/NumberFormat/Bug4838107.java index 452660b1ab2..e672bbfcf14 100644 --- a/test/jdk/java/text/Format/NumberFormat/Bug4838107.java +++ b/test/jdk/java/text/Format/NumberFormat/Bug4838107.java @@ -24,228 +24,259 @@ /* * @test * @bug 4838107 8008577 - * @summary Confirm that DecimalFormat can format a number with negative exponent number correctly. + * @summary Confirm that DecimalFormat can format a number with a negative + * exponent number correctly. Tests also involve using a DecimalFormat + * with a custom pattern or a custom minus sign. * @run junit/othervm -Djava.locale.providers=COMPAT,SPI Bug4838107 */ -import java.math.*; -import java.util.*; -import java.text.*; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; +import java.util.stream.Stream; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; +/* + * This bug is about exponential formatting. But I added test cases for: + * - Double and BigDecimal numbers which don't have exponent parts. + * - Long and BigInteger numbers which don't support exponential + * notation. + * because there are few test cases for suffix and prefix. + * And also, I added test cases to guarantee further formatting and + * parsing using the same DecimalFormat instance will not change the + * Number's value anymore. + */ public class Bug4838107 { - static DecimalFormat df; - static DecimalFormatSymbols dfs; - static boolean err = false; + // Save JVM default Locale + private static final Locale savedLocale = Locale.getDefault(); - static public void main(String[] args) { - Locale defaultLoc = Locale.getDefault(); + // Set JVM default Locale to US + @BeforeAll + static void init() { Locale.setDefault(Locale.US); - - /** - * This bug is about exponential formatting. But I added test cases for: - * - Double and BigDecimal numbers which don't have exponent parts. - * - Long and BigInteger numbers which don't support exponential - * notation. - * because there are few test cases for suffix and prefix. - * And also, I added test cases to guarantee further formatting and - * parsing using the same DecimalFormat instance will not change the - * Number's value anymore. - */ - - test_double(); - test_long(); - test_BigDecimal(); - test_BigInteger(); - - Locale.setDefault(defaultLoc); - - if (err) { - throw new RuntimeException("Wrong format with DecimalFormat"); - } } - static void test_double() { - df = new DecimalFormat(); - dfs = df.getDecimalFormatSymbols(); - - /* Test with default pattern */ - test(1234D, "1,234"); - test(0.1234, "0.123"); // rounded - test(-1234D, "-1,234"); - test(-0.1234, "-0.123"); // rounded - - test(Double.POSITIVE_INFINITY, "\u221e"); - test(Double.NEGATIVE_INFINITY, "-\u221e"); - test(Double.NaN, "\ufffd"); // without prefix and suffix - test(0.0, "0"); - test(-0.0, "-0"); // with the minus sign - - /* Specify a pattern and the minus sign. */ - prepareFormatter("

#.###E00", 'm'); - test(1234D, "

1.234E03"); - test(0.1234, "

1.234Em01"); - test(-1234D, "m

1.234E03"); - test(-0.1234, "m

1.234Em01"); - - prepareFormatter("

#.###E00;#.###E00", 'm'); - test(1234D, "

1.234E03"); - test(0.1234, "

1.234Em01"); - test(-1234D, "1.234E03"); - test(-0.1234, "1.234Em01"); - - prepareFormatter("#.###E00;

#.###E00", 'm'); - test(1234D, "1.234E03"); - test(0.1234, "1.234Em01"); - test(-1234D, "

1.234E03"); - test(-0.1234, "

1.234Em01"); - - prepareFormatter("

#.###E00;

-#.###E00", 'm'); - test(1234D, "

1.234E03"); - test(0.1234, "

1.234Em01"); - test(-1234D, "

m1.234E03"); - test(-0.1234, "

m1.234Em01"); - - test(Double.POSITIVE_INFINITY, "

\u221e"); - test(Double.NEGATIVE_INFINITY, "

m\u221e"); - test(Double.NaN, "\ufffd"); // without prefix and suffix - test(0.0, "

0E00"); - test(-0.0, "

m0E00"); // with the minus sign + // Restore the original JVM default locale + @AfterAll + static void tearDown() { + Locale.setDefault(savedLocale); } - static void test_BigDecimal() { - df = new DecimalFormat(); - dfs = df.getDecimalFormatSymbols(); - - /* Test with default pattern */ - test(new BigDecimal("123456789012345678901234567890"), - "123,456,789,012,345,678,901,234,567,890"); - test(new BigDecimal("0.000000000123456789012345678901234567890"), - "0"); - test(new BigDecimal("-123456789012345678901234567890"), - "-123,456,789,012,345,678,901,234,567,890"); - test(new BigDecimal("-0.000000000123456789012345678901234567890"), - "-0"); - - test(new BigDecimal("0"), "0"); - test(new BigDecimal("-0"), "0"); - - /* Specify a pattern and the minus sign. */ - prepareFormatter("

#.####################E00;

-#.####################E00", 'm'); - test(new BigDecimal("123456789012345678901234567890"), - "

1.23456789012345678901E29"); - test(new BigDecimal("0.000000000123456789012345678901234567890"), - "

1.23456789012345678901Em10"); - test(new BigDecimal("-123456789012345678901234567890"), - "

m1.23456789012345678901E29"); - test(new BigDecimal("-0.000000000123456789012345678901234567890"), - "

m1.23456789012345678901Em10"); - - test(new BigDecimal("0"), "

0E00"); - test(new BigDecimal("-0"), "

0E00"); + // Check that negative exponent number recognized for doubles + @ParameterizedTest + @MethodSource("doubles") + public void doubleTest(Number num, String str, DecimalFormat df) { + test(num, str, df); } - static void test_long() { - df = new DecimalFormat(); - dfs = df.getDecimalFormatSymbols(); - - /* Test with default pattern */ - test(123456789L, "123,456,789"); - test(-123456789L, "-123,456,789"); - - test(0L, "0"); - test(-0L, "0"); - - /* Specify a pattern and the minus sign. */ - prepareFormatter("

#,###;

-#,###", 'm'); - test(123456789L, "

123,456,789"); - test(-123456789L, "

m123,456,789"); - - test(0L, "

0"); - test(-0L, "

0"); + // Provides a double to be formatted, which is compared to the expected String. + // Additionally, provides a DecimalFormat to do the formatting (can have a custom + // pattern and minus sign). Given in the form (double, String, DecimalFormat). + private static Stream doubles() { + DecimalFormat defaultDf = new DecimalFormat(); + DecimalFormat customDf1 = getDecimalFormat("

#.###E00", 'm'); + DecimalFormat customDf2 = getDecimalFormat("

#.###E00;#.###E00", 'm'); + DecimalFormat customDf3 = getDecimalFormat("#.###E00;

#.###E00", 'm'); + DecimalFormat customDf4 = getDecimalFormat("

#.###E00;

-#.###E00", 'm'); + return Stream.of( + // Test with default pattern + Arguments.of(1234D, "1,234", defaultDf), + Arguments.of(0.1234, "0.123", defaultDf), // rounded + Arguments.of(-1234D, "-1,234", defaultDf), + Arguments.of(-0.1234, "-0.123", defaultDf), // rounded + Arguments.of(Double.POSITIVE_INFINITY, "\u221e", defaultDf), + Arguments.of(Double.NEGATIVE_INFINITY, "-\u221e", defaultDf), + Arguments.of(Double.NaN, "\ufffd", defaultDf), // without prefix and suffix + Arguments.of(0.0, "0", defaultDf), + Arguments.of(-0.0, "-0", defaultDf), // with the minus sign + // Test with a pattern and the minus sign + Arguments.of(1234D, "

1.234E03", customDf1), + Arguments.of(0.1234, "

1.234Em01", customDf1), + Arguments.of(-1234D, "m

1.234E03", customDf1), + Arguments.of(-0.1234, "m

1.234Em01", customDf1), + Arguments.of(1234D, "

1.234E03", customDf2), + Arguments.of(0.1234, "

1.234Em01", customDf2), + Arguments.of(-1234D, "1.234E03", customDf2), + Arguments.of(-0.1234, "1.234Em01", customDf2), + Arguments.of(1234D, "1.234E03", customDf3), + Arguments.of(0.1234, "1.234Em01", customDf3), + Arguments.of(-1234D, "

1.234E03", customDf3), + Arguments.of(-0.1234, "

1.234Em01", customDf3), + Arguments.of(1234D, "

1.234E03", customDf4), + Arguments.of(0.1234, "

1.234Em01", customDf4), + Arguments.of(-1234D, "

m1.234E03", customDf4), + Arguments.of(-0.1234, "

m1.234Em01", customDf4), + Arguments.of(Double.POSITIVE_INFINITY, "

\u221e", customDf4), + Arguments.of(Double.NEGATIVE_INFINITY, "

m\u221e", customDf4), + Arguments.of(Double.NaN, "\ufffd", customDf4), // without prefix and suffix + Arguments.of(0.0, "

0E00", customDf4), + Arguments.of(-0.0, "

m0E00", customDf4) // with the minus sign + ); } - static void test_BigInteger() { - df = new DecimalFormat(); - dfs = df.getDecimalFormatSymbols(); - - /* Test with default pattern */ - test(new BigInteger("123456789012345678901234567890"), - "123,456,789,012,345,678,901,234,567,890"); - test(new BigInteger("-123456789012345678901234567890"), - "-123,456,789,012,345,678,901,234,567,890"); - - test(new BigInteger("0"), "0"); - test(new BigInteger("-0"), "0"); - - /* Specify a pattern and the minus sign. */ - prepareFormatter("

#,###;

-#,###", 'm'); - test(new BigInteger("123456789012345678901234567890"), - "

123,456,789,012,345,678,901,234,567,890"); - test(new BigInteger("-123456789012345678901234567890"), - "

m123,456,789,012,345,678,901,234,567,890"); - - test(new BigInteger("0"), "

0"); - test(new BigInteger("-0"), "

0"); + // Check that negative exponent number recognized for longs + @ParameterizedTest + @MethodSource("longs") + public void longTest(Number num, String str, DecimalFormat df) { + test(num, str, df); } - static void prepareFormatter(String pattern, char minusSign) { - dfs = df.getDecimalFormatSymbols(); - df.applyPattern(pattern); - dfs.setMinusSign(minusSign); - df.setDecimalFormatSymbols(dfs); + // Same as doubles() data provider, but with long values + // Given in the form (long, String, DecimalFormat). + private static Stream longs() { + DecimalFormat defaultDf = new DecimalFormat(); + DecimalFormat customDf = getDecimalFormat( + "

#,###;

-#,###", 'm'); + return Stream.of( + // Test with default pattern + Arguments.of(123456789L, "123,456,789", defaultDf), + Arguments.of(-123456789L, "-123,456,789", defaultDf), + Arguments.of(0L, "0", defaultDf), + Arguments.of(-0L, "0", defaultDf), + // Test with a pattern and the minus sign + Arguments.of(123456789L, "

123,456,789", customDf), + Arguments.of(-123456789L, "

m123,456,789", customDf), + Arguments.of(0L, "

0", customDf), + Arguments.of(-0L, "

0", customDf) + ); } - static void test(Number num, String str) { + // Check that negative exponent number recognized for bigDecimals + @ParameterizedTest + @MethodSource("bigDecimals") + public void bigDecimalTest(Number num, String str, DecimalFormat df) { + test(num, str, df); + } + + // Same as doubles() data provider, but with BigDecimal values + // Given in the form (BigDecimal, String, DecimalFormat). + private static Stream bigDecimals() { + DecimalFormat defaultDf = new DecimalFormat(); + DecimalFormat customDf = getDecimalFormat( + "

#.####################E00;

-#.####################E00", 'm'); + return Stream.of( + // Test with default pattern + Arguments.of(new BigDecimal("123456789012345678901234567890"), + "123,456,789,012,345,678,901,234,567,890", defaultDf), + Arguments.of(new BigDecimal("0.000000000123456789012345678901234567890"), + "0", defaultDf), + Arguments.of(new BigDecimal("-123456789012345678901234567890"), + "-123,456,789,012,345,678,901,234,567,890", defaultDf), + Arguments.of(new BigDecimal("-0.000000000123456789012345678901234567890"), + "-0", defaultDf), + Arguments.of(new BigDecimal("0"), "0", defaultDf), + Arguments.of(new BigDecimal("-0"), "0", defaultDf), + // Test with a pattern and the minus sign + Arguments.of(new BigDecimal("123456789012345678901234567890"), + "

1.23456789012345678901E29", customDf), + Arguments.of(new BigDecimal("0.000000000123456789012345678901234567890"), + "

1.23456789012345678901Em10", customDf), + Arguments.of(new BigDecimal("-123456789012345678901234567890"), + "

m1.23456789012345678901E29", customDf), + Arguments.of(new BigDecimal("-0.000000000123456789012345678901234567890"), + "

m1.23456789012345678901Em10", customDf), + Arguments.of(new BigDecimal("0"), "

0E00", customDf), + Arguments.of(new BigDecimal("-0"), "

0E00", customDf) + ); + } + + // Check that negative exponent number recognized for bigIntegers + @ParameterizedTest + @MethodSource("bigIntegers") + public void bigIntegerTest(Number num, String str, DecimalFormat df) { + test(num, str, df); + } + + // Same as doubles() data provider, but with BigInteger values + // Given in the form (BigInteger, String, DecimalFormat). + private static Stream bigIntegers() { + DecimalFormat defaultDf = new DecimalFormat(); + DecimalFormat customDf = getDecimalFormat( + "

#,###;

-#,###", 'm'); + return Stream.of( + // Test with default pattern + Arguments.of(new BigInteger("123456789012345678901234567890"), + "123,456,789,012,345,678,901,234,567,890", defaultDf), + Arguments.of(new BigInteger("-123456789012345678901234567890"), + "-123,456,789,012,345,678,901,234,567,890", defaultDf), + Arguments.of(new BigInteger("0"), "0", defaultDf), + Arguments.of(new BigInteger("-0"), "0", defaultDf), + // Test with a pattern and the minus sign + Arguments.of(new BigInteger("123456789012345678901234567890"), + "

123,456,789,012,345,678,901,234,567,890", customDf), + Arguments.of(new BigInteger("-123456789012345678901234567890"), + "

m123,456,789,012,345,678,901,234,567,890", customDf), + Arguments.of(new BigInteger("0"), "

0", customDf), + Arguments.of(new BigInteger("-0"), "

0", customDf) + ); + } + + // Check that the formatted value is correct and also check that + // it can be round-tripped via parse() and format() + private static void test(Number num, String str, DecimalFormat df) { String formatted = df.format(num); - if (!formatted.equals(str)) { - err = true; - System.err.println(" DecimalFormat format(" + - num.getClass().getName() + - ") error: \n\tnumber: " + num + - "\n\tminus sign: " + dfs.getMinusSign() + - "\n\tgot: " + formatted + - "\n\texpected: " + str); - return; - } + assertEquals(str, formatted, String.format("DecimalFormat format(%s) " + + "Error: number: %s, minus sign: %s", num.getClass().getName(), num, df.getDecimalFormatSymbols().getMinusSign())); if (num instanceof BigDecimal || num instanceof BigInteger) { df.setParseBigDecimal(true); } + testRoundTrip(formatted, str, num, df); + } + + // Test that a parsed value can be round-tripped via format() and parse() + private static void testRoundTrip(String formatted, String str, + Number num, DecimalFormat df) { Number parsed1 = null, parsed2 = null; try { parsed1 = df.parse(formatted); formatted = df.format(parsed1); parsed2 = df.parse(formatted); - if (!parsed1.equals(parsed2)) { - err = true; - System.err.println(" DecimalFormat roundtrip parse(" + - num.getClass().getName() + - ") error: \n\toriginal number: " + str + - "\n\tparsed number: " + parsed1 + - " (" + parsed1.getClass().getName() + ")" + - "\n\tformatted number: " + formatted + - "\n\tre-parsed number: " + parsed2 + - " (" + parsed2.getClass().getName() + ")" + - "\n\tminus sign: " + dfs.getMinusSign()); - } + assertEquals(parsed2, parsed1, """ + DecimalFormat round trip parse(%s) error: + original number: %s + parsed number: %s + (%s) + formatted number: %s + re-parsed number: %s + (%s) + minus sign: %s + """.formatted(num.getClass().getName(), str, parsed1, parsed1.getClass().getName(), + formatted, parsed2, parsed2.getClass().getName(), df.getDecimalFormatSymbols().getMinusSign())); } catch (Exception e) { - err = true; - System.err.println(" DecimalFormat parse(" + - num.getClass().getName() + - ") threw an Exception: " + e.getMessage() + - "\n\toriginal number: " + str + - "\n\tparsed number : " + parsed1 + - " (" + parsed1.getClass().getName() + ")" + - "\n\tformatted number: " + formatted + - "\n\tre-parsed number: " + parsed2 + - " (" + parsed2.getClass().getName() + ")" + - "\n\tminus sign: " + dfs.getMinusSign()); + fail(""" + DecimalFormat parse(%s) threw an Exception: %s + original number: %s + parsed number: %s + (%s) + formatted number: %s + re-parsed number: %s + (%s) + minus sign: %s + """.formatted(num.getClass().getName(), e.getMessage(), str, parsed1, parsed1.getClass().getName(), + formatted, parsed2, parsed2.getClass().getName(), df.getDecimalFormatSymbols().getMinusSign())); } } + + // Set up custom DecimalFormat with DecimalFormatSymbols + private static DecimalFormat getDecimalFormat(String pattern, char minusSign) { + DecimalFormat df = new DecimalFormat(); + DecimalFormatSymbols dfs = df.getDecimalFormatSymbols(); + df.applyPattern(pattern); + dfs.setMinusSign(minusSign); + df.setDecimalFormatSymbols(dfs); + return df; + } } diff --git a/test/jdk/java/text/Format/NumberFormat/Bug4944439.java b/test/jdk/java/text/Format/NumberFormat/Bug4944439.java index 0e85a98119d..561052e9a95 100644 --- a/test/jdk/java/text/Format/NumberFormat/Bug4944439.java +++ b/test/jdk/java/text/Format/NumberFormat/Bug4944439.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,87 +25,97 @@ * @test * @bug 4944439 * @summary Confirm that numbers where all digits after the decimal separator are 0 - * and which are between Long.MIN_VALUE and Long.MAX_VALUE are returned as Long(not double). + * and which are between Long.MIN_VALUE and Long.MAX_VALUE are returned + * as Long(not double). + * @run junit Bug4944439 */ -import java.math.BigDecimal; -import java.math.BigInteger; import java.text.DecimalFormat; +import java.util.ArrayList; import java.util.Locale; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; public class Bug4944439 { - static boolean err = false; - static DecimalFormat df; + // Save JVM default locale + private static final Locale savedLocale = Locale.getDefault(); + private static final DecimalFormat df = new DecimalFormat(); - public static void main(String[] args) throws Exception { - - Locale defaultLoc = Locale.getDefault(); + // Set JVM default locale to US for testing + @BeforeAll + static void initAll() { Locale.setDefault(Locale.US); - - df = new DecimalFormat(); - String s = "-9223372036854775809"; // Long.MIN_VALUE-1 - check_Double(s); - - test(Long.MIN_VALUE, Long.MIN_VALUE+10); - test(-10, 10); - test(Long.MAX_VALUE-10, Long.MAX_VALUE-1); - - s = "9223372036854775807.00"; // Long.MAX_VALUE - check_Long(s); - s = "9223372036854775808"; // Long.MAX_VALUE+1 - check_Double(s); - - s = "-0.0"; - check_Double(s); - s = "0.0"; - check_Long(s); - - Locale.setDefault(defaultLoc); - - if (err) { - throw new RuntimeException("Wrong parsing with DecimalFormat"); - } } - private static void test(long from, long to) throws Exception { + // Restore JVM default locale + @AfterAll + static void tearDownAll() { + Locale.setDefault(savedLocale); + } + + // Check return type and value returned by DecimalFormat.parse() for longs + @ParameterizedTest + @MethodSource("longs") + public void parseLongTest(String s) { + // This was originally intended to ensure a ParseException is not thrown + Number parsedNumber = assertDoesNotThrow(() -> df.parse(s), + "DecimalFormat.parse(\"%s\") should not throw an Exception"); + assertInstanceOf(Long.class, parsedNumber, + "DecimalFormat.parse(\"%s\") did not return Long"); + // Grab integer portion of value + Long expectedVal = Long.valueOf(s.substring(0, s.indexOf('.'))); + assertEquals(parsedNumber, expectedVal, + "DecimalFormat.parse(\"%s\") returned numerically incorrect value"); + } + + // Test some values between Long.MIN_VALUE and Long.MAX_VALUE + private static Stream longs() { + ArrayList longs = new ArrayList<>(); + addLongData(Long.MIN_VALUE, Long.MIN_VALUE+10, longs); + addLongData(-10, 10, longs); + addLongData(Long.MAX_VALUE-10, Long.MAX_VALUE-1, longs); + longs.add("9223372036854775807.00"); + longs.add("0.0"); + return longs.stream(); + } + + // Utility to add values between parameters(long, to) to testLongs ArrayList + private static void addLongData(long from, long to, ArrayList testLongs){ for (long l = from; l <= to; l++) { - check_Long(Long.toString(l) + ".00"); + testLongs.add(l + ".00"); } } - private static void check_Long(String s) throws Exception { - Number number = df.parse(s); - if (!(number instanceof Long)) { - err = true; - System.err.println("Failed: DecimalFormat.parse(\"" + s + - "\") should return a Long, but returned a " + - number.getClass().getName()); - } - - int index = s.indexOf('.'); - Long l = Long.valueOf(s.substring(0, index)); - if (!l.equals(number)) { - err = true; - System.err.println("Failed: DecimalFormat.parse(" + s + - ") should return a Long(" + l + "), but returned " + number); - } + // Check return type and value returned by DecimalFormat.parse() for doubles + @ParameterizedTest + @MethodSource("doubles") + public void parseDoubleTest(String s) { + // This was originally intended to ensure a ParseException is not thrown + Number parsedNumber = assertDoesNotThrow(() -> df.parse(s), + "DecimalFormat.parse(\"%s\") should not throw an Exception"); + assertInstanceOf(Double.class, parsedNumber, + "DecimalFormat.parse(\"%s\") did not return Double"); + Double expectedVal = Double.valueOf(s); + assertEquals(parsedNumber, expectedVal, + "DecimalFormat.parse(\"%s\") returned numerically incorrect value"); } - private static void check_Double(String s) throws Exception { - Number number = df.parse(s); - if (!(number instanceof Double)) { - err = true; - System.err.println("Failed: DecimalFormat.parse(\"" + s + - "\") should return a Double, but returned a " + - number.getClass().getName()); - } - - Double d = Double.valueOf(s); - if (!d.equals(number)) { - err = true; - System.err.println("Failed: DecimalFormat.parse(" + s + - ") should return a Double(" + d + "), but returned " + number); - } + // Check values not between Long.MIN_VALUE and Long.MAX_VALUE + private static Stream doubles() { + return Stream.of( + "-9223372036854775809", // Long.MIN_VALUE-1 + "9223372036854775808", // Long.MAX_VALUE+1 + "-0.0" + ); } } diff --git a/test/jdk/java/text/Format/NumberFormat/Bug4990596.java b/test/jdk/java/text/Format/NumberFormat/Bug4990596.java index 2b311fb1b5f..36c0259ae56 100644 --- a/test/jdk/java/text/Format/NumberFormat/Bug4990596.java +++ b/test/jdk/java/text/Format/NumberFormat/Bug4990596.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,20 +21,32 @@ * questions. */ -/** +/* * @test * @bug 4990596 - * @summary Make sure that any subclass of Number can be formatted using DecimalFormat.format(). + * @summary Make sure that any subclass of Number can be formatted using + * DecimalFormat.format() without throwing an exception. + * @run junit Bug4990596 */ import java.text.DecimalFormat; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + public class Bug4990596 { - public static void main(String[] args) { - new DecimalFormat().format(new MutableInteger(0)); + // Test that a custom subclass of Number can be formatted by + // DecimalFormat without throwing an IllegalArgumentException + @Test + public void formatSubclassedNumberTest() { + assertDoesNotThrow(() -> new DecimalFormat().format(new MutableInteger(0)), + "DecimalFormat.format() should support subclasses of Number"); } + // A custom subclass of Number. Prior to this fix, if an instance of this + // class was formatted by DecimalFormat, an exception would be thrown. @SuppressWarnings("serial") public static class MutableInteger extends Number { public int value; diff --git a/test/jdk/java/text/Format/NumberFormat/Bug6278616.java b/test/jdk/java/text/Format/NumberFormat/Bug6278616.java index b1684b4d177..8f9fc7ca52d 100644 --- a/test/jdk/java/text/Format/NumberFormat/Bug6278616.java +++ b/test/jdk/java/text/Format/NumberFormat/Bug6278616.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,46 +24,55 @@ /* * @test * @summary Confirm that AtomicInteger and AtomicLong are formatted correctly. + * That is, make sure they are not treated as a double when formatted + * anymore (which can result in the loss of precision). * @bug 6278616 + * @run junit Bug6278616 */ import java.text.NumberFormat; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import java.util.Locale; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class Bug6278616 { - static final int[] ints = { - Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE - }; + private static final NumberFormat nf = NumberFormat.getInstance(); - static final long[] longs = { - Long.MIN_VALUE, -1, 0, 1, Long.MAX_VALUE - }; + // Test that NumberFormat formats numerically equivalent int + // and AtomicInteger values the same + @ParameterizedTest + @MethodSource("ints") + public void formattedAtomicIntTest(int testInt) { + String formattedInt = nf.format(testInt); + String formattedAtomicInt = nf.format(new AtomicInteger(testInt)); + assertEquals(formattedAtomicInt, formattedInt, "Formatting numerically" + + " equivalent AtomicInteger and int should produce the same String value"); + } - public static void main(String[] args) { - NumberFormat nf = NumberFormat.getInstance(); + // Various int values + private static int[] ints() { + return new int[] { Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE}; + } - for (int j = 0; j < ints.length; j++) { - String s_i = nf.format(ints[j]); - String s_ai = nf.format(new AtomicInteger(ints[j])); - if (!s_i.equals(s_ai)) { - throw new RuntimeException("format(AtomicInteger " + s_ai + - ") doesn't equal format(Integer " + - s_i + ")"); - } - } + // Test that NumberFormat formats numerically equivalent long + // and AtomicLong values the same + @ParameterizedTest + @MethodSource("longs") + public void formattedAtomicLongTest(long testLong) { + String formattedLong = nf.format(testLong); + String formattedAtomicLong = nf.format(new AtomicLong(testLong)); + assertEquals(formattedAtomicLong, formattedLong, "Formatting numerically" + + " equivalent AtomicLong and long should produce the same String value"); + } - for (int j = 0; j < longs.length; j++) { - String s_l = nf.format(longs[j]); - String s_al = nf.format(new AtomicLong(longs[j])); - if (!s_l.equals(s_al)) { - throw new RuntimeException("format(AtomicLong " + s_al + - ") doesn't equal format(Long " + - s_l + ")"); - } - } + // Various long values + private static long[] longs() { + return new long[] { Long.MIN_VALUE, -1, 0, 1, Long.MAX_VALUE}; } } diff --git a/test/jdk/java/text/Format/NumberFormat/Bug8132125.java b/test/jdk/java/text/Format/NumberFormat/Bug8132125.java index 04583dfd019..1f702a9ba24 100644 --- a/test/jdk/java/text/Format/NumberFormat/Bug8132125.java +++ b/test/jdk/java/text/Format/NumberFormat/Bug8132125.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,20 +26,27 @@ * @bug 8132125 8202537 * @summary Checks Swiss' number elements * @modules jdk.localedata + * @run junit Bug8132125 */ -import java.text.*; -import java.util.*; +import java.text.NumberFormat; +import java.util.Locale; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class Bug8132125 { - public static void main(String[] args) { + + // Ensure the CLDRConverter does not omit the Swiss number elements + @Test + public void swissNumElementsTest() { Locale deCH = Locale.of("de", "CH"); NumberFormat nf = NumberFormat.getInstance(deCH); - String expected = "54\u2019839\u2019483.142"; // i.e. "\u2019" as decimal separator, "\u2019" as grouping separator + // "\u002E" as decimal separator, "\u2019" as grouping separator + String expected = "54\u2019839\u2019483.142"; String actual = nf.format(54839483.1415); - if (!actual.equals(expected)) { - throw new RuntimeException("incorrect for de_CH: " + expected + " vs. actual " + actual); - } + assertEquals(expected, actual, "incorrect number elements for de_CH"); } } diff --git a/test/jdk/java/text/Format/NumberFormat/CurrencyFormat.java b/test/jdk/java/text/Format/NumberFormat/CurrencyFormat.java index 8ed5ad8ce20..f8890b2c1ba 100644 --- a/test/jdk/java/text/Format/NumberFormat/CurrencyFormat.java +++ b/test/jdk/java/text/Format/NumberFormat/CurrencyFormat.java @@ -25,15 +25,19 @@ * @test * @bug 4290801 4942982 5102005 8008577 8021121 8210153 8227313 8301991 * @summary Basic tests for currency formatting. + * Tests both COMPAT and CLDR data. * @modules jdk.localedata - * @run main/othervm -Djava.locale.providers=COMPAT CurrencyFormat COMPAT - * @run main/othervm -Djava.locale.providers=CLDR CurrencyFormat CLDR + * @run junit/othervm -Djava.locale.providers=COMPAT CurrencyFormat + * @run junit/othervm -Djava.locale.providers=CLDR CurrencyFormat */ import java.io.File; import java.io.FileInputStream; +import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.util.ArrayList; import java.util.Currency; import java.util.Locale; import java.util.Properties; @@ -42,127 +46,140 @@ import java.util.TimeZone; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.SimpleDateFormat; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class CurrencyFormat { - private static boolean isCompat; + // Expected data is switched depending on COMPAT or CLDR + // currencySymbolsTest() is only ran for COMPAT + private static final boolean isCompat = + "COMPAT".equals(System.getProperty("java.locale.providers")); - public static void main(String[] args) throws Exception { - isCompat = "COMPAT".equals(args[0]); - testFormatting(); - testSymbols(); + // Tests the formatting of data for COMPAT + CLDR under various currencies + // Using a NumberFormat generated by getCurrencyInstance() + @ParameterizedTest + @MethodSource("currencyFormatDataProvider") + public void currencyFormatTest(String expected, Currency currency, + NumberFormat format, Locale locale) { + if (currency != null) { + format.setCurrency(currency); + int digits = currency.getDefaultFractionDigits(); + format.setMinimumFractionDigits(digits); + format.setMaximumFractionDigits(digits); + } + String result = format.format(1234.56); + assertEquals(expected, result, String.format("Failed with locale: %s%s", + locale, (currency == null ? ", default currency" : (", currency: " + currency)))); } - static void testFormatting() { - boolean failed = false; + // Generate a combination of expected data for 1234.56 formatted + // under various currencies/locale provider/locale + private static Stream currencyFormatDataProvider() { + ArrayList data = new ArrayList(); Locale[] locales = { - Locale.US, - Locale.JAPAN, - Locale.GERMANY, - Locale.ITALY, - Locale.of("it", "IT", "EURO"), - Locale.forLanguageTag("de-AT"), - Locale.forLanguageTag("fr-CH"), + Locale.US, + Locale.JAPAN, + Locale.GERMANY, + Locale.ITALY, + Locale.of("it", "IT", "EURO"), + Locale.forLanguageTag("de-AT"), + Locale.forLanguageTag("fr-CH"), }; Currency[] currencies = { - null, - Currency.getInstance("USD"), - Currency.getInstance("JPY"), - Currency.getInstance("DEM"), - Currency.getInstance("EUR"), + null, + Currency.getInstance("USD"), + Currency.getInstance("JPY"), + Currency.getInstance("DEM"), + Currency.getInstance("EUR"), }; - String[][] expecteds = { - {"$1,234.56", "$1,234.56", "JPY1,235", "DEM1,234.56", "EUR1,234.56"}, - {"\uFFE51,235", "USD1,234.56", "\uFFE51,235", "DEM1,234.56", "EUR1,234.56"}, - {"1.234,56 \u20AC", "1.234,56 USD", "1.235 JPY", "1.234,56 DM", "1.234,56 \u20AC"}, - {"\u20AC 1.234,56", "USD 1.234,56", "JPY 1.235", "DEM 1.234,56", "\u20AC 1.234,56"}, - {"\u20AC 1.234,56", "USD 1.234,56", "JPY 1.235", "DEM 1.234,56", "\u20AC 1.234,56"}, - {"\u20AC 1.234,56", "USD 1.234,56", "JPY 1.235", "DEM 1.234,56", "\u20AC 1.234,56"}, - {"SFr. 1'234.56", "USD 1'234.56", "JPY 1'235", "DEM 1'234.56", "EUR 1'234.56"}, + String[][] expectedCOMPATData = { + {"$1,234.56", "$1,234.56", "JPY1,235", "DEM1,234.56", "EUR1,234.56"}, + {"\uFFE51,235", "USD1,234.56", "\uFFE51,235", "DEM1,234.56", "EUR1,234.56"}, + {"1.234,56 \u20AC", "1.234,56 USD", "1.235 JPY", "1.234,56 DM", "1.234,56 \u20AC"}, + {"\u20AC 1.234,56", "USD 1.234,56", "JPY 1.235", "DEM 1.234,56", "\u20AC 1.234,56"}, + {"\u20AC 1.234,56", "USD 1.234,56", "JPY 1.235", "DEM 1.234,56", "\u20AC 1.234,56"}, + {"\u20AC 1.234,56", "USD 1.234,56", "JPY 1.235", "DEM 1.234,56", "\u20AC 1.234,56"}, + {"SFr. 1'234.56", "USD 1'234.56", "JPY 1'235", "DEM 1'234.56", "EUR 1'234.56"}, }; - String[][] expecteds_cldr = { - {"$1,234.56", "$1,234.56", "\u00a51,235", "DEM1,234.56", "\u20ac1,234.56"}, - {"\uFFE51,235", "$1,234.56", "\uFFE51,235", "DEM1,234.56", "\u20ac1,234.56"}, - {"1.234,56\u00a0\u20ac", "1.234,56\u00a0$", "1.235\u00a0\u00a5", "1.234,56\u00a0DM", "1.234,56\u00a0\u20ac"}, - {"1.234,56\u00a0\u20ac", "1.234,56\u00a0USD", "1.235\u00a0JPY", "1.234,56\u00a0DEM", "1.234,56\u00a0\u20ac"}, - {"1.234,56\u00a0\u20ac", "1.234,56\u00a0USD", "1.235\u00a0JPY", "1.234,56\u00a0DEM", "1.234,56\u00a0\u20ac"}, - {"\u20ac\u00a01.234,56", "$\u00a01.234,56", "\u00a5\u00a01.235", "DM\u00a01.234,56", "\u20ac\u00a01.234,56"}, - {"1\u202f234.56\u00a0CHF", "1\u202f234.56\u00a0$US", "1\u202f235\u00a0JPY", "1\u202f234.56\u00a0DEM", "1\u202f234.56\u00a0\u20ac"}, + String[][] expectedCLDRData = { + {"$1,234.56", "$1,234.56", "\u00a51,235", "DEM1,234.56", "\u20ac1,234.56"}, + {"\uFFE51,235", "$1,234.56", "\uFFE51,235", "DEM1,234.56", "\u20ac1,234.56"}, + {"1.234,56\u00a0\u20ac", "1.234,56\u00a0$", "1.235\u00a0\u00a5", "1.234,56\u00a0DM", "1.234,56\u00a0\u20ac"}, + {"1.234,56\u00a0\u20ac", "1.234,56\u00a0USD", "1.235\u00a0JPY", "1.234,56\u00a0DEM", "1.234,56\u00a0\u20ac"}, + {"1.234,56\u00a0\u20ac", "1.234,56\u00a0USD", "1.235\u00a0JPY", "1.234,56\u00a0DEM", "1.234,56\u00a0\u20ac"}, + {"\u20ac\u00a01.234,56", "$\u00a01.234,56", "\u00a5\u00a01.235", "DM\u00a01.234,56", "\u20ac\u00a01.234,56"}, + {"1\u202f234.56\u00a0CHF", "1\u202f234.56\u00a0$US", "1\u202f235\u00a0JPY", "1\u202f234.56\u00a0DEM", "1\u202f234.56\u00a0\u20ac"}, }; - for (int i = 0; i < locales.length; i++) { Locale locale = locales[i]; NumberFormat format = NumberFormat.getCurrencyInstance(locale); for (int j = 0; j < currencies.length; j++) { Currency currency = currencies[j]; - String expected = isCompat ? expecteds[i][j] : expecteds_cldr[i][j]; - if (currency != null) { - format.setCurrency(currency); - int digits = currency.getDefaultFractionDigits(); - format.setMinimumFractionDigits(digits); - format.setMaximumFractionDigits(digits); - } - String result = format.format(1234.56); - if (!result.equals(expected)) { - failed = true; - System.out.println("FAIL: Locale " + locale - + (currency == null ? ", default currency" : (", currency: " + currency)) - + ", expected: " + expected - + ", actual: " + result); - } + String expected = isCompat ? expectedCOMPATData[i][j] : expectedCLDRData[i][j]; + data.add(Arguments.of(expected, currency, format, locale)); } } + return data.stream(); + } - if (failed) { - throw new RuntimeException(); + // Compares the expected currency symbol of a locale to the value returned by + // DecimalFormatSymbols.getCurrencySymbol(). + @ParameterizedTest + @MethodSource("currencySymbolsDataProvider") + public void currencySymbolsTest(String expected, Locale locale) throws ParseException { + if (!isCompat) { + return; // For COMPAT only. + } + if (expected == null) { + System.out.println("Warning: No expected currency symbol defined for locale " + locale); + } else { + // Reserved for when a currency will change its symbol at a given time in the future + if (expected.contains(";")) { + expected = getFutureSymbol(expected); + } + DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale); + String result = symbols.getCurrencySymbol(); + assertEquals(expected, result, "Wrong currency symbol for locale " + + locale + ", expected: " + expected + ", got: " + result); } } - static void testSymbols() throws Exception { - if (!isCompat) { - // For COMPAT only. - return; - } - - FileInputStream stream = new FileInputStream(new File(System.getProperty("test.src", "."), "CurrencySymbols.properties")); + // Grabs the custom CurrencySymbols.properties and loads the file into a Properties + // instance. Building the data set, which consists of the currency symbol for the locale. + private static Stream currencySymbolsDataProvider() throws IOException { + ArrayList data = new ArrayList(); + FileInputStream stream = new FileInputStream(new File( + System.getProperty("test.src", "."), "CurrencySymbols.properties")); InputStreamReader streamReader = new InputStreamReader(stream, StandardCharsets.UTF_8); Properties props = new Properties(); props.load(streamReader); - SimpleDateFormat format = null; - Locale[] locales = NumberFormat.getAvailableLocales(); - for (int i = 0; i < locales.length; i++) { - Locale locale = locales[i]; - DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale); - String result = symbols.getCurrencySymbol(); + for (Locale locale : locales) { String expected = (String) props.get(locale.toString()); + data.add(Arguments.of(expected, locale)); + } + return data.stream(); + } - if (expected == null) { - System.out.println("Warning: No expected currency symbol defined for locale " + locale); - } else { - if (expected.contains(";")) { - StringTokenizer tokens = new StringTokenizer(expected, ";"); - int tokensCount = tokens.countTokens(); - - if (tokensCount == 3) { - expected = tokens.nextToken(); - if (format == null) { - format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US); - format.setTimeZone(TimeZone.getTimeZone("GMT")); - format.setLenient(false); - } - - if (format.parse(tokens.nextToken()).getTime() < System.currentTimeMillis()) { - expected = tokens.nextToken(); - } - } - } - - if (!expected.equals(result)) { - throw new RuntimeException("Wrong currency symbol for locale " + - locale + ", expected: " + expected + ", got: " + result); - } + // Utility to grab the future symbol if in the right format and date cut-over allows + private static String getFutureSymbol(String expected) throws ParseException { + StringTokenizer tokens = new StringTokenizer(expected, ";"); + int tokensCount = tokens.countTokens(); + if (tokensCount == 3) { + expected = tokens.nextToken(); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + format.setLenient(false); + if (format.parse(tokens.nextToken()).getTime() < System.currentTimeMillis()) { + expected = tokens.nextToken(); } } + return expected; } } diff --git a/test/jdk/java/text/Format/NumberFormat/TestPeruCurrencyFormat.java b/test/jdk/java/text/Format/NumberFormat/TestPeruCurrencyFormat.java index 6ca07288723..12403b022a8 100644 --- a/test/jdk/java/text/Format/NumberFormat/TestPeruCurrencyFormat.java +++ b/test/jdk/java/text/Format/NumberFormat/TestPeruCurrencyFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,27 +21,31 @@ * questions. */ -/** +/* * @test * @bug 8206879 - * @summary Currency decimal marker incorrect for Peru. * @modules jdk.localedata - * @run main/othervm -Djava.locale.providers=JRE TestPeruCurrencyFormat + * @summary Currency decimal marker incorrect for Peru (COMPAT). + * @run junit/othervm -Djava.locale.providers=COMPAT TestPeruCurrencyFormat */ import java.text.NumberFormat; import java.util.Locale; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + public class TestPeruCurrencyFormat { - public static void main(String[] args) { + // Confirm correct decimal marker for Peru locale on COMPAT + @Test + public void peruDecimalMarketCOMPAT() { final String expected = "S/.1,234.56"; NumberFormat currencyFmt = NumberFormat.getCurrencyInstance(Locale.of("es", "PE")); String s = currencyFmt.format(1234.56); - - if (!s.equals(expected)) { - throw new RuntimeException("Currency format for Peru failed, expected " + expected + ", got " + s); - } + assertEquals(expected, s, + "Currency format for Peru failed, expected " + expected + ", got " + s); } }