]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/tests/core/src/com/ibm/icu/dev/test/calendar/CalendarTest.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / tests / core / src / com / ibm / icu / dev / test / calendar / CalendarTest.java
1 /*\r
2  *******************************************************************************\r
3  * Copyright (C) 1996-2010, International Business Machines Corporation and    *\r
4  * others. All Rights Reserved.                                                *\r
5  *******************************************************************************\r
6  */\r
7 \r
8 package com.ibm.icu.dev.test.calendar;\r
9 \r
10 import java.util.Date;\r
11 import java.util.Enumeration;\r
12 import java.util.Hashtable;\r
13 import java.util.Locale;\r
14 \r
15 import com.ibm.icu.dev.test.TestFmwk;\r
16 import com.ibm.icu.text.DateFormat;\r
17 import com.ibm.icu.text.SimpleDateFormat;\r
18 import com.ibm.icu.util.Calendar;\r
19 import com.ibm.icu.util.ChineseCalendar;\r
20 import com.ibm.icu.util.GregorianCalendar;\r
21 import com.ibm.icu.util.SimpleTimeZone;\r
22 \r
23 /**\r
24  * A base class for classes that test individual Calendar subclasses.\r
25  * Defines various useful utility methods and constants\r
26  */\r
27 public class CalendarTest extends TestFmwk {\r
28     \r
29     // Constants for use by subclasses, solely to save typing\r
30     public final static int SUN = Calendar.SUNDAY;\r
31     public final static int MON = Calendar.MONDAY;\r
32     public final static int TUE = Calendar.TUESDAY;\r
33     public final static int WED = Calendar.WEDNESDAY;\r
34     public final static int THU = Calendar.THURSDAY;\r
35     public final static int FRI = Calendar.FRIDAY;\r
36     public final static int SAT = Calendar.SATURDAY;\r
37 \r
38     public final static int ERA     = Calendar.ERA;\r
39     public final static int YEAR    = Calendar.YEAR;\r
40     public final static int MONTH   = Calendar.MONTH;\r
41     public final static int DATE    = Calendar.DATE;\r
42     public final static int HOUR    = Calendar.HOUR;\r
43     public final static int MINUTE  = Calendar.MINUTE;\r
44     public final static int SECOND  = Calendar.SECOND;\r
45     public final static int DOY     = Calendar.DAY_OF_YEAR;\r
46     public final static int WOY     = Calendar.WEEK_OF_YEAR;\r
47     public final static int WOM     = Calendar.WEEK_OF_MONTH;\r
48     public final static int DOW     = Calendar.DAY_OF_WEEK;\r
49     public final static int DOWM    = Calendar.DAY_OF_WEEK_IN_MONTH;\r
50     \r
51     public final static SimpleTimeZone UTC = new SimpleTimeZone(0, "GMT");\r
52 \r
53     private static final String[] FIELD_NAME = {\r
54         "ERA", "YEAR", "MONTH", "WEEK_OF_YEAR", "WEEK_OF_MONTH",\r
55         "DAY_OF_MONTH", "DAY_OF_YEAR", "DAY_OF_WEEK",\r
56         "DAY_OF_WEEK_IN_MONTH", "AM_PM", "HOUR", "HOUR_OF_DAY",\r
57         "MINUTE", "SECOND", "MILLISECOND", "ZONE_OFFSET",\r
58         "DST_OFFSET", "YEAR_WOY", "DOW_LOCAL", "EXTENDED_YEAR",\r
59         "JULIAN_DAY", "MILLISECONDS_IN_DAY",\r
60         "IS_LEAP_MONTH" // (ChineseCalendar only)\r
61     };\r
62 \r
63     public static final String fieldName(int f) {\r
64         return (f>=0 && f<FIELD_NAME.length) ?\r
65             FIELD_NAME[f] : ("<Field " + f + ">");\r
66     }\r
67 \r
68     /**\r
69      * Iterates through a list of calendar <code>TestCase</code> objects and\r
70      * makes sure that the time-to-fields and fields-to-time calculations work\r
71      * correnctly for the values in each test case.\r
72      */\r
73     public void doTestCases(TestCase[] cases, Calendar cal)\r
74     {\r
75         cal.setTimeZone(UTC);\r
76         \r
77         // Get a format to use for printing dates in the calendar system we're testing\r
78         DateFormat format = DateFormat.getDateTimeInstance(cal, DateFormat.SHORT, -1, Locale.getDefault());\r
79 \r
80         final String pattern = (cal instanceof ChineseCalendar) ?\r
81             "E MMl/dd/y G HH:mm:ss.S z" :\r
82             "E, MM/dd/yyyy G HH:mm:ss.S z";\r
83     \r
84         ((SimpleDateFormat)format).applyPattern(pattern);\r
85 \r
86         // This format is used for printing Gregorian dates.\r
87         DateFormat gregFormat = new SimpleDateFormat(pattern);\r
88         gregFormat.setTimeZone(UTC);\r
89 \r
90         GregorianCalendar pureGreg = new GregorianCalendar(UTC);\r
91         pureGreg.setGregorianChange(new Date(Long.MIN_VALUE));\r
92         DateFormat pureGregFmt = new SimpleDateFormat("E M/d/yyyy G");\r
93         pureGregFmt.setCalendar(pureGreg);\r
94         \r
95         // Now iterate through the test cases and see what happens\r
96         for (int i = 0; i < cases.length; i++)\r
97         {\r
98             logln("\ntest case: " + i);\r
99             TestCase test = cases[i];\r
100             \r
101             //\r
102             // First we want to make sure that the millis -> fields calculation works\r
103             // test.applyTime will call setTime() on the calendar object, and\r
104             // test.fieldsEqual will retrieve all of the field values and make sure\r
105             // that they're the same as the ones in the testcase\r
106             //\r
107             test.applyTime(cal);\r
108             if (!test.fieldsEqual(cal, this)) {\r
109                 errln("Fail: (millis=>fields) " +\r
110                       gregFormat.format(test.getTime()) + " => " +\r
111                       format.format(cal.getTime()) +\r
112                       ", expected " + test);\r
113             }\r
114 \r
115             //\r
116             // If that was OK, check the fields -> millis calculation\r
117             // test.applyFields will set all of the calendar's fields to \r
118             // match those in the test case.\r
119             //\r
120             cal.clear();\r
121             test.applyFields(cal);\r
122             if (!test.equals(cal)) {\r
123                 errln("Fail: (fields=>millis) " + test + " => " +\r
124                       pureGregFmt.format(cal.getTime()) +\r
125                       ", expected " + pureGregFmt.format(test.getTime()));\r
126             }\r
127         }\r
128     }\r
129     \r
130     static public final boolean ROLL = true;\r
131     static public final boolean ADD = false;\r
132     \r
133     /**\r
134      * Process test cases for <code>add</code> and <code>roll</code> methods.\r
135      * Each test case is an array of integers, as follows:\r
136      * <ul>\r
137      *  <li>0: input year\r
138      *  <li>1:       month  (zero-based)\r
139      *  <li>2:       day\r
140      *  <li>3: field to roll or add to\r
141      *  <li>4: amount to roll or add\r
142      *  <li>5: result year\r
143      *  <li>6:        month (zero-based)\r
144      *  <li>7:        day\r
145      * </ul>\r
146      * For example:\r
147      * <pre>\r
148      *   //       input                add by          output\r
149      *   //  year  month     day     field amount    year  month     day\r
150      *   {   5759, HESHVAN,   2,     MONTH,   1,     5759, KISLEV,    2 },\r
151      * </pre>\r
152      *\r
153      * @param roll  <code>true</code> or <code>ROLL</code> to test the <code>roll</code> method;\r
154      *              <code>false</code> or <code>ADD</code> to test the <code>add</code method\r
155      */\r
156     public void doRollAdd(boolean roll, Calendar cal, int[][] tests)\r
157     {\r
158         String name = roll ? "rolling" : "adding";\r
159         \r
160         for (int i = 0; i < tests.length; i++) {\r
161             int[] test = tests[i];\r
162 \r
163             cal.clear();\r
164             if (cal instanceof ChineseCalendar) {\r
165                 cal.set(Calendar.EXTENDED_YEAR, test[0]);\r
166                 cal.set(Calendar.MONTH, test[1]);\r
167                 cal.set(Calendar.DAY_OF_MONTH, test[2]);\r
168             } else {\r
169                 cal.set(test[0], test[1], test[2]);\r
170             }\r
171             double day0 = getJulianDay(cal);\r
172             if (roll) {\r
173                 cal.roll(test[3], test[4]);\r
174             } else {\r
175                 cal.add(test[3], test[4]);\r
176             }\r
177             int y = cal.get(cal instanceof ChineseCalendar ?\r
178                             Calendar.EXTENDED_YEAR : YEAR);\r
179             if (y != test[5] || cal.get(MONTH) != test[6]\r
180                     || cal.get(DATE) != test[7])\r
181             {\r
182                 errln("Fail: " + name + " "+ ymdToString(test[0], test[1], test[2])\r
183                     + " (" + day0 + ")"\r
184                     + " " + FIELD_NAME[test[3]] + " by " + test[4]\r
185                     + ": expected " + ymdToString(test[5], test[6], test[7])\r
186                     + ", got " + ymdToString(cal));\r
187             } else if (isVerbose()) {\r
188                 logln("OK: " + name + " "+ ymdToString(test[0], test[1], test[2])\r
189                     + " (" + day0 + ")"\r
190                     + " " + FIELD_NAME[test[3]] + " by " + test[4]\r
191                     + ": got " + ymdToString(cal));\r
192             }\r
193         }\r
194     }\r
195 \r
196     /**\r
197      * Test the functions getXxxMinimum() and getXxxMaximum() by marching a\r
198      * test calendar 'cal' through 'numberOfDays' sequential days starting\r
199      * with 'startDate'.  For each date, read a field value along with its\r
200      * reported actual minimum and actual maximum.  These values are\r
201      * checked against one another as well as against getMinimum(),\r
202      * getGreatestMinimum(), getLeastMaximum(), and getMaximum().  We\r
203      * expect to see:\r
204      *\r
205      * 1. minimum <= actualMinimum <= greatestMinimum <=\r
206      *    leastMaximum <= actualMaximum <= maximum\r
207      *\r
208      * 2. actualMinimum <= value <= actualMaximum\r
209      *\r
210      * Note: In addition to outright failures, this test reports some\r
211      * results as warnings.  These are not generally of concern, but they\r
212      * should be evaluated by a human.  To see these, run this test in\r
213      * verbose mode.\r
214      * @param cal the calendar to be tested\r
215      * @param fieldsToTest an array of field values to be tested, e.g., new\r
216      * int[] { Calendar.MONTH, Calendar.DAY_OF_MONTH }.  It only makes\r
217      * sense to test the day fields; the time fields are not tested by this\r
218      * method.  If null, then test all standard fields.\r
219      * @param startDate the first date to test\r
220      * @param testDuration if positive, the number of days to be tested.\r
221      * If negative, the number of seconds to run the test.\r
222      */\r
223     public void doLimitsTest(Calendar cal, int[] fieldsToTest,\r
224                                 Date startDate, int testDuration) {\r
225         GregorianCalendar greg = new GregorianCalendar();\r
226         greg.setTime(startDate);\r
227         logln("Start: " + startDate);\r
228 \r
229         if (fieldsToTest == null) {\r
230             fieldsToTest = new int[] {\r
231                 Calendar.ERA, Calendar.YEAR, Calendar.MONTH,\r
232                 Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH,\r
233                 Calendar.DAY_OF_MONTH, Calendar.DAY_OF_YEAR,\r
234                 Calendar.DAY_OF_WEEK_IN_MONTH, Calendar.YEAR_WOY,\r
235                 Calendar.EXTENDED_YEAR\r
236             };\r
237         }\r
238 \r
239         // Keep a record of minima and maxima that we actually see.\r
240         // These are kept in an array of arrays of hashes.\r
241         Hashtable[][] limits = new Hashtable[fieldsToTest.length][2];\r
242         Object nub = new Object(); // Meaningless placeholder\r
243 \r
244         // This test can run for a long time; show progress.\r
245         long millis = System.currentTimeMillis();\r
246         long mark = millis + 5000; // 5 sec\r
247         millis -= testDuration * 1000; // stop time if testDuration<0\r
248 \r
249         for (int i=0;\r
250              testDuration>0 ? i<testDuration\r
251                             : System.currentTimeMillis()<millis;\r
252              ++i) {\r
253             if (System.currentTimeMillis() >= mark) {\r
254                 logln("(" + i + " days)");\r
255                 mark += 5000; // 5 sec\r
256             }\r
257             cal.setTimeInMillis(greg.getTimeInMillis());\r
258             for (int j=0; j<fieldsToTest.length; ++j) {\r
259                 int f = fieldsToTest[j];\r
260                 int v = cal.get(f);\r
261                 int minActual = cal.getActualMinimum(f);\r
262                 int maxActual = cal.getActualMaximum(f);\r
263                 int minLow = cal.getMinimum(f);\r
264                 int minHigh = cal.getGreatestMinimum(f);\r
265                 int maxLow = cal.getLeastMaximum(f);\r
266                 int maxHigh = cal.getMaximum(f);\r
267 \r
268                 // Fetch the hash for this field and keep track of the\r
269                 // minima and maxima.\r
270                 Hashtable[] h = limits[j];\r
271                 if (h[0] == null) {\r
272                     h[0] = new Hashtable();\r
273                     h[1] = new Hashtable();\r
274                 }\r
275                 h[0].put(new Integer(minActual), nub);\r
276                 h[1].put(new Integer(maxActual), nub);\r
277 \r
278                 if (minActual < minLow || minActual > minHigh) {\r
279                     errln("Fail: " + ymdToString(cal) +\r
280                           " Range for min of " + FIELD_NAME[f] + "(" + f +\r
281                           ")=" + minLow + ".." + minHigh +\r
282                           ", actual_min=" + minActual);\r
283                 }\r
284                 if (maxActual < maxLow || maxActual > maxHigh) {\r
285                     errln("Fail: " + ymdToString(cal) +\r
286                           " Range for max of " + FIELD_NAME[f] + "(" + f +\r
287                           ")=" + maxLow + ".." + maxHigh +\r
288                           ", actual_max=" + maxActual);\r
289                 }\r
290                 if (v < minActual || v > maxActual) {\r
291                     errln("Fail: " + ymdToString(cal) +\r
292                           " " + FIELD_NAME[f] + "(" + f + ")=" + v +\r
293                           ", actual range=" + minActual + ".." + maxActual +\r
294                           ", allowed=(" + minLow + ".." + minHigh + ")..(" +\r
295                           maxLow + ".." + maxHigh + ")");\r
296                 }\r
297             }\r
298             greg.add(Calendar.DAY_OF_YEAR, 1);\r
299         }\r
300 \r
301         // Check actual maxima and minima seen against ranges returned\r
302         // by API.\r
303         StringBuffer buf = new StringBuffer();\r
304         for (int j=0; j<fieldsToTest.length; ++j) {\r
305             int f = fieldsToTest[j];\r
306             buf.setLength(0);\r
307             buf.append(FIELD_NAME[f]);\r
308             Hashtable[] h = limits[j];\r
309             boolean fullRangeSeen = true;\r
310             for (int k=0; k<2; ++k) {\r
311                 int rangeLow = (k==0) ?\r
312                     cal.getMinimum(f) : cal.getLeastMaximum(f);\r
313                 int rangeHigh = (k==0) ?\r
314                     cal.getGreatestMinimum(f) : cal.getMaximum(f);\r
315                 // If either the top of the range or the bottom was never\r
316                 // seen, then there may be a problem.\r
317                 if (h[k].get(new Integer(rangeLow)) == null ||\r
318                     h[k].get(new Integer(rangeHigh)) == null) {\r
319                     fullRangeSeen = false;\r
320                 }\r
321                 buf.append(k==0 ? " minima seen=(" : "; maxima seen=(");\r
322                 for (Enumeration e=h[k].keys(); e.hasMoreElements(); ) {\r
323                     int v = ((Integer) e.nextElement()).intValue();\r
324                     buf.append(" " + v);\r
325                 }\r
326                 buf.append(") range=" + rangeLow + ".." + rangeHigh);\r
327             }\r
328             if (fullRangeSeen) {\r
329                 logln("OK: " + buf.toString());\r
330             } else {\r
331                 // This may or may not be an error -- if the range of dates\r
332                 // we scan over doesn't happen to contain a minimum or\r
333                 // maximum, it doesn't mean some other range won't.\r
334                 logln("Warning: " + buf.toString());\r
335             }\r
336         }\r
337 \r
338         logln("End: " + greg.getTime());\r
339     }\r
340 \r
341     /**\r
342      * doLimitsTest with default test duration\r
343      */\r
344     public void doLimitsTest(Calendar cal, int[] fieldsToTest, Date startDate) {\r
345         int testTime = getInclusion() <= 5 ? -3 : -120; // in seconds\r
346         doLimitsTest(cal, fieldsToTest, startDate, testTime);\r
347     }\r
348     \r
349     /**\r
350      * Test the functions getMaximum/getGeratestMinimum logically correct.\r
351      * This method assumes day of week cycle is consistent.\r
352      * @param cal The calendar instance to be tested.\r
353      * @param leapMonth true if the calendar system has leap months\r
354      */\r
355     public void doTheoreticalLimitsTest(Calendar cal, boolean leapMonth) {\r
356         int nDOW = cal.getMaximum(Calendar.DAY_OF_WEEK);\r
357         int maxDOY = cal.getMaximum(Calendar.DAY_OF_YEAR);\r
358         int lmaxDOW = cal.getLeastMaximum(Calendar.DAY_OF_YEAR);\r
359         int maxWOY = cal.getMaximum(Calendar.WEEK_OF_YEAR);\r
360         int lmaxWOY = cal.getLeastMaximum(Calendar.WEEK_OF_YEAR);\r
361         int maxM = cal.getMaximum(Calendar.MONTH) + 1;\r
362         int lmaxM = cal.getLeastMaximum(Calendar.MONTH) + 1;\r
363         int maxDOM = cal.getMaximum(Calendar.DAY_OF_MONTH);\r
364         int lmaxDOM = cal.getLeastMaximum(Calendar.DAY_OF_MONTH);\r
365         int maxDOWIM = cal.getMaximum(Calendar.DAY_OF_WEEK_IN_MONTH);\r
366         int lmaxDOWIM = cal.getLeastMaximum(Calendar.DAY_OF_WEEK_IN_MONTH);\r
367         int maxWOM = cal.getMaximum(Calendar.WEEK_OF_MONTH);\r
368         int lmaxWOM = cal.getLeastMaximum(Calendar.WEEK_OF_MONTH);\r
369         int minDaysInFirstWeek = cal.getMinimalDaysInFirstWeek();\r
370 \r
371         // Day of year\r
372         int expected;\r
373         if (!leapMonth) {\r
374             expected = maxM*maxDOM;\r
375             if (maxDOY > expected) {\r
376                 errln("FAIL: Maximum value of DAY_OF_YEAR is too big: " + maxDOY + "/expected: <=" + expected);\r
377             }\r
378             expected = lmaxM*lmaxDOM;\r
379             if (lmaxDOW < expected) {\r
380                 errln("FAIL: Least maximum value of DAY_OF_YEAR is too small: " + lmaxDOW + "/expected: >=" + expected);\r
381             }\r
382         }\r
383 \r
384         // Week of year\r
385         expected = maxDOY/nDOW + 1;\r
386         if (maxWOY > expected) {\r
387             errln("FAIL: Maximum value of WEEK_OF_YEAR is too big: " + maxWOY + "/expected: <=" + expected);\r
388         }\r
389         expected = lmaxDOW/nDOW;\r
390         if (lmaxWOY < expected) {\r
391             errln("FAIL: Least maximum value of WEEK_OF_YEAR is too small: " + lmaxWOY + "/expected >=" + expected);\r
392         }\r
393 \r
394         // Day of week in month\r
395         expected = (maxDOM + nDOW - 1)/nDOW;\r
396         if (maxDOWIM != expected) {\r
397             errln("FAIL: Maximum value of DAY_OF_WEEK_IN_MONTH is incorrect: " + maxDOWIM + "/expected: " + expected);\r
398         }\r
399         expected = (lmaxDOM + nDOW - 1)/nDOW;\r
400         if (lmaxDOWIM != expected) {\r
401             errln("FAIL: Least maximum value of DAY_OF_WEEK_IN_MONTH is incorrect: " + lmaxDOWIM + "/expected: " + expected);\r
402         }\r
403 \r
404         // Week of month\r
405         expected = (maxDOM + (nDOW - 1) + (nDOW - minDaysInFirstWeek)) / nDOW;\r
406         if (maxWOM != expected) {\r
407             errln("FAIL: Maximum value of WEEK_OF_MONTH is incorrect: " + maxWOM + "/expected: " + expected);\r
408         }\r
409         expected = (lmaxDOM + (nDOW - minDaysInFirstWeek)) / nDOW;\r
410         if (lmaxWOM != expected) {\r
411             errln("FAIL: Least maximum value of WEEK_OF_MONTH is incorrect: " + lmaxWOM + "/expected: " + expected);\r
412         }\r
413     }\r
414     \r
415     /**\r
416      * Convert year,month,day values to the form "year/month/day".\r
417      * On input the month value is zero-based, but in the result string it is one-based.\r
418      */\r
419     static public String ymdToString(int year, int month, int day) {\r
420         return "" + year + "/" + (month+1) + "/" + day;\r
421     }\r
422 \r
423     /**\r
424      * Convert year,month,day values to the form "year/month/day".\r
425      */\r
426     static public String ymdToString(Calendar cal) {\r
427         double day = getJulianDay(cal);\r
428         if (cal instanceof ChineseCalendar) {\r
429             return "" + cal.get(Calendar.EXTENDED_YEAR) + "/" +\r
430                 (cal.get(Calendar.MONTH)+1) +\r
431                 (cal.get(Calendar.IS_LEAP_MONTH)==1?"(leap)":"") + "/" +\r
432                 cal.get(Calendar.DATE) + " (" + day + ", time=" + cal.getTimeInMillis() + ")";\r
433         }\r
434         return ymdToString(cal.get(Calendar.EXTENDED_YEAR),\r
435                             cal.get(MONTH), cal.get(DATE)) +\r
436                             " (" + day + ", time=" + cal.getTimeInMillis() + ")";\r
437     }\r
438 \r
439     static double getJulianDay(Calendar cal) {\r
440         return (cal.getTime().getTime() - JULIAN_EPOCH) / DAY_MS;\r
441     }\r
442 \r
443     static final double DAY_MS = 24*60*60*1000.0;\r
444     static final long JULIAN_EPOCH = -210866760000000L;   // 1/1/4713 BC 12:00\r
445 }\r