From 6808c5c89d1e30f481ebe444f93175232bcd30e2 Mon Sep 17 00:00:00 2001 From: Suchismith Roy Date: Wed, 3 Apr 2024 08:00:56 +0000 Subject: [PATCH 1/6] 8320005: Allow loading of shared objects with .a extension on AIX Backport-of: e85355ada4ac1061c49ee9f1247d37a437c7b5ab --- src/hotspot/os/aix/os_aix.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 85a97888d90..5b877b159b4 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -1313,10 +1313,9 @@ bool os::dll_address_to_library_name(address addr, char* buf, // Loads .dll/.so and in case of error it checks if .dll/.so was built // for the same architecture as Hotspot is running on. -void *os::dll_load(const char *filename, char *ebuf, int ebuflen) { +static void* dll_load_library(const char *filename, char *ebuf, int ebuflen) { log_info(os)("attempting shared library load of %s", filename); - if (ebuf && ebuflen > 0) { ebuf[0] = '\0'; ebuf[ebuflen - 1] = '\0'; @@ -1359,6 +1358,26 @@ void* os::dll_lookup(void* handle, const char* name) { void* os::get_default_process_handle() { return (void*)::dlopen(NULL, RTLD_LAZY); } +// Load library named +// If filename matches .so, and loading fails, repeat with .a. +void *os::dll_load(const char *filename, char *ebuf, int ebuflen) { + void* result = nullptr; + char* const file_path = strdup(filename); + char* const pointer_to_dot = strrchr(file_path, '.'); + const char old_extension[] = ".so"; + const char new_extension[] = ".a"; + STATIC_ASSERT(sizeof(old_extension) >= sizeof(new_extension)); + // First try to load the existing file. + result = dll_load_library(filename, ebuf, ebuflen); + // If the load fails,we try to reload by changing the extension to .a for .so files only. + // Shared object in .so format dont have braces, hence they get removed for archives with members. + if (result == nullptr && pointer_to_dot != nullptr && strcmp(pointer_to_dot, old_extension) == 0) { + snprintf(pointer_to_dot, sizeof(old_extension), "%s", new_extension); + result = dll_load_library(file_path, ebuf, ebuflen); + } + FREE_C_HEAP_ARRAY(char, file_path); + return result; +} void os::print_dll_info(outputStream *st) { st->print_cr("Dynamic libraries:"); From 9ac4063d14dfa9633cea90e13e2cd13681826255 Mon Sep 17 00:00:00 2001 From: Paul Hohensee Date: Thu, 4 Apr 2024 22:08:01 +0000 Subject: [PATCH 2/6] 8299677: Formatter.format might take a long time to format an integer or floating-point Reviewed-by: mdoerr, shade Backport-of: 33412c102ce799ff2de3512df77e6e07d76acd36 --- .../share/classes/java/util/Formatter.java | 9 +- test/jdk/java/util/Formatter/Padding.java | 313 ++++++++++++++++++ 2 files changed, 317 insertions(+), 5 deletions(-) create mode 100644 test/jdk/java/util/Formatter/Padding.java diff --git a/src/java.base/share/classes/java/util/Formatter.java b/src/java.base/share/classes/java/util/Formatter.java index 0fd1176b970..160d77e7e54 100644 --- a/src/java.base/share/classes/java/util/Formatter.java +++ b/src/java.base/share/classes/java/util/Formatter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, 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 @@ -4526,10 +4526,9 @@ private StringBuilder localizedMagnitude(StringBuilder sb, } // apply zero padding - if (width != -1 && f.contains(Flags.ZERO_PAD)) { - for (int k = sb.length(); k < width; k++) { - sb.insert(begin, zero); - } + if (width > sb.length() && f.contains(Flags.ZERO_PAD)) { + String zeros = String.valueOf(zero).repeat(width - sb.length()); + sb.insert(begin, zeros); } return sb; diff --git a/test/jdk/java/util/Formatter/Padding.java b/test/jdk/java/util/Formatter/Padding.java new file mode 100644 index 00000000000..982b6967928 --- /dev/null +++ b/test/jdk/java/util/Formatter/Padding.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4906370 + * @summary Tests to excercise padding on int and double values, + * with various flag combinations. + * @run junit Padding + */ + +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.params.provider.Arguments.arguments; + +public class Padding { + + static Arguments[] padding() { + return new Arguments[] { + /* blank padding, right adjusted, optional plus sign */ + arguments("12", "%1d", 12), + arguments("12", "%2d", 12), + arguments(" 12", "%3d", 12), + arguments(" 12", "%4d", 12), + arguments(" 12", "%5d", 12), + arguments(" 12", "%10d", 12), + + arguments("-12", "%1d", -12), + arguments("-12", "%2d", -12), + arguments("-12", "%3d", -12), + arguments(" -12", "%4d", -12), + arguments(" -12", "%5d", -12), + arguments(" -12", "%10d", -12), + + arguments("1.2", "%1.1f", 1.2), + arguments("1.2", "%2.1f", 1.2), + arguments("1.2", "%3.1f", 1.2), + arguments(" 1.2", "%4.1f", 1.2), + arguments(" 1.2", "%5.1f", 1.2), + arguments(" 1.2", "%10.1f", 1.2), + + arguments("-1.2", "%1.1f", -1.2), + arguments("-1.2", "%2.1f", -1.2), + arguments("-1.2", "%3.1f", -1.2), + arguments("-1.2", "%4.1f", -1.2), + arguments(" -1.2", "%5.1f", -1.2), + arguments(" -1.2", "%10.1f", -1.2), + + /* blank padding, right adjusted, mandatory plus sign */ + arguments("+12", "%+1d", 12), + arguments("+12", "%+2d", 12), + arguments("+12", "%+3d", 12), + arguments(" +12", "%+4d", 12), + arguments(" +12", "%+5d", 12), + arguments(" +12", "%+10d", 12), + + arguments("-12", "%+1d", -12), + arguments("-12", "%+2d", -12), + arguments("-12", "%+3d", -12), + arguments(" -12", "%+4d", -12), + arguments(" -12", "%+5d", -12), + arguments(" -12", "%+10d", -12), + + arguments("+1.2", "%+1.1f", 1.2), + arguments("+1.2", "%+2.1f", 1.2), + arguments("+1.2", "%+3.1f", 1.2), + arguments("+1.2", "%+4.1f", 1.2), + arguments(" +1.2", "%+5.1f", 1.2), + arguments(" +1.2", "%+10.1f", 1.2), + + arguments("-1.2", "%+1.1f", -1.2), + arguments("-1.2", "%+2.1f", -1.2), + arguments("-1.2", "%+3.1f", -1.2), + arguments("-1.2", "%+4.1f", -1.2), + arguments(" -1.2", "%+5.1f", -1.2), + arguments(" -1.2", "%+10.1f", -1.2), + + /* blank padding, right adjusted, mandatory blank sign */ + arguments(" 12", "% 1d", 12), + arguments(" 12", "% 2d", 12), + arguments(" 12", "% 3d", 12), + arguments(" 12", "% 4d", 12), + arguments(" 12", "% 5d", 12), + arguments(" 12", "% 10d", 12), + + arguments("-12", "% 1d", -12), + arguments("-12", "% 2d", -12), + arguments("-12", "% 3d", -12), + arguments(" -12", "% 4d", -12), + arguments(" -12", "% 5d", -12), + arguments(" -12", "% 10d", -12), + + arguments(" 1.2", "% 1.1f", 1.2), + arguments(" 1.2", "% 2.1f", 1.2), + arguments(" 1.2", "% 3.1f", 1.2), + arguments(" 1.2", "% 4.1f", 1.2), + arguments(" 1.2", "% 5.1f", 1.2), + arguments(" 1.2", "% 10.1f", 1.2), + + arguments("-1.2", "% 1.1f", -1.2), + arguments("-1.2", "% 2.1f", -1.2), + arguments("-1.2", "% 3.1f", -1.2), + arguments("-1.2", "% 4.1f", -1.2), + arguments(" -1.2", "% 5.1f", -1.2), + arguments(" -1.2", "% 10.1f", -1.2), + + /* blank padding, left adjusted, optional sign */ + arguments("12", "%-1d", 12), + arguments("12", "%-2d", 12), + arguments("12 ", "%-3d", 12), + arguments("12 ", "%-4d", 12), + arguments("12 ", "%-5d", 12), + arguments("12 ", "%-10d", 12), + + arguments("-12", "%-1d", -12), + arguments("-12", "%-2d", -12), + arguments("-12", "%-3d", -12), + arguments("-12 ", "%-4d", -12), + arguments("-12 ", "%-5d", -12), + arguments("-12 ", "%-10d", -12), + + arguments("1.2", "%-1.1f", 1.2), + arguments("1.2", "%-2.1f", 1.2), + arguments("1.2", "%-3.1f", 1.2), + arguments("1.2 ", "%-4.1f", 1.2), + arguments("1.2 ", "%-5.1f", 1.2), + arguments("1.2 ", "%-10.1f", 1.2), + + arguments("-1.2", "%-1.1f", -1.2), + arguments("-1.2", "%-2.1f", -1.2), + arguments("-1.2", "%-3.1f", -1.2), + arguments("-1.2", "%-4.1f", -1.2), + arguments("-1.2 ", "%-5.1f", -1.2), + arguments("-1.2 ", "%-10.1f", -1.2), + + /* blank padding, left adjusted, mandatory plus sign */ + arguments("+12", "%-+1d", 12), + arguments("+12", "%-+2d", 12), + arguments("+12", "%-+3d", 12), + arguments("+12 ", "%-+4d", 12), + arguments("+12 ", "%-+5d", 12), + arguments("+12 ", "%-+10d", 12), + + arguments("-12", "%-+1d", -12), + arguments("-12", "%-+2d", -12), + arguments("-12", "%-+3d", -12), + arguments("-12 ", "%-+4d", -12), + arguments("-12 ", "%-+5d", -12), + arguments("-12 ", "%-+10d", -12), + + arguments("+1.2", "%-+1.1f", 1.2), + arguments("+1.2", "%-+2.1f", 1.2), + arguments("+1.2", "%-+3.1f", 1.2), + arguments("+1.2", "%-+4.1f", 1.2), + arguments("+1.2 ", "%-+5.1f", 1.2), + arguments("+1.2 ", "%-+10.1f", 1.2), + + arguments("-1.2", "%-+1.1f", -1.2), + arguments("-1.2", "%-+2.1f", -1.2), + arguments("-1.2", "%-+3.1f", -1.2), + arguments("-1.2", "%-+4.1f", -1.2), + arguments("-1.2 ", "%-+5.1f", -1.2), + arguments("-1.2 ", "%-+10.1f", -1.2), + + /* blank padding, left adjusted, mandatory blank sign */ + arguments(" 12", "%- 1d", 12), + arguments(" 12", "%- 2d", 12), + arguments(" 12", "%- 3d", 12), + arguments(" 12 ", "%- 4d", 12), + arguments(" 12 ", "%- 5d", 12), + arguments(" 12 ", "%- 10d", 12), + + arguments("-12", "%- 1d", -12), + arguments("-12", "%- 2d", -12), + arguments("-12", "%- 3d", -12), + arguments("-12 ", "%- 4d", -12), + arguments("-12 ", "%- 5d", -12), + arguments("-12 ", "%- 10d", -12), + + arguments(" 1.2", "%- 1.1f", 1.2), + arguments(" 1.2", "%- 2.1f", 1.2), + arguments(" 1.2", "%- 3.1f", 1.2), + arguments(" 1.2", "%- 4.1f", 1.2), + arguments(" 1.2 ", "%- 5.1f", 1.2), + arguments(" 1.2 ", "%- 10.1f", 1.2), + + arguments("-1.2", "%- 1.1f", -1.2), + arguments("-1.2", "%- 2.1f", -1.2), + arguments("-1.2", "%- 3.1f", -1.2), + arguments("-1.2", "%- 4.1f", -1.2), + arguments("-1.2 ", "%- 5.1f", -1.2), + arguments("-1.2 ", "%- 10.1f", -1.2), + + /* zero padding, right adjusted, optional sign */ + arguments("12", "%01d", 12), + arguments("12", "%02d", 12), + arguments("012", "%03d", 12), + arguments("0012", "%04d", 12), + arguments("00012", "%05d", 12), + arguments("0000000012", "%010d", 12), + + arguments("-12", "%01d", -12), + arguments("-12", "%02d", -12), + arguments("-12", "%03d", -12), + arguments("-012", "%04d", -12), + arguments("-0012", "%05d", -12), + arguments("-000000012", "%010d", -12), + + arguments("1.2", "%01.1f", 1.2), + arguments("1.2", "%02.1f", 1.2), + arguments("1.2", "%03.1f", 1.2), + arguments("01.2", "%04.1f", 1.2), + arguments("001.2", "%05.1f", 1.2), + arguments("00000001.2", "%010.1f", 1.2), + + arguments("-1.2", "%01.1f", -1.2), + arguments("-1.2", "%02.1f", -1.2), + arguments("-1.2", "%03.1f", -1.2), + arguments("-1.2", "%04.1f", -1.2), + arguments("-01.2", "%05.1f", -1.2), + arguments("-0000001.2", "%010.1f", -1.2), + + /* zero padding, right adjusted, mandatory plus sign */ + arguments("+12", "%+01d", 12), + arguments("+12", "%+02d", 12), + arguments("+12", "%+03d", 12), + arguments("+012", "%+04d", 12), + arguments("+0012", "%+05d", 12), + arguments("+000000012", "%+010d", 12), + + arguments("-12", "%+01d", -12), + arguments("-12", "%+02d", -12), + arguments("-12", "%+03d", -12), + arguments("-012", "%+04d", -12), + arguments("-0012", "%+05d", -12), + arguments("-000000012", "%+010d", -12), + + arguments("+1.2", "%+01.1f", 1.2), + arguments("+1.2", "%+02.1f", 1.2), + arguments("+1.2", "%+03.1f", 1.2), + arguments("+1.2", "%+04.1f", 1.2), + arguments("+01.2", "%+05.1f", 1.2), + arguments("+0000001.2", "%+010.1f", 1.2), + + arguments("-1.2", "%+01.1f", -1.2), + arguments("-1.2", "%+02.1f", -1.2), + arguments("-1.2", "%+03.1f", -1.2), + arguments("-1.2", "%+04.1f", -1.2), + arguments("-01.2", "%+05.1f", -1.2), + arguments("-0000001.2", "%+010.1f", -1.2), + + /* zero padding, right adjusted, mandatory blank sign */ + arguments(" 12", "% 01d", 12), + arguments(" 12", "% 02d", 12), + arguments(" 12", "% 03d", 12), + arguments(" 012", "% 04d", 12), + arguments(" 0012", "% 05d", 12), + arguments(" 000000012", "% 010d", 12), + + arguments("-12", "% 01d", -12), + arguments("-12", "% 02d", -12), + arguments("-12", "% 03d", -12), + arguments("-012", "% 04d", -12), + arguments("-0012", "% 05d", -12), + arguments("-000000012", "% 010d", -12), + + arguments(" 1.2", "% 01.1f", 1.2), + arguments(" 1.2", "% 02.1f", 1.2), + arguments(" 1.2", "% 03.1f", 1.2), + arguments(" 1.2", "% 04.1f", 1.2), + arguments(" 01.2", "% 05.1f", 1.2), + arguments(" 0000001.2", "% 010.1f", 1.2), + + arguments("-1.2", "% 01.1f", -1.2), + arguments("-1.2", "% 02.1f", -1.2), + arguments("-1.2", "% 03.1f", -1.2), + arguments("-1.2", "% 04.1f", -1.2), + arguments("-01.2", "% 05.1f", -1.2), + arguments("-0000001.2", "% 010.1f", -1.2), + + }; + } + + @ParameterizedTest + @MethodSource + void padding(String expected, String format, Object value) { + assertEquals(expected, String.format(format, value)); + } + +} From 2f285fd96aaf064e7040b20e51297c9b75c8f501 Mon Sep 17 00:00:00 2001 From: Andrew Lu Date: Fri, 5 Apr 2024 02:01:40 +0000 Subject: [PATCH 3/6] 8323994: gtest runner repeats test name for every single gtest assertion 8158048: Fix failure message from jtreg gtest wrapper 8263659: Reflow GTestResultParser for better readability Reviewed-by: lucy Backport-of: 1aae980c549741cf5fc5ca51f3c299285bafa49d --- .../jtreg/gtest/GTestResultParser.java | 81 +++++++++++++++++++ test/hotspot/jtreg/gtest/GTestWrapper.java | 56 ++++++++----- 2 files changed, 118 insertions(+), 19 deletions(-) create mode 100644 test/hotspot/jtreg/gtest/GTestResultParser.java diff --git a/test/hotspot/jtreg/gtest/GTestResultParser.java b/test/hotspot/jtreg/gtest/GTestResultParser.java new file mode 100644 index 00000000000..ffa98bf29f2 --- /dev/null +++ b/test/hotspot/jtreg/gtest/GTestResultParser.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2019, 2024, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.xml.XMLConstants; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class GTestResultParser { + private final List _failedTests; + + public GTestResultParser(Path file) { + List failedTests = new ArrayList<>(); + try (Reader r = Files.newBufferedReader(file)) { + XMLInputFactory factory = XMLInputFactory.newInstance(); + factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); + XMLStreamReader xmlReader = factory.createXMLStreamReader(r); + String testSuite = null; + String testCase = null; + while (xmlReader.hasNext()) { + int code = xmlReader.next(); + if (code == XMLStreamConstants.START_ELEMENT) { + switch (xmlReader.getLocalName()) { + case "testsuite": + testSuite = xmlReader.getAttributeValue("", "name"); + break; + case "testcase": + testCase = xmlReader.getAttributeValue("", "name"); + break; + case "failure": + String failedStr = testSuite + "::" + testCase; + if (!failedTests.contains(failedStr)) { + failedTests.add(failedStr); + } + break; + default: + // ignore + } + } + } + } catch (XMLStreamException e) { + throw new IllegalArgumentException("can't open parse xml " + file, e); + } catch (IOException e) { + throw new IllegalArgumentException("can't open result file " + file, e); + } + _failedTests = Collections.unmodifiableList(failedTests); + } + + public List failedTests() { + return _failedTests; + } +} diff --git a/test/hotspot/jtreg/gtest/GTestWrapper.java b/test/hotspot/jtreg/gtest/GTestWrapper.java index 08ee033a60b..4f9a775f020 100644 --- a/test/hotspot/jtreg/gtest/GTestWrapper.java +++ b/test/hotspot/jtreg/gtest/GTestWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, 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,24 +25,22 @@ * @summary a jtreg wrapper for gtest tests * @library /test/lib * @modules java.base/jdk.internal.misc + * java.xml * @requires vm.flagless * @run main/native GTestWrapper */ -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.stream.Stream; -import java.util.stream.Collectors; - -import java.io.File; -import java.nio.file.Paths; -import java.nio.file.Path; - import jdk.test.lib.Platform; import jdk.test.lib.Utils; import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.process.OutputAnalyzer; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; +import java.util.Map; public class GTestWrapper { public static void main(String[] args) throws Throwable { @@ -77,13 +75,33 @@ public static void main(String[] args) throws Throwable { env.put(pathVar, path + File.pathSeparator + ldLibraryPath); } - pb.command(new String[] { - execPath.toString(), - "-jdk", - System.getProperty("test.jdk"), - "--gtest_catch_exceptions=0" - }); - ProcessTools.executeCommand(pb).shouldHaveExitValue(0); + Path resultFile = Paths.get("test_result.xml"); + pb.command(execPath.toAbsolutePath().toString(), + "-jdk", Utils.TEST_JDK, + "--gtest_output=xml:" + resultFile); + int exitCode = ProcessTools.executeCommand(pb).getExitValue(); + if (exitCode != 0) { + List failedTests = failedTests(resultFile); + String message = "gtest execution failed; exit code = " + exitCode + "."; + if (!failedTests.isEmpty()) { + message += " the failed tests: " + failedTests; + } + throw new AssertionError(message); + } + } + + private static List failedTests(Path xml) { + if (!Files.exists(xml)) { + System.err.println("WARNING: test result file (" + xml + ") hasn't been found"); + } + + try { + return new GTestResultParser(xml).failedTests(); + } catch (Throwable t) { + System.err.println("WARNING: failed to parse result file (" + xml + ") " + t); + t.printStackTrace(); + } + return Collections.emptyList(); } private static String getJVMVariantSubDir() { From eca369614ccaa0c0109724231984a3acfd8e2756 Mon Sep 17 00:00:00 2001 From: Andrew Lu Date: Fri, 5 Apr 2024 02:01:58 +0000 Subject: [PATCH 4/6] 8310923: Refactor Currency tests to use JUnit Reviewed-by: lucy Backport-of: e848d9471f5de86e5ac157b710cd7371f12f0024 --- test/jdk/java/util/Currency/Bug4512215.java | 68 --- test/jdk/java/util/Currency/Bug6807534.java | 84 ---- test/jdk/java/util/Currency/Bug8154295.java | 55 --- .../java/util/Currency/CNPGetDisplayName.java | 101 ++++ .../java/util/Currency/CheckDataVersion.java | 22 +- test/jdk/java/util/Currency/CurrencyTest.java | 456 +++++++++-------- .../Currency/NoMinorUnitCurrenciesTest.java | 66 +++ .../util/Currency/NumCodeAsStringTest.java | 61 +++ .../java/util/Currency/ValidateISO4217.java | 460 +++++++++--------- 9 files changed, 721 insertions(+), 652 deletions(-) delete mode 100644 test/jdk/java/util/Currency/Bug4512215.java delete mode 100644 test/jdk/java/util/Currency/Bug6807534.java delete mode 100644 test/jdk/java/util/Currency/Bug8154295.java create mode 100644 test/jdk/java/util/Currency/CNPGetDisplayName.java create mode 100644 test/jdk/java/util/Currency/NoMinorUnitCurrenciesTest.java create mode 100644 test/jdk/java/util/Currency/NumCodeAsStringTest.java diff --git a/test/jdk/java/util/Currency/Bug4512215.java b/test/jdk/java/util/Currency/Bug4512215.java deleted file mode 100644 index 2e2a78c279b..00000000000 --- a/test/jdk/java/util/Currency/Bug4512215.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2007, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -/* - * @test - * @bug 4512215 4818420 4819436 - * @summary Updated currency data. - */ - -import java.util.Currency; -import java.util.Locale; - -public class Bug4512215 { - - public static void main(String[] args) throws Exception { - testCurrencyDefined("XBD", -1); - testCountryCurrency("TJ", "TJS", 2); - testCountryCurrency("FO", "DKK", 2); - testCountryCurrency("FK", "FKP", 2); - - testCountryCurrency("AF", "AFN", 2); // changed from "AFA" - - // Newsletter V-5 on ISO 3166-1 (2002-05-20) - testCountryCurrency("TL", "USD", 2); // successor to TP/TPE - - // Newsletter V-8 on ISO 3166-1 (2003-07-23) - testCountryCurrency("CS", "CSD", 2); // successor to YU/YUM - } - - private static void testCountryCurrency(String country, String currencyCode, - int digits) { - testCurrencyDefined(currencyCode, digits); - Currency currency = Currency.getInstance(new Locale("", country)); - if (!currency.getCurrencyCode().equals(currencyCode)) { - throw new RuntimeException("[" + country - + "] expected: " + currencyCode - + "; got: " + currency.getCurrencyCode()); - } - } - - private static void testCurrencyDefined(String currencyCode, int digits) { - Currency currency = Currency.getInstance(currencyCode); - if (currency.getDefaultFractionDigits() != digits) { - throw new RuntimeException("[" + currencyCode - + "] expected: " + digits - + "; got: " + currency.getDefaultFractionDigits()); - } - } -} diff --git a/test/jdk/java/util/Currency/Bug6807534.java b/test/jdk/java/util/Currency/Bug6807534.java deleted file mode 100644 index 073518d45ac..00000000000 --- a/test/jdk/java/util/Currency/Bug6807534.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2010, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -/* - * @test - * @bug 6807534 - * @summary check whether the default implementation of - * CurrencNameProvider.getDisplayName(String, Locale) throws appropriate - * exceptions when necessary. - */ - -import java.util.Locale; -import java.util.spi.CurrencyNameProvider; - -public class Bug6807534 { - - static final CurrencyNameProvider cnp = new CurrencyNameProviderImpl(); - - public static void main(String[] args) throws Exception { - // test for NullPointerException (currencyCode) - try { - cnp.getDisplayName(null, Locale.US); - throwException("NPE was not thrown with null currencyCode"); - } catch (NullPointerException npe) {} - - // test for NullPointerException (locale) - try { - cnp.getDisplayName("USD", null); - throwException("NPE was not thrown with null locale"); - } catch (NullPointerException npe) {} - - // test for IllegalArgumentException (illegal currencyCode) - try { - cnp.getDisplayName("INVALID", Locale.US); - throwException("IllegalArgumentException was not thrown with invalid currency code"); - } catch (IllegalArgumentException iae) {} - try { - cnp.getDisplayName("inv", Locale.US); - throwException("IllegalArgumentException was not thrown with invalid currency code"); - } catch (IllegalArgumentException iae) {} - - // test for IllegalArgumentException (non-supported locale) - try { - cnp.getDisplayName("USD", Locale.JAPAN); - throwException("IllegalArgumentException was not thrown with non-supported locale"); - } catch (IllegalArgumentException iae) {} - } - - static void throwException(String msg) { - throw new RuntimeException("test failed. "+msg); - } - - static class CurrencyNameProviderImpl extends CurrencyNameProvider { - // dummy implementation - public String getSymbol(String currencyCode, Locale locale) { - return ""; - } - - public Locale[] getAvailableLocales() { - Locale[] avail = new Locale[1]; - avail[0] = Locale.US; - return avail; - } - } -} diff --git a/test/jdk/java/util/Currency/Bug8154295.java b/test/jdk/java/util/Currency/Bug8154295.java deleted file mode 100644 index 356d00ba36b..00000000000 --- a/test/jdk/java/util/Currency/Bug8154295.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2016, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -/* - * @test - * @bug 8154295 - * @summary Check getNumericCodeAsString() method which returns numeric code as a 3 digit String. - */ - -import java.util.Currency; - -public class Bug8154295 { - - public static void main(String[] args) { - - String numericCode = Currency.getInstance("AFA").getNumericCodeAsString(); - if (!numericCode.equals("004")) { //should return "004" (a 3 digit string) - throw new RuntimeException("[Expected 004, " - + "found "+numericCode+" for AFA]"); - } - - numericCode = Currency.getInstance("AUD").getNumericCodeAsString(); - if (!numericCode.equals("036")) { //should return "036" (a 3 digit string) - throw new RuntimeException("[Expected 036, " - + "found "+numericCode+" for AUD]"); - } - - numericCode = Currency.getInstance("USD").getNumericCodeAsString(); - if (!numericCode.equals("840")) {// should return "840" (a 3 digit string) - throw new RuntimeException("[Expected 840, " - + "found "+numericCode+" for USD]"); - } - - } - -} diff --git a/test/jdk/java/util/Currency/CNPGetDisplayName.java b/test/jdk/java/util/Currency/CNPGetDisplayName.java new file mode 100644 index 00000000000..af399bee540 --- /dev/null +++ b/test/jdk/java/util/Currency/CNPGetDisplayName.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6807534 + * @summary check whether the default implementation of + * CurrencyNameProvider.getDisplayName(String, Locale) throws appropriate + * exceptions when necessary. + * @run junit CNPGetDisplayName + */ + +import java.util.Locale; +import java.util.spi.CurrencyNameProvider; +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.assertThrows; + +public class CNPGetDisplayName { + + static final CurrencyNameProvider cnp = new CurrencyNameProviderImpl(); + + /** + * Tests that the currency name provider throws a NullPointerException + * under the expected circumstances. + */ + @ParameterizedTest + @MethodSource("nullArgProvider") + public void NPETest(String currencyCode, Locale locale, String err) { + assertThrows(NullPointerException.class, + () -> cnp.getDisplayName(currencyCode, locale), err); + } + + /** + * Tests that the currency name provider throws a IllegalArgumentException + * under the expected circumstances. + */ + @ParameterizedTest + @MethodSource("illegalArgProvider") + public void IAETest(String currencyCode, Locale locale, String err) { + assertThrows(IllegalArgumentException.class, + () -> cnp.getDisplayName(currencyCode, locale), err); + } + + private static Stream nullArgProvider() { + return Stream.of( + Arguments.of(null, Locale.US, + "NPE was not thrown with null currencyCode"), + Arguments.of("USD", null, + "NPE was not thrown with null locale") + ); + } + + private static Stream illegalArgProvider() { + return Stream.of( + Arguments.of("INVALID", Locale.US, + "IAE was not thrown with invalid currency code"), + Arguments.of("inv", Locale.US, + "IAE was not thrown with invalid currency code"), + Arguments.of("USD", Locale.JAPAN, + "IllegalArgumentException was not thrown with non-supported locale") + ); + } + + static class CurrencyNameProviderImpl extends CurrencyNameProvider { + // dummy implementation + public String getSymbol(String currencyCode, Locale locale) { + return ""; + } + + public Locale[] getAvailableLocales() { + Locale[] avail = new Locale[1]; + avail[0] = Locale.US; + return avail; + } + } +} diff --git a/test/jdk/java/util/Currency/CheckDataVersion.java b/test/jdk/java/util/Currency/CheckDataVersion.java index 204a80a8557..ba18677dbbe 100644 --- a/test/jdk/java/util/Currency/CheckDataVersion.java +++ b/test/jdk/java/util/Currency/CheckDataVersion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -20,15 +20,21 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -/** - * - * - * Check the consistency between the regression tests and the currency data in the JRE + + +/* + Check the consistency between the regression tests and the currency + data in the JRE. This class is used by other test classes. */ -import java.io.*; -import java.lang.reflect.*; -import java.security.*; +import java.io.BufferedReader; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Currency; class CheckDataVersion { diff --git a/test/jdk/java/util/Currency/CurrencyTest.java b/test/jdk/java/util/Currency/CurrencyTest.java index ad7c596485b..380bf3b338a 100644 --- a/test/jdk/java/util/Currency/CurrencyTest.java +++ b/test/jdk/java/util/Currency/CurrencyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -20,6 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + /* * @test * @bug 4290801 4692419 4693631 5101540 5104960 6296410 6336600 6371531 @@ -28,6 +29,7 @@ * @summary Basic tests for Currency class. * @modules java.base/java.util:open * jdk.localedata + * @run junit CurrencyTest */ import java.io.ByteArrayInputStream; @@ -38,198 +40,236 @@ import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Currency; +import java.util.List; import java.util.Locale; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +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.assertThrows; public class CurrencyTest { - public static void main(String[] args) throws Exception { + // 'tablea1.txt' should be up-to-date before testing + @Test + public void dataVersionTest() { CheckDataVersion.check(); - testCurrencyCodeValidation(); - testLocaleMapping(); - testSymbols(); - testFractionDigits(); - testSerialization(); - testDisplayNames(); - testFundsCodes(); } - static void testCurrencyCodeValidation() { - // test creation of some valid currencies - testValidCurrency("USD"); - testValidCurrency("EUR"); - testValidCurrency("GBP"); - testValidCurrency("JPY"); - testValidCurrency("CNY"); - testValidCurrency("CHF"); - - // test creation of some fictitious currencies - testInvalidCurrency("AQD"); - testInvalidCurrency("US$"); - testInvalidCurrency("\u20AC"); - } + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + class CodeValidationTests { + // Calling getInstance() on equal currency codes should return equal currencies + @ParameterizedTest + @MethodSource("validCurrencies") + public void validCurrencyTest(String currencyCode) { + compareCurrencies(currencyCode); + } - static void testValidCurrency(String currencyCode) { - Currency currency1 = Currency.getInstance(currencyCode); - Currency currency2 = Currency.getInstance(currencyCode); - if (currency1 != currency2) { - throw new RuntimeException("Didn't get same instance for same currency code"); + private Stream validCurrencies() { + return Stream.of("USD", "EUR", "GBP", "JPY", "CNY", "CHF"); + } + + // Calling getInstance() with an invalid currency code should throw an IAE + @ParameterizedTest + @MethodSource("invalidCurrencies") + public void invalidCurrencyTest(String currencyCode) { + assertThrows(IllegalArgumentException.class, () -> + Currency.getInstance(currencyCode), "getInstance() did not throw IAE"); } - if (!currency1.getCurrencyCode().equals(currencyCode)) { - throw new RuntimeException("Currency code changed"); + + private Stream invalidCurrencies() { + return Stream.of("AQD", "US$", "\u20AC"); } } - static void testInvalidCurrency(String currencyCode) { - boolean gotException = false; - try { - Currency currency = Currency.getInstance(currencyCode); - } catch (IllegalArgumentException e) { - gotException = true; + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + class FundsCodesTests { + // Calling getInstance() on equal currency codes should return equal currencies + @ParameterizedTest + @MethodSource("fundsCodes") + public void validCurrencyTest(String currencyCode) { + compareCurrencies(currencyCode); } - if (!gotException) { - throw new RuntimeException("didn't get specified exception"); + + // Verify a currency has the expected fractional digits + @ParameterizedTest + @MethodSource("fundsCodes") + public void fractionDigitTest(String currencyCode, int expectedFractionDigits) { + compareFractionDigits(currencyCode, expectedFractionDigits); + } + + // Verify a currency has the expected numeric code + @ParameterizedTest + @MethodSource("fundsCodes") + public void numericCodeTest(String currencyCode, int ignored, int expectedNumeric) { + int numeric = Currency.getInstance(currencyCode).getNumericCode(); + assertEquals(numeric, expectedNumeric, String.format( + "Wrong numeric code for currency %s, expected %s, got %s", + currencyCode, expectedNumeric, numeric)); + } + + private Stream fundsCodes() { + return Stream.of( + Arguments.of("BOV", 2, 984), Arguments.of("CHE", 2, 947), + Arguments.of("CHW", 2, 948), Arguments.of("CLF", 4, 990), + Arguments.of("COU", 2, 970), Arguments.of("MXV", 2, 979), + Arguments.of("USN", 2, 997), Arguments.of("UYI", 0, 940) + ); } } - static void testLocaleMapping() { + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + class LocaleMappingTests { + // very basic test: most countries have their own currency, and then // their currency code is an extension of their country code. - Locale[] locales = Locale.getAvailableLocales(); - int goodCountries = 0; - int ownCurrencies = 0; - for (int i = 0; i < locales.length; i++) { - Locale locale = locales[i]; - String ctryCode = locale.getCountry(); - int ctryLength = ctryCode.length(); - if (ctryLength == 0 || - ctryLength == 3 || // UN M.49 code - ctryCode.matches("AA|Q[M-Z]|X[A-JL-Z]|ZZ" + // user defined codes, excluding "XK" (Kosovo) - "AC|CP|DG|EA|EU|FX|IC|SU|TA|UK")) { // exceptional reservation codes - boolean gotException = false; - try { - Currency.getInstance(locale); - } catch (IllegalArgumentException e) { - gotException = true; - } - if (!gotException) { - throw new RuntimeException("didn't get specified exception"); - } - } else { - goodCountries++; - Currency currency = Currency.getInstance(locale); - if (currency.getCurrencyCode().indexOf(locale.getCountry()) == 0) { - ownCurrencies++; + @Test + public void localeMappingTest() { + Locale[] locales = Locale.getAvailableLocales(); + int goodCountries = 0; + int ownCurrencies = 0; + for (Locale locale : locales) { + String ctryCode = locale.getCountry(); + int ctryLength = ctryCode.length(); + if (ctryLength == 0 || + ctryLength == 3 || // UN M.49 code + ctryCode.matches("AA|Q[M-Z]|X[A-JL-Z]|ZZ" + // user defined codes, excluding "XK" (Kosovo) + "AC|CP|DG|EA|EU|FX|IC|SU|TA|UK")) { // exceptional reservation codes + assertThrows(IllegalArgumentException.class, () -> Currency.getInstance(locale), "Did not throw IAE"); + } else { + goodCountries++; + Currency currency = Currency.getInstance(locale); + if (currency.getCurrencyCode().indexOf(locale.getCountry()) == 0) { + ownCurrencies++; + } } } + System.out.println("Countries tested: " + goodCountries + + ", own currencies: " + ownCurrencies); + if (ownCurrencies < (goodCountries / 2 + 1)) { + throw new RuntimeException("suspicious: not enough countries have their own currency."); + } } - System.out.println("Countries tested: " + goodCountries + - ", own currencies: " + ownCurrencies); - if (ownCurrencies < (goodCountries / 2 + 1)) { - throw new RuntimeException("suspicious: not enough countries have their own currency."); + + // Check an invalid country code + @Test + public void invalidCountryTest() { + assertThrows(IllegalArgumentException.class, ()-> + Currency.getInstance(new Locale("", "EU")), "Did not throw IAE"); } - // check a few countries that don't change their currencies too often - String[] country1 = {"US", "CA", "JP", "CN", "SG", "CH"}; - String[] currency1 = {"USD", "CAD", "JPY", "CNY", "SGD", "CHF"}; - for (int i = 0; i < country1.length; i++) { - checkCountryCurrency(country1[i], currency1[i]); + // Ensure a selection of countries have the expected currency + @ParameterizedTest + @MethodSource({"countryProvider", "switchedOverCountries"}) + public void countryCurrencyTest(String countryCode, String expected) { + Locale locale = new Locale("", countryCode); + Currency currency = Currency.getInstance(locale); + String code = (currency != null) ? currency.getCurrencyCode() : null; + assertEquals(expected, code, generateErrMsg( + "currency for", locale.getDisplayCountry(), expected, code)); } - /* - * check currency changes - * In current implementation, there is no data of old currency and transition date at jdk/make/data/currency/CurrencyData.properties. - * So, all the switch data arrays are empty. In the future, if data of old currency and transition date are necessary for any country, the - * arrays here can be updated so that the program can check the currency switch. - */ - String[] switchOverCtry = {}; - String[] switchOverOld = {}; - String[] switchOverNew = {}; - String[] switchOverTZ = {}; - int[] switchOverYear = {}; - int[] switchOverMonth = {}; // java.time APIs accept month starting from 1 i.e. 01 for January - int[] switchOverDay = {}; - - for (int i = 0; i < switchOverCtry.length; i++) { - ZoneId zoneId = ZoneId.of(switchOverTZ[i]); - ZonedDateTime zonedDateAndTime = ZonedDateTime.of(LocalDate.of(switchOverYear[i], switchOverMonth[i], switchOverDay[i]), - LocalTime.MIDNIGHT, zoneId); - ZonedDateTime currentZonedDateAndTime = ZonedDateTime.now(zoneId); - checkCountryCurrency(switchOverCtry[i], (currentZonedDateAndTime.isAfter(zonedDateAndTime) || - currentZonedDateAndTime.isEqual(zonedDateAndTime)) ? switchOverNew[i] : switchOverOld[i]); + private Stream countryProvider() { + return Stream.of( + // Check country that does not have a currency + Arguments.of("AQ", null), + // Check some countries that don't change their currencies often + Arguments.of("US", "USD"), + Arguments.of("CA", "CAD"), + Arguments.of("JP", "JPY"), + Arguments.of("CN", "CNY"), + Arguments.of("SG", "SGD"), + Arguments.of("CH", "CHF") + ); } - // check a country code which doesn't have a currency - checkCountryCurrency("AQ", null); + /* + * Check Currency Changes + * In the current implementation, there is no data of old currency and transition + * date at jdk/src/java.base/share/data/currency/CurrencyData.properties. + * So, all the switch data arrays are empty. In the future, if data of old + * currency and transition date are necessary for any country, the + * arrays here can be updated so that the program can check the currency switch. + */ + private List switchedOverCountries() { + List switched = new ArrayList(); + String[] switchOverCtry = {}; + String[] switchOverOld = {}; + String[] switchOverNew = {}; + String[] switchOverTZ = {}; + int[] switchOverYear = {}; + int[] switchOverMonth = {}; // java.time APIs accept month starting from 1 i.e. 01 for January + int[] switchOverDay = {}; - // check an invalid country code - boolean gotException = false; - try { - Currency.getInstance(new Locale("", "EU")); - } catch (IllegalArgumentException e) { - gotException = true; - } - if (!gotException) { - throw new RuntimeException("didn't get specified exception."); + for (int i = 0; i < switchOverCtry.length; i++) { + ZoneId zoneId = ZoneId.of(switchOverTZ[i]); + ZonedDateTime zonedDateAndTime = ZonedDateTime.of(LocalDate.of( + switchOverYear[i], switchOverMonth[i], switchOverDay[i]), LocalTime.MIDNIGHT, zoneId); + ZonedDateTime currentZonedDateAndTime = ZonedDateTime.now(zoneId); + switched.add(Arguments.of(switchOverCtry[i], (currentZonedDateAndTime.isAfter(zonedDateAndTime) + || currentZonedDateAndTime.isEqual(zonedDateAndTime)) ? switchOverNew[i] : switchOverOld[i])); + } + return switched; } } - static void checkCountryCurrency(String countryCode, String expected) { - Locale locale = new Locale("", countryCode); - Currency currency = Currency.getInstance(locale); - String code = (currency != null) ? currency.getCurrencyCode() : null; - if (!(expected == null ? code == null : expected.equals(code))) { - throw new RuntimeException("Wrong currency for " + - locale.getDisplayCountry() + - ": expected " + expected + ", got " + code); - } - } + // NON-NESTED TESTS - static void testSymbols() { - testSymbol("USD", Locale.US, "$"); - testSymbol("EUR", Locale.GERMANY, "\u20AC"); - testSymbol("USD", Locale.PRC, "US$"); + // Ensure selection of currencies have the correct fractional digits + @ParameterizedTest + @MethodSource("expectedFractionsProvider") + public void fractionDigitsTest(String currencyCode, int expectedFractionDigits) { + compareFractionDigits(currencyCode, expectedFractionDigits); } - static void testSymbol(String currencyCode, Locale locale, String expectedSymbol) { - String symbol = Currency.getInstance(currencyCode).getSymbol(locale); - if (!symbol.equals(expectedSymbol)) { - throw new RuntimeException("Wrong symbol for currency " + - currencyCode +": expected " + expectedSymbol + - ", got " + symbol); - } + private static Stream expectedFractionsProvider() { + return Stream.of( + Arguments.of("USD", 2), Arguments.of("EUR", 2), + Arguments.of("JPY", 0), Arguments.of("XDR", -1), + Arguments.of("BHD", 3), Arguments.of("IQD", 3), + Arguments.of("JOD", 3), Arguments.of("KWD", 3), + Arguments.of("LYD", 3), Arguments.of("OMR", 3), + Arguments.of("TND", 3), + + // Old and New Turkish Lira + Arguments.of("TRL", 0), Arguments.of("TRY", 2) + ); } - static void testFractionDigits() { - testFractionDigits("USD", 2); - testFractionDigits("EUR", 2); - testFractionDigits("JPY", 0); - testFractionDigits("XDR", -1); - - testFractionDigits("BHD", 3); - testFractionDigits("IQD", 3); - testFractionDigits("JOD", 3); - testFractionDigits("KWD", 3); - testFractionDigits("LYD", 3); - testFractionDigits("OMR", 3); - testFractionDigits("TND", 3); - - // Turkish Lira - testFractionDigits("TRL", 0); - testFractionDigits("TRY", 2); + // Ensure selection of currencies have the expected symbol + @ParameterizedTest + @MethodSource("symbolProvider") + public void symbolTest(String currencyCode, Locale locale, String expectedSymbol) { + String symbol = Currency.getInstance(currencyCode).getSymbol(locale); + assertEquals(symbol, expectedSymbol, generateErrMsg( + "symbol for", currencyCode, expectedSymbol, symbol)); } - static void testFractionDigits(String currencyCode, int expectedFractionDigits) { - int digits = Currency.getInstance(currencyCode).getDefaultFractionDigits(); - if (digits != expectedFractionDigits) { - throw new RuntimeException("Wrong number of fraction digits for currency " + - currencyCode +": expected " + expectedFractionDigits + - ", got " + digits); - } + private static Stream symbolProvider() { + return Stream.of( + Arguments.of("USD", Locale.US, "$"), + Arguments.of("EUR", Locale.GERMANY, "\u20AC"), + Arguments.of("USD", Locale.PRC, "US$") + ); } - static void testSerialization() throws Exception { + // Ensure serialization does not break class invariant. + // Currency should be able to round-trip and remain the same value. + @Test + public void serializationTest() throws Exception { Currency currency1 = Currency.getInstance("DEM"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -241,74 +281,66 @@ static void testSerialization() throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(bytes); ObjectInputStream iStream = new ObjectInputStream(bais); Currency currency2 = (Currency) iStream.readObject(); - - if (currency1 != currency2) { - throw new RuntimeException("serialization breaks class invariant"); - } + assertEquals(currency1, currency2, "serialization breaks class invariant"); } - static void testDisplayNames() { - // null argument test - try { - testDisplayName("USD", null, ""); - throw new RuntimeException("getDisplayName(NULL) did not throw an NPE."); - } catch (NullPointerException npe) {} - - testDisplayName("USD", Locale.ENGLISH, "US Dollar"); - testDisplayName("FRF", Locale.FRENCH, "franc fran\u00e7ais"); - testDisplayName("DEM", Locale.GERMAN, "Deutsche Mark"); - testDisplayName("ESP", new Locale("es"), "peseta espa\u00f1ola"); - testDisplayName("ITL", new Locale("it"), "lira italiana"); - testDisplayName("JPY", Locale.JAPANESE, "\u65e5\u672c\u5186"); - testDisplayName("KRW", Locale.KOREAN, "\ub300\ud55c\ubbfc\uad6d \uc6d0"); - testDisplayName("SEK", new Locale("sv"), "svensk krona"); - testDisplayName("CNY", Locale.SIMPLIFIED_CHINESE, "\u4eba\u6c11\u5e01"); - testDisplayName("TWD", Locale.TRADITIONAL_CHINESE, "\u65b0\u53f0\u5e63"); + // Ensure getInstance() throws null when passed a null locale + @Test + public void nullDisplayNameTest() { + assertThrows(NullPointerException.class, ()-> + Currency.getInstance("USD").getDisplayName(null)); } - static void testDisplayName(String currencyCode, Locale locale, String expectedName) { + // Ensure a selection of currencies/locale combos have the correct display name + @ParameterizedTest + @MethodSource("displayNameProvider") + public void displayNameTest(String currencyCode, Locale locale, String expectedName) { String name = Currency.getInstance(currencyCode).getDisplayName(locale); - if (!name.equals(expectedName)) { - throw new RuntimeException("Wrong display name for currency " + - currencyCode +": expected '" + expectedName + - "', got '" + name + "'"); - } + assertEquals(name, expectedName, generateErrMsg( + "display name for", currencyCode, expectedName, name)); } - static void testFundsCodes() { - testValidCurrency("BOV"); - testValidCurrency("CHE"); - testValidCurrency("CHW"); - testValidCurrency("CLF"); - testValidCurrency("COU"); - testValidCurrency("MXV"); - testValidCurrency("USN"); - testValidCurrency("UYI"); - - testFractionDigits("BOV", 2); - testFractionDigits("CHE", 2); - testFractionDigits("CHW", 2); - testFractionDigits("CLF", 4); - testFractionDigits("COU", 2); - testFractionDigits("MXV", 2); - testFractionDigits("USN", 2); - testFractionDigits("UYI", 0); - - testNumericCode("BOV", 984); - testNumericCode("CHE", 947); - testNumericCode("CHW", 948); - testNumericCode("CLF", 990); - testNumericCode("COU", 970); - testNumericCode("MXV", 979); - testNumericCode("USN", 997); - testNumericCode("UYI", 940); + + private static Stream displayNameProvider() { + return Stream.of( + Arguments.of("USD", Locale.ENGLISH, "US Dollar"), + Arguments.of("FRF", Locale.FRENCH, "franc fran\u00e7ais"), + Arguments.of("DEM", Locale.GERMAN, "Deutsche Mark"), + Arguments.of("ESP", new Locale("es"), "peseta espa\u00f1ola"), + Arguments.of("ITL", Locale.ITALIAN, "lira italiana"), + Arguments.of("JPY", Locale.JAPANESE, "\u65e5\u672c\u5186"), + Arguments.of("KRW", Locale.KOREAN, "\ub300\ud55c\ubbfc\uad6d \uc6d0"), + Arguments.of("SEK", new Locale("sv"), "svensk krona"), + Arguments.of("CNY", Locale.SIMPLIFIED_CHINESE, "\u4eba\u6c11\u5e01"), + Arguments.of("TWD", Locale.TRADITIONAL_CHINESE, "\u65b0\u53f0\u5e63") + ); } - static void testNumericCode(String currencyCode, int expectedNumeric) { - int numeric = Currency.getInstance(currencyCode).getNumericCode(); - if (numeric != expectedNumeric) { - throw new RuntimeException("Wrong numeric code for currency " + - currencyCode +": expected " + expectedNumeric + - ", got " + numeric); - } + // HELPER FUNCTIONS + + // A Currency instance returned from getInstance() should always be + // equal if supplied the same currencyCode. getCurrencyCode() should + // always be equal to the currencyCode used to create the Currency. + private static void compareCurrencies(String currencyCode) { + Currency currency1 = Currency.getInstance(currencyCode); + Currency currency2 = Currency.getInstance(currencyCode); + assertEquals(currency1, currency2, "Didn't get same instance for same currency code"); + assertEquals(currency1.getCurrencyCode(), currencyCode, "getCurrencyCode()" + + " did not return the expected value"); + } + + // Ensures the getDefaultFractionDigits() method returns the expected amount + private static void compareFractionDigits(String currencyCode, + int expectedFractionDigits) { + int digits = Currency.getInstance(currencyCode).getDefaultFractionDigits(); + assertEquals(digits, expectedFractionDigits, generateErrMsg( + "number of fraction digits for currency", + currencyCode, Integer.toString(expectedFractionDigits), Integer.toString(digits))); + } + + // Used for logging on failing tests + private static String generateErrMsg(String subject, String currency, + String expected, String got) { + return String.format("Wrong %s %s: expected '%s', got '%s'", + subject, currency, expected, got); } } diff --git a/test/jdk/java/util/Currency/NoMinorUnitCurrenciesTest.java b/test/jdk/java/util/Currency/NoMinorUnitCurrenciesTest.java new file mode 100644 index 00000000000..1fe9c8b64d8 --- /dev/null +++ b/test/jdk/java/util/Currency/NoMinorUnitCurrenciesTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2007, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4512215 4818420 4819436 8310923 + * @summary Test currencies without minor units. + * @run junit NoMinorUnitCurrenciesTest + */ + +import java.util.Currency; +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 NoMinorUnitCurrenciesTest { + + /** + * Spot check some minor undefined currencies and ensure their default fraction + * digits are not 2. + */ + @ParameterizedTest + @MethodSource("minorUndefined") + public void checkFractionDigits(String currencyCode, int digits) { + Currency currency = Currency.getInstance(currencyCode); + assertEquals(currency.getCurrencyCode(), currencyCode); + assertEquals(currency.getDefaultFractionDigits(), digits, String.format( + "[%s] expected: %s; got: %s", currencyCode, digits, currency.getDefaultFractionDigits())); + } + + // Currencies from the minorUndefined key of CurrencyData.properties + // (These are currencies without minor units) + private static Stream minorUndefined() { + return Stream.of( + Arguments.of("XBD", -1), + Arguments.of("XAG", -1), + Arguments.of("XAU", -1), + Arguments.of("XBA", -1), + Arguments.of("XBB", -1) + ); + } +} diff --git a/test/jdk/java/util/Currency/NumCodeAsStringTest.java b/test/jdk/java/util/Currency/NumCodeAsStringTest.java new file mode 100644 index 00000000000..8bec0856364 --- /dev/null +++ b/test/jdk/java/util/Currency/NumCodeAsStringTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8154295 + * @summary Check getNumericCodeAsString() method which returns numeric code as a 3 digit String. + * @run junit NumCodeAsStringTest + */ + +import java.util.Currency; +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 NumCodeAsStringTest { + + /** + * Ensure getNumericCodeAsString() returns the correct 3-digit numeric code + * for the associated currency Code. + */ + @ParameterizedTest + @MethodSource("codeProvider") + public void checkNumCodeTest(String currCode, String expectedNumCode) { + String actualNumCode = Currency.getInstance(currCode).getNumericCodeAsString(); + assertEquals(expectedNumCode, actualNumCode, String.format( + "Expected: %s, but got: %s, for %s", expectedNumCode, actualNumCode, currCode)); + } + + private static Stream codeProvider() { + return Stream.of( + Arguments.of("AFA", "004"), + Arguments.of("AUD", "036"), + Arguments.of("USD", "840") + ); + } +} diff --git a/test/jdk/java/util/Currency/ValidateISO4217.java b/test/jdk/java/util/Currency/ValidateISO4217.java index 3f8d858b8a1..9a92e74eb3f 100644 --- a/test/jdk/java/util/Currency/ValidateISO4217.java +++ b/test/jdk/java/util/Currency/ValidateISO4217.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -20,6 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + /* * @test * @bug 4691089 4819436 4942982 5104960 6544471 6627549 7066203 7195759 @@ -28,285 +29,294 @@ * @summary Validate ISO 4217 data for Currency class. * @modules java.base/java.util:open * jdk.localedata + * @run junit ValidateISO4217 */ -/* - * ############################################################################ - * - * ValidateISO4217 is a tool to detect differences between the latest ISO 4217 - * data and Java's currency data which is based on ISO 4217. - * If there is a difference, the following file which includes currency data - * may need to be updated. - * src/share/classes/java/util/CurrencyData.properties - * - * ############################################################################ - * - * 1) Make a golden-data file. - * From BSi's ISO4217 data (TABLE A1.doc), extract four (or eight, if currency is changing) - * fields and save as ./tablea1.txt. - * \t\t\t[\t\t\t\t] - * The Cutover Date is given in SimpleDateFormat's 'yyyy-MM-dd-HH-mm-ss' format in the GMT time zone. - * - * 2) Compile ValidateISO4217.java - * - * 3) Execute ValidateISO4217 as follows: - * java ValidateISO4217 +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Currency; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.TimeZone; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +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.assertNull; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * This class tests the latest ISO 4217 data and Java's currency data which is + * based on ISO 4217. The golden-data file (ISO 4217 data) 'tablea1.txt' has the following + * format: \t\t\t[\t\t\t\t] + * The Cutover Date is given in SimpleDateFormat's 'yyyy-MM-dd-HH-mm-ss' format in the GMT time zone. */ - -import java.io.*; -import java.text.*; -import java.util.*; - public class ValidateISO4217 { - static final int ALPHA_NUM = 26; - - static final byte UNDEFINED = 0; - static final byte DEFINED = 1; - static final byte SKIPPED = 2; - - /* input files */ - static final String datafile = "tablea1.txt"; - - /* alpha2-code table */ - static byte[] codes = new byte[ALPHA_NUM * ALPHA_NUM]; - - static final String[][] additionalCodes = { - /* Defined in ISO 4217 list, but don't have code and minor unit info. */ - {"AQ", "", "", "0"}, // Antarctica - - /* - * Defined in ISO 4217 list, but don't have code and minor unit info in - * it. On the other hand, both code and minor unit are defined in - * .properties file. I don't know why, though. - */ - {"GS", "GBP", "826", "2"}, // South Georgia And The South Sandwich Islands - - /* Not defined in ISO 4217 list, but defined in .properties file. */ - {"AX", "EUR", "978", "2"}, // \u00c5LAND ISLANDS - {"PS", "ILS", "376", "2"}, // Palestinian Territory, Occupied - - /* Not defined in ISO 4217 list, but added in ISO 3166 country code list */ - {"JE", "GBP", "826", "2"}, // Jersey - {"GG", "GBP", "826", "2"}, // Guernsey - {"IM", "GBP", "826", "2"}, // Isle of Man - {"BL", "EUR", "978", "2"}, // Saint Barthelemy - {"MF", "EUR", "978", "2"}, // Saint Martin - - /* Defined neither in ISO 4217 nor ISO 3166 list */ - {"XK", "EUR", "978", "2"}, // Kosovo + // Input golden-data file + private static final File dataFile = new File(System.getProperty( + "test.src", "."), "tablea1.txt"); + // Code statuses + private static final byte UNDEFINED = 0; + private static final byte DEFINED = 1; + private static final byte SKIPPED = 2; + private static final byte TESTED = 4; + private static final int ALPHA_NUM = 26; + // An alpha2 code table which maps the status of a country + private static final byte[] codes = new byte[ALPHA_NUM * ALPHA_NUM]; + // Codes derived from ISO4217 golden-data file + private static final List ISO4217Codes = new ArrayList(); + // Additional codes not from the ISO4217 golden-data file + private static final List additionalCodes = new ArrayList(); + // Currencies to test (derived from ISO4217Codes and additionalCodes) + private static final Set testCurrencies = new HashSet<>(); + // Codes that are obsolete, do not have related country, extra currency + private static final String otherCodes = + "ADP-AFA-ATS-AYM-AZM-BEF-BGL-BOV-BYB-BYR-CHE-CHW-CLF-COU-CUC-CYP-" + + "DEM-EEK-ESP-FIM-FRF-GHC-GRD-GWP-HRK-IEP-ITL-LTL-LUF-LVL-MGF-MRO-MTL-MXV-MZM-NLG-" + + "PTE-ROL-RUR-SDD-SIT-SLL-SKK-SRG-STD-TMM-TPE-TRL-VEF-UYI-USN-USS-VEB-VED-" + + "XAG-XAU-XBA-XBB-XBC-XBD-XDR-XFO-XFU-XPD-XPT-XSU-XTS-XUA-XXX-" + + "YUM-ZMK-ZWD-ZWN-ZWR"; + private static final String[][] extraCodes = { + /* Defined in ISO 4217 list, but don't have code and minor unit info. */ + {"AQ", "", "", "0"}, // Antarctica + /* + * Defined in ISO 4217 list, but don't have code and minor unit info in + * it. On the other hand, both code and minor unit are defined in + * .properties file. I don't know why, though. + */ + {"GS", "GBP", "826", "2"}, // South Georgia And The South Sandwich Islands + /* Not defined in ISO 4217 list, but defined in .properties file. */ + {"AX", "EUR", "978", "2"}, // \u00c5LAND ISLANDS + {"PS", "ILS", "376", "2"}, // Palestinian Territory, Occupied + /* Not defined in ISO 4217 list, but added in ISO 3166 country code list */ + {"JE", "GBP", "826", "2"}, // Jersey + {"GG", "GBP", "826", "2"}, // Guernsey + {"IM", "GBP", "826", "2"}, // Isle of Man + {"BL", "EUR", "978", "2"}, // Saint Barthelemy + {"MF", "EUR", "978", "2"}, // Saint Martin + /* Defined neither in ISO 4217 nor ISO 3166 list */ + {"XK", "EUR", "978", "2"}, // Kosovo }; - - /* Codes that are obsolete, do not have related country, extra currency */ - static final String otherCodes = - "ADP-AFA-ATS-AYM-AZM-BEF-BGL-BOV-BYB-BYR-CHE-CHW-CLF-COU-CUC-CYP-" - + "DEM-EEK-ESP-FIM-FRF-GHC-GRD-GWP-HRK-IEP-ITL-LTL-LUF-LVL-MGF-MRO-MTL-MXV-MZM-NLG-" - + "PTE-ROL-RUR-SDD-SIT-SLL-SKK-SRG-STD-TMM-TPE-TRL-VEF-UYI-USN-USS-VEB-VED-" - + "XAG-XAU-XBA-XBB-XBC-XBD-XDR-XFO-XFU-XPD-XPT-XSU-XTS-XUA-XXX-" - + "YUM-ZMK-ZWD-ZWN-ZWR"; - - static boolean err = false; - - static Set testCurrencies = new HashSet(); - - public static void main(String[] args) throws Exception { - CheckDataVersion.check(); - test1(); - test2(); - getAvailableCurrenciesTest(); - - if (err) { - throw new RuntimeException("Failed: Validation ISO 4217 data"); - } + private static SimpleDateFormat format = null; + + // Sets up the following test data: + // ISO4217Codes, additionalCodes, testCurrencies, codes + @BeforeAll + static void setUpTestingData() throws Exception { + // These functions laterally setup 'testCurrencies' and 'codes' + // at the same time + setUpISO4217Codes(); + setUpAdditionalCodes(); + setUpOtherCurrencies(); } - static void test1() throws Exception { - - try (FileReader fr = new FileReader(new File(System.getProperty("test.src", "."), datafile)); + // Parse the ISO4217 file and populate ISO4217Codes and testCurrencies. + private static void setUpISO4217Codes() throws Exception{ + try (FileReader fr = new FileReader(dataFile); BufferedReader in = new BufferedReader(fr)) { String line; - SimpleDateFormat format = null; - while ((line = in.readLine()) != null) { if (line.length() == 0 || line.charAt(0) == '#') { + // Skip comments and empty lines continue; } - StringTokenizer tokens = new StringTokenizer(line, "\t"); String country = tokens.nextToken(); if (country.length() != 2) { + // Skip invalid countries continue; } + // If the country is valid, process the additional columns + processColumns(tokens, country); + } + } + } - String currency; - String numeric; - String minorUnit; - int tokensCount = tokens.countTokens(); - if (tokensCount < 3) { - currency = ""; - numeric = "0"; - minorUnit = "0"; - } else { + private static void processColumns(StringTokenizer tokens, String country) throws ParseException { + String currency; + String numeric; + String minorUnit; + int tokensCount = tokens.countTokens(); + if (tokensCount < 3) { + // Ill-defined columns + currency = ""; + numeric = "0"; + minorUnit = "0"; + } else { + // Fully defined columns + currency = tokens.nextToken(); + numeric = tokens.nextToken(); + minorUnit = tokens.nextToken(); + testCurrencies.add(Currency.getInstance(currency)); + // Check for the cut-over if a currency is changing + if (tokensCount > 3) { + if (format == null) { + createDateFormat(); + } + // If the cut-over already passed, use the new curency for ISO4217Codes + if (format.parse(tokens.nextToken()).getTime() < System.currentTimeMillis()) { currency = tokens.nextToken(); numeric = tokens.nextToken(); minorUnit = tokens.nextToken(); testCurrencies.add(Currency.getInstance(currency)); - - // check for the cutover - if (tokensCount > 3) { - 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()) { - currency = tokens.nextToken(); - numeric = tokens.nextToken(); - minorUnit = tokens.nextToken(); - testCurrencies.add(Currency.getInstance(currency)); - } - } } - int index = toIndex(country); - testCountryCurrency(country, currency, Integer.parseInt(numeric), - Integer.parseInt(minorUnit), index); } } + int index = toIndex(country); + ISO4217Codes.add(Arguments.of(country, currency, Integer.parseInt(numeric), + Integer.parseInt(minorUnit), index)); + codes[index] = DEFINED; + } + + // Generates a unique index for an alpha-2 country + private static int toIndex(String country) { + return ((country.charAt(0) - 'A') * ALPHA_NUM + country.charAt(1) - 'A'); + } + + private static void createDateFormat() { + format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + format.setLenient(false); + } - for (int i = 0; i < additionalCodes.length; i++) { - int index = toIndex(additionalCodes[i][0]); - if (additionalCodes[i][1].length() != 0) { - testCountryCurrency(additionalCodes[i][0], additionalCodes[i][1], - Integer.parseInt(additionalCodes[i][2]), - Integer.parseInt(additionalCodes[i][3]), index); - testCurrencies.add(Currency.getInstance(additionalCodes[i][1])); + // Process 'extraCodes', turning them into JUnit arguments and populate + // both additionalCodes and testCurrencies. + private static void setUpAdditionalCodes() { + for (String[] extraCode : extraCodes) { + int index = toIndex(extraCode[0]); + if (extraCode[1].length() != 0) { + additionalCodes.add(Arguments.of(extraCode[0], extraCode[1], + Integer.parseInt(extraCode[2]), Integer.parseInt(extraCode[3]), index)); + testCurrencies.add(Currency.getInstance(extraCode[1])); } else { - codes[index] = SKIPPED; + codes[index] = SKIPPED; // For example, Antarctica } } } - static int toIndex(String s) { - return ((s.charAt(0) - 'A') * ALPHA_NUM + s.charAt(1) - 'A'); + // The previous set-up method populated most of testCurrencies. This + // method finishes populating the list with 'otherCodes'. + private static void setUpOtherCurrencies() { + // Add otherCodes + StringTokenizer st = new StringTokenizer(otherCodes, "-"); + while (st.hasMoreTokens()) { + testCurrencies.add(Currency.getInstance(st.nextToken())); + } } - static void testCountryCurrency(String country, String currencyCode, - int numericCode, int digits, int index) { - if (currencyCode.length() == 0) { - return; - } - testCurrencyDefined(currencyCode, numericCode, digits); + // Check that the data file is up-to-date + @Test + public void dataVersionTest() { + CheckDataVersion.check(); + } - Locale loc = new Locale("", country); - try { - Currency currency = Currency.getInstance(loc); - if (!currency.getCurrencyCode().equals(currencyCode)) { - System.err.println("Error: [" + country + ":" + - loc.getDisplayCountry() + "] expected: " + currencyCode + - ", got: " + currency.getCurrencyCode()); - err = true; - } + /** + * Tests the JDK's ISO4217 data and ensures the values for getNumericCode(), + * getDefaultFractionDigits(), and getCurrencyCode() are as expected. + */ + @ParameterizedTest + @MethodSource({"ISO4217CodesProvider", "additionalCodesProvider"}) + public void countryCurrencyTest(String country, String currencyCode, + int numericCode, int digits, int index) { + currencyTest(currencyCode, numericCode, digits); + countryTest(country, currencyCode); + assertNotEquals(codes[index], TESTED, + "Error: Re-testing a previously defined code, possible duplication"); + codes[index] = TESTED; + } - if (codes[index] != UNDEFINED) { - System.out.println("Warning: [" + country + ":" + - loc.getDisplayCountry() + - "] multiple definitions. currency code=" + currencyCode); - } - codes[index] = DEFINED; - } - catch (Exception e) { - System.err.println("Error: " + e + ": Country=" + country); - err = true; - } + // Test a Currency built from currencyCode + private static void currencyTest(String currencyCode, int numericCode, int digits) { + Currency currency = Currency.getInstance(currencyCode); + assertEquals(currency.getNumericCode(), numericCode); + assertEquals(currency.getDefaultFractionDigits(), digits); + } + + // Test a Currency built from country + private static void countryTest(String country, String currencyCode) { + Locale loc = new Locale("", country); + Currency currency = Currency.getInstance(loc); + assertEquals(currency.getCurrencyCode(), currencyCode); } - static void testCurrencyDefined(String currencyCode, int numericCode, int digits) { - try { - Currency currency = currency = Currency.getInstance(currencyCode); + private static List ISO4217CodesProvider() { + return ISO4217Codes; + } - if (currency.getNumericCode() != numericCode) { - System.err.println("Error: [" + currencyCode + "] expected: " + - numericCode + "; got: " + currency.getNumericCode()); - err = true; - } + private static List additionalCodesProvider() { + return additionalCodes; + } - if (currency.getDefaultFractionDigits() != digits) { - System.err.println("Error: [" + currencyCode + "] expected: " + - digits + "; got: " + currency.getDefaultFractionDigits()); - err = true; + /** + * Tests trying to create a Currency from an invalid alpha-2 country either + * throws an IllegalArgumentException or returns null. The test data + * supplied is every possible combination of AA -> ZZ. + */ + @Test + public void twoLetterCodesTest() { + for (String country : codeCombos()) { + if (codes[toIndex(country)] == UNDEFINED) { + // if a code is undefined / 0, creating a Currency from it + // should throw an IllegalArgumentException + assertThrows(IllegalArgumentException.class, + () -> Currency.getInstance(new Locale("", country)), + "Error: This should be an undefined code and throw IllegalArgumentException: " + country); + } else if (codes[toIndex(country)] == SKIPPED) { + // if a code is marked as skipped / 2, creating a Currency from it + // should return null + assertNull(Currency.getInstance(new Locale("", country)), + "Error: Currency.getInstance() for this locale should return null: " + country); } } - catch (Exception e) { - System.err.println("Error: " + e + ": Currency code=" + - currencyCode); - err = true; - } } - static void test2() { + // This method generates code combos from AA to ZZ + private static List codeCombos() { + List codeCombos = new ArrayList<>(); for (int i = 0; i < ALPHA_NUM; i++) { for (int j = 0; j < ALPHA_NUM; j++) { char[] code = new char[2]; - code[0] = (char)('A'+ i); - code[1] = (char)('A'+ j); - String country = new String(code); - boolean ex; - - if (codes[toIndex(country)] == UNDEFINED) { - ex = false; - try { - Currency.getInstance(new Locale("", country)); - } - catch (IllegalArgumentException e) { - ex = true; - } - if (!ex) { - System.err.println("Error: This should be an undefined code and throw IllegalArgumentException: " + - country); - err = true; - } - } else if (codes[toIndex(country)] == SKIPPED) { - Currency cur = null; - try { - cur = Currency.getInstance(new Locale("", country)); - } - catch (Exception e) { - System.err.println("Error: " + e + ": Country=" + - country); - err = true; - } - if (cur != null) { - System.err.println("Error: Currency.getInstance() for an this locale should return null: " + - country); - err = true; - } - } + code[0] = (char) ('A' + i); + code[1] = (char) ('A' + j); + codeCombos.add(new String(code)); } } + return codeCombos; } - /** - * This test depends on test1(), where 'testCurrencies' set is constructed - */ - static void getAvailableCurrenciesTest() { + // This method ensures that getAvailableCurrencies() returns + // the expected amount of currencies. + @Test + public void getAvailableCurrenciesTest() { Set jreCurrencies = Currency.getAvailableCurrencies(); + // Ensure that testCurrencies has all the JRE currency codes + assertTrue(testCurrencies.containsAll(jreCurrencies), + getSetDiffs(jreCurrencies, testCurrencies)); + } - // add otherCodes - StringTokenizer st = new StringTokenizer(otherCodes, "-"); - while (st.hasMoreTokens()) { - testCurrencies.add(Currency.getInstance(st.nextToken())); - } - - if (!testCurrencies.containsAll(jreCurrencies)) { - System.err.print("Error: getAvailableCurrencies() returned extra currencies than expected: "); - jreCurrencies.removeAll(testCurrencies); - for (Currency c : jreCurrencies) { - System.err.print(" "+c); - } - System.err.println(); - err = true; + private static String getSetDiffs(Set jreCurrencies, Set testCurrencies) { + StringBuilder bldr = new StringBuilder(); + bldr.append("Error: getAvailableCurrencies() returned unexpected currencies: "); + jreCurrencies.removeAll(testCurrencies); + for (Currency curr : jreCurrencies) { + bldr.append(" " + curr); } + bldr.append("\n"); + return bldr.toString(); } } From 2e46aad2d2521555b3cc1b35706e65b3f66ce4c0 Mon Sep 17 00:00:00 2001 From: Sergey Nazarkin Date: Fri, 5 Apr 2024 05:27:56 +0000 Subject: [PATCH 5/6] 8268974: GetJREPath() JLI function fails to locate libjava.so if not standard Java launcher is used Reviewed-by: yan Backport-of: 984003d5c969443abae2d889e92cba30da26e55f --- .../unix/native/libjli/java_md_common.c | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/java.base/unix/native/libjli/java_md_common.c b/src/java.base/unix/native/libjli/java_md_common.c index 2d87a9ad1a4..48e57e380ff 100644 --- a/src/java.base/unix/native/libjli/java_md_common.c +++ b/src/java.base/unix/native/libjli/java_md_common.c @@ -44,20 +44,27 @@ char* findLastPathComponent(char *buffer, const char *comp) { /* * Removes the trailing file name and any intermediate platform * directories, if any, and its enclosing directory. + * Second parameter is a hint about the type of a file. JNI_TRUE is for + * shared libraries and JNI_FALSE is for executables. * Ex: if a buffer contains "/foo/bin/javac" or "/foo/bin/x64/javac", the * truncated resulting buffer will contain "/foo". */ jboolean -TruncatePath(char *buf) +TruncatePath(char *buf, jboolean pathisdll) { - // try bin directory, maybe an executable - char *p = findLastPathComponent(buf, "/bin/"); + /* + * If the file is a library, try lib directory first and then bin + * directory. + * If the file is an executable, try bin directory first and then lib + * directory. + */ + + char *p = findLastPathComponent(buf, pathisdll ? "/lib/" : "/bin/"); if (p != NULL) { *p = '\0'; return JNI_TRUE; } - // try lib directory, maybe a library - p = findLastPathComponent(buf, "/lib/"); + p = findLastPathComponent(buf, pathisdll ? "/bin/" : "/lib/"); if (p != NULL) { *p = '\0'; return JNI_TRUE; @@ -79,7 +86,7 @@ GetApplicationHome(char *buf, jint bufsize) } else { return JNI_FALSE; } - return TruncatePath(buf); + return TruncatePath(buf, JNI_FALSE); } /* @@ -94,7 +101,7 @@ GetApplicationHomeFromDll(char *buf, jint bufsize) if (dladdr((void*)&GetApplicationHomeFromDll, &info) != 0) { char *path = realpath(info.dli_fname, buf); if (path == buf) { - return TruncatePath(buf); + return TruncatePath(buf, JNI_TRUE); } } return JNI_FALSE; From aa16bfe54906a89671cf7837de31ec7b9e7c55b9 Mon Sep 17 00:00:00 2001 From: Andrew Lu Date: Tue, 9 Apr 2024 08:40:37 +0000 Subject: [PATCH 6/6] 8319128: sun/security/pkcs11 tests fail on OL 7.9 aarch64 8319136: Skip pkcs11 tests on linux-aarch64 Reviewed-by: lucy Backport-of: 1f9b03e597d87679964e8772c6bea538c74feb2c --- ...TestMutuallyExclusivePlatformPredicates.java | 2 +- test/jdk/sun/security/pkcs11/PKCS11Test.java | 17 ++++++++++++++--- .../pkcs11/Provider/MultipleLogins.java | 15 ++++++++++----- .../security/pkcs11/Provider/MultipleLogins.sh | 1 + test/lib/jdk/test/lib/Platform.java | 15 +++++++++++++++ 5 files changed, 41 insertions(+), 9 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java b/test/hotspot/jtreg/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java index 271c3fd2443..126a43a9001 100644 --- a/test/hotspot/jtreg/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java +++ b/test/hotspot/jtreg/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java @@ -52,7 +52,7 @@ private static enum MethodGroup { MODE("isInt", "isMixed", "isComp"), IGNORED("isEmulatedClient", "isDebugBuild", "isFastDebugBuild", "isSlowDebugBuild", "hasSA", "isRoot", "isTieredSupported", "areCustomLoadersSupportedForCDS", - "isHardenedOSX", "hasOSXPlistEntries"); + "isHardenedOSX", "hasOSXPlistEntries", "isOracleLinux7"); public final List methodNames; diff --git a/test/jdk/sun/security/pkcs11/PKCS11Test.java b/test/jdk/sun/security/pkcs11/PKCS11Test.java index ca5ed02db7e..a539c879b03 100644 --- a/test/jdk/sun/security/pkcs11/PKCS11Test.java +++ b/test/jdk/sun/security/pkcs11/PKCS11Test.java @@ -57,7 +57,11 @@ import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import jdk.test.lib.Platform; import jdk.test.lib.artifacts.Artifact; import jdk.test.lib.artifacts.ArtifactResolver; import jdk.test.lib.artifacts.ArtifactResolverException; @@ -925,11 +929,18 @@ private static String fetchNssLib(String osId) { return fetchNssLib(MACOSX_AARCH64.class); case "Linux-amd64-64": - return fetchNssLib(LINUX_X64.class); + if (Platform.isOracleLinux7()) { + throw new SkippedException("Skipping Oracle Linux prior to v8"); + } else { + return fetchNssLib(LINUX_X64.class); + } case "Linux-aarch64-64": - return fetchNssLib(LINUX_AARCH64.class); - + if (Platform.isOracleLinux7()) { + throw new SkippedException("Skipping Oracle Linux prior to v8"); + } else { + return fetchNssLib(LINUX_AARCH64.class); + } default: return null; } diff --git a/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java b/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java index 353a5b99a64..855abe50e7b 100644 --- a/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java +++ b/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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,6 +21,7 @@ * questions. */ + import sun.security.pkcs11.SunPKCS11; import javax.security.auth.Subject; @@ -32,12 +33,10 @@ import java.io.IOException; import java.lang.ref.WeakReference; import java.security.*; -import java.util.Iterator; import java.util.PropertyPermission; -import java.util.ServiceConfigurationError; -import java.util.ServiceLoader; import jdk.test.lib.util.ForceGC; +import jtreg.SkippedException; public class MultipleLogins { private static final String KS_TYPE = "PKCS11"; @@ -47,7 +46,13 @@ public class MultipleLogins { static final Policy DEFAULT_POLICY = Policy.getPolicy(); public static void main(String[] args) throws Exception { - String nssConfig = PKCS11Test.getNssConfig(); + String nssConfig = null; + try { + nssConfig = PKCS11Test.getNssConfig(); + } catch (SkippedException exc) { + System.out.println("Skipping test: " + exc.getMessage()); + } + if (nssConfig == null) { // No test framework support yet. Ignore System.out.println("No NSS config found. Skipping."); diff --git a/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.sh b/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.sh index e4ca0d9d64d..c768834da7f 100644 --- a/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.sh +++ b/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.sh @@ -26,6 +26,7 @@ # @summary # @library /test/lib/ # @build jdk.test.lib.util.ForceGC +# jdk.test.lib.Platform # @run shell MultipleLogins.sh # set a few environment variables so that the shell-script can run stand-alone diff --git a/test/lib/jdk/test/lib/Platform.java b/test/lib/jdk/test/lib/Platform.java index 371560956b8..6269373c2b6 100644 --- a/test/lib/jdk/test/lib/Platform.java +++ b/test/lib/jdk/test/lib/Platform.java @@ -33,6 +33,7 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; import java.util.regex.Pattern; public class Platform { @@ -338,6 +339,20 @@ private static boolean isArch(String archnameRE) { .matches(); } + public static boolean isOracleLinux7() { + if (System.getProperty("os.name").toLowerCase().contains("linux") && + System.getProperty("os.version").toLowerCase().contains("el")) { + Pattern p = Pattern.compile("el(\\d+)"); + Matcher m = p.matcher(System.getProperty("os.version")); + if (m.find()) { + try { + return Integer.parseInt(m.group(1)) <= 7; + } catch (NumberFormatException nfe) {} + } + } + return false; + } + /** * Returns file extension of shared library, e.g. "so" on linux, "dll" on windows. * @return file extension