8317372: Refactor some NumberFormat tests to use JUnit

Reviewed-by: naoto
This commit is contained in:
Justin Lu 2023-10-13 20:49:11 +00:00
parent 72c4dcbfee
commit 9622de2aa8
9 changed files with 761 additions and 566 deletions

View File

@ -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<Arguments> bigIntegers = new ArrayList<Arguments>();
private static final ArrayList<Arguments> bigDecimals = new ArrayList<Arguments>();
// 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<Arguments> 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<Arguments> 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;
}
}

View File

@ -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<Arguments> 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<Arguments> 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<Arguments> 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<Arguments> 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);
}
}

View File

@ -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("<P>#.###E00<S>", 'm');
test(1234D, "<P>1.234E03<S>");
test(0.1234, "<P>1.234Em01<S>");
test(-1234D, "m<P>1.234E03<S>");
test(-0.1234, "m<P>1.234Em01<S>");
prepareFormatter("<P>#.###E00<S>;#.###E00", 'm');
test(1234D, "<P>1.234E03<S>");
test(0.1234, "<P>1.234Em01<S>");
test(-1234D, "1.234E03");
test(-0.1234, "1.234Em01");
prepareFormatter("#.###E00;<P>#.###E00<S>", 'm');
test(1234D, "1.234E03");
test(0.1234, "1.234Em01");
test(-1234D, "<P>1.234E03<S>");
test(-0.1234, "<P>1.234Em01<S>");
prepareFormatter("<P>#.###E00<S>;<p>-#.###E00<s>", 'm');
test(1234D, "<P>1.234E03<S>");
test(0.1234, "<P>1.234Em01<S>");
test(-1234D, "<p>m1.234E03<s>");
test(-0.1234, "<p>m1.234Em01<s>");
test(Double.POSITIVE_INFINITY, "<P>\u221e<S>");
test(Double.NEGATIVE_INFINITY, "<p>m\u221e<s>");
test(Double.NaN, "\ufffd"); // without prefix and suffix
test(0.0, "<P>0E00<S>");
test(-0.0, "<p>m0E00<s>"); // 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("<P>#.####################E00<S>;<p>-#.####################E00<s>", 'm');
test(new BigDecimal("123456789012345678901234567890"),
"<P>1.23456789012345678901E29<S>");
test(new BigDecimal("0.000000000123456789012345678901234567890"),
"<P>1.23456789012345678901Em10<S>");
test(new BigDecimal("-123456789012345678901234567890"),
"<p>m1.23456789012345678901E29<s>");
test(new BigDecimal("-0.000000000123456789012345678901234567890"),
"<p>m1.23456789012345678901Em10<s>");
test(new BigDecimal("0"), "<P>0E00<S>");
test(new BigDecimal("-0"), "<P>0E00<S>");
// 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("<P>#,###<S>;<p>-#,###<s>", 'm');
test(123456789L, "<P>123,456,789<S>");
test(-123456789L, "<p>m123,456,789<s>");
test(0L, "<P>0<S>");
test(-0L, "<P>0<S>");
// 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<Arguments> doubles() {
DecimalFormat defaultDf = new DecimalFormat();
DecimalFormat customDf1 = getDecimalFormat("<P>#.###E00<S>", 'm');
DecimalFormat customDf2 = getDecimalFormat("<P>#.###E00<S>;#.###E00", 'm');
DecimalFormat customDf3 = getDecimalFormat("#.###E00;<P>#.###E00<S>", 'm');
DecimalFormat customDf4 = getDecimalFormat("<P>#.###E00<S>;<p>-#.###E00<s>", '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, "<P>1.234E03<S>", customDf1),
Arguments.of(0.1234, "<P>1.234Em01<S>", customDf1),
Arguments.of(-1234D, "m<P>1.234E03<S>", customDf1),
Arguments.of(-0.1234, "m<P>1.234Em01<S>", customDf1),
Arguments.of(1234D, "<P>1.234E03<S>", customDf2),
Arguments.of(0.1234, "<P>1.234Em01<S>", 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, "<P>1.234E03<S>", customDf3),
Arguments.of(-0.1234, "<P>1.234Em01<S>", customDf3),
Arguments.of(1234D, "<P>1.234E03<S>", customDf4),
Arguments.of(0.1234, "<P>1.234Em01<S>", customDf4),
Arguments.of(-1234D, "<p>m1.234E03<s>", customDf4),
Arguments.of(-0.1234, "<p>m1.234Em01<s>", customDf4),
Arguments.of(Double.POSITIVE_INFINITY, "<P>\u221e<S>", customDf4),
Arguments.of(Double.NEGATIVE_INFINITY, "<p>m\u221e<s>", customDf4),
Arguments.of(Double.NaN, "\ufffd", customDf4), // without prefix and suffix
Arguments.of(0.0, "<P>0E00<S>", customDf4),
Arguments.of(-0.0, "<p>m0E00<s>", 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("<P>#,###<S>;<p>-#,###<s>", 'm');
test(new BigInteger("123456789012345678901234567890"),
"<P>123,456,789,012,345,678,901,234,567,890<S>");
test(new BigInteger("-123456789012345678901234567890"),
"<p>m123,456,789,012,345,678,901,234,567,890<s>");
test(new BigInteger("0"), "<P>0<S>");
test(new BigInteger("-0"), "<P>0<S>");
// 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<Arguments> longs() {
DecimalFormat defaultDf = new DecimalFormat();
DecimalFormat customDf = getDecimalFormat(
"<P>#,###<S>;<p>-#,###<s>", '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, "<P>123,456,789<S>", customDf),
Arguments.of(-123456789L, "<p>m123,456,789<s>", customDf),
Arguments.of(0L, "<P>0<S>", customDf),
Arguments.of(-0L, "<P>0<S>", 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<Arguments> bigDecimals() {
DecimalFormat defaultDf = new DecimalFormat();
DecimalFormat customDf = getDecimalFormat(
"<P>#.####################E00<S>;<p>-#.####################E00<s>", '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"),
"<P>1.23456789012345678901E29<S>", customDf),
Arguments.of(new BigDecimal("0.000000000123456789012345678901234567890"),
"<P>1.23456789012345678901Em10<S>", customDf),
Arguments.of(new BigDecimal("-123456789012345678901234567890"),
"<p>m1.23456789012345678901E29<s>", customDf),
Arguments.of(new BigDecimal("-0.000000000123456789012345678901234567890"),
"<p>m1.23456789012345678901Em10<s>", customDf),
Arguments.of(new BigDecimal("0"), "<P>0E00<S>", customDf),
Arguments.of(new BigDecimal("-0"), "<P>0E00<S>", 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<Arguments> bigIntegers() {
DecimalFormat defaultDf = new DecimalFormat();
DecimalFormat customDf = getDecimalFormat(
"<P>#,###<S>;<p>-#,###<s>", '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"),
"<P>123,456,789,012,345,678,901,234,567,890<S>", customDf),
Arguments.of(new BigInteger("-123456789012345678901234567890"),
"<p>m123,456,789,012,345,678,901,234,567,890<s>", customDf),
Arguments.of(new BigInteger("0"), "<P>0<S>", customDf),
Arguments.of(new BigInteger("-0"), "<P>0<S>", 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;
}
}

View File

@ -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<String> longs() {
ArrayList<String> 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<String> 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<String> doubles() {
return Stream.of(
"-9223372036854775809", // Long.MIN_VALUE-1
"9223372036854775808", // Long.MAX_VALUE+1
"-0.0"
);
}
}

View File

@ -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;

View File

@ -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};
}
}

View File

@ -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");
}
}

View File

@ -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<Arguments> currencyFormatDataProvider() {
ArrayList<Arguments> data = new ArrayList<Arguments>();
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<Arguments> currencySymbolsDataProvider() throws IOException {
ArrayList<Arguments> data = new ArrayList<Arguments>();
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;
}
}

View File

@ -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);
}
}