From 1ab75afc5fba4b11700a9625aa236eadf7493327 Mon Sep 17 00:00:00 2001 From: Frank Tang Date: Wed, 9 Oct 2019 18:00:51 +0000 Subject: [PATCH] ICU-20706 Fix DateInterval::createInstance w/ test See #876 --- icu4c/source/i18n/dtitvinf.cpp | 29 ++++++++- icu4c/source/test/intltest/dtifmtts.cpp | 36 +++++++++++ icu4c/source/test/intltest/dtifmtts.h | 1 + icu4c/source/test/intltest/restsnew.cpp | 64 +++++++++++++++++++ icu4c/source/test/intltest/restsnew.h | 1 + .../test/format/DateIntervalFormatTest.java | 18 ++++++ 6 files changed, 146 insertions(+), 3 deletions(-) diff --git a/icu4c/source/i18n/dtitvinf.cpp b/icu4c/source/i18n/dtitvinf.cpp index 35ee8c16261..0ee17e88e81 100644 --- a/icu4c/source/i18n/dtitvinf.cpp +++ b/icu4c/source/i18n/dtitvinf.cpp @@ -50,6 +50,7 @@ U_NAMESPACE_BEGIN UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalInfo) static const char gCalendarTag[]="calendar"; +static const char gGenericTag[]="generic"; static const char gGregorianTag[]="gregorian"; static const char gIntervalDateTimePatternTag[]="intervalFormats"; static const char gFallbackPatternTag[]="fallback"; @@ -421,14 +422,36 @@ DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& status) UResourceBundle *calTypeBundle, *itvDtPtnResource; // Get the fallback pattern - const UChar* resStr; + const UChar* resStr = nullptr; int32_t resStrLen = 0; calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, NULL, &status); itvDtPtnResource = ures_getByKeyWithFallback(calTypeBundle, gIntervalDateTimePatternTag, NULL, &status); - resStr = ures_getStringByKeyWithFallback(itvDtPtnResource, gFallbackPatternTag, - &resStrLen, &status); + // TODO(ICU-20400): After the fixing, we should find the "fallback" from + // the rb directly by the path "calendar/${calendar}/intervalFormats/fallback". if ( U_SUCCESS(status) ) { + resStr = ures_getStringByKeyWithFallback(itvDtPtnResource, gFallbackPatternTag, + &resStrLen, &status); + if ( U_FAILURE(status) ) { + // Try to find "fallback" from "generic" to work around the bug in + // ures_getByKeyWithFallback + UErrorCode localStatus = U_ZERO_ERROR; + UResourceBundle *genericCalBundle = + ures_getByKeyWithFallback(calBundle, gGenericTag, NULL, &localStatus); + UResourceBundle *genericItvDtPtnResource = + ures_getByKeyWithFallback( + genericCalBundle, gIntervalDateTimePatternTag, NULL, &localStatus); + resStr = ures_getStringByKeyWithFallback( + genericItvDtPtnResource, gFallbackPatternTag, &resStrLen, &localStatus); + ures_close(genericItvDtPtnResource); + ures_close(genericCalBundle); + if ( U_SUCCESS(localStatus) ) { + status = U_USING_FALLBACK_WARNING;; + } + } + } + + if ( U_SUCCESS(status) && (resStr != nullptr)) { UnicodeString pattern = UnicodeString(TRUE, resStr, resStrLen); setFallbackIntervalPattern(pattern, status); } diff --git a/icu4c/source/test/intltest/dtifmtts.cpp b/icu4c/source/test/intltest/dtifmtts.cpp index e2a9faa25a0..7edb6a43a1c 100644 --- a/icu4c/source/test/intltest/dtifmtts.cpp +++ b/icu4c/source/test/intltest/dtifmtts.cpp @@ -57,6 +57,7 @@ void DateIntervalFormatTest::runIndexedTest( int32_t index, UBool exec, const ch TESTCASE(8, testTicket11669); TESTCASE(9, testTicket12065); TESTCASE(10, testFormattedDateInterval); + TESTCASE(11, testCreateInstanceForAllLocales); default: name = ""; break; } } @@ -1768,5 +1769,40 @@ void DateIntervalFormatTest::testFormattedDateInterval() { } } +void DateIntervalFormatTest::testCreateInstanceForAllLocales() { + IcuTestErrorCode status(*this, "testCreateInstanceForAllLocales"); + int32_t locale_count = 0; + const Locale* locales = icu::Locale::getAvailableLocales(locale_count); + // Iterate through all locales + for (int32_t i = 0; i < locale_count; i++) { + std::unique_ptr calendars( + icu::Calendar::getKeywordValuesForLocale( + "calendar", locales[i], FALSE, status)); + int32_t calendar_count = calendars->count(status); + if (status.errIfFailureAndReset()) { break; } + // In quick mode, only run 1/5 of locale combination + // to make the test run faster. + if (quick && (i % 5 != 0)) continue; + LocalPointer fmt( + DateIntervalFormat::createInstance(u"dMMMMy", locales[i], status), + status); + if (status.errIfFailureAndReset(locales[i].getName())) { + continue; + } + // Iterate through all calendars in this locale + for (int32_t j = 0; j < calendar_count; j++) { + // In quick mode, only run 1/7 of locale/calendar combination + // to make the test run faster. + if (quick && ((i * j) % 7 != 0)) continue; + const char* calendar = calendars->next(nullptr, status); + Locale locale(locales[i]); + locale.setKeywordValue("calendar", calendar, status); + fmt.adoptInsteadAndCheckErrorCode( + DateIntervalFormat::createInstance(u"dMMMMy", locale, status), + status); + status.errIfFailureAndReset(locales[i].getName()); + } + } +} #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/test/intltest/dtifmtts.h b/icu4c/source/test/intltest/dtifmtts.h index 9a72d6ebd1e..b05a57a8d08 100644 --- a/icu4c/source/test/intltest/dtifmtts.h +++ b/icu4c/source/test/intltest/dtifmtts.h @@ -65,6 +65,7 @@ public: void testTicket12065(); void testFormattedDateInterval(); + void testCreateInstanceForAllLocales(); private: /** diff --git a/icu4c/source/test/intltest/restsnew.cpp b/icu4c/source/test/intltest/restsnew.cpp index c55f2239225..b2d72d98a82 100644 --- a/icu4c/source/test/intltest/restsnew.cpp +++ b/icu4c/source/test/intltest/restsnew.cpp @@ -7,6 +7,7 @@ #include "unicode/utypes.h" +#include "charstr.h" #include "cmemory.h" #include "cstring.h" #include "unicode/unistr.h" @@ -14,6 +15,7 @@ #include "unicode/brkiter.h" #include "unicode/utrace.h" #include "unicode/ucurr.h" +#include "uresimp.h" #include "restsnew.h" #include @@ -40,6 +42,7 @@ void NewResourceBundleTest::runIndexedTest( int32_t index, UBool exec, const cha TESTCASE_AUTO(TestGetByFallback); TESTCASE_AUTO(TestFilter); + TESTCASE_AUTO(TestIntervalAliasFallbacks); #if U_ENABLE_TRACING TESTCASE_AUTO(TestTrace); @@ -1392,6 +1395,67 @@ void NewResourceBundleTest::TestFilter() { } } +void NewResourceBundleTest::TestIntervalAliasFallbacks() { + const char* locales[] = { + // Thee will not cause infinity loop + "en", + "ja", + + // These will cause infinity loop + "fr_CA", + "en_150", + "es_419", + "id", + "in", + "pl", + "pt_PT", + "sr_ME", + "zh_Hant", + "zh_Hant_TW", + "zh_TW", + }; + const char* calendars[] = { + // These won't cause infinity loop + "gregorian", + "chinese", + + // These will cause infinity loop + "islamic", + "islamic-civil", + "islamic-tbla", + "islamic-umalqura", + "ethiopic-amete-alem", + "islamic-rgsa", + "japanese", + "roc", + }; + + for (int lidx = 0; lidx < UPRV_LENGTHOF(locales); lidx++) { + UErrorCode status = U_ZERO_ERROR; + UResourceBundle *rb = ures_open(NULL, locales[lidx], &status); + if (U_FAILURE(status)) { + errln("Cannot open bundle for locale %s", locales[lidx]); + break; + } + for (int cidx = 0; cidx < UPRV_LENGTHOF(calendars); cidx++) { + CharString key; + key.append("calendar/", status); + key.append(calendars[cidx], status); + key.append("/intervalFormats/fallback", status); + if (! logKnownIssue("20400")) { + int32_t resStrLen = 0; + ures_getStringByKeyWithFallback(rb, key.data(), &resStrLen, &status); + } + if (U_FAILURE(status)) { + errln("Cannot ures_getStringByKeyWithFallback('%s') on locale %s", + key.data(), locales[lidx]); + break; + } + } + ures_close(rb); + } +} + #if U_ENABLE_TRACING static std::vector gResourcePathsTraced; diff --git a/icu4c/source/test/intltest/restsnew.h b/icu4c/source/test/intltest/restsnew.h index 9ae3bf048e2..d3b2d9c38a1 100644 --- a/icu4c/source/test/intltest/restsnew.h +++ b/icu4c/source/test/intltest/restsnew.h @@ -39,6 +39,7 @@ public: void TestGetByFallback(void); void TestFilter(void); + void TestIntervalAliasFallbacks(void); #if U_ENABLE_TRACING void TestTrace(void); diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java index 5805b03ef2f..ff442f8b094 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java @@ -2032,4 +2032,22 @@ public class DateIntervalFormatTest extends TestFmwk { expectedFieldPositions); } } + @Test + public void testCreateInstanceForAllLocales() { + boolean quick = (getExhaustiveness() <= 5); + int count = 0; + for (ULocale locale : ULocale.getAvailableLocalesByType( + ULocale.AvailableType.WITH_LEGACY_ALIASES)) { + // Only test 1/5 of the locale in quick mode. + if (quick && (count++ % 5 > 0)) continue; + DateIntervalFormat fmt = DateIntervalFormat.getInstance("dMMMMy", locale); + for (String calendar : Calendar.getKeywordValuesForLocale( + "calendar", locale, false)) { + // Only test 1/7 of case in quick mode. + if (quick && (count++ % 7 > 0)) continue; + ULocale l = locale.setKeywordValue("calendar", calendar); + fmt = DateIntervalFormat.getInstance("dMMMMy", l); + } + } + } }