1// Copyright (C) 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/********************************************************************
4 * Copyright (c) 2008-2016, International Business Machines Corporation and
5 * others. All Rights Reserved.
6 ********************************************************************/
7
8#include "unicode/utypes.h"
9
10#if !UCONFIG_NO_FORMATTING
11
12#include "unicode/decimfmt.h"
13#include "unicode/tmunit.h"
14#include "unicode/tmutamt.h"
15#include "unicode/tmutfmt.h"
16#include "unicode/ustring.h"
17#include "cmemory.h"
18#include "intltest.h"
19
20//TODO: put as compilation flag
21//#define TUFMTTS_DEBUG 1
22
23#ifdef TUFMTTS_DEBUG
24#include <iostream>
25#endif
26
27class TimeUnitTest : public IntlTest {
28    void runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/ ) {
29        if (exec) logln("TestSuite TimeUnitTest");
30        TESTCASE_AUTO_BEGIN;
31        TESTCASE_AUTO(testBasic);
32        TESTCASE_AUTO(testAPI);
33        TESTCASE_AUTO(testGreekWithFallback);
34        TESTCASE_AUTO(testGreekWithSanitization);
35        TESTCASE_AUTO(test10219Plurals);
36        TESTCASE_AUTO(TestBritishShortHourFallback);
37        TESTCASE_AUTO_END;
38    }
39
40public:
41    /**
42     * Performs basic tests
43     **/
44    void testBasic();
45
46    /**
47     * Performs API tests
48     **/
49    void testAPI();
50
51    /**
52     * Performs tests for Greek
53     * This tests that requests for short unit names correctly fall back
54     * to long unit names for a locale where the locale data does not
55     * provide short unit names. As of CLDR 1.9, Greek is one such language.
56     **/
57    void testGreekWithFallback();
58
59    /**
60     * Performs tests for Greek
61     * This tests that if the plural count listed in time unit format does not
62     * match those in the plural rules for the locale, those plural count in
63     * time unit format will be ingored and subsequently, fall back will kick in
64     * which is tested above.
65     * Without data sanitization, setNumberFormat() would crash.
66     * As of CLDR shiped in ICU4.8, Greek is one such language.
67     */
68    void testGreekWithSanitization();
69
70    /**
71     * Performs unit test for ticket 10219 making sure that plurals work
72     * correctly with rounding.
73     */
74    void test10219Plurals();
75
76    void TestBritishShortHourFallback();
77};
78
79extern IntlTest *createTimeUnitTest() {
80    return new TimeUnitTest();
81}
82
83// This function is more lenient than equals operator as it considers integer 3 hours and
84// double 3.0 hours to be equal
85static UBool tmaEqual(const TimeUnitAmount& left, const TimeUnitAmount& right) {
86    if (left.getTimeUnitField() != right.getTimeUnitField()) {
87        return FALSE;
88    }
89    UErrorCode status = U_ZERO_ERROR;
90    if (!left.getNumber().isNumeric() || !right.getNumber().isNumeric()) {
91        return FALSE;
92    }
93    UBool result = left.getNumber().getDouble(status) == right.getNumber().getDouble(status);
94    if (U_FAILURE(status)) {
95        return FALSE;
96    }
97    return result;
98}
99
100/**
101 * Test basic
102 */
103void TimeUnitTest::testBasic() {
104    const char* locales[] = {"en", "sl", "fr", "zh", "ar", "ru", "zh_Hant", "pa"};
105    for ( unsigned int locIndex = 0;
106          locIndex < UPRV_LENGTHOF(locales);
107          ++locIndex ) {
108        UErrorCode status = U_ZERO_ERROR;
109        Locale loc(locales[locIndex]);
110        TimeUnitFormat** formats = new TimeUnitFormat*[2];
111        formats[UTMUTFMT_FULL_STYLE] = new TimeUnitFormat(loc, status);
112        if (!assertSuccess("TimeUnitFormat(full)", status, TRUE)) return;
113        formats[UTMUTFMT_ABBREVIATED_STYLE] = new TimeUnitFormat(loc, UTMUTFMT_ABBREVIATED_STYLE, status);
114        if (!assertSuccess("TimeUnitFormat(short)", status)) return;
115#ifdef TUFMTTS_DEBUG
116        std::cout << "locale: " << locales[locIndex] << "\n";
117#endif
118        for (int style = UTMUTFMT_FULL_STYLE;
119             style <= UTMUTFMT_ABBREVIATED_STYLE;
120             ++style) {
121          for (TimeUnit::UTimeUnitFields j = TimeUnit::UTIMEUNIT_YEAR;
122             j < TimeUnit::UTIMEUNIT_FIELD_COUNT;
123             j = (TimeUnit::UTimeUnitFields)(j+1)) {
124#ifdef TUFMTTS_DEBUG
125            std::cout << "time unit: " << j << "\n";
126#endif
127            double tests[] = {0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 5, 10, 100, 101.35};
128            for (unsigned int i = 0; i < UPRV_LENGTHOF(tests); ++i) {
129#ifdef TUFMTTS_DEBUG
130                std::cout << "number: " << tests[i] << "\n";
131#endif
132                TimeUnitAmount* source = new TimeUnitAmount(tests[i], j, status);
133                if (!assertSuccess("TimeUnitAmount()", status)) return;
134                UnicodeString formatted;
135                Formattable formattable;
136                formattable.adoptObject(source);
137                formatted = ((Format*)formats[style])->format(formattable, formatted, status);
138                if (!assertSuccess("format()", status)) return;
139#ifdef TUFMTTS_DEBUG
140                char formatResult[1000];
141                formatted.extract(0, formatted.length(), formatResult, "UTF-8");
142                std::cout << "format result: " << formatResult << "\n";
143#endif
144                Formattable result;
145                ((Format*)formats[style])->parseObject(formatted, result, status);
146                if (!assertSuccess("parseObject()", status)) return;
147                if (!tmaEqual(*((TimeUnitAmount *)result.getObject()), *((TimeUnitAmount *) formattable.getObject()))) {
148                    dataerrln("No round trip: ");
149                }
150                // other style parsing
151                Formattable result_1;
152                ((Format*)formats[1-style])->parseObject(formatted, result_1, status);
153                if (!assertSuccess("parseObject()", status)) return;
154                if (!tmaEqual(*((TimeUnitAmount *)result_1.getObject()), *((TimeUnitAmount *) formattable.getObject()))) {
155                    dataerrln("No round trip: ");
156                }
157            }
158          }
159        }
160        delete formats[UTMUTFMT_FULL_STYLE];
161        delete formats[UTMUTFMT_ABBREVIATED_STYLE];
162        delete[] formats;
163    }
164}
165
166
167void TimeUnitTest::testAPI() {
168    //================= TimeUnit =================
169    UErrorCode status = U_ZERO_ERROR;
170
171    TimeUnit* tmunit = TimeUnit::createInstance(TimeUnit::UTIMEUNIT_YEAR, status);
172    if (!assertSuccess("TimeUnit::createInstance", status)) return;
173
174    TimeUnit* another = (TimeUnit*)tmunit->clone();
175    TimeUnit third(*tmunit);
176    TimeUnit fourth = third;
177
178    assertTrue("orig and clone are equal", (*tmunit == *another));
179    assertTrue("copied and assigned are equal", (third == fourth));
180
181    TimeUnit* tmunit_m = TimeUnit::createInstance(TimeUnit::UTIMEUNIT_MONTH, status);
182    assertTrue("year != month", (*tmunit != *tmunit_m));
183
184    TimeUnit::UTimeUnitFields field = tmunit_m->getTimeUnitField();
185    assertTrue("field of month time unit is month", (field == TimeUnit::UTIMEUNIT_MONTH));
186
187    //===== Interoperability with MeasureUnit ======
188    MeasureUnit **ptrs = new MeasureUnit *[TimeUnit::UTIMEUNIT_FIELD_COUNT];
189
190    ptrs[TimeUnit::UTIMEUNIT_YEAR] = MeasureUnit::createYear(status);
191    ptrs[TimeUnit::UTIMEUNIT_MONTH] = MeasureUnit::createMonth(status);
192    ptrs[TimeUnit::UTIMEUNIT_DAY] = MeasureUnit::createDay(status);
193    ptrs[TimeUnit::UTIMEUNIT_WEEK] = MeasureUnit::createWeek(status);
194    ptrs[TimeUnit::UTIMEUNIT_HOUR] = MeasureUnit::createHour(status);
195    ptrs[TimeUnit::UTIMEUNIT_MINUTE] = MeasureUnit::createMinute(status);
196    ptrs[TimeUnit::UTIMEUNIT_SECOND] = MeasureUnit::createSecond(status);
197    if (!assertSuccess("TimeUnit::createInstance", status)) return;
198
199    for (TimeUnit::UTimeUnitFields j = TimeUnit::UTIMEUNIT_YEAR;
200            j < TimeUnit::UTIMEUNIT_FIELD_COUNT;
201            j = (TimeUnit::UTimeUnitFields)(j+1)) {
202        MeasureUnit *ptr = TimeUnit::createInstance(j, status);
203        if (!assertSuccess("TimeUnit::createInstance", status)) return;
204        // We have to convert *ptr to a MeasureUnit or else == will fail over
205        // differing types (TimeUnit vs. MeasureUnit).
206        assertTrue(
207                "Time unit should be equal to corresponding MeasureUnit",
208                MeasureUnit(*ptr) == *ptrs[j]);
209        delete ptr;
210    }
211    delete tmunit;
212    delete another;
213    delete tmunit_m;
214    for (int i = 0; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; ++i) {
215        delete ptrs[i];
216    }
217    delete [] ptrs;
218
219    //
220    //================= TimeUnitAmount =================
221
222    Formattable formattable((int32_t)2);
223    TimeUnitAmount tma_long(formattable, TimeUnit::UTIMEUNIT_DAY, status);
224    if (!assertSuccess("TimeUnitAmount(formattable...)", status)) return;
225
226    formattable.setDouble(2);
227    TimeUnitAmount tma_double(formattable, TimeUnit::UTIMEUNIT_DAY, status);
228    if (!assertSuccess("TimeUnitAmount(formattable...)", status)) return;
229
230    formattable.setDouble(3);
231    TimeUnitAmount tma_double_3(formattable, TimeUnit::UTIMEUNIT_DAY, status);
232    if (!assertSuccess("TimeUnitAmount(formattable...)", status)) return;
233
234    TimeUnitAmount tma(2, TimeUnit::UTIMEUNIT_DAY, status);
235    if (!assertSuccess("TimeUnitAmount(number...)", status)) return;
236
237    TimeUnitAmount tma_h(2, TimeUnit::UTIMEUNIT_HOUR, status);
238    if (!assertSuccess("TimeUnitAmount(number...)", status)) return;
239
240    TimeUnitAmount second(tma);
241    TimeUnitAmount third_tma = tma;
242    TimeUnitAmount* fourth_tma = (TimeUnitAmount*)tma.clone();
243
244    assertTrue("orig and copy are equal", (second == tma));
245    assertTrue("clone and assigned are equal", (third_tma == *fourth_tma));
246    assertTrue("different if number diff", (tma_double != tma_double_3));
247    assertTrue("different if number type diff", (tma_double != tma_long));
248    assertTrue("different if time unit diff", (tma != tma_h));
249    assertTrue("same even different constructor", (tma_double == tma));
250
251    assertTrue("getTimeUnitField", (tma.getTimeUnitField() == TimeUnit::UTIMEUNIT_DAY));
252    delete fourth_tma;
253    //
254    //================= TimeUnitFormat =================
255    //
256    TimeUnitFormat* tmf_en = new TimeUnitFormat(Locale("en"), status);
257    if (!assertSuccess("TimeUnitFormat(en...)", status, TRUE)) return;
258    TimeUnitFormat tmf_fr(Locale("fr"), status);
259    if (!assertSuccess("TimeUnitFormat(fr...)", status)) return;
260
261    assertTrue("TimeUnitFormat: en and fr diff", (*tmf_en != tmf_fr));
262
263    TimeUnitFormat tmf_assign = *tmf_en;
264    assertTrue("TimeUnitFormat: orig and assign are equal", (*tmf_en == tmf_assign));
265
266    TimeUnitFormat tmf_copy(tmf_fr);
267    assertTrue("TimeUnitFormat: orig and copy are equal", (tmf_fr == tmf_copy));
268
269    TimeUnitFormat* tmf_clone = (TimeUnitFormat*)tmf_en->clone();
270    assertTrue("TimeUnitFormat: orig and clone are equal", (*tmf_en == *tmf_clone));
271    delete tmf_clone;
272
273    tmf_en->setLocale(Locale("fr"), status);
274    if (!assertSuccess("setLocale(fr...)", status)) return;
275
276    NumberFormat* numberFmt = NumberFormat::createInstance(
277                                 Locale("fr"), status);
278    if (!assertSuccess("NumberFormat::createInstance()", status)) return;
279    tmf_en->setNumberFormat(*numberFmt, status);
280    if (!assertSuccess("setNumberFormat(en...)", status)) return;
281    assertTrue("TimeUnitFormat: setLocale", (*tmf_en == tmf_fr));
282
283    delete tmf_en;
284
285    TimeUnitFormat* en_long = new TimeUnitFormat(Locale("en"), UTMUTFMT_FULL_STYLE, status);
286    if (!assertSuccess("TimeUnitFormat(en...)", status)) return;
287    delete en_long;
288
289    TimeUnitFormat* en_short = new TimeUnitFormat(Locale("en"), UTMUTFMT_ABBREVIATED_STYLE, status);
290    if (!assertSuccess("TimeUnitFormat(en...)", status)) return;
291    delete en_short;
292
293    TimeUnitFormat* format = new TimeUnitFormat(status);
294    format->setLocale(Locale("zh"), status);
295    format->setNumberFormat(*numberFmt, status);
296    if (!assertSuccess("TimeUnitFormat(en...)", status)) return;
297    delete numberFmt;
298    delete format;
299}
300
301/* @bug 7902
302 * Tests for Greek Language.
303 * This tests that requests for short unit names correctly fall back
304 * to long unit names for a locale where the locale data does not
305 * provide short unit names. As of CLDR 1.9, Greek is one such language.
306 */
307void TimeUnitTest::testGreekWithFallback() {
308    UErrorCode status = U_ZERO_ERROR;
309
310    const char* locales[] = {"el-GR", "el"};
311    TimeUnit::UTimeUnitFields tunits[] = {TimeUnit::UTIMEUNIT_SECOND, TimeUnit::UTIMEUNIT_MINUTE, TimeUnit::UTIMEUNIT_HOUR, TimeUnit::UTIMEUNIT_DAY, TimeUnit::UTIMEUNIT_MONTH, TimeUnit::UTIMEUNIT_YEAR};
312    UTimeUnitFormatStyle styles[] = {UTMUTFMT_FULL_STYLE, UTMUTFMT_ABBREVIATED_STYLE};
313    const int numbers[] = {1, 7};
314
315    const UChar oneSecond[] = {0x0031, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x03b5, 0x03c1, 0x03cc, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03bf, 0};
316    const UChar oneSecondShort[] = {0x0031, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x002e, 0};
317    const UChar oneMinute[] = {0x0031, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03cc, 0};
318    const UChar oneMinuteShort[] = {0x0031, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x002e, 0};
319    const UChar oneHour[] = {0x0031, 0x0020, 0x03ce, 0x03c1, 0x03b1, 0};
320    const UChar oneDay[] = {0x0031, 0x0020, 0x03b7, 0x03bc, 0x03ad, 0x03c1, 0x03b1, 0};
321    const UChar oneMonth[] = {0x0031, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x03b1, 0x03c2, 0};
322    const UChar oneMonthShort[] = {0x0031, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x002e, 0};
323    const UChar oneYear[] = {0x0031, 0x0020, 0x03ad, 0x03c4, 0x03bf, 0x03c2, 0};
324    const UChar oneYearShort[] = {0x0031, 0x0020, 0x03ad, 0x03c4, 0x002e, 0};
325    const UChar sevenSeconds[] = {0x0037, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x03b5, 0x03c1, 0x03cc, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03b1, 0};
326    const UChar sevenSecondsShort[] = {0x0037, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x002e, 0};
327    const UChar sevenMinutes[] = {0x0037, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03ac, 0};
328    const UChar sevenMinutesShort[] = {0x0037, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x002e, 0};
329    const UChar sevenHours[] = {0x0037, 0x0020, 0x03ce, 0x03c1, 0x03b5, 0x03c2, 0};
330    const UChar sevenHoursShort[] = {0x0037, 0x0020, 0x03ce, 0x03c1, 0x002e, 0};
331    const UChar sevenDays[] = {0x0037, 0x0020, 0x03b7, 0x03bc, 0x03ad, 0x03c1, 0x03b5, 0x03c2, 0};
332    const UChar sevenMonths[] = {0x0037, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x03b5, 0x3c2, 0};
333    const UChar sevenMonthsShort[] = {0x0037, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x002e, 0};
334    const UChar sevenYears[] = {0x0037, 0x0020, 0x03ad, 0x03c4, 0x03b7, 0};
335    const UChar sevenYearsShort[] = {0x0037, 0x0020, 0x03ad, 0x03c4, 0x002e, 0};
336
337    const UnicodeString oneSecondStr(oneSecond);
338    const UnicodeString oneSecondShortStr(oneSecondShort);
339    const UnicodeString oneMinuteStr(oneMinute);
340    const UnicodeString oneMinuteShortStr(oneMinuteShort);
341    const UnicodeString oneHourStr(oneHour);
342    const UnicodeString oneDayStr(oneDay);
343    const UnicodeString oneMonthStr(oneMonth);
344    const UnicodeString oneMonthShortStr(oneMonthShort);
345    const UnicodeString oneYearStr(oneYear);
346    const UnicodeString oneYearShortStr(oneYearShort);
347    const UnicodeString sevenSecondsStr(sevenSeconds);
348    const UnicodeString sevenSecondsShortStr(sevenSecondsShort);
349    const UnicodeString sevenMinutesStr(sevenMinutes);
350    const UnicodeString sevenMinutesShortStr(sevenMinutesShort);
351    const UnicodeString sevenHoursStr(sevenHours);
352    const UnicodeString sevenHoursShortStr(sevenHoursShort);
353    const UnicodeString sevenDaysStr(sevenDays);
354    const UnicodeString sevenMonthsStr(sevenMonths);
355    const UnicodeString sevenMonthsShortStr(sevenMonthsShort);
356    const UnicodeString sevenYearsStr(sevenYears);
357    const UnicodeString sevenYearsShortStr(sevenYearsShort);
358
359    const UnicodeString expected[] = {
360            oneSecondStr, oneMinuteStr, oneHourStr, oneDayStr, oneMonthStr, oneYearStr,
361            oneSecondShortStr, oneMinuteShortStr, oneHourStr, oneDayStr, oneMonthShortStr, oneYearShortStr,
362            sevenSecondsStr, sevenMinutesStr, sevenHoursStr, sevenDaysStr, sevenMonthsStr, sevenYearsStr,
363            sevenSecondsShortStr, sevenMinutesShortStr, sevenHoursShortStr, sevenDaysStr, sevenMonthsShortStr, sevenYearsShortStr,
364
365            oneSecondStr, oneMinuteStr, oneHourStr, oneDayStr, oneMonthStr, oneYearStr,
366            oneSecondShortStr, oneMinuteShortStr, oneHourStr, oneDayStr, oneMonthShortStr, oneYearShortStr,
367            sevenSecondsStr, sevenMinutesStr, sevenHoursStr, sevenDaysStr, sevenMonthsStr, sevenYearsStr,
368            sevenSecondsShortStr, sevenMinutesShortStr, sevenHoursShortStr, sevenDaysStr, sevenMonthsShortStr, sevenYearsShortStr};
369
370    int counter = 0;
371    for ( unsigned int locIndex = 0;
372        locIndex < UPRV_LENGTHOF(locales);
373        ++locIndex ) {
374
375        Locale l = Locale::createFromName(locales[locIndex]);
376
377        for ( unsigned int numberIndex = 0;
378            numberIndex < UPRV_LENGTHOF(numbers);
379            ++numberIndex ) {
380
381            for ( unsigned int styleIndex = 0;
382                styleIndex < UPRV_LENGTHOF(styles);
383                ++styleIndex ) {
384
385                for ( unsigned int unitIndex = 0;
386                    unitIndex < UPRV_LENGTHOF(tunits);
387                    ++unitIndex ) {
388
389                    TimeUnitAmount *tamt = new TimeUnitAmount(numbers[numberIndex], tunits[unitIndex], status);
390                    if (U_FAILURE(status)) {
391                        dataerrln("generating TimeUnitAmount Object failed.");
392#ifdef TUFMTTS_DEBUG
393                        std::cout << "Failed to get TimeUnitAmount for " << tunits[unitIndex] << "\n";
394#endif
395                        return;
396                    }
397
398                    TimeUnitFormat *tfmt = new TimeUnitFormat(l, styles[styleIndex], status);
399                    if (U_FAILURE(status)) {
400                        dataerrln("generating TimeUnitAmount Object failed.");
401#ifdef TUFMTTS_DEBUG
402                       std::cout <<  "Failed to get TimeUnitFormat for " << locales[locIndex] << "\n";
403#endif
404                       return;
405                    }
406
407                    Formattable fmt;
408                    UnicodeString str;
409
410                    fmt.adoptObject(tamt);
411                    str = ((Format *)tfmt)->format(fmt, str, status);
412                    if (!assertSuccess("formatting relative time failed", status)) {
413                        delete tfmt;
414#ifdef TUFMTTS_DEBUG
415                        std::cout <<  "Failed to format" << "\n";
416#endif
417                        return;
418                    }
419
420#ifdef TUFMTTS_DEBUG
421                    char tmp[128];    //output
422                    char tmp1[128];    //expected
423                    int len = 0;
424                    u_strToUTF8(tmp, 128, &len, str.getTerminatedBuffer(), str.length(), &status);
425                    u_strToUTF8(tmp1, 128, &len, expected[counter].unescape().getTerminatedBuffer(), expected[counter].unescape().length(), &status);
426                    std::cout <<  "Formatted string : " << tmp << " expected : " << tmp1 << "\n";
427#endif
428                    if (!assertEquals("formatted time string is not expected, locale: " + UnicodeString(locales[locIndex]) + " style: " + (int)styles[styleIndex] + " units: " + (int)tunits[unitIndex], expected[counter], str)) {
429                        delete tfmt;
430                        str.remove();
431                        return;
432                    }
433                    delete tfmt;
434                    str.remove();
435                    ++counter;
436                }
437            }
438        }
439    }
440}
441
442// Test bug9042
443void TimeUnitTest::testGreekWithSanitization() {
444
445    UErrorCode status = U_ZERO_ERROR;
446    Locale elLoc("el");
447    NumberFormat* numberFmt = NumberFormat::createInstance(Locale("el"), status);
448    if (!assertSuccess("NumberFormat::createInstance for el locale", status, TRUE)) return;
449    numberFmt->setMaximumFractionDigits(1);
450
451    TimeUnitFormat* timeUnitFormat = new TimeUnitFormat(elLoc, status);
452    if (!assertSuccess("TimeUnitFormat::TimeUnitFormat for el locale", status)) return;
453
454    timeUnitFormat->setNumberFormat(*numberFmt, status);
455
456    delete numberFmt;
457    delete timeUnitFormat;
458}
459
460void TimeUnitTest::test10219Plurals() {
461    Locale usLocale("en_US");
462    double values[2] = {1.588, 1.011};
463    UnicodeString expected[2][3] = {
464        {"1 minute", "1.5 minutes", "1.58 minutes"},
465        {"1 minute", "1.0 minutes", "1.01 minutes"}
466    };
467    UErrorCode status = U_ZERO_ERROR;
468    TimeUnitFormat tuf(usLocale, status);
469    if (U_FAILURE(status)) {
470        dataerrln("generating TimeUnitFormat Object failed: %s", u_errorName(status));
471        return;
472    }
473    LocalPointer<DecimalFormat> nf((DecimalFormat *) NumberFormat::createInstance(usLocale, status));
474    if (U_FAILURE(status)) {
475        dataerrln("generating NumberFormat Object failed: %s", u_errorName(status));
476        return;
477    }
478    for (int32_t j = 0; j < UPRV_LENGTHOF(values); ++j) {
479        for (int32_t i = 0; i < UPRV_LENGTHOF(expected[j]); ++i) {
480            nf->setMinimumFractionDigits(i);
481            nf->setMaximumFractionDigits(i);
482            nf->setRoundingMode(DecimalFormat::kRoundDown);
483            tuf.setNumberFormat(*nf, status);
484            if (U_FAILURE(status)) {
485                dataerrln("setting NumberFormat failed: %s", u_errorName(status));
486                return;
487            }
488            UnicodeString actual;
489            Formattable fmt;
490            LocalPointer<TimeUnitAmount> tamt(
491                new TimeUnitAmount(values[j], TimeUnit::UTIMEUNIT_MINUTE, status), status);
492            if (U_FAILURE(status)) {
493                dataerrln("generating TimeUnitAmount Object failed: %s", u_errorName(status));
494                return;
495            }
496            fmt.adoptObject(tamt.orphan());
497            tuf.format(fmt, actual, status);
498            if (U_FAILURE(status)) {
499                dataerrln("Actual formatting failed: %s", u_errorName(status));
500                return;
501            }
502            if (expected[j][i] != actual) {
503                errln("Expected " + expected[j][i] + ", got " + actual);
504            }
505        }
506    }
507
508    // test parsing
509    Formattable result;
510    ParsePosition pos;
511    UnicodeString formattedString = "1 minutes";
512    tuf.parseObject(formattedString, result, pos);
513    if (formattedString.length() != pos.getIndex()) {
514        errln("Expect parsing to go all the way to the end of the string.");
515    }
516}
517
518void TimeUnitTest::TestBritishShortHourFallback() {
519    // See ticket #11986 "incomplete fallback in MeasureFormat".
520    UErrorCode status = U_ZERO_ERROR;
521    Formattable oneHour(new TimeUnitAmount(1, TimeUnit::UTIMEUNIT_HOUR, status));
522    Locale en_GB("en_GB");
523    TimeUnitFormat formatter(en_GB, UTMUTFMT_ABBREVIATED_STYLE, status);
524    UnicodeString result;
525    formatter.format(oneHour, result, status);
526    assertSuccess("TestBritishShortHourFallback()", status);
527    assertEquals("TestBritishShortHourFallback()", UNICODE_STRING_SIMPLE("1 hr"), result, TRUE);
528}
529
530#endif
531