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