1/* GENERATED SOURCE. DO NOT MODIFY. */
2// © 2016 and later: Unicode, Inc. and others.
3// License & terms of use: http://www.unicode.org/copyright.html#License
4/*
5 *******************************************************************************
6 * Copyright (C) 2006-2016, Google, International Business Machines Corporation
7 * and others. All Rights Reserved.
8 *******************************************************************************
9 */
10
11package android.icu.dev.test.format;
12
13import java.text.ParsePosition;
14import java.util.Collection;
15import java.util.Date;
16import java.util.HashSet;
17import java.util.Iterator;
18import java.util.LinkedHashMap;
19import java.util.LinkedHashSet;
20import java.util.List;
21import java.util.Locale;
22import java.util.Map;
23import java.util.Random;
24import java.util.Set;
25
26import org.junit.Test;
27
28import android.icu.dev.test.TestFmwk;
29import android.icu.impl.PatternTokenizer;
30import android.icu.impl.Utility;
31import android.icu.text.DateFormat;
32import android.icu.text.DateTimePatternGenerator;
33import android.icu.text.DateTimePatternGenerator.FormatParser;
34import android.icu.text.DateTimePatternGenerator.VariableField;
35import android.icu.text.SimpleDateFormat;
36import android.icu.text.UTF16;
37import android.icu.text.UnicodeSet;
38import android.icu.util.Calendar;
39import android.icu.util.GregorianCalendar;
40import android.icu.util.SimpleTimeZone;
41import android.icu.util.TimeZone;
42import android.icu.util.ULocale;
43
44public class DateTimeGeneratorTest extends TestFmwk {
45    public static boolean GENERATE_TEST_DATA;
46    static {
47        try {
48            GENERATE_TEST_DATA = System.getProperty("GENERATE_TEST_DATA") != null;
49        } catch (SecurityException e) {
50            GENERATE_TEST_DATA = false;
51        }
52    };
53    public static int RANDOM_COUNT = 1000;
54    public static boolean DEBUG = false;
55
56    @Test
57    public void TestC() {
58        String[][] tests = {
59                {"zh", "Cm", "Bh:mm"},
60                {"de", "Cm", "HH:mm"},
61                {"en", "Cm", "h:mm a"},
62                {"en-BN", "Cm", "h:mm b"},
63                {"gu-IN", "Cm", "h:mm B"},
64                {"und-IN", "Cm", "h:mm a"},
65        };
66        for (String[] test : tests) {
67            DateTimePatternGenerator gen = DateTimePatternGenerator.getInstance(ULocale.forLanguageTag(test[0]));
68            String skeleton = test[1];
69            String pattern = gen.getBestPattern(skeleton);
70            assertEquals(test[0] + "/" + skeleton, test[2], pattern);
71        }
72    }
73
74    @Test
75    public void TestSimple() {
76        // some simple use cases
77        ULocale locale = ULocale.GERMANY;
78        TimeZone zone = TimeZone.getTimeZone("Europe/Paris");
79
80        // make from locale
81        DateTimePatternGenerator gen = DateTimePatternGenerator.getInstance(locale);
82        SimpleDateFormat format = new SimpleDateFormat(gen.getBestPattern("MMMddHmm"), locale);
83        format.setTimeZone(zone);
84        assertEquals("simple format: MMMddHmm", "14. Okt., 08:58", format.format(sampleDate));
85        // (a generator can be built from scratch, but that is not a typical use case)
86
87        // modify the generator by adding patterns
88        DateTimePatternGenerator.PatternInfo returnInfo = new DateTimePatternGenerator.PatternInfo();
89        gen.addPattern("d'. von' MMMM", true, returnInfo);
90        // the returnInfo is mostly useful for debugging problem cases
91        format.applyPattern(gen.getBestPattern("MMMMdHmm"));
92        assertEquals("modified format: MMMdHmm", "14. von Oktober, 08:58", format.format(sampleDate));
93
94        // get a pattern and modify it
95        format = (SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, locale);
96        format.setTimeZone(zone);
97        String pattern = format.toPattern();
98        assertEquals("full-date", "Donnerstag, 14. Oktober 1999 um 08:58:59 Mitteleurop\u00E4ische Sommerzeit", format.format(sampleDate));
99
100        // modify it to change the zone.
101        String newPattern = gen.replaceFieldTypes(pattern, "vvvv");
102        format.applyPattern(newPattern);
103        assertEquals("full-date: modified zone", "Donnerstag, 14. Oktober 1999 um 08:58:59 Mitteleurop\u00E4ische Zeit", format.format(sampleDate));
104
105        // add test of basic cases
106
107        //lang  YYYYMMM MMMd    MMMdhmm hmm hhmm    Full Date-Time
108        // en  Mar 2007    Mar 4   6:05 PM Mar 4   6:05 PM 06:05 PM    Sunday, March 4, 2007 6:05:05 PM PT
109        DateTimePatternGenerator enGen = DateTimePatternGenerator.getInstance(ULocale.ENGLISH);
110        TimeZone enZone = TimeZone.getTimeZone("Etc/GMT");
111        SimpleDateFormat enFormat = (SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, ULocale.ENGLISH);
112        enFormat.setTimeZone(enZone);
113        String[][] tests = {
114                {"yyyyMMMdd", "Oct 14, 1999"},
115                {"yyyyqqqq", "4th quarter 1999"},
116                {"yMMMdd", "Oct 14, 1999"},
117                {"EyyyyMMMdd", "Thu, Oct 14, 1999"},
118                {"yyyyMMdd", "10/14/1999"},
119                {"yyyyMMM", "Oct 1999"},
120                {"yyyyMM", "10/1999"},
121                {"yyMM", "10/99"},
122                {"yMMMMMd", "O 14, 1999"},  // narrow format
123                {"EEEEEMMMMMd", "T, O 14"},  // narrow format
124                {"MMMd", "Oct 14"},
125                {"MMMdhmm", "Oct 14, 6:58 AM"},
126                {"EMMMdhmms", "Thu, Oct 14, 6:58:59 AM"},
127                {"MMdhmm", "10/14, 6:58 AM"},
128                {"EEEEMMMdhmms", "Thursday, Oct 14, 6:58:59 AM"},
129                {"yyyyMMMddhhmmss", "Oct 14, 1999, 6:58:59 AM"}, // (fixed expected result per ticket 6872<-7180)
130                {"EyyyyMMMddhhmmss", "Thu, Oct 14, 1999, 6:58:59 AM"}, // (fixed expected result per ticket 6872<-7180)
131                {"hmm", "6:58 AM"},
132                {"hhmm", "6:58 AM"}, // (fixed expected result per ticket 6872<-7180)
133                {"hhmmVVVV", "6:58 AM GMT"}, // (fixed expected result per ticket 6872<-7180)
134        };
135        for (int i = 0; i < tests.length; ++i) {
136            final String testSkeleton = tests[i][0];
137            String pat = enGen.getBestPattern(testSkeleton);
138            enFormat.applyPattern(pat);
139            String formattedDate = enFormat.format(sampleDate);
140            assertEquals("Testing skeleton '" + testSkeleton + "' with  " + sampleDate, tests[i][1], formattedDate);
141        }
142    }
143
144    @Test
145    public void TestRoot() {
146        DateTimePatternGenerator rootGen = DateTimePatternGenerator.getInstance(ULocale.ROOT);
147        SimpleDateFormat rootFormat = new SimpleDateFormat(rootGen.getBestPattern("yMdHms"), ULocale.ROOT);
148        rootFormat.setTimeZone(gmt);
149        // *** expected result should be "1999-10-14 6:58:59" with current data, changed test temporarily to match current result, needs investigation
150        assertEquals("root format: yMdHms", "1999-10-14 06:58:59", rootFormat.format(sampleDate));
151    }
152
153    @Test
154    public void TestEmpty() {
155        // now nothing
156        DateTimePatternGenerator nullGen = DateTimePatternGenerator.getEmptyInstance();
157        SimpleDateFormat format = new SimpleDateFormat(nullGen.getBestPattern("yMdHms"), ULocale.ROOT);
158        TimeZone rootZone = TimeZone.getTimeZone("Etc/GMT");
159        format.setTimeZone(rootZone);
160    }
161
162    @Test
163    public void TestPatternParser() {
164        StringBuffer buffer = new StringBuffer();
165        PatternTokenizer pp = new PatternTokenizer()
166        .setIgnorableCharacters(new UnicodeSet("[-]"))
167        .setSyntaxCharacters(new UnicodeSet("[a-zA-Z]"))
168        .setEscapeCharacters(new UnicodeSet("[b#]"))
169        .setUsingQuote(true);
170        logln("Using Quote");
171        for (int i = 0; i < patternTestData.length; ++i) {
172            String patternTest = (String) patternTestData[i];
173            CheckPattern(buffer, pp, patternTest);
174        }
175        String[] randomSet = {"abcdef", "$12!@#-", "'\\"};
176        for (int i = 0; i < RANDOM_COUNT; ++i) {
177            String patternTest = getRandomString(randomSet, 0, 10);
178            CheckPattern(buffer, pp, patternTest);
179        }
180        logln("Using Backslash");
181        pp.setUsingQuote(false).setUsingSlash(true);
182        for (int i = 0; i < patternTestData.length; ++i) {
183            String patternTest = (String) patternTestData[i];
184            CheckPattern(buffer, pp, patternTest);
185        }
186        for (int i = 0; i < RANDOM_COUNT; ++i) {
187            String patternTest = getRandomString(randomSet, 0, 10);
188            CheckPattern(buffer, pp, patternTest);
189        }
190    }
191
192    Random random = new java.util.Random(-1);
193
194    private String getRandomString(String[] randomList, int minLen, int maxLen) {
195        StringBuffer result = new StringBuffer();
196        int len = random.nextInt(maxLen + 1 - minLen) + minLen;
197        for (int i = minLen; i < len; ++ i) {
198            String source = randomList[random.nextInt(randomList.length)]; // don't bother with surrogates
199            char ch = source.charAt(random.nextInt(source.length()));
200            UTF16.append(result, ch);
201        }
202        return result.toString();
203    }
204
205    private void CheckPattern(StringBuffer buffer, PatternTokenizer pp, String patternTest) {
206        pp.setPattern(patternTest);
207        if (DEBUG && isVerbose()) {
208            showItems(buffer, pp, patternTest);
209        }
210        String normalized = pp.setStart(0).normalize();
211        logln("input:\t<" + patternTest + ">" + "\tnormalized:\t<" + normalized + ">");
212        String doubleNormalized = pp.setPattern(normalized).normalize();
213        if (!normalized.equals(doubleNormalized)) {
214            errln("Normalization not idempotent:\t" + patternTest + "\tnormalized: " + normalized +  "\tnormalized2: " + doubleNormalized);
215            // allow for debugging at the point of failure
216            if (DEBUG) {
217                pp.setPattern(patternTest);
218                normalized = pp.setStart(0).normalize();
219                pp.setPattern(normalized);
220                showItems(buffer, pp, normalized);
221                doubleNormalized = pp.normalize();
222            }
223        }
224    }
225
226    private void showItems(StringBuffer buffer, PatternTokenizer pp, String patternTest) {
227        logln("input:\t<" + patternTest + ">");
228        while (true) {
229            buffer.setLength(0);
230            int status = pp.next(buffer);
231            if (status == PatternTokenizer.DONE) break;
232            String lit = "";
233            if (status != PatternTokenizer.SYNTAX ) {
234                lit = "\t<" + pp.quoteLiteral(buffer) + ">";
235            }
236            logln("\t" + statusName[status] + "\t<" + buffer + ">" + lit);
237        }
238    }
239
240    static final String[] statusName = {"DONE", "SYNTAX", "LITERAL", "BROKEN_QUOTE", "BROKEN_ESCAPE", "UNKNOWN"};
241
242    @Test
243    public void TestBasic() {
244        ULocale uLocale = null;
245        DateTimePatternGenerator dtfg = null;
246        Date date = null;
247        for (int i = 0; i < dateTestData.length; ++i) {
248            if (dateTestData[i] instanceof ULocale) {
249                uLocale = (ULocale) dateTestData[i];
250                dtfg = DateTimePatternGenerator.getInstance(uLocale);
251                if (GENERATE_TEST_DATA) logln("new ULocale(\"" + uLocale.toString() + "\"),");
252            } else if (dateTestData[i] instanceof Date) {
253                date = (Date) dateTestData[i];
254                if (GENERATE_TEST_DATA) logln("new Date(" + date.getTime()+ "L),");
255            } else if (dateTestData[i] instanceof String) {
256                String testSkeleton = (String) dateTestData[i];
257                String pattern = dtfg.getBestPattern(testSkeleton);
258                SimpleDateFormat sdf = new SimpleDateFormat(pattern, uLocale);
259                String formatted = sdf.format(date);
260                if (GENERATE_TEST_DATA) logln("new String[] {\"" + testSkeleton + "\", \"" + Utility.escape(formatted) + "\"},");
261                //logln(uLocale + "\t" + testSkeleton + "\t" + pattern + "\t" + sdf.format(date));
262            } else {
263                String[] testPair = (String[]) dateTestData[i];
264                String testSkeleton = testPair[0];
265                String testFormatted = testPair[1];
266                String pattern = dtfg.getBestPattern(testSkeleton);
267                SimpleDateFormat sdf = new SimpleDateFormat(pattern, uLocale);
268                String formatted = sdf.format(date);
269                if (GENERATE_TEST_DATA) {
270                    logln("new String[] {\"" + testSkeleton + "\", \"" + Utility.escape(formatted) + "\"},");
271                } else if (!formatted.equals(testFormatted)) {
272                    errln(uLocale + "\tformatted string doesn't match test case: " + testSkeleton + "\t generated: " +  pattern + "\t expected: " + testFormatted + "\t got: " + formatted);
273                    if (true) { // debug
274                        pattern = dtfg.getBestPattern(testSkeleton);
275                        sdf = new SimpleDateFormat(pattern, uLocale);
276                        formatted = sdf.format(date);
277                    }
278                }
279                //logln(uLocale + "\t" + testSkeleton + "\t" + pattern + "\t" + sdf.format(date));
280            }
281        }
282    }
283
284    static final Object[] patternTestData = {
285        "'$f''#c",
286        "'' 'a",
287        "'.''.'",
288        "\\u0061\\\\",
289        "mm.dd 'dd ' x",
290        "'' ''",
291    };
292
293    // can be generated by using GENERATE_TEST_DATA. Must be reviewed before adding
294    static final Object[] dateTestData = {
295        new Date(916300739123L), // 1999-01-13T23:58:59.123,0-0800
296
297        new ULocale("en_US"),
298        new String[] {"yM", "1/1999"},
299        new String[] {"yMMM", "Jan 1999"},
300        new String[] {"yMd", "1/13/1999"},
301        new String[] {"yMMMd", "Jan 13, 1999"},
302        new String[] {"Md", "1/13"},
303        new String[] {"MMMd", "Jan 13"},
304        new String[] {"MMMMd", "January 13"},
305        new String[] {"yQQQ", "Q1 1999"},
306        new String[] {"hhmm", "11:58 PM"},
307        new String[] {"HHmm", "23:58"},
308        new String[] {"jjmm", "11:58 PM"},
309        new String[] {"mmss", "58:59"},
310        new String[] {"yyyyMMMM", "January 1999"}, // (new item for testing 6872<-5702)
311        new String[] {"MMMEd", "Wed, Jan 13"},
312        new String[] {"Ed", "13 Wed"},
313        new String[] {"jmmssSSS", "11:58:59.123 PM"},
314        new String[] {"JJmm", "11:58"},
315
316        new ULocale("en_US@calendar=japanese"), // (new locale for testing ticket 6872<-5702)
317        new String[] {"yM", "1/11 H"},
318        new String[] {"yMMM", "Jan 11 Heisei"},
319        new String[] {"yMd", "1/13/11 H"},
320        new String[] {"yMMMd", "Jan 13, 11 Heisei"},
321        new String[] {"Md", "1/13"},
322        new String[] {"MMMd", "Jan 13"},
323        new String[] {"MMMMd", "January 13"},
324        new String[] {"yQQQ", "Q1 11 Heisei"},
325        new String[] {"hhmm", "11:58 PM"},
326        new String[] {"HHmm", "23:58"},
327        new String[] {"jjmm", "11:58 PM"},
328        new String[] {"mmss", "58:59"},
329        new String[] {"yyyyMMMM", "January 11 Heisei"},
330        new String[] {"MMMEd", "Wed, Jan 13"},
331        new String[] {"Ed", "13 Wed"},
332        new String[] {"jmmssSSS", "11:58:59.123 PM"},
333        new String[] {"JJmm", "11:58"},
334
335        new ULocale("de_DE"),
336        new String[] {"yM", "1.1999"},
337        new String[] {"yMMM", "Jan. 1999"},
338        new String[] {"yMd", "13.1.1999"},
339        new String[] {"yMMMd", "13. Jan. 1999"},
340        new String[] {"Md", "13.1."},   // 13.1
341        new String[] {"MMMd", "13. Jan."},
342        new String[] {"MMMMd", "13. Januar"},
343        new String[] {"yQQQ", "Q1 1999"},
344        new String[] {"hhmm", "11:58 nachm."},
345        new String[] {"HHmm", "23:58"},
346        new String[] {"jjmm", "23:58"},
347        new String[] {"mmss", "58:59"},
348        new String[] {"yyyyMMMM", "Januar 1999"}, // (new item for testing 6872<-5702)
349        new String[] {"MMMEd", "Mi., 13. Jan."},
350        new String[] {"Ed", "Mi., 13."},
351        new String[] {"jmmssSSS", "23:58:59,123"},
352        new String[] {"JJmm", "23:58"},
353
354        new ULocale("fi"),
355        new String[] {"yM", "1.1999"}, // (fixed expected result per ticket 6872<-6626)
356        new String[] {"yMMM", "tammi 1999"}, // (fixed expected result per ticket 6872<-7007)
357        new String[] {"yMd", "13.1.1999"},
358        new String[] {"yMMMd", "13. tammik. 1999"},
359        new String[] {"Md", "13.1."},
360        new String[] {"MMMd", "13. tammik."},
361        new String[] {"MMMMd", "13. tammikuuta"},
362        new String[] {"yQQQ", "1. nelj. 1999"},
363        new String[] {"hhmm", "11.58 ip."},
364        new String[] {"HHmm", "23.58"},
365        new String[] {"jjmm", "23.58"},
366        new String[] {"mmss", "58.59"},
367        new String[] {"yyyyMMMM", "tammikuu 1999"}, // (new item for testing 6872<-5702,7007)
368        new String[] {"MMMEd", "ke 13. tammik."},
369        new String[] {"Ed", "ke 13."},
370        new String[] {"jmmssSSS", "23.58.59,123"},
371        new String[] {"JJmm", "23.58"},
372
373        new ULocale("es"),
374        new String[] {"yM", "1/1999"},
375        new String[] {"yMMM", "ene. 1999"},
376        new String[] {"yMd", "13/1/1999"},
377        new String[] {"yMMMd", "13 ene. 1999"},
378        new String[] {"Md", "13/1"},
379        new String[] {"MMMd", "13 ene."},
380        new String[] {"MMMMd", "13 de enero"},
381        new String[] {"yQQQ", "T1 1999"},
382        new String[] {"hhmm", "11:58 p. m."},
383        new String[] {"HHmm", "23:58"},
384        new String[] {"jjmm", "23:58"},
385        new String[] {"mmss", "58:59"},
386        new String[] {"yyyyMMMM", "enero de 1999"},
387        new String[] {"MMMEd", "mi\u00E9., 13 ene."},
388        new String[] {"Ed", "mi\u00E9. 13"},
389        new String[] {"jmmssSSS", "23:58:59,123"},
390        new String[] {"JJmm", "23:58"},
391
392        new ULocale("ja"), // (new locale for testing ticket 6872<-6626)
393        new String[] {"yM", "1999/1"},
394        new String[] {"yMMM", "1999\u5E741\u6708"},
395        new String[] {"yMd", "1999/1/13"},
396        new String[] {"yMMMd", "1999\u5E741\u670813\u65E5"},
397        new String[] {"Md", "1/13"},
398        new String[] {"MMMd", "1\u670813\u65E5"},
399        new String[] {"MMMMd", "1\u670813\u65E5"},
400        new String[] {"yQQQ", "1999/Q1"},
401        new String[] {"hhmm", "\u5348\u5F8C11:58"},
402        new String[] {"HHmm", "23:58"},
403        new String[] {"jjmm", "23:58"},
404        new String[] {"mmss", "58:59"},
405        new String[] {"yyyyMMMM", "1999\u5E741\u6708"}, // (new item for testing 6872<-5702)
406        new String[] {"MMMEd", "1\u670813\u65E5(\u6C34)"},
407        new String[] {"Ed", "13\u65E5(\u6C34)"},
408        new String[] {"jmmssSSS", "23:58:59.123"},
409        new String[] {"JJmm", "23:58"},
410
411        new ULocale("ja@calendar=japanese"), // (new locale for testing ticket 6872<-5702)
412        new String[] {"yM", "\u5E73\u621011/1"},
413        new String[] {"yMMM", "\u5E73\u621011\u5E741\u6708"},
414        new String[] {"yMd", "\u5E73\u621011/1/13"},
415        new String[] {"yMMMd", "\u5E73\u621011\u5E741\u670813\u65E5"},
416        new String[] {"Md", "1/13"},
417        new String[] {"MMMd", "1\u670813\u65E5"},
418        new String[] {"MMMMd", "1\u670813\u65E5"},
419        new String[] {"yQQQ", "\u5E73\u621011/Q1"},
420        new String[] {"hhmm", "\u5348\u5F8C11:58"},
421        new String[] {"HHmm", "23:58"},
422        new String[] {"jjmm", "23:58"},
423        new String[] {"mmss", "58:59"},
424        new String[] {"yyyyMMMM", "\u5E73\u621011\u5E741\u6708"},
425        new String[] {"MMMEd", "1\u670813\u65E5(\u6C34)"},
426        new String[] {"Ed", "13\u65E5(\u6C34)"},
427        new String[] {"jmmssSSS", "23:58:59.123"},
428        new String[] {"JJmm", "23:58"},
429
430        new ULocale("zh_Hans_CN"),
431        new String[] {"yM", "1999\u5E741\u6708"},
432        new String[] {"yMMM", "1999\u5E741\u6708"}, // (fixed expected result per ticket 6872<-6626)
433        new String[] {"yMd", "1999/1/13"},
434        new String[] {"yMMMd", "1999\u5E741\u670813\u65E5"}, // (fixed expected result per ticket 6872<-6626)
435        new String[] {"Md", "1/13"},
436        new String[] {"MMMd", "1\u670813\u65E5"}, // (fixed expected result per ticket 6872<-6626)
437        new String[] {"MMMMd", "1\u670813\u65E5"},
438        new String[] {"yQQQ", "1999\u5E74\u7B2C1\u5B63\u5EA6"},
439        new String[] {"hhmm", "\u4E0B\u534811:58"},
440        new String[] {"HHmm", "23:58"},
441        new String[] {"jjmm", "\u4E0B\u534811:58"},
442        new String[] {"mmss", "58:59"},
443        new String[] {"yyyyMMMM", "1999\u5E741\u6708"}, // (new item for testing 6872<-5702)
444        new String[] {"MMMEd", "1\u670813\u65E5\u5468\u4E09"},
445        new String[] {"Ed", "13\u65E5\u5468\u4E09"},
446        new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
447        new String[] {"JJmm", "11:58"},
448
449        new ULocale("zh_TW@calendar=roc"), // (new locale for testing ticket 6872<-5702)
450        new String[] {"yM", "\u6C11\u570B88/1"},
451        new String[] {"yMMM", "\u6C11\u570B88\u5E741\u6708"},
452        new String[] {"yMd", "\u6C11\u570B88/1/13"},
453        new String[] {"yMMMd", "\u6C11\u570B88\u5E741\u670813\u65E5"},
454        new String[] {"Md", "1/13"},
455        new String[] {"MMMd", "1\u670813\u65E5"},
456        new String[] {"MMMMd", "1\u670813\u65E5"},
457        new String[] {"yQQQ", "\u6C11\u570B88\u5E741\u5B63"},
458        new String[] {"hhmm", "\u4E0B\u534811:58"},
459        new String[] {"HHmm", "23:58"},
460        new String[] {"jjmm", "\u4E0B\u534811:58"},
461        new String[] {"mmss", "58:59"},
462        new String[] {"yyyyMMMM", "\u6C11\u570B88\u5E741\u6708"},
463        new String[] {"MMMEd", "1\u670813\u65E5\u9031\u4E09"},
464        new String[] {"Ed", "13 \u9031\u4E09"},
465        new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
466        new String[] {"JJmm", "11:58"},
467
468        new ULocale("ru"),
469        new String[] {"yM", "01.1999"},
470        new String[] {"yMMM", "\u044F\u043D\u0432. 1999 \u0433."},
471        new String[] {"yMd", "13.01.1999"},
472        new String[] {"yMMMd", "13 \u044F\u043D\u0432. 1999 \u0433."},
473        new String[] {"Md", "13.01"},
474        new String[] {"MMMd", "13 \u044F\u043D\u0432."},
475        new String[] {"MMMMd", "13 \u044F\u043D\u0432\u0430\u0440\u044F"},
476        new String[] {"yQQQ", "1-\u0439 \u043A\u0432. 1999 \u0433."},
477        new String[] {"hhmm", "11:58 \u041F\u041F"},
478        new String[] {"HHmm", "23:58"},
479        new String[] {"jjmm", "23:58"},
480        new String[] {"mmss", "58:59"},
481        new String[] {"yyyyMMMM", "\u044F\u043D\u0432\u0430\u0440\u044C 1999 \u0433."},
482        new String[] {"MMMEd", "\u0441\u0440, 13 \u044F\u043D\u0432."},
483        new String[] {"Ed", "\u0441\u0440, 13"},
484        new String[] {"jmmssSSS", "23:58:59,123"},
485        new String[] {"JJmm", "23:58"},
486
487        new ULocale("zh@calendar=chinese"),
488        new String[] {"yM", "1998\u620A\u5BC5\u5E74\u5341\u4E00\u6708"},
489        new String[] {"yMMM", "1998\u620A\u5BC5\u5E74\u5341\u4E00\u6708"},
490        new String[] {"yMd", "1998\u5E74\u5341\u4E00\u670826"},
491        new String[] {"yMMMd", "1998\u5E74\u5341\u4E00\u670826"},
492        new String[] {"Md", "11-26"},
493        new String[] {"MMMd", "\u5341\u4E00\u670826\u65E5"},
494        new String[] {"MMMMd", "\u5341\u4E00\u670826\u65E5"},
495        new String[] {"yQQQ", "1998\u620A\u5BC5\u5E74\u7B2C\u56DB\u5B63\u5EA6"},
496        new String[] {"hhmm", "\u4E0B\u534811:58"},
497        new String[] {"HHmm", "23:58"},
498        new String[] {"jjmm", "\u4E0B\u534811:58"},
499        new String[] {"mmss", "58:59"},
500        new String[] {"yyyyMMMM", "1998\u620A\u5BC5\u5E74\u5341\u4E00\u6708"},
501        new String[] {"MMMEd", "\u5341\u4E00\u670826\u65E5\u5468\u4E09"},
502        new String[] {"Ed", "26\u65E5\u5468\u4E09"},
503        new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
504        new String[] {"JJmm", "11:58"},
505    };
506
507    @Test
508    public void DayMonthTest() {
509        final ULocale locale = ULocale.FRANCE;
510
511        // set up the generator
512        DateTimePatternGenerator dtpgen
513        = DateTimePatternGenerator.getInstance(locale);
514
515        // get a pattern for an abbreviated month and day
516        final String pattern = dtpgen.getBestPattern("MMMd");
517        SimpleDateFormat formatter = new SimpleDateFormat(pattern, locale);
518
519        // use it to format (or parse)
520        String formatted = formatter.format(new Date());
521        logln("formatted=" + formatted);
522        // for French, the result is "13 sept."
523    }
524
525    @Test
526    public void TestOrdering() {
527        ULocale[] locales = ULocale.getAvailableLocales();
528        for (int i = 0; i < locales.length; ++i) {
529            for (int style1 = DateFormat.FULL; style1 <= DateFormat.SHORT; ++style1) {
530                for (int style2 = DateFormat.FULL; style2 < style1; ++style2) {
531                    checkCompatible(style1, style2, locales[i]);
532                }
533            }
534        }
535    }
536
537    @Test
538    public void TestReplacingZoneString() {
539        Date testDate = new Date();
540        TimeZone testTimeZone = TimeZone.getTimeZone("America/New_York");
541        TimeZone bogusTimeZone = new SimpleTimeZone(1234, "Etc/Unknown");
542        Calendar calendar = Calendar.getInstance();
543        ParsePosition parsePosition = new ParsePosition(0);
544
545        ULocale[] locales = ULocale.getAvailableLocales();
546        int count = 0;
547        for (int i = 0; i < locales.length; ++i) {
548            // skip the country locales unless we are doing exhaustive tests
549            if (getExhaustiveness() < 6) {
550                if (locales[i].getCountry().length() > 0) {
551                    continue;
552                }
553            }
554            count++;
555            // Skipping some test case in the non-exhaustive mode to reduce the test time
556            //ticket#6503
557            if(getExhaustiveness()<=5 && count%3!=0){
558                continue;
559            }
560            logln(locales[i].toString());
561            DateTimePatternGenerator dtpgen
562            = DateTimePatternGenerator.getInstance(locales[i]);
563
564            for (int style1 = DateFormat.FULL; style1 <= DateFormat.SHORT; ++style1) {
565                final SimpleDateFormat oldFormat = (SimpleDateFormat) DateFormat.getTimeInstance(style1, locales[i]);
566                String pattern = oldFormat.toPattern();
567                String newPattern = dtpgen.replaceFieldTypes(pattern, "VVVV"); // replaceZoneString(pattern, "VVVV");
568                if (newPattern.equals(pattern)) {
569                    continue;
570                }
571                // verify that it roundtrips parsing
572                SimpleDateFormat newFormat = new SimpleDateFormat(newPattern, locales[i]);
573                newFormat.setTimeZone(testTimeZone);
574                String formatted = newFormat.format(testDate);
575                calendar.setTimeZone(bogusTimeZone);
576                parsePosition.setIndex(0);
577                newFormat.parse(formatted, calendar, parsePosition);
578                if (parsePosition.getErrorIndex() >= 0) {
579                    errln("Failed parse with VVVV:\t" + locales[i] + ",\t\"" + pattern + "\",\t\"" + newPattern + "\",\t\"" + formatted.substring(0,parsePosition.getErrorIndex()) + "{}" + formatted.substring(parsePosition.getErrorIndex()) + "\"");
580                } else if (!calendar.getTimeZone().getID().equals(testTimeZone.getID())) {
581                    errln("Failed timezone roundtrip with VVVV:\t" + locales[i] + ",\t\"" + pattern + "\",\t\"" + newPattern + "\",\t\"" + formatted + "\",\t" + calendar.getTimeZone().getID() + " != " + testTimeZone.getID());
582                } else {
583                    logln(locales[i] + ":\t\"" + pattern + "\" => \t\"" + newPattern + "\"\t" + formatted);
584                }
585            }
586        }
587    }
588
589    @Test
590    public void TestVariableCharacters() {
591        UnicodeSet valid = new UnicodeSet("[G y Y u U r Q q M L l w W d D F g E e c a h H K k m s S A z Z O v V X x]");
592        for (char c = 0; c < 0xFF; ++c) {
593            boolean works = false;
594            try {
595                VariableField vf = new VariableField(String.valueOf(c), true);
596                logln("VariableField " + vf.toString());
597                works = true;
598            } catch (Exception e) {}
599            if (works != valid.contains(c)) {
600                if (works) {
601                    errln("VariableField can be created with illegal character: " + c);
602                } else {
603                    errln("VariableField can't be created with legal character: " + c);
604                }
605            }
606        }
607    }
608
609    static String[] DATE_STYLE_NAMES = {
610        "FULL", "LONG", "MEDIUM", "SHORT"
611    };
612
613    /**
614     * @param fullOrder
615     * @param longOrder
616     */
617    private void checkCompatible(int style1, int style2, ULocale uLocale) {
618        DateOrder order1 = getOrdering(style1, uLocale);
619        DateOrder order2 = getOrdering(style2, uLocale);
620        if (!order1.hasSameOrderAs(order2)) {
621            // Note: This test case was updated by #6806 and no longer reports
622            // ordering difference as an error case.
623            logln(showOrderComparison(uLocale, style1, style2, order1, order2));
624        }
625    }
626
627    private String showOrderComparison(ULocale uLocale, int style1, int style2, DateOrder order1, DateOrder order2) {
628        String pattern1 = ((SimpleDateFormat) DateFormat.getDateInstance(style1, uLocale)).toPattern();
629        String pattern2 = ((SimpleDateFormat) DateFormat.getDateInstance(style2, uLocale)).toPattern();
630        return "Mismatch in in ordering for " + uLocale + ": " + DATE_STYLE_NAMES[style1] + ": " + order1 + ", <" + pattern1
631                + ">; "
632                + DATE_STYLE_NAMES[style2] + ": " + order2 + ", <" + pattern2 + ">; " ;
633    }
634
635    /**
636     * Main date fields -- Poor-man's enum -- change to real enum when we get JDK 1.5
637     */
638    public static class DateFieldType {
639        private String name;
640        private DateFieldType(String string) {
641            name = string;
642        }
643
644        public static DateFieldType
645        YEAR = new DateFieldType("YEAR"),
646        MONTH = new DateFieldType("MONTH"),
647        DAY = new DateFieldType("DAY");
648
649        @Override
650        public String toString() {
651            return name;
652        }
653    }
654
655    /**
656     * Simple struct for output from getOrdering
657     */
658    static class DateOrder {
659        int monthLength;
660        DateFieldType[] fields = new DateFieldType[3];
661
662        public boolean isCompatible(DateOrder other) {
663            return monthLength == other.monthLength;
664        }
665        /**
666         * @param order2
667         * @return
668         */
669        public boolean hasSameOrderAs(DateOrder other) {
670            // TODO Auto-generated method stub
671            return fields[0] == other.fields[0] && fields[1] == other.fields[1] && fields[2] == other.fields[2];
672        }
673        @Override
674        public String toString() {
675            return "{" + monthLength + ", " + fields[0]  + ", " + fields[1]  + ", " + fields[2] + "}";
676        }
677        @Override
678        public boolean equals(Object that) {
679            DateOrder other = (DateOrder) that;
680            return monthLength == other.monthLength && fields[0] == other.fields[0] && fields[1] == other.fields[1] && fields[2] == other.fields[2];
681        }
682    }
683
684    DateTimePatternGenerator.FormatParser formatParser = new DateTimePatternGenerator.FormatParser ();
685    DateTimePatternGenerator generator = DateTimePatternGenerator.getEmptyInstance();
686
687    private Calendar sampleCalendar;
688    {
689        sampleCalendar = new GregorianCalendar(TimeZone.getTimeZone("America/Los_Angeles"));
690        sampleCalendar.set(1999, Calendar.OCTOBER, 13, 23, 58, 59);
691    }
692
693    private Date sampleDate = sampleCalendar.getTime();
694    private TimeZone gmt = TimeZone.getTimeZone("Etc/GMT");
695
696    /**
697     * Replace the zone string with a different type, eg v's for z's, etc. <p>Called with a pattern, such as one gotten from
698     * <pre>
699     * String pattern = ((SimpleDateFormat) DateFormat.getTimeInstance(style, locale)).toPattern();
700     * </pre>
701     * @param pattern original pattern to change, such as "HH:mm zzzz"
702     * @param newZone Must be: z, zzzz, Z, ZZZZ, v, vvvv, V, or VVVV
703     * @return
704     */
705    public String replaceZoneString(String pattern, String newZone) {
706        final List itemList = formatParser.set(pattern).getItems();
707        boolean changed = false;
708        for (int i = 0; i < itemList.size(); ++i) {
709            Object item = itemList.get(i);
710            if (item instanceof VariableField) {
711                VariableField variableField = (VariableField) item;
712                if (variableField.getType() == DateTimePatternGenerator.ZONE) {
713                    if (!variableField.toString().equals(newZone)) {
714                        changed = true;
715                        itemList.set(i, new VariableField(newZone, true));
716                    }
717                }
718            }
719        }
720        return changed ? formatParser.toString() : pattern;
721    }
722
723    public boolean containsZone(String pattern) {
724        for (Iterator it = formatParser.set(pattern).getItems().iterator(); it.hasNext();) {
725            Object item = it.next();
726            if (item instanceof VariableField) {
727                VariableField variableField = (VariableField) item;
728                if (variableField.getType() == DateTimePatternGenerator.ZONE) {
729                    return true;
730                }
731            }
732        }
733        return false;
734    }
735
736    /**
737     * Get the ordering from a particular date format. Best is to use
738     * DateFormat.FULL to get the format with String form month (like "January")
739     * and DateFormat.SHORT for the numeric format order. They may be different.
740     * (Theoretically all 4 formats could be different but that never happens in
741     * practice.)
742     *
743     * @param style
744     *          DateFormat.FULL..DateFormat.SHORT
745     * @param locale
746     *          desired locale.
747     * @return
748     * @return list of ordered items DateFieldType (I
749     *         didn't know what form you really wanted so this is just a
750     *         stand-in.)
751     */
752    private DateOrder getOrdering(int style, ULocale locale) {
753        // and the date pattern
754        String pattern = ((SimpleDateFormat) DateFormat.getDateInstance(style, locale)).toPattern();
755        int count = 0;
756        DateOrder result = new DateOrder();
757
758        for (Iterator it = formatParser.set(pattern).getItems().iterator(); it.hasNext();) {
759            Object item = it.next();
760            if (!(item instanceof String)) {
761                // the first character of the variable field determines the type,
762                // according to CLDR.
763                String variableField = item.toString();
764                switch (variableField.charAt(0)) {
765                case 'y': case 'Y': case 'u':
766                    result.fields[count++] = DateFieldType.YEAR;
767                    break;
768                case 'M': case 'L':
769                    result.monthLength = variableField.length();
770                    if (result.monthLength < 2) {
771                        result.monthLength = 2;
772                    }
773                    result.fields[count++] = DateFieldType.MONTH;
774                    break;
775                case 'd': case 'D': case 'F': case 'g':
776                    result.fields[count++] = DateFieldType.DAY;
777                    break;
778                }
779            }
780        }
781        return result;
782    }
783
784    /* Tests the method
785     *        public static DateTimePatternGenerator getInstance()
786     */
787    @Test
788    public void TestGetInstance(){
789        try{
790            DateTimePatternGenerator.getInstance();
791        } catch(Exception e){
792            errln("DateTimePatternGenerator.getInstance() was not suppose to " +
793                    "return an exception.");
794        }
795    }
796
797    /* Tests the method
798     *        public String getSkeleton(String pattern)
799     */
800    @Test
801    public void TestGetSkeleton(){
802        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
803        String[] cases = {"MMDD","MMMDD","MMM-DD","DD/MMM","ddM","MMMMd"};
804        String[] results = {"MMDD","MMMDD","MMMDD","MMMDD","Mdd","MMMMd"};
805        for(int i=0; i<cases.length; i++){
806            if(!dtpg.getSkeleton(cases[i]).equals(results[i])){
807                errln("DateTimePatternGenerator.getSkeleton(String) did " +
808                        "return the expected result when passing " + cases[i] +
809                        " and expected " + results[i] + " but got " +
810                        dtpg.getSkeleton(cases[i]));
811            }
812        }
813    }
814
815    /* Tests the method
816     *        public String getCanonicalSkeletonAllowingDuplicates(String pattern)
817     */
818    public void TestGetCanonicalSkeletonAllowingDuplicates(){
819        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
820        String[] cases = {"GyQMwEdaHmsSv","LegH","Legh"};
821        String[] results = {"GyQMwEdHmsSv","MEdH","MEdh"};
822        for(int i=0; i<cases.length; i++){
823            if(!dtpg.getCanonicalSkeletonAllowingDuplicates(cases[i]).equals(results[i])){
824                errln("DateTimePatternGenerator.getCanonicalSkeletonAllowingDuplicates(String) did " +
825                        "return the expected result when passing " + cases[i] +
826                        " and expected " + results[i] + " but got " +
827                        dtpg.getCanonicalSkeletonAllowingDuplicates(cases[i]));
828            }
829        }
830    }
831
832    /* Tests the method
833     *        public String getBaseSkeleton(String pattern)
834     */
835    @Test
836    public void TestGetBaseSkeleton(){
837        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
838        String[] cases = {"MMDD","MMMDD","MMM-DD","DD/MMM","ddM","MMMMd"};
839        String[] results = {"MD","MMMD","MMMD","MMMD","Md","MMMMd"};
840        for(int i=0; i<cases.length; i++){
841            if(!dtpg.getBaseSkeleton(cases[i]).equals(results[i])){
842                errln("DateTimePatternGenerator.getSkeleton(String) did " +
843                        "return the expected result when passing " + cases[i] +
844                        " and expected " + results[i] + " but got " +
845                        dtpg.getBaseSkeleton(cases[i]));
846            }
847        }
848    }
849
850    /* Tests the method
851     *        public Map<String, String> getSkeletons(Map<String, String> result)
852     */
853    @Test
854    public void TestGetSkeletons(){
855        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
856        // Tests when "if (result == null)" is true
857        try{
858            dtpg.getSkeletons(null);
859        } catch(Exception e){
860            errln("DateTimePatternGenerator.getSkeletons(Map) was suppose to " +
861                    "return a new LinkedHashMap for a null parameter.");
862        }
863
864        // Tests when "if (result == null)" is false
865        Map<String,String> mm = new LinkedHashMap<String, String>();
866        try{
867            dtpg.getSkeletons(mm);
868        } catch(Exception e){
869            errln("DateTimePatternGenerator.getSkeletons(Map) was suppose to " +
870                    "return a new LinkedHashMap for a LinkedHashMap parameter.");
871        }
872    }
873
874    /* Tests the method
875     *        public Set<String> getBaseSkeletons(Set<String> result)
876     */
877    @Test
878    public void TestGetBaseSkeletons(){
879        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
880        // Tests when "if (result == null)" is true
881        try{
882            dtpg.getBaseSkeletons(null);
883        } catch(Exception e){
884            errln("DateTimePatternGenerator.getBaseSkeletons(Map) was suppose to " +
885                    "return a new LinkedHashMap for a null parameter.");
886        }
887
888        // Tests when "if (result == null)" is false
889        Set<String> mm = new HashSet<String>();
890        try{
891            dtpg.getBaseSkeletons(mm);
892        } catch(Exception e){
893            errln("DateTimePatternGenerator.getBaseSkeletons(Map) was suppose to " +
894                    "return a new LinkedHashMap for a HashSet parameter.");
895        }
896    }
897
898    /* Tests the method
899     *        public String getDecimal()
900     */
901    @Test
902    public void TestGetDecimal(){
903        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
904        if(!dtpg.getDecimal().equals(".")){
905            errln("DateTimePatternGenerator.getDecimal() was to return '.' " +
906                    "when the object gets a new instance.");
907        }
908
909        String[] cases = {",","-","","*","&","a","0"};
910        for(int i=0; i<cases.length; i++){
911            dtpg.setDecimal(cases[i]);
912            if(!dtpg.getDecimal().equals(cases[i])){
913                errln("DateTimePatternGenerator.getDecimal() was to return " + cases[i] +
914                        "when setting decimal with " + cases[i]);
915            }
916        }
917    }
918
919    /* Tests the method
920     *        public Collection<String> getRedundants(Collection<String> output)
921     */
922    @Test
923    public void TestGetRedundants(){
924        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
925
926        // Tests when "if (output == null)" is true
927        try{
928            dtpg.getRedundants(null);
929        } catch(Exception e){
930            errln("DateTimeGenerator.getRedundants was not supposed to return " +
931                    "an exception when passing a null parameter: " + e);
932        }
933
934        // Tests when "if (output == null)" is false
935        try{
936            Collection<String> out = new LinkedHashSet<String>();
937            dtpg.getRedundants(out);
938        } catch(Exception e){
939            errln("DateTimeGenerator.getRedundants was not supposed to return " +
940                    "an exception when passing a new LinkedHashSet<String>() parameter: " + e);
941        }
942    }
943
944    /* Tests the method
945     *        public String setAppendItemFormat(int field)
946     */
947    @Test
948    public void TestSetAppendItemFormat(){
949        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
950        String[] cases = {"d","u","m","m","y"};
951        for(int i=0; i<cases.length; i++){
952            dtpg.setAppendItemFormat(i, cases[i]);
953            if(!dtpg.getAppendItemFormat(i).equals(cases[i])){
954                errln("DateTimePatternGenerator.getAppendItemFormat(int field) " +
955                        "did not return as expected. Value set at " + i + " was " +
956                        cases[i] + " but got back " + dtpg.getAppendItemFormat(i));
957            }
958        }
959    }
960
961    /* Tests the method
962     *        public String getAppendItemFormat(int field)
963     */
964    @Test
965    public void TestGetAppendItemFormat(){
966        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(ULocale.ENGLISH);
967        int[] fields = {DateTimePatternGenerator.ERA,DateTimePatternGenerator.DAY,DateTimePatternGenerator.SECOND};
968        String[] results = {"{0} {1}","{0} ({2}: {1})","{0} ({2}: {1})"};
969        for(int i=0; i<fields.length; i++){
970            if(!dtpg.getAppendItemFormat(fields[i]).equals(results[i])){
971                errln("DateTimePatternGenerator.getAppendItemFormat(int field) " +
972                        "did not return as expected. For field " + fields[i] + ", was expecting " +
973                        results[i] + " but got back " + dtpg.getAppendItemFormat(fields[i]));
974            }
975        }
976    }
977
978    /* Tests the method
979     *    public String getAppendItemName(int field)
980     */
981    private final class AppendItemName {
982        public int field;
983        public String name;
984        public AppendItemName(int f, String n) {
985            field = f;
986            name = n;
987        }
988    }
989
990    @Test
991    public void TestGetAppendItemName(){
992        final AppendItemName[] appendItemNames = {
993                new AppendItemName( DateTimePatternGenerator.YEAR,    "vuosi" ),
994                new AppendItemName( DateTimePatternGenerator.MONTH,   "kuukausi" ),
995                new AppendItemName( DateTimePatternGenerator.WEEKDAY, "viikonp\u00E4iv\u00E4" ),
996                new AppendItemName( DateTimePatternGenerator.DAY,     "p\u00E4iv\u00E4" ),
997                new AppendItemName( DateTimePatternGenerator.HOUR,    "tunti" ),
998        };
999
1000        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
1001        String[] cases = {"d","u","m","m","y"};
1002        for(int i=0; i<cases.length; i++){
1003            dtpg.setAppendItemName(i, cases[i]);
1004            if(!dtpg.getAppendItemName(i).equals(cases[i])){
1005                errln("DateTimePatternGenerator.getAppendItemFormat(int field) " +
1006                        "did not return as expected. Value set at " + i + " was " +
1007                        cases[i] + " but got back " + dtpg.getAppendItemName(i));
1008            }
1009        }
1010
1011        DateTimePatternGenerator dtpgfi = DateTimePatternGenerator.getInstance(ULocale.forLanguageTag("fi"));
1012        for (AppendItemName appendItemName: appendItemNames) {
1013            String name = dtpgfi.getAppendItemName(appendItemName.field);
1014            if (!name.equals(appendItemName.name)) {
1015                errln("DateTimePatternGenerator.getAppendItemName returns invalid name for field " + appendItemName.field
1016                        + ": got " + name + " but expected " + appendItemName.name);
1017            }
1018        }
1019    }
1020
1021    /* Tests the method
1022     *    public static boolean isSingleField(String skeleton)
1023     */
1024    @SuppressWarnings("static-access")
1025    @Test
1026    public void TestIsSingleField(){
1027        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
1028        String[] cases = {" ", "m","mm","md","mmd","mmdd"};
1029        boolean[] results = {true,true,true,false,false,false};
1030        for(int i=0; i<cases.length; i++){
1031            if(dtpg.isSingleField(cases[i]) != results[i]){
1032                errln("DateTimePatternGenerator.isSingleField(String skeleton) " +
1033                        "did not return as expected. Value passed was " + cases[i] +
1034                        " but got back " + dtpg.isSingleField(cases[i]));
1035            }
1036        }
1037    }
1038
1039    /* Tests the method
1040     *    public Object freeze()
1041     *    public Object cloneAsThawed()
1042     */
1043    @Test
1044    public void TestFreezeAndCloneAsThawed(){
1045        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
1046
1047        if(dtpg.isFrozen() != false){
1048            errln("DateTimePatternGenerator.isFrozen() is suppose to return false " +
1049                    "for a DateTimePatternGenerator object that was just " +
1050                    "created.");
1051        }
1052
1053        dtpg.freeze();
1054        if(dtpg.isFrozen() != true){
1055            errln("DateTimePatternGenerator.isFrozen() is suppose to return true " +
1056                    "for a DateTimePatternGenerator object that was just " +
1057                    "created and freeze.");
1058        }
1059
1060        DateTimePatternGenerator dtpg2 = dtpg.cloneAsThawed();
1061        if(dtpg.isFrozen() != false){
1062            errln("DateTimePatternGenerator.isFrozen() is suppose to return false " +
1063                    "for a DateTimePatternGenerator object that was just " +
1064                    "clone as thawed.");
1065        }
1066        if(dtpg2.isFrozen() != false){
1067            errln("DateTimePatternGenerator.isFrozen() is suppose to return false " +
1068                    "for a second DateTimePatternGenerator object that was just " +
1069                    "clone as thawed.");
1070        }
1071    }
1072
1073    /* Tests the method
1074     *    public Object clone()
1075     */
1076    @Test
1077    public void TestClone(){
1078        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
1079        DateTimePatternGenerator dtpg2 = (DateTimePatternGenerator) dtpg.clone();
1080        dtpg = (DateTimePatternGenerator) dtpg2.clone();
1081    }
1082
1083    /* Tests the constructor
1084     *    public VariableField(String string)
1085     */
1086    //TODO(junit) why is this "unused"
1087    @SuppressWarnings("unused")
1088    @Test
1089    public void TestVariableField_String(){
1090        String[] cases = {"d","mm","aa"};
1091        String[] invalid = {null,"","dummy"};
1092        for(int i=0; i<cases.length; i++){
1093            try{
1094                VariableField vf = new VariableField(cases[i]);
1095            } catch(Exception e){
1096                errln("VariableField constructor was not suppose to return " +
1097                        "an exception when created when passing " + cases[i]);
1098            }
1099        }
1100        for(int i=0; i<invalid.length; i++){
1101            try{
1102                VariableField vf = new VariableField(invalid[i]);
1103                errln("VariableField constructor was suppose to return " +
1104                        "an exception when created when passing " + invalid[i]);
1105            } catch(Exception e){}
1106        }
1107    }
1108
1109    /* Tests the method
1110     *    public FormatParser set(String string, boolean strict)
1111     */
1112    @Test
1113    public void TestSet(){
1114        FormatParser fp = new FormatParser();
1115        //Tests when "if (string.length() == 0)" is true
1116        try{
1117            fp.set("",true);
1118        }catch(Exception e){
1119            errln("FormatParser.set(String,boolean) was not suppose to " +
1120                    "return an exception.");
1121        }
1122    }
1123
1124    /* Tests the method
1125     *    public String toString()
1126     */
1127    @Test
1128    public void TestToString(){
1129        FormatParser fp = new FormatParser();
1130        if(!fp.toString().equals("")){
1131            errln("FormatParser.toString() was suppose to return an " +
1132                    "empty string for a new FormatParser object.");
1133        }
1134
1135        String[] cases = {"m","d","y","mm","mmm","mm dd","mm':'dd","mm-dd-yyyy"};
1136        String[] results = {"m","d","y","mm","mmm","mm dd","mm:dd","mm-dd-yyyy"};
1137        for(int i=0; i<cases.length; i++){
1138            fp.set(cases[i]);
1139            if(!fp.toString().equals(results[i])){
1140                errln("FormatParser.toString() was suppose to return " + results[i] +
1141                        " after setting the object. Got: " + fp.toString());
1142            }
1143        }
1144    }
1145
1146    /* Tests the method
1147     *    public boolean hasDateAndTimeFields()
1148     */
1149    @Test
1150    public void TestHasDateAndTimeFields(){
1151        FormatParser fp = new FormatParser();
1152        if(fp.hasDateAndTimeFields() != false){
1153            errln("FormatParser.hasDateAndTimeFields() was suppose to return " +
1154                    "false when a new object is created.");
1155        }
1156
1157        String[] cases = {"MMDDYY", "HHMMSS", "", "MM/DD/YYYY HH:MM:SS",
1158                "MMDDYY HHMMSS", "HHMMSS MMDDYYYY", "HMS MDY"};
1159        boolean[] results = {false,true,false,true,true,true,true};
1160        for(int i=0; i<cases.length; i++){
1161            fp.set(cases[i]);
1162            if(fp.hasDateAndTimeFields() != results[i]){
1163                errln("FormatParser.hasDateAndTimeFields() was suppose to " +
1164                        "return " + results[i] + " but returned " +
1165                        fp.hasDateAndTimeFields() + " for parameter " +
1166                        cases[i] + " that is set to FormatParser.");
1167            }
1168        }
1169    }
1170
1171    /* Tests the method
1172     *    private void checkFrozen()
1173     * from public void setDateTimeFormat(String dateTimeFormat)
1174     */
1175    @Test
1176    public void TestCheckFrozen(){
1177        // Tests when "if (isFrozen())" is true
1178        DateTimePatternGenerator dt = DateTimePatternGenerator.getInstance();
1179        try{
1180            dt.freeze();
1181            dt.setDateTimeFormat("MMDDYYYY");
1182            errln("DateTimePatternGenerator.checkFrozen() was suppose to " +
1183                    "return an exception when trying to setDateTimeFormat " +
1184                    "for a frozen object.");
1185        } catch(Exception e){}
1186        dt = dt.cloneAsThawed();
1187    }
1188
1189    /* Tests the method
1190     *    public String getFields(String pattern)
1191     */
1192    @Test
1193    public void TestGetFields(){
1194        DateTimePatternGenerator dt = DateTimePatternGenerator.getInstance();
1195        String[] cases = {"MMDDYY", "HHMMSS", "", "MM/DD/YYYY HH:MM:SS",
1196                "MMDDYY HHMMSS", "HHMMSS MMDDYYYY", "HMS MDY"};
1197        String[] results = {"{Month:N}{Day_Of_Year:N}{Year:N}",
1198                "{Hour:N}{Month:N}{Fractional_Second:N}","",
1199                "{Month:N}/{Day_Of_Year:N}/{Year:N} {Hour:N}:{Month:N}:{Fractional_Second:N}",
1200                "{Month:N}{Day_Of_Year:N}{Year:N} {Hour:N}{Month:N}{Fractional_Second:N}",
1201                "{Hour:N}{Month:N}{Fractional_Second:N} {Month:N}{Day_Of_Year:N}{Year:N}",
1202        "{Hour:N}{Month:N}{Fractional_Second:N} {Month:N}{Day_Of_Year:N}{Year:N}"};
1203        for(int i=0; i<cases.length; i++){
1204            if(!dt.getFields(cases[i]).equals(results[i])) {
1205                errln("DateTimePatternGenerator.getFields(String) did not " +
1206                        "not return an expected result when passing " + cases[i] +
1207                        ". Got " + dt.getFields(cases[i]) + " but expected " +
1208                        results[i]);
1209            }
1210        }
1211    }
1212
1213    /*
1214     * Test case for DateFormatPatternGenerator threading problem #7169
1215     */
1216    @Test
1217    public void TestT7169() {
1218        Thread[] workers = new Thread[10];
1219        for (int i = 0 ; i < workers.length; i++) {
1220            workers[i] = new Thread(new Runnable() {
1221                @Override
1222                public void run() {
1223                    try {
1224                        for (int i = 0; i < 50; i++) {
1225                            DateTimePatternGenerator patternGenerator =
1226                                    DateTimePatternGenerator.getFrozenInstance(ULocale.US);
1227                            patternGenerator.getBestPattern("MMMMd");
1228                        }
1229                    } catch (Exception e) {
1230                        errln("FAIL: Caught an exception (frozen)" + e);
1231                    }
1232                    try {
1233                        for (int i = 0; i < 50; i++) {
1234                            DateTimePatternGenerator patternGenerator =
1235                                    DateTimePatternGenerator.getInstance(ULocale.US);
1236                            patternGenerator.getBestPattern("MMMMd");
1237                        }
1238                    } catch (Exception e) {
1239                        errln("FAIL: Caught an exception " + e);
1240                    }
1241                }
1242            });
1243        }
1244        for (Thread wk : workers) {
1245            wk.start();
1246        }
1247        for (Thread wk : workers) {
1248            try {
1249                wk.join();
1250            } catch (InterruptedException ie) {
1251
1252            }
1253        }
1254    }
1255
1256    /**
1257     * Test handling of options
1258     *
1259     * For reference, as of ICU 4.3.3,
1260     *  root/gregorian has
1261     *      Hm{"H:mm"}
1262     *      Hms{"H:mm:ss"}
1263     *      hm{"h:mm a"}
1264     *      hms{"h:mm:ss a"}
1265     *  en/gregorian has
1266     *      Hm{"H:mm"}
1267     *      Hms{"H:mm:ss"}
1268     *      hm{"h:mm a"}
1269     *  be/gregorian has
1270     *      HHmmss{"HH.mm.ss"}
1271     *      Hm{"HH.mm"}
1272     *      hm{"h.mm a"}
1273     *      hms{"h.mm.ss a"}
1274     */
1275    private final class TestOptionsItem {
1276        public String locale;
1277        public String skeleton;
1278        public String expectedPattern;
1279        public int options;
1280        // Simple constructor
1281        public TestOptionsItem(String loc, String skel, String expectedPat, int opts) {
1282            locale = loc;
1283            skeleton = skel;
1284            expectedPattern = expectedPat;
1285            options = opts;
1286        }
1287    }
1288    @Test
1289    public void TestOptions() {
1290        final TestOptionsItem[] testOptionsData = {
1291                new TestOptionsItem( "en", "Hmm",  "HH:mm",   DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
1292                new TestOptionsItem( "en", "HHmm", "HH:mm",   DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
1293                new TestOptionsItem( "en", "hhmm", "h:mm a",  DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
1294                new TestOptionsItem( "en", "Hmm",  "HH:mm",   DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
1295                new TestOptionsItem( "en", "HHmm", "HH:mm",   DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
1296                new TestOptionsItem( "en", "hhmm", "hh:mm a", DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
1297                new TestOptionsItem( "da", "Hmm",  "HH.mm",   DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
1298                new TestOptionsItem( "da", "HHmm", "HH.mm",   DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
1299                new TestOptionsItem( "da", "hhmm", "h.mm a",  DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
1300                new TestOptionsItem( "da", "Hmm",  "H.mm",    DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
1301                new TestOptionsItem( "da", "HHmm", "HH.mm",   DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
1302                new TestOptionsItem( "da", "hhmm", "hh.mm a", DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
1303                //
1304                new TestOptionsItem( "en",                   "yyyy",  "yyyy",  DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1305                new TestOptionsItem( "en",                   "YYYY",  "YYYY",  DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1306                new TestOptionsItem( "en",                   "U",     "y",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1307                new TestOptionsItem( "en@calendar=japanese", "yyyy",  "y G",   DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1308                new TestOptionsItem( "en@calendar=japanese", "YYYY",  "Y G",   DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1309                new TestOptionsItem( "en@calendar=japanese", "U",     "y G",   DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1310                new TestOptionsItem( "en@calendar=chinese",  "yyyy",  "r(U)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1311                new TestOptionsItem( "en@calendar=chinese",  "YYYY",  "Y(Y)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ), // not a good result, want r(Y) or r(U)
1312                new TestOptionsItem( "en@calendar=chinese",  "U",     "r(U)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1313                new TestOptionsItem( "en@calendar=chinese",  "Gy",    "r(U)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1314                new TestOptionsItem( "en@calendar=chinese",  "GU",    "r(U)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1315                new TestOptionsItem( "en@calendar=chinese",  "ULLL",  "MMM U",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1316                new TestOptionsItem( "en@calendar=chinese",  "yMMM",  "MMM r(U)", DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1317                new TestOptionsItem( "en@calendar=chinese",  "GUMMM", "MMM r(U)", DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1318                new TestOptionsItem( "zh@calendar=chinese",  "yyyy",  "rU\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1319                new TestOptionsItem( "zh@calendar=chinese",  "YYYY",  "YY\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ), // not a good result, want r(Y) or r(U)
1320                new TestOptionsItem( "zh@calendar=chinese",  "U",     "rU\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1321                new TestOptionsItem( "zh@calendar=chinese",  "Gy",    "rU\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1322                new TestOptionsItem( "zh@calendar=chinese",  "GU",    "rU\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1323                new TestOptionsItem( "zh@calendar=chinese",  "ULLL",  "U\u5E74MMM",  DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1324                new TestOptionsItem( "zh@calendar=chinese",  "yMMM",  "rU\u5E74MMM", DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1325                new TestOptionsItem( "zh@calendar=chinese",  "GUMMM", "rU\u5E74MMM", DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1326        };
1327
1328        for (int i = 0; i < testOptionsData.length; ++i) {
1329            ULocale uloc = new ULocale(testOptionsData[i].locale);
1330            DateTimePatternGenerator dtpgen = DateTimePatternGenerator.getInstance(uloc);
1331            String pattern = dtpgen.getBestPattern(testOptionsData[i].skeleton, testOptionsData[i].options);
1332            if (pattern.compareTo(testOptionsData[i].expectedPattern) != 0) {
1333                errln("Locale " + testOptionsData[i].locale + ", skeleton " + testOptionsData[i].skeleton +
1334                        ", options " + ((testOptionsData[i].options != 0)? "!=0": "==0") +
1335                        ", expected pattern " + testOptionsData[i].expectedPattern + ", got " + pattern);
1336            }
1337        }
1338    }
1339
1340    /**
1341     * Test that DTPG can handle all valid pattern character / length combinations
1342     */
1343    private final class AllFieldsTestItem {
1344        public char patternChar;
1345        public int[] fieldLengths;
1346        public String mustIncludeOneOf;
1347        // Simple constructor
1348        public AllFieldsTestItem(char pC, int[] fL, String mI) {
1349            patternChar = pC;
1350            fieldLengths = fL;
1351            mustIncludeOneOf = mI;
1352        }
1353    }
1354
1355    @Test
1356    public void TestAllFieldPatterns() {
1357        String[] localeNames = {
1358                "root",
1359                "root@calendar=japanese",
1360                "root@calendar=chinese",
1361                "en",
1362                "en@calendar=japanese",
1363                "en@calendar=chinese",
1364        };
1365        final AllFieldsTestItem[] testItems = {
1366                //                     pat   fieldLengths             generated pattern must
1367                //                     chr   to test                  include one of these
1368                new AllFieldsTestItem( 'G',  new int[]{1,2,3,4,5},    "G"    ), // era
1369                // year
1370                new AllFieldsTestItem( 'y',  new int[]{1,2,3,4},      "yU"   ), // year
1371                new AllFieldsTestItem( 'Y',  new int[]{1,2,3,4},      "Y"    ), // year for week of year
1372                new AllFieldsTestItem( 'u',  new int[]{1,2,3,4,5},    "yuU"  ), // extended year
1373                new AllFieldsTestItem( 'U',  new int[]{1,2,3,4,5},    "yU"   ), // cyclic year name
1374                // quarter
1375                new AllFieldsTestItem( 'Q',  new int[]{1,2,3,4},      "Qq"   ), // x
1376                new AllFieldsTestItem( 'q',  new int[]{1,2,3,4},      "Qq"   ), // standalone
1377                // month
1378                new AllFieldsTestItem( 'M',  new int[]{1,2,3,4,5},    "ML"   ), // x
1379                new AllFieldsTestItem( 'L',  new int[]{1,2,3,4,5},    "ML"   ), // standalone
1380                // week
1381                new AllFieldsTestItem( 'w',  new int[]{1,2},          "w"    ), // week of year
1382                new AllFieldsTestItem( 'W',  new int[]{1},            "W"    ), // week of month
1383                // day
1384                new AllFieldsTestItem( 'd',  new int[]{1,2},          "d"    ), // day of month
1385                new AllFieldsTestItem( 'D',  new int[]{1,2,3},        "D"    ), // day of year
1386                new AllFieldsTestItem( 'F',  new int[]{1},            "F"    ), // day of week in month
1387                new AllFieldsTestItem( 'g',  new int[]{7},            "g"    ), // modified julian day
1388                // weekday
1389                new AllFieldsTestItem( 'E',  new int[]{1,2,3,4,5,6},  "Eec"  ), // day of week
1390                new AllFieldsTestItem( 'e',  new int[]{1,2,3,4,5,6},  "Eec"  ), // local day of week
1391                new AllFieldsTestItem( 'c',  new int[]{1,2,3,4,5,6},  "Eec"  ), // standalone local day of week
1392                // day period
1393                //  new AllFieldsTestItem( 'a',  new int[]{1},            "a"    ), // am or pm   // not clear this one is supposed to work (it doesn't)
1394                // hour
1395                new AllFieldsTestItem( 'h',  new int[]{1,2},          "hK"   ), // 12 (1-12)
1396                new AllFieldsTestItem( 'H',  new int[]{1,2},          "Hk"   ), // 24 (0-23)
1397                new AllFieldsTestItem( 'K',  new int[]{1,2},          "hK"   ), // 12 (0-11)
1398                new AllFieldsTestItem( 'k',  new int[]{1,2},          "Hk"   ), // 24 (1-24)
1399                new AllFieldsTestItem( 'j',  new int[]{1,2},          "hHKk" ), // locale default
1400                // minute
1401                new AllFieldsTestItem( 'm',  new int[]{1,2},          "m"    ), // x
1402                // second & fractions
1403                new AllFieldsTestItem( 's',  new int[]{1,2},          "s"    ), // x
1404                new AllFieldsTestItem( 'S',  new int[]{1,2,3,4},      "S"    ), // fractional second
1405                new AllFieldsTestItem( 'A',  new int[]{8},            "A"    ), // milliseconds in day
1406                // zone
1407                new AllFieldsTestItem( 'z',  new int[]{1,2,3,4},      "z"    ), // x
1408                new AllFieldsTestItem( 'Z',  new int[]{1,2,3,4,5},    "Z"    ), // x
1409                new AllFieldsTestItem( 'O',  new int[]{1,4},          "O"    ), // x
1410                new AllFieldsTestItem( 'v',  new int[]{1,4},          "v"    ), // x
1411                new AllFieldsTestItem( 'V',  new int[]{1,2,3,4},      "V"    ), // x
1412                new AllFieldsTestItem( 'X',  new int[]{1,2,3,4,5},    "X"    ), // x
1413                new AllFieldsTestItem( 'x',  new int[]{1,2,3,4,5},    "x"    ), // x
1414        };
1415        final int FIELD_LENGTH_MAX = 8;
1416
1417        for (String localeName: localeNames) {
1418            ULocale uloc = new ULocale(localeName);
1419            DateTimePatternGenerator dtpgen = DateTimePatternGenerator.getInstance(uloc);
1420            for (AllFieldsTestItem testItem: testItems) {
1421                char[] skelBuf = new char[FIELD_LENGTH_MAX];
1422                for (int chrIndx = 0; chrIndx < FIELD_LENGTH_MAX; chrIndx++) {
1423                    skelBuf[chrIndx] = testItem.patternChar;
1424                }
1425                for (int lenIndx = 0; lenIndx < testItem.fieldLengths.length; lenIndx++) {
1426                    int skelLen = testItem.fieldLengths[lenIndx];
1427                    if (skelLen > FIELD_LENGTH_MAX) {
1428                        continue;
1429                    };
1430                    String skeleton = new String(skelBuf, 0, skelLen);
1431                    String pattern = dtpgen.getBestPattern(skeleton);
1432                    if (pattern.length() <= 0) {
1433                        errln("DateTimePatternGenerator getBestPattern for locale " + localeName +
1434                                ", skeleton " + skeleton + ", produces 0-length pattern");
1435                    } else {
1436                        // test that resulting pattern has at least one char in mustIncludeOneOf
1437                        boolean inQuoted = false;
1438                        int patIndx, patLen = pattern.length();
1439                        for (patIndx = 0; patIndx < patLen; patIndx++) {
1440                            char c = pattern.charAt(patIndx);
1441                            if (c == '\'') {
1442                                inQuoted = !inQuoted;
1443                            } else if (!inQuoted && c <= 'z' && c >= 'A') {
1444                                if (testItem.mustIncludeOneOf.indexOf(c) >= 0) {
1445                                    break;
1446                                }
1447                            }
1448                        }
1449                        if (patIndx >= patLen) {
1450                            errln("DateTimePatternGenerator getBestPattern for locale " + localeName +
1451                                    ", skeleton " + skeleton +
1452                                    ", produces pattern without required chars: " + pattern);
1453                        }
1454                    }
1455                }
1456            }
1457        }
1458    }
1459
1460    @Test
1461    public void TestJavaLocale() {
1462        DateTimePatternGenerator genUloc = DateTimePatternGenerator.getInstance(ULocale.GERMANY);
1463        DateTimePatternGenerator genLoc = DateTimePatternGenerator.getInstance(Locale.GERMANY);
1464
1465        final String pat = "yMdHms";
1466        String patUloc = genUloc.getBestPattern(pat);
1467        String patLoc = genLoc.getBestPattern(pat);
1468
1469        assertEquals("German pattern 'yMdHms' - getInstance with Java Locale", patUloc, patLoc);
1470    }
1471
1472    /* Tests the method
1473     *    public static int getAppendFormatNumber(String string)
1474     */
1475    @Test
1476    public void TestGetAppendFormatNumber(){
1477        int fieldNum;
1478        fieldNum = DateTimePatternGenerator.getAppendFormatNumber("Era");
1479        assertEquals("DateTimePatternGenerator.getAppendFormatNumber for Era", 0, fieldNum);
1480        fieldNum = DateTimePatternGenerator.getAppendFormatNumber("Timezone");
1481        assertEquals("DateTimePatternGenerator.getAppendFormatNumber for Timezone", 15, fieldNum);
1482    }
1483
1484    /*
1485     * Coverage for methods otherwise not covered by other tests.
1486     */
1487    @Test
1488    public void TestCoverage() {
1489        DateTimePatternGenerator dtpg;
1490
1491        // DateTimePatternGenerator#getDefaultHourFormatChar
1492        // DateTimePatternGenerator#setDefaultHourFormatChar
1493        {
1494            dtpg = DateTimePatternGenerator.getEmptyInstance();
1495            assertEquals("Default hour char on empty instance", 'H', dtpg.getDefaultHourFormatChar());
1496            dtpg.setDefaultHourFormatChar('e');
1497            assertEquals("Default hour char after explicit set", 'e', dtpg.getDefaultHourFormatChar());
1498            dtpg = DateTimePatternGenerator.getInstance(ULocale.ENGLISH);
1499            assertEquals("Default hour char on populated English instance", 'h', dtpg.getDefaultHourFormatChar());
1500        }
1501
1502        // DateTimePatternGenerator#getSkeletonAllowingDuplicates
1503        // DateTimePatternGenerator#getCanonicalSkeletonAllowingDuplicates
1504        // DateTimePatternGenerator#getCanonicalChar
1505        {
1506            dtpg = DateTimePatternGenerator.getInstance(ULocale.ENGLISH);
1507            assertEquals("Example skeleton with no duplicate fields", "MMMdd", dtpg.getSkeleton("dd/MMM"));
1508            assertEquals("Should return same result as getSkeleton with no duplicate fields",
1509                    dtpg.getSkeleton("dd/MMM"), dtpg.getSkeletonAllowingDuplicates("dd/MMM"));
1510
1511            try {
1512                dtpg.getSkeleton("dd/MMM Zz");
1513                fail("getSkeleton should throw upon duplicate fields");
1514            } catch(IllegalArgumentException e) {
1515                assertEquals("getSkeleton should throw upon duplicate fields",
1516                        "Conflicting fields:\tZ, z\t in dd/MMM Zz", e.getMessage());
1517            }
1518
1519            assertEquals("Should not throw upon duplicate fields",
1520                    "MMMddZ", dtpg.getSkeletonAllowingDuplicates("dd/MMM Zz"));
1521            assertEquals("Should not throw upon duplicate fields and should return Canonical fields",
1522                    "MMMddv", dtpg.getCanonicalSkeletonAllowingDuplicates("dd/MMM Zz"));
1523        }
1524
1525        // DistanceInfo#toString
1526        // DateTimePatternGenerator#showMask
1527        try {
1528            String actual = invokeToString("android.icu.text.DateTimePatternGenerator$DistanceInfo");
1529            assertEquals("DistanceInfo toString", "missingFieldMask: , extraFieldMask: ", actual);
1530        } catch(Exception e) {
1531            errln("Couldn't call DistanceInfo.toString(): " + e.toString());
1532        }
1533
1534        // DateTimePatternGenerator#skeletonsAreSimilar
1535        // DateTimePatternGenerator#getSet
1536        {
1537            dtpg = DateTimePatternGenerator.getInstance(ULocale.ENGLISH);
1538            assertTrue("Trivial skeletonsAreSimilar", dtpg.skeletonsAreSimilar("MMMdd", "MMMdd"));
1539            assertTrue("Different number of chars in skeletonsAreSimilar", dtpg.skeletonsAreSimilar("Mddd", "MMMdd"));
1540            assertFalse("Failure case for skeletonsAreSimilar", dtpg.skeletonsAreSimilar("mmDD", "MMMdd"));
1541        }
1542    }
1543
1544    @Test
1545    public void TestEmptyInstance() {
1546        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getEmptyInstance();
1547        String skeleton = "GrMMd";
1548        String message = "DTPG getEmptyInstance should not throw exceptions on basic operations and should conform to "
1549                + "the example in setAppendItemFormat";
1550        assertEquals(message, "G ├'F7': d┤ ├'F3': MM┤ ├'F1': y┤", dtpg.getBestPattern(skeleton));
1551        dtpg.addPattern("d-MM-yyyy", false, new DateTimePatternGenerator.PatternInfo());
1552        assertEquals(message, "d-MM-y ├'F0': G┤", dtpg.getBestPattern(skeleton));
1553        dtpg.setAppendItemFormat(DateTimePatternGenerator.ERA, "{0}, {1}");
1554        assertEquals(message, "d-MM-y, G", dtpg.getBestPattern(skeleton));
1555    }
1556}
1557