diff --git a/.github/scripts/gen-test-results.sh b/.github/scripts/gen-test-results.sh index 73edb8b3d11..9e85eef4dc0 100644 --- a/.github/scripts/gen-test-results.sh +++ b/.github/scripts/gen-test-results.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 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 @@ -44,8 +44,8 @@ for test in $failures $errors; do base_path="$(echo "$test" | tr '#' '_')" report_file="$report_dir/$base_path.jtr" hs_err_files=$(ls $report_dir/$base_path/hs_err*.log 2> /dev/null || true) + replay_files=$(ls $report_dir/$base_path/replay*.log 2> /dev/null || true) echo "#### $test" - echo '
View test results' echo '' echo '```' @@ -73,6 +73,20 @@ for test in $failures $errors; do echo '' fi + if [[ "$replay_files" != "" ]]; then + echo '
View HotSpot replay file' + echo '' + for replay in $replay_files; do + echo '```' + echo "$replay:" + echo '' + cat "$replay" + echo '```' + done + + echo '
' + echo '' + fi done >> $GITHUB_STEP_SUMMARY # With many failures, the summary can easily exceed 1024 kB, the limit set by Github diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6ccb02e39ce..5d91e057a64 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,6 +63,7 @@ jobs: - 'hs/tier1 compiler part 1' - 'hs/tier1 compiler part 2' - 'hs/tier1 compiler part 3' + - 'hs/tier1 compiler not-xcomp' - 'hs/tier1 gc' - 'hs/tier1 runtime' - 'hs/tier1 serviceability' @@ -90,13 +91,17 @@ jobs: debug-suffix: -debug - test-name: 'hs/tier1 compiler part 2' - test-suite: 'test/hotspot/jtreg/:tier1_compiler_2 test/hotspot/jtreg/:tier1_compiler_not_xcomp' + test-suite: 'test/hotspot/jtreg/:tier1_compiler_2' debug-suffix: -debug - test-name: 'hs/tier1 compiler part 3' test-suite: 'test/hotspot/jtreg/:tier1_compiler_3' debug-suffix: -debug + - test-name: 'hs/tier1 compiler not-xcomp' + test-suite: 'test/hotspot/jtreg/:tier1_compiler_not_xcomp' + debug-suffix: -debug + - test-name: 'hs/tier1 gc' test-suite: 'test/hotspot/jtreg/:tier1_gc' debug-suffix: -debug diff --git a/make/data/currency/CurrencyData.properties b/make/data/currency/CurrencyData.properties index 12c0c69801e..26f4aa24d88 100644 --- a/make/data/currency/CurrencyData.properties +++ b/make/data/currency/CurrencyData.properties @@ -32,7 +32,7 @@ formatVersion=3 # Version of the currency code information in this class. # It is a serial number that accompanies with each amendment. -dataVersion=175 +dataVersion=176 # List of all valid ISO 4217 currency codes. # To ensure compatibility, do not remove codes. @@ -55,7 +55,7 @@ all=ADP020-AED784-AFA004-AFN971-ALL008-AMD051-ANG532-AOA973-ARS032-ATS040-AUD036 SRD968-SRG740-SSP728-STD678-STN930-SVC222-SYP760-SZL748-THB764-TJS972-TMM795-TMT934-TND788-TOP776-\ TPE626-TRL792-TRY949-TTD780-TWD901-TZS834-UAH980-UGX800-USD840-USN997-USS998-UYI940-\ UYU858-UZS860-VEB862-VED926-VEF937-VES928-VND704-VUV548-WST882-XAF950-XAG961-XAU959-XBA955-\ - XBB956-XBC957-XBD958-XCD951-XDR960-XFO000-XFU000-XOF952-XPD964-XPF953-\ + XBB956-XBC957-XBD958-XCD951-XCG532-XDR960-XFO000-XFU000-XOF952-XPD964-XPF953-\ XPT962-XSU994-XTS963-XUA965-XXX999-YER886-YUM891-ZAR710-ZMK894-ZMW967-ZWD716-ZWL932-\ ZWN942-ZWR935 @@ -189,11 +189,11 @@ CR=CRC # COTE D'IVOIRE CI=XOF # CROATIA -HR=HRK;2022-12-31-23-00-00;EUR +HR=EUR # CUBA CU=CUP # Cura\u00e7ao -CW=ANG +CW=ANG;2025-04-01-04-00-00;XCG # CYPRUS CY=EUR # CZECHIA @@ -510,7 +510,7 @@ SR=SRD # SVALBARD AND JAN MAYEN SJ=NOK # Sint Maarten (Dutch part) -SX=ANG +SX=ANG;2025-04-01-04-00-00;XCG # ESWATINI SZ=SZL # SWEDEN diff --git a/make/jdk/src/classes/build/tools/generatecurrencydata/GenerateCurrencyData.java b/make/jdk/src/classes/build/tools/generatecurrencydata/GenerateCurrencyData.java index 561edbef034..9655e08016c 100644 --- a/make/jdk/src/classes/build/tools/generatecurrencydata/GenerateCurrencyData.java +++ b/make/jdk/src/classes/build/tools/generatecurrencydata/GenerateCurrencyData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -32,6 +32,7 @@ import java.io.FileOutputStream; import java.io.InputStream; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.Locale; @@ -339,9 +340,15 @@ private static void buildOtherTables() { validCurrencyCodes.substring(i * 7 + 3, i * 7 + 6)); checkCurrencyCode(currencyCode); int tableEntry = mainTable[(currencyCode.charAt(0) - 'A') * A_TO_Z + (currencyCode.charAt(1) - 'A')]; - if (tableEntry == INVALID_COUNTRY_ENTRY || - (tableEntry & SPECIAL_CASE_COUNTRY_MASK) != 0 || - (tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) != (currencyCode.charAt(2) - 'A')) { + + // Do not allow a future currency to be classified as an otherCurrency, + // otherwise it will leak out into Currency:getAvailableCurrencies + boolean futureCurrency = Arrays.asList(specialCaseNewCurrencies).contains(currencyCode); + boolean simpleCurrency = (tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) == (currencyCode.charAt(2) - 'A'); + + // If neither a simple currency, or one defined in the future + // then the current currency is applicable to be added to the otherTable + if (!futureCurrency && !simpleCurrency) { if (otherCurrenciesCount == maxOtherCurrencies) { throw new RuntimeException("too many other currencies"); } diff --git a/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties b/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties index 53bf1d837ff..c231cadc416 100644 --- a/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties +++ b/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2005, 2022, 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 @@ -266,6 +266,7 @@ XBB=XBB XBC=XBC XBD=XBD XCD=XCD +XCG=XCG XDR=XDR XFO=XFO XFU=XFU @@ -491,6 +492,7 @@ xbb=European Monetary Unit xbc=European Unit of Account (XBC) xbd=European Unit of Account (XBD) xcd=East Caribbean Dollar +xcg=Caribbean Guilder xdr=Special Drawing Rights xfo=French Gold Franc xfu=French UIC-Franc diff --git a/src/java.base/unix/native/libnet/Inet4AddressImpl.c b/src/java.base/unix/native/libnet/Inet4AddressImpl.c index 8cde515b7d4..5b8f38b4523 100644 --- a/src/java.base/unix/native/libnet/Inet4AddressImpl.c +++ b/src/java.base/unix/native/libnet/Inet4AddressImpl.c @@ -259,7 +259,11 @@ tcp_ping4(JNIEnv *env, SOCKETADDRESS *sa, SOCKETADDRESS *netif, jint timeout, // set TTL if (ttl > 0) { - setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); + if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) { + NET_ThrowNew(env, errno, "setsockopt IP_TTL failed"); + close(fd); + return JNI_FALSE; + } } // A network interface was specified, so let's bind to it. @@ -345,11 +349,19 @@ ping4(JNIEnv *env, jint fd, SOCKETADDRESS *sa, SOCKETADDRESS *netif, struct timeval tv = { 0, 0 }; const size_t plen = ICMP_MINLEN + sizeof(tv); - setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) { + NET_ThrowNew(env, errno, "setsockopt SO_RCVBUF failed"); + close(fd); + return JNI_FALSE; + } // sets the ttl (max number of hops) if (ttl > 0) { - setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); + if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) { + NET_ThrowNew(env, errno, "setsockopt IP_TTL failed"); + close(fd); + return JNI_FALSE; + } } // a specific interface was specified, so let's bind the socket diff --git a/src/java.base/unix/native/libnet/Inet6AddressImpl.c b/src/java.base/unix/native/libnet/Inet6AddressImpl.c index 27e79b80e8b..e9e7adb192b 100644 --- a/src/java.base/unix/native/libnet/Inet6AddressImpl.c +++ b/src/java.base/unix/native/libnet/Inet6AddressImpl.c @@ -463,12 +463,16 @@ tcp_ping6(JNIEnv *env, SOCKETADDRESS *sa, SOCKETADDRESS *netif, jint timeout, // set TTL if (ttl > 0) { - setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) { + NET_ThrowNew(env, errno, "setsockopt IPV6_UNICAST_HOPS failed"); + close(fd); + return JNI_FALSE; + } } // A network interface was specified, so let's bind to it. if (netif != NULL) { - if (bind(fd, &netif->sa, sizeof(struct sockaddr_in6)) <0) { + if (bind(fd, &netif->sa, sizeof(struct sockaddr_in6)) < 0) { NET_ThrowNew(env, errno, "Can't bind socket"); close(fd); return JNI_FALSE; @@ -557,11 +561,19 @@ ping6(JNIEnv *env, jint fd, SOCKETADDRESS *sa, SOCKETADDRESS *netif, setsockopt(fd, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sizeof(int)); #endif - setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) { + NET_ThrowNew(env, errno, "setsockopt SO_RCVBUF failed"); + close(fd); + return JNI_FALSE; + } // sets the ttl (max number of hops) if (ttl > 0) { - setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) { + NET_ThrowNew(env, errno, "setsockopt IPV6_UNICAST_HOPS failed"); + close(fd); + return JNI_FALSE; + } } // a specific interface was specified, so let's bind the socket diff --git a/src/java.base/unix/native/libnet/net_util_md.c b/src/java.base/unix/native/libnet/net_util_md.c index 7c939e80feb..4ec11a13626 100644 --- a/src/java.base/unix/native/libnet/net_util_md.c +++ b/src/java.base/unix/native/libnet/net_util_md.c @@ -661,7 +661,9 @@ NET_SetSockOpt(int fd, int level, int opt, const void *arg, } if (sotype == SOCK_DGRAM) { - setsockopt(fd, level, SO_REUSEPORT, arg, len); + if (setsockopt(fd, level, SO_REUSEPORT, arg, len) < 0) { + return -1; + } } } #endif diff --git a/src/java.base/windows/native/libnet/Inet4AddressImpl.c b/src/java.base/windows/native/libnet/Inet4AddressImpl.c index 75dfb57eb2f..56adec28c72 100644 --- a/src/java.base/windows/native/libnet/Inet4AddressImpl.c +++ b/src/java.base/windows/native/libnet/Inet4AddressImpl.c @@ -232,7 +232,11 @@ tcp_ping4(JNIEnv *env, SOCKETADDRESS *sa, SOCKETADDRESS *netif, jint timeout, // set TTL if (ttl > 0) { - setsockopt(fd, IPPROTO_IP, IP_TTL, (const char *)&ttl, sizeof(ttl)); + if (setsockopt(fd, IPPROTO_IP, IP_TTL, (const char *)&ttl, sizeof(ttl)) == SOCKET_ERROR) { + NET_ThrowNew(env, WSAGetLastError(), "setsockopt IP_TTL failed"); + closesocket(fd); + return JNI_FALSE; + } } // A network interface was specified, so let's bind to it. diff --git a/src/java.base/windows/native/libnet/Inet6AddressImpl.c b/src/java.base/windows/native/libnet/Inet6AddressImpl.c index cc2b8d5a74c..8941506da77 100644 --- a/src/java.base/windows/native/libnet/Inet6AddressImpl.c +++ b/src/java.base/windows/native/libnet/Inet6AddressImpl.c @@ -310,7 +310,11 @@ tcp_ping6(JNIEnv *env, SOCKETADDRESS *sa, SOCKETADDRESS *netif, jint timeout, // set TTL if (ttl > 0) { - setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (const char *)&ttl, sizeof(ttl)); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (const char *)&ttl, sizeof(ttl)) == SOCKET_ERROR) { + NET_ThrowNew(env, WSAGetLastError(), "setsockopt IPV6_UNICAST_HOPS failed"); + closesocket(fd); + return JNI_FALSE; + } } // A network interface was specified, so let's bind to it. diff --git a/src/java.base/windows/native/libnio/ch/Net.c b/src/java.base/windows/native/libnio/ch/Net.c index ada8ae6a83e..d4410c0ca2d 100644 --- a/src/java.base/windows/native/libnio/ch/Net.c +++ b/src/java.base/windows/native/libnio/ch/Net.c @@ -156,7 +156,7 @@ Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, if (s != INVALID_SOCKET) { SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0); - /* IPV6_V6ONLY is true by default */ + /* Attempt to disable IPV6_V6ONLY to ensure dual-socket support; ignore errors */ if (domain == AF_INET6) { int opt = 0; setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, diff --git a/src/java.base/windows/native/libnio/ch/WindowsAsynchronousServerSocketChannelImpl.c b/src/java.base/windows/native/libnio/ch/WindowsAsynchronousServerSocketChannelImpl.c index a08f3a3564f..2c6de95ac65 100644 --- a/src/java.base/windows/native/libnio/ch/WindowsAsynchronousServerSocketChannelImpl.c +++ b/src/java.base/windows/native/libnio/ch/WindowsAsynchronousServerSocketChannelImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -69,6 +69,10 @@ Java_sun_nio_ch_WindowsAsynchronousServerSocketChannelImpl_initIDs(JNIEnv* env, DWORD dwBytes; s = socket(AF_INET, SOCK_STREAM, 0); + if (s == INVALID_SOCKET && WSAGetLastError() == WSAEAFNOSUPPORT) { + /* IPv4 unavailable... try IPv6 instead */ + s = socket(AF_INET6, SOCK_STREAM, 0); + } if (s == INVALID_SOCKET) { JNU_ThrowIOExceptionWithLastError(env, "socket failed"); return; @@ -127,7 +131,9 @@ Java_sun_nio_ch_WindowsAsynchronousServerSocketChannelImpl_updateAcceptContext(J SOCKET s1 = (SOCKET)jlong_to_ptr(listenSocket); SOCKET s2 = (SOCKET)jlong_to_ptr(acceptSocket); - setsockopt(s2, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *)&s1, sizeof(s1)); + if (setsockopt(s2, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *)&s1, sizeof(s1)) == SOCKET_ERROR) { + JNU_ThrowIOExceptionWithLastError(env, "setsockopt failed"); + } } diff --git a/src/java.base/windows/native/libnio/ch/WindowsAsynchronousSocketChannelImpl.c b/src/java.base/windows/native/libnio/ch/WindowsAsynchronousSocketChannelImpl.c index 40b8d8ba8dc..b325c34c9d8 100644 --- a/src/java.base/windows/native/libnio/ch/WindowsAsynchronousSocketChannelImpl.c +++ b/src/java.base/windows/native/libnio/ch/WindowsAsynchronousSocketChannelImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -66,6 +66,10 @@ Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_initIDs(JNIEnv* env, jclass DWORD dwBytes; s = socket(AF_INET, SOCK_STREAM, 0); + if (s == INVALID_SOCKET && WSAGetLastError() == WSAEAFNOSUPPORT) { + /* IPv4 unavailable... try IPv6 instead */ + s = socket(AF_INET6, SOCK_STREAM, 0); + } if (s == INVALID_SOCKET) { JNU_ThrowIOExceptionWithLastError(env, "socket failed"); return; @@ -119,7 +123,9 @@ Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_updateConnectContext(JNIEnv jlong socket) { SOCKET s = (SOCKET)jlong_to_ptr(socket); - setsockopt(s, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0); + if (setsockopt(s, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0) == SOCKET_ERROR) { + JNU_ThrowIOExceptionWithLastError(env, "setsockopt failed"); + } } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/stress/jni/TEST.properties b/test/hotspot/jtreg/vmTestbase/nsk/stress/jni/TEST.properties deleted file mode 100644 index 8b51b2a9115..00000000000 --- a/test/hotspot/jtreg/vmTestbase/nsk/stress/jni/TEST.properties +++ /dev/null @@ -1,24 +0,0 @@ -# -# Copyright (c) 2018, 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. -# - -exclusiveAccess.dirs=. diff --git a/test/jdk/java/security/cert/CertPathBuilder/akiExt/AKISerialNumber.java b/test/jdk/java/security/cert/CertPathBuilder/akiExt/AKISerialNumber.java index e8ab65d78dd..53db87cfe63 100644 --- a/test/jdk/java/security/cert/CertPathBuilder/akiExt/AKISerialNumber.java +++ b/test/jdk/java/security/cert/CertPathBuilder/akiExt/AKISerialNumber.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -118,6 +118,8 @@ public static void main(String[] args) throws Exception { PKIXBuilderParameters params = new PKIXBuilderParameters (Collections.singleton(anchor), sel); params.setRevocationEnabled(false); + // Set date to 2024-01-01 to satisfy cert constraints + params.setDate(new java.util.Date(1704067200000l)); ArrayList certs = new ArrayList<>(); certs.add(intCert); 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..256e6ee8650 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,232 @@ 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.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 + 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 static 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 static 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 + 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 static 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 + 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 static 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 static 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 +277,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 1644153b28a..3d2bae0e5e7 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,293 +20,303 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + /* * @test * @bug 4691089 4819436 4942982 5104960 6544471 6627549 7066203 7195759 * 8039317 8074350 8074351 8145952 8187946 8193552 8202026 8204269 - * 8208746 8209775 8264792 8274658 8283277 8296239 + * 8208746 8209775 8264792 8274658 8283277 8296239 8321480 * @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-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(); } -} +} \ No newline at end of file diff --git a/test/jdk/java/util/Currency/tablea1.txt b/test/jdk/java/util/Currency/tablea1.txt index 4e33a62c05e..6e85de5e6d2 100644 --- a/test/jdk/java/util/Currency/tablea1.txt +++ b/test/jdk/java/util/Currency/tablea1.txt @@ -1,12 +1,12 @@ # # -# Amendments up until ISO 4217 AMENDMENT NUMBER 175 -# (As of 31 March 2023) +# Amendments up until ISO 4217 AMENDMENT NUMBER 176 +# (As of 06 December 2023) # # Version FILEVERSION=3 -DATAVERSION=175 +DATAVERSION=176 # ISO 4217 currency data AF AFN 971 2 @@ -67,9 +67,9 @@ CD CDF 976 2 CK NZD 554 2 CR CRC 188 2 CI XOF 952 0 -HR HRK 191 2 2022-12-31-23-00-00 EUR 978 2 +HR EUR 978 2 CU CUP 192 2 -CW ANG 532 2 +CW ANG 532 2 2025-04-01-04-00-00 XCG 532 2 CY EUR 978 2 CZ CZK 203 2 DK DKK 208 2 @@ -233,7 +233,7 @@ LK LKR 144 2 SD SDG 938 2 SR SRD 968 2 SJ NOK 578 2 -SX ANG 532 2 +SX ANG 532 2 2025-04-01-04-00-00 XCG 532 2 SZ SZL 748 2 SE SEK 752 2 CH CHF 756 2 diff --git a/test/jdk/javax/swing/text/html/TableView/bug4813831.java b/test/jdk/javax/swing/text/html/TableView/bug4813831.java new file mode 100644 index 00000000000..4d7f286f8aa --- /dev/null +++ b/test/jdk/javax/swing/text/html/TableView/bug4813831.java @@ -0,0 +1,116 @@ +/* + * 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 + * 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 4813831 + * @summary Verifies contents of table cells in HTML in JEditorPane wraps correctly + * @key headful + * @run main bug4813831 +*/ + +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import javax.swing.text.View; +import javax.swing.text.ParagraphView; +import javax.swing.text.html.HTMLEditorKit; + +import java.awt.Robot; +import java.awt.Shape; + +public class bug4813831 { + + private static boolean passed = false; + private boolean finished = false; + + private static JEditorPane jep; + private static JFrame f; + + public void init() { + + String text = + "" + + "" + + "" + + "
XXXXXXXXXXXXXX
X
X
" + + ""; + + f = new JFrame(); + jep = new JEditorPane(); + jep.setEditorKit(new HTMLEditorKit()); + jep.setEditable(false); + + jep.setText(text); + + f.getContentPane().add(jep); + f.setSize(20,500); + f.setLocationRelativeTo(null); + f.setVisible(true); + } + + + public static void main(String args[]) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + bug4813831 test = new bug4813831(); + try { + SwingUtilities.invokeAndWait(() -> test.init()); + robot.waitForIdle(); + robot.delay(1000); + Shape r = jep.getBounds(); + View v = jep.getUI().getRootView(jep); + do { + int n = v.getViewCount(); + Shape sh = v.getChildAllocation(n - 1, r); + if (sh != null) { + r = sh; + } + v = v.getView(n - 1); + } while (!(v instanceof ParagraphView)); + + int n = v.getViewCount(); + // there should be 3 lines or more (if the first long line was wrapped) in a cell + passed = n >= 3; + + if (passed) { + Shape sh = v.getChildAllocation(n - 2, r); + int x1 = sh.getBounds().x; + sh = v.getChildAllocation(n - 1, r); + int x2 = sh.getBounds().x; + System.out.println("x1: " + x1 + " x2: " + x2); + // lines should be equally aligned + passed = (x1 == x2); + } + if (!passed) { + throw new RuntimeException("Test failed."); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/tree/DefaultTreeCellEditor/bug4480602.java b/test/jdk/javax/swing/tree/DefaultTreeCellEditor/bug4480602.java new file mode 100644 index 00000000000..d9073297a52 --- /dev/null +++ b/test/jdk/javax/swing/tree/DefaultTreeCellEditor/bug4480602.java @@ -0,0 +1,133 @@ +/* + * 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 + * 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 4480602 + * @summary Verifies if DefaultTreeCellEditor.inHitRegion() incorrectly + * handles row bounds + * @key headful + * @run main bug4480602 +*/ + +import java.awt.ComponentOrientation; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.event.MouseEvent; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellEditor; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.SwingUtilities; + +import java.util.Date; + +public class bug4480602 { + + static JTree tree; + static JFrame fr; + static MyTreeCellEditor editor; + + static Robot robot; + boolean passed = false; + boolean do_test = false; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + try { + SwingUtilities.invokeAndWait(() -> { + fr = new JFrame("Test"); + + String s = "0\u05D01\u05D02\u05D03\u05D04\u05D05"; + DefaultMutableTreeNode root = new DefaultMutableTreeNode(s); + root.add(new DefaultMutableTreeNode(s)); + root.add(new DefaultMutableTreeNode(s)); + + tree = new JTree(root); + editor = new MyTreeCellEditor(tree, new DefaultTreeCellRenderer()); + tree.setCellEditor(editor); + tree.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); + tree.setEditable(true); + JScrollPane sp = new JScrollPane(tree); + fr.getContentPane().add(sp); + + fr.setSize(250,200); + fr.setLocationRelativeTo(null); + fr.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + Rectangle rect = tree.getRowBounds(1); + editor.testTreeCellEditor(rect); + }); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (fr != null) { + fr.dispose(); + } + }); + } + } + + static class MyTreeCellEditor extends DefaultTreeCellEditor { + + public MyTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) { + super(tree, renderer); + } + + public void testTreeCellEditor(Rectangle rect) { + int x = rect.x + 10; + int y = rect.y + rect.height / 2; + MouseEvent me = new MouseEvent(tree, + MouseEvent.MOUSE_PRESSED, + (new Date()).getTime(), + MouseEvent.BUTTON1_DOWN_MASK, + rect.x + 10, rect.y + 10, + 1, true); + isCellEditable(me); + + if (tree == null) { + throw new RuntimeException("isCellEditable() should set the tree"); + } + if (lastRow != 1) { + throw new RuntimeException("isCellEditable() should set the lastRow"); + } + if (offset == 0) { + throw new RuntimeException("isCellEditable() should determine offset"); + } + + if (!inHitRegion(x,y)) { + throw new RuntimeException("Hit region should contain point ("+x+", "+y+")"); + } + x = rect.x + rect.width - 10; + if (inHitRegion(x,y)) { + throw new RuntimeException("Hit region shouldn't contain point ("+x+", "+y+")"); + } + } + } + +} diff --git a/test/jdk/javax/swing/tree/DefaultTreeCellRenderer/bug4180224.java b/test/jdk/javax/swing/tree/DefaultTreeCellRenderer/bug4180224.java new file mode 100644 index 00000000000..f9d88f29acf --- /dev/null +++ b/test/jdk/javax/swing/tree/DefaultTreeCellRenderer/bug4180224.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 1999, 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 4180224 + * @summary DefaultTreeCellRenderer.hasFocus protected (not private) now. + * @key headful + * @run main bug4180224 +*/ + +import javax.swing.tree.DefaultTreeCellRenderer; + +public class bug4180224 { + + static class MyDTCR extends DefaultTreeCellRenderer { + void test() { + hasFocus = false; + } + } + + public static void main(String[] argv) { + MyDTCR m = new MyDTCR(); + m.test(); + } +} diff --git a/test/jdk/javax/swing/tree/FixedHeightLayoutCache/bug4745001.java b/test/jdk/javax/swing/tree/FixedHeightLayoutCache/bug4745001.java new file mode 100644 index 00000000000..d2beface3c2 --- /dev/null +++ b/test/jdk/javax/swing/tree/FixedHeightLayoutCache/bug4745001.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2002, 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 4745001 + * @summary JTree with setLargeModel(true) not display correctly + * when we expand/collapse nodes + * @key headful + * @run main bug4745001 +*/ + +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Robot; + +import javax.swing.JFrame; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import javax.swing.SwingUtilities; + +public class bug4745001 { + + static JTree tree; + static JFrame fr; + boolean stateChanged; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + bug4745001 test = new bug4745001(); + try { + SwingUtilities.invokeAndWait(() -> test.init()); + robot.waitForIdle(); + robot.delay(1000); + test.start(); + robot.delay(1000); + test.destroy(); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (fr != null) { + fr.dispose(); + } + }); + } + } + + public void init() { + fr = new JFrame("Test"); + fr.getContentPane().setLayout(new FlowLayout()); + + tree = new JTree(); + tree.setRowHeight(20); + tree.setLargeModel(true); + tree.setPreferredSize(new Dimension(100, 400)); + tree.setRootVisible(false); + tree.setShowsRootHandles(true); + + DefaultMutableTreeNode root = new DefaultMutableTreeNode(""); + DefaultMutableTreeNode a = new DefaultMutableTreeNode("a"); + DefaultMutableTreeNode b = new DefaultMutableTreeNode("b"); + DefaultMutableTreeNode c = new DefaultMutableTreeNode("c"); + root.add(a); + root.add(b); + root.add(c); + b.add(new DefaultMutableTreeNode("b1")); + c.add(new DefaultMutableTreeNode("c2")); + tree.setModel(new DefaultTreeModel(root)); + + fr.getContentPane().add(tree); + + tree.addTreeExpansionListener(new TreeExpansionListener() { + public void treeExpanded(TreeExpansionEvent e) { + TreePath path = e.getPath(); + if (path != null) { + DefaultMutableTreeNode node = + (DefaultMutableTreeNode)path.getLastPathComponent(); + node.removeAllChildren(); + String s = (String)node.getUserObject(); + node.add(new DefaultMutableTreeNode(s + "1")); + node.add(new DefaultMutableTreeNode(s + "2")); + node.add(new DefaultMutableTreeNode(s + "3")); + DefaultTreeModel model = (DefaultTreeModel)tree.getModel(); + model.nodeStructureChanged(node); + synchronized (bug4745001.this) { + stateChanged = true; + bug4745001.this.notifyAll(); + } + } + } + + public void treeCollapsed(TreeExpansionEvent e) { + synchronized (bug4745001.this) { + stateChanged = true; + bug4745001.this.notifyAll(); + } + } + }); + + fr.pack(); + fr.setVisible(true); + } + + void changeNodeStateForRow(final int row, final boolean expand) throws Exception { + try { + stateChanged = false; + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + try { + if (expand) { + tree.expandRow(row); + } else { + tree.collapseRow(row); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }); + synchronized (this) { + while (!stateChanged) { + bug4745001.this.wait(); + } + } + } catch (Throwable t) { + t.printStackTrace(); + } + } + + public void start() throws Exception { + // expand node "c" + changeNodeStateForRow(2, true); + // expand node "b" + changeNodeStateForRow(1, true); + // collapse node "c" + changeNodeStateForRow(1, false); + } + + String[] expected = new String[] {"a", "b", "c", "c1", "c2", "c3"}; + + public void destroy() { + for (int i = 0; i < expected.length; i++) { + Object obj = tree.getPathForRow(i).getLastPathComponent(); + if (!obj.toString().equals(expected[i])) { + throw new RuntimeException("Unexpected node at row "+i); + } + } + } + +}