]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/test/intltest/tufmtts.cpp
ICU-62107.0.1.tar.gz
[apple/icu.git] / icuSources / test / intltest / tufmtts.cpp
index 90efa96fcf291295c5edb4213c07ddc8712ff6ff..91a26678caf62bb6eebd79f47f180f7af1d6b8ab 100644 (file)
@@ -1,5 +1,7 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /********************************************************************
- * Copyright (c) 2008-2011, International Business Machines Corporation and
+ * Copyright (c) 2008-2016, International Business Machines Corporation and
  * others. All Rights Reserved.
  ********************************************************************/
 
@@ -7,11 +9,13 @@
 
 #if !UCONFIG_NO_FORMATTING
 
+#include "unicode/decimfmt.h"
 #include "unicode/tmunit.h"
 #include "unicode/tmutamt.h"
 #include "unicode/tmutfmt.h"
-#include "tufmtts.h"
 #include "unicode/ustring.h"
+#include "cmemory.h"
+#include "intltest.h"
 
 //TODO: put as compilation flag
 //#define TUFMTTS_DEBUG 1
 #include <iostream>
 #endif
 
-void TimeUnitTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) {
-    if (exec) logln("TestSuite TimeUnitTest");
-    switch (index) {
-        TESTCASE(0, testBasic);
-        TESTCASE(1, testAPI);
-        TESTCASE(2, testGreek);
-        default: name = ""; break;
+class TimeUnitTest : public IntlTest {
+    void runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/ ) {
+        if (exec) logln("TestSuite TimeUnitTest");
+        TESTCASE_AUTO_BEGIN;
+        TESTCASE_AUTO(testBasic);
+        TESTCASE_AUTO(testAPI);
+        TESTCASE_AUTO(testGreekWithFallback);
+        TESTCASE_AUTO(testGreekWithSanitization);
+        TESTCASE_AUTO(test10219Plurals);
+        TESTCASE_AUTO(TestBritishShortHourFallback);
+        TESTCASE_AUTO_END;
     }
+
+public:
+    /**
+     * Performs basic tests
+     **/
+    void testBasic();
+
+    /**
+     * Performs API tests
+     **/
+    void testAPI();
+
+    /**
+     * Performs tests for Greek
+     * This tests that requests for short unit names correctly fall back 
+     * to long unit names for a locale where the locale data does not 
+     * provide short unit names. As of CLDR 1.9, Greek is one such language.
+     **/
+    void testGreekWithFallback();
+
+    /**
+     * Performs tests for Greek
+     * This tests that if the plural count listed in time unit format does not
+     * match those in the plural rules for the locale, those plural count in
+     * time unit format will be ingored and subsequently, fall back will kick in
+     * which is tested above.
+     * Without data sanitization, setNumberFormat() would crash.
+     * As of CLDR shiped in ICU4.8, Greek is one such language.
+     */
+    void testGreekWithSanitization();
+
+    /**
+     * Performs unit test for ticket 10219 making sure that plurals work
+     * correctly with rounding.
+     */
+    void test10219Plurals();
+
+    void TestBritishShortHourFallback();
+};
+
+extern IntlTest *createTimeUnitTest() {
+    return new TimeUnitTest();
+}
+
+// This function is more lenient than equals operator as it considers integer 3 hours and
+// double 3.0 hours to be equal
+static UBool tmaEqual(const TimeUnitAmount& left, const TimeUnitAmount& right) {
+    if (left.getTimeUnitField() != right.getTimeUnitField()) {
+        return FALSE;
+    }
+    UErrorCode status = U_ZERO_ERROR;
+    if (!left.getNumber().isNumeric() || !right.getNumber().isNumeric()) {
+        return FALSE;
+    }
+    UBool result = left.getNumber().getDouble(status) == right.getNumber().getDouble(status);
+    if (U_FAILURE(status)) {
+        return FALSE;
+    }
+    return result;
 }
 
 /**
@@ -36,7 +103,7 @@ void TimeUnitTest::runIndexedTest( int32_t index, UBool exec, const char* &name,
 void TimeUnitTest::testBasic() {
     const char* locales[] = {"en", "sl", "fr", "zh", "ar", "ru", "zh_Hant", "pa"};
     for ( unsigned int locIndex = 0; 
-          locIndex < sizeof(locales)/sizeof(locales[0]); 
+          locIndex < UPRV_LENGTHOF(locales); 
           ++locIndex ) {
         UErrorCode status = U_ZERO_ERROR;
         Locale loc(locales[locIndex]);
@@ -58,7 +125,7 @@ void TimeUnitTest::testBasic() {
             std::cout << "time unit: " << j << "\n";
 #endif
             double tests[] = {0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 5, 10, 100, 101.35};
-            for (unsigned int i = 0; i < sizeof(tests)/sizeof(tests[0]); ++i) {
+            for (unsigned int i = 0; i < UPRV_LENGTHOF(tests); ++i) {
 #ifdef TUFMTTS_DEBUG
                 std::cout << "number: " << tests[i] << "\n";
 #endif
@@ -77,14 +144,14 @@ void TimeUnitTest::testBasic() {
                 Formattable result;
                 ((Format*)formats[style])->parseObject(formatted, result, status);
                 if (!assertSuccess("parseObject()", status)) return;
-                if (result != formattable) {
+                if (!tmaEqual(*((TimeUnitAmount *)result.getObject()), *((TimeUnitAmount *) formattable.getObject()))) {
                     dataerrln("No round trip: ");
                 }
                 // other style parsing
                 Formattable result_1;
                 ((Format*)formats[1-style])->parseObject(formatted, result_1, status);
                 if (!assertSuccess("parseObject()", status)) return;
-                if (result_1 != formattable) {
+                if (!tmaEqual(*((TimeUnitAmount *)result_1.getObject()), *((TimeUnitAmount *) formattable.getObject()))) {
                     dataerrln("No round trip: ");
                 }
             }
@@ -116,10 +183,39 @@ void TimeUnitTest::testAPI() {
 
     TimeUnit::UTimeUnitFields field = tmunit_m->getTimeUnitField();
     assertTrue("field of month time unit is month", (field == TimeUnit::UTIMEUNIT_MONTH));
-    
+
+    //===== Interoperability with MeasureUnit ======
+    MeasureUnit **ptrs = new MeasureUnit *[TimeUnit::UTIMEUNIT_FIELD_COUNT];
+
+    ptrs[TimeUnit::UTIMEUNIT_YEAR] = MeasureUnit::createYear(status);
+    ptrs[TimeUnit::UTIMEUNIT_MONTH] = MeasureUnit::createMonth(status);
+    ptrs[TimeUnit::UTIMEUNIT_DAY] = MeasureUnit::createDay(status);
+    ptrs[TimeUnit::UTIMEUNIT_WEEK] = MeasureUnit::createWeek(status);
+    ptrs[TimeUnit::UTIMEUNIT_HOUR] = MeasureUnit::createHour(status);
+    ptrs[TimeUnit::UTIMEUNIT_MINUTE] = MeasureUnit::createMinute(status);
+    ptrs[TimeUnit::UTIMEUNIT_SECOND] = MeasureUnit::createSecond(status);
+    if (!assertSuccess("TimeUnit::createInstance", status)) return;
+
+    for (TimeUnit::UTimeUnitFields j = TimeUnit::UTIMEUNIT_YEAR; 
+            j < TimeUnit::UTIMEUNIT_FIELD_COUNT; 
+            j = (TimeUnit::UTimeUnitFields)(j+1)) {
+        MeasureUnit *ptr = TimeUnit::createInstance(j, status);
+        if (!assertSuccess("TimeUnit::createInstance", status)) return;
+        // We have to convert *ptr to a MeasureUnit or else == will fail over
+        // differing types (TimeUnit vs. MeasureUnit).
+        assertTrue(
+                "Time unit should be equal to corresponding MeasureUnit",
+                MeasureUnit(*ptr) == *ptrs[j]);
+        delete ptr;
+    }
     delete tmunit;
     delete another;
     delete tmunit_m;
+    for (int i = 0; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; ++i) {
+        delete ptrs[i];
+    }
+    delete [] ptrs;
+
     //
     //================= TimeUnitAmount =================
 
@@ -208,7 +304,7 @@ void TimeUnitTest::testAPI() {
  * to long unit names for a locale where the locale data does not 
  * provide short unit names. As of CLDR 1.9, Greek is one such language.
  */
-void TimeUnitTest::testGreek() {
+void TimeUnitTest::testGreekWithFallback() {
     UErrorCode status = U_ZERO_ERROR;
 
     const char* locales[] = {"el-GR", "el"};
@@ -217,57 +313,77 @@ void TimeUnitTest::testGreek() {
     const int numbers[] = {1, 7};
 
     const UChar oneSecond[] = {0x0031, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x03b5, 0x03c1, 0x03cc, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03bf, 0};
+    const UChar oneSecondShort[] = {0x0031, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x002e, 0};
     const UChar oneMinute[] = {0x0031, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03cc, 0};
+    const UChar oneMinuteShort[] = {0x0031, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x002e, 0};
     const UChar oneHour[] = {0x0031, 0x0020, 0x03ce, 0x03c1, 0x03b1, 0};
     const UChar oneDay[] = {0x0031, 0x0020, 0x03b7, 0x03bc, 0x03ad, 0x03c1, 0x03b1, 0};
     const UChar oneMonth[] = {0x0031, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x03b1, 0x03c2, 0};
+    const UChar oneMonthShort[] = {0x0031, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x002e, 0};
     const UChar oneYear[] = {0x0031, 0x0020, 0x03ad, 0x03c4, 0x03bf, 0x03c2, 0};
+    const UChar oneYearShort[] = {0x0031, 0x0020, 0x03ad, 0x03c4, 0x002e, 0};
     const UChar sevenSeconds[] = {0x0037, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x03b5, 0x03c1, 0x03cc, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03b1, 0};
+    const UChar sevenSecondsShort[] = {0x0037, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x002e, 0};
     const UChar sevenMinutes[] = {0x0037, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03ac, 0};
+    const UChar sevenMinutesShort[] = {0x0037, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x002e, 0};
     const UChar sevenHours[] = {0x0037, 0x0020, 0x03ce, 0x03c1, 0x03b5, 0x03c2, 0};
+    const UChar sevenHoursShort[] = {0x0037, 0x0020, 0x03ce, 0x03c1, 0x002e, 0};
     const UChar sevenDays[] = {0x0037, 0x0020, 0x03b7, 0x03bc, 0x03ad, 0x03c1, 0x03b5, 0x03c2, 0};
     const UChar sevenMonths[] = {0x0037, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x03b5, 0x3c2, 0};
+    const UChar sevenMonthsShort[] = {0x0037, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x002e, 0};
     const UChar sevenYears[] = {0x0037, 0x0020, 0x03ad, 0x03c4, 0x03b7, 0};
+    const UChar sevenYearsShort[] = {0x0037, 0x0020, 0x03ad, 0x03c4, 0x002e, 0};
 
     const UnicodeString oneSecondStr(oneSecond);
+    const UnicodeString oneSecondShortStr(oneSecondShort);
     const UnicodeString oneMinuteStr(oneMinute);
+    const UnicodeString oneMinuteShortStr(oneMinuteShort);
     const UnicodeString oneHourStr(oneHour);
     const UnicodeString oneDayStr(oneDay);
     const UnicodeString oneMonthStr(oneMonth);
+    const UnicodeString oneMonthShortStr(oneMonthShort);
     const UnicodeString oneYearStr(oneYear);
+    const UnicodeString oneYearShortStr(oneYearShort);
     const UnicodeString sevenSecondsStr(sevenSeconds);
+    const UnicodeString sevenSecondsShortStr(sevenSecondsShort);
     const UnicodeString sevenMinutesStr(sevenMinutes);
+    const UnicodeString sevenMinutesShortStr(sevenMinutesShort);
     const UnicodeString sevenHoursStr(sevenHours);
+    const UnicodeString sevenHoursShortStr(sevenHoursShort);
     const UnicodeString sevenDaysStr(sevenDays);
     const UnicodeString sevenMonthsStr(sevenMonths);
+    const UnicodeString sevenMonthsShortStr(sevenMonthsShort);
     const UnicodeString sevenYearsStr(sevenYears);
+    const UnicodeString sevenYearsShortStr(sevenYearsShort);
 
-    const UnicodeString expected[] = {oneSecondStr, oneMinuteStr, oneHourStr, oneDayStr, oneMonthStr, oneYearStr,
-                              oneSecondStr, oneMinuteStr, oneHourStr, oneDayStr, oneMonthStr, oneYearStr,
-                              sevenSecondsStr, sevenMinutesStr, sevenHoursStr, sevenDaysStr, sevenMonthsStr, sevenYearsStr,
-                              sevenSecondsStr, sevenMinutesStr, sevenHoursStr, sevenDaysStr, sevenMonthsStr, sevenYearsStr,
-                              oneSecondStr, oneMinuteStr, oneHourStr, oneDayStr, oneMonthStr, oneYearStr,
-                              oneSecondStr, oneMinuteStr, oneHourStr, oneDayStr, oneMonthStr, oneYearStr,
-                              sevenSecondsStr, sevenMinutesStr, sevenHoursStr, sevenDaysStr, sevenMonthsStr, sevenYearsStr,
-                              sevenSecondsStr, sevenMinutesStr, sevenHoursStr, sevenDaysStr, sevenMonthsStr, sevenYearsStr};
+    const UnicodeString expected[] = {
+            oneSecondStr, oneMinuteStr, oneHourStr, oneDayStr, oneMonthStr, oneYearStr,
+            oneSecondShortStr, oneMinuteShortStr, oneHourStr, oneDayStr, oneMonthShortStr, oneYearShortStr,
+            sevenSecondsStr, sevenMinutesStr, sevenHoursStr, sevenDaysStr, sevenMonthsStr, sevenYearsStr,
+            sevenSecondsShortStr, sevenMinutesShortStr, sevenHoursShortStr, sevenDaysStr, sevenMonthsShortStr, sevenYearsShortStr,
+
+            oneSecondStr, oneMinuteStr, oneHourStr, oneDayStr, oneMonthStr, oneYearStr,
+            oneSecondShortStr, oneMinuteShortStr, oneHourStr, oneDayStr, oneMonthShortStr, oneYearShortStr,
+            sevenSecondsStr, sevenMinutesStr, sevenHoursStr, sevenDaysStr, sevenMonthsStr, sevenYearsStr,
+            sevenSecondsShortStr, sevenMinutesShortStr, sevenHoursShortStr, sevenDaysStr, sevenMonthsShortStr, sevenYearsShortStr};
 
     int counter = 0;
     for ( unsigned int locIndex = 0;
-        locIndex < sizeof(locales)/sizeof(locales[0]);
+        locIndex < UPRV_LENGTHOF(locales);
         ++locIndex ) {
 
         Locale l = Locale::createFromName(locales[locIndex]);
 
         for ( unsigned int numberIndex = 0;
-            numberIndex < sizeof(numbers)/sizeof(int);
+            numberIndex < UPRV_LENGTHOF(numbers);
             ++numberIndex ) {
 
             for ( unsigned int styleIndex = 0;
-                styleIndex < sizeof(styles)/sizeof(styles[0]);
+                styleIndex < UPRV_LENGTHOF(styles);
                 ++styleIndex ) {
 
                 for ( unsigned int unitIndex = 0;
-                    unitIndex < sizeof(tunits)/sizeof(tunits[0]);
+                    unitIndex < UPRV_LENGTHOF(tunits);
                     ++unitIndex ) {
 
                     TimeUnitAmount *tamt = new TimeUnitAmount(numbers[numberIndex], tunits[unitIndex], status);
@@ -323,4 +439,92 @@ void TimeUnitTest::testGreek() {
     }
 }
 
+// Test bug9042
+void TimeUnitTest::testGreekWithSanitization() {
+    
+    UErrorCode status = U_ZERO_ERROR;
+    Locale elLoc("el");
+    NumberFormat* numberFmt = NumberFormat::createInstance(Locale("el"), status);
+    if (!assertSuccess("NumberFormat::createInstance for el locale", status, TRUE)) return;
+    numberFmt->setMaximumFractionDigits(1);
+
+    TimeUnitFormat* timeUnitFormat = new TimeUnitFormat(elLoc, status);
+    if (!assertSuccess("TimeUnitFormat::TimeUnitFormat for el locale", status)) return;
+
+    timeUnitFormat->setNumberFormat(*numberFmt, status);
+
+    delete numberFmt;
+    delete timeUnitFormat;
+}
+
+void TimeUnitTest::test10219Plurals() {
+    Locale usLocale("en_US");
+    double values[2] = {1.588, 1.011};
+    UnicodeString expected[2][3] = {
+        {"1 minute", "1.5 minutes", "1.58 minutes"},
+        {"1 minute", "1.0 minutes", "1.01 minutes"}
+    };
+    UErrorCode status = U_ZERO_ERROR;
+    TimeUnitFormat tuf(usLocale, status);
+    if (U_FAILURE(status)) {
+        dataerrln("generating TimeUnitFormat Object failed: %s", u_errorName(status));
+        return;
+    }
+    LocalPointer<DecimalFormat> nf((DecimalFormat *) NumberFormat::createInstance(usLocale, status));
+    if (U_FAILURE(status)) {
+        dataerrln("generating NumberFormat Object failed: %s", u_errorName(status));
+        return;
+    }
+    for (int32_t j = 0; j < UPRV_LENGTHOF(values); ++j) {
+        for (int32_t i = 0; i < UPRV_LENGTHOF(expected[j]); ++i) {
+            nf->setMinimumFractionDigits(i);
+            nf->setMaximumFractionDigits(i);
+            nf->setRoundingMode(DecimalFormat::kRoundDown);
+            tuf.setNumberFormat(*nf, status);
+            if (U_FAILURE(status)) {
+                dataerrln("setting NumberFormat failed: %s", u_errorName(status));
+                return;
+            }
+            UnicodeString actual;
+            Formattable fmt;
+            LocalPointer<TimeUnitAmount> tamt(
+                new TimeUnitAmount(values[j], TimeUnit::UTIMEUNIT_MINUTE, status), status);
+            if (U_FAILURE(status)) {
+                dataerrln("generating TimeUnitAmount Object failed: %s", u_errorName(status));
+                return;
+            }
+            fmt.adoptObject(tamt.orphan());
+            tuf.format(fmt, actual, status);
+            if (U_FAILURE(status)) {
+                dataerrln("Actual formatting failed: %s", u_errorName(status));
+                return;
+            }
+            if (expected[j][i] != actual) {
+                errln("Expected " + expected[j][i] + ", got " + actual);
+            }
+        }
+    }
+
+    // test parsing
+    Formattable result;
+    ParsePosition pos;
+    UnicodeString formattedString = "1 minutes";
+    tuf.parseObject(formattedString, result, pos);
+    if (formattedString.length() != pos.getIndex()) {
+        errln("Expect parsing to go all the way to the end of the string.");
+    }
+}
+
+void TimeUnitTest::TestBritishShortHourFallback() {
+    // See ticket #11986 "incomplete fallback in MeasureFormat".
+    UErrorCode status = U_ZERO_ERROR;
+    Formattable oneHour(new TimeUnitAmount(1, TimeUnit::UTIMEUNIT_HOUR, status));
+    Locale en_GB("en_GB");
+    TimeUnitFormat formatter(en_GB, UTMUTFMT_ABBREVIATED_STYLE, status);
+    UnicodeString result;
+    formatter.format(oneHour, result, status);
+    assertSuccess("TestBritishShortHourFallback()", status);
+    assertEquals("TestBritishShortHourFallback()", UNICODE_STRING_SIMPLE("1 hr"), result, TRUE);
+}
+
 #endif