001: /*
002: * Copyright 2001-2005 Stephen Colebourne
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.joda.time.chrono.gj;
017:
018: import java.util.Random;
019:
020: import junit.framework.TestCase;
021:
022: import org.joda.time.Chronology;
023: import org.joda.time.DateTime;
024: import org.joda.time.DateTimeField;
025: import org.joda.time.chrono.GregorianChronology;
026: import org.joda.time.chrono.JulianChronology;
027:
028: /**
029: * Tests either the Julian or Gregorian chronology from org.joda.time.chrono.gj
030: * against the implementations in this package. It tests all the date fields
031: * against their principal methods.
032: * <p>
033: * Randomly generated values are fed into the DateTimeField methods and the
034: * results are compared between the two chronologies. If any result doesn't
035: * match, an error report is generated and the program exits. Each time this
036: * test program is run, the pseudo random number generator is seeded with the
037: * same value. This ensures consistent results between test runs.
038: * <p>
039: * The main method accepts three optional arguments: iterations, mode, seed. By
040: * default, iterations is set to 1,000,000. The test will take several minutes
041: * to run, depending on the computer's performance. Every 5 seconds a progress
042: * message is printed.
043: * <p>
044: * The mode can be either 'g' for proleptic gregorian (the default) or 'j' for
045: * proleptic julian. To override the default random number generator seed, pass
046: * in a third argument which accepts a long signed integer.
047: *
048: * @author Brian S O'Neill
049: */
050: public class MainTest extends TestCase {
051: public static final int GREGORIAN_MODE = 0;
052: public static final int JULIAN_MODE = 1;
053:
054: private static final long MILLIS_PER_YEAR = (long) 365.2425 * 24
055: * 60 * 60 * 1000;
056: private static final long _1000_YEARS = 1000 * MILLIS_PER_YEAR;
057: private static final long _500_YEARS = 500 * MILLIS_PER_YEAR;
058: private static final long MAX_MILLIS = (10000 - 1970)
059: * MILLIS_PER_YEAR;
060: private static final long MIN_MILLIS = (-10000 - 1970)
061: * MILLIS_PER_YEAR;
062:
063: // Show progess reports every 5 seconds.
064: private static final long UPDATE_INTERVAL = 5000;
065:
066: /**
067: * Arguments: iterations [mode [seed]]
068: */
069: public static void main(String[] args) throws Exception {
070: int iterations = 1000000;
071: int mode = GREGORIAN_MODE;
072: long seed = 1345435247779935L;
073:
074: if (args.length > 0) {
075: iterations = Integer.parseInt(args[0]);
076: if (args.length > 1) {
077: if (args[1].startsWith("g")) {
078: mode = GREGORIAN_MODE;
079: } else if (args[1].startsWith("j")) {
080: mode = JULIAN_MODE;
081: } else {
082: throw new IllegalArgumentException("Unknown mode: "
083: + args[1]);
084: }
085: if (args.length > 2) {
086: seed = Long.parseLong(args[2]);
087: }
088: }
089: }
090:
091: new MainTest(iterations, mode, seed).testChronology();
092: }
093:
094: //-----------------------------------------------------------------------
095: private final int iIterations;
096: private final int iMode;
097: private final long iSeed;
098: private final Chronology iTest;
099: private final Chronology iActual;
100:
101: /**
102: * @param iterations number of test iterations to perform
103: * @param mode GREGORIAN_MODE or JULIAN_MODE,0=Gregorian, 1=Julian
104: * @param seed seed for random number generator
105: */
106: public MainTest(int iterations, int mode, long seed) {
107: super ("testChronology");
108: iIterations = iterations;
109: iMode = mode;
110: iSeed = seed;
111: if (mode == GREGORIAN_MODE) {
112: iTest = new TestGregorianChronology();
113: iActual = GregorianChronology.getInstanceUTC();
114: } else {
115: iTest = new TestJulianChronology();
116: iActual = JulianChronology.getInstanceUTC();
117: }
118: }
119:
120: //-----------------------------------------------------------------------
121: /**
122: * Main junit test
123: */
124: public void testChronology() {
125: int iterations = iIterations;
126: long seed = iSeed;
127: String modeStr;
128: if (iMode == GREGORIAN_MODE) {
129: modeStr = "Gregorian";
130: } else {
131: modeStr = "Julian";
132: }
133:
134: System.out.println("\nTesting " + modeStr + " chronology over "
135: + iterations + " iterations");
136:
137: Random rnd = new Random(seed);
138: long updateMillis = System.currentTimeMillis()
139: + UPDATE_INTERVAL;
140:
141: for (int i = 0; i < iterations; i++) {
142: long now = System.currentTimeMillis();
143: if (now >= updateMillis) {
144: updateMillis = now + UPDATE_INTERVAL;
145: double complete = ((int) ((double) i / iterations * 1000.0)) / 10d;
146: if (complete < 100) {
147: System.out.println("" + complete + "% complete (i="
148: + i + ")");
149: }
150: }
151:
152: long millis = randomMillis(rnd);
153: int value = rnd.nextInt(200) - 100;
154: // millis2 is used for difference tests.
155: long millis2 = millis + rnd.nextLong() % _1000_YEARS
156: - _500_YEARS;
157:
158: try {
159: testFields(millis, value, millis2);
160: } catch (RuntimeException e) {
161: System.out.println("Failure index: " + i);
162: System.out.println("Test millis: " + millis);
163: System.out.println("Test value: " + value);
164: System.out.println("Test millis2: " + millis2);
165: fail(e.getMessage());
166: }
167: }
168:
169: System.out.println("100% complete (i=" + iterations + ")");
170: }
171:
172: //-----------------------------------------------------------------------
173: private void testFields(long millis, int value, long millis2) {
174: testField(iTest.year(), iActual.year(), millis, value, millis2);
175: testField(iTest.monthOfYear(), iActual.monthOfYear(), millis,
176: value, millis2);
177: testField(iTest.dayOfMonth(), iActual.dayOfMonth(), millis,
178: value, millis2);
179: testField(iTest.weekyear(), iActual.weekyear(), millis, value,
180: millis2);
181: testField(iTest.weekOfWeekyear(), iActual.weekOfWeekyear(),
182: millis, value, millis2);
183: testField(iTest.dayOfWeek(), iActual.dayOfWeek(), millis,
184: value, millis2);
185: testField(iTest.dayOfYear(), iActual.dayOfYear(), millis,
186: value, millis2);
187: }
188:
189: private void testField(DateTimeField fieldA, DateTimeField fieldB,
190: long millis, int value, long millis2) {
191: int a, b;
192: long x, y;
193: boolean m, n;
194:
195: // get test
196: a = fieldA.get(millis);
197: b = fieldB.get(millis);
198: testValue(fieldA, fieldB, "get", millis, a, b);
199:
200: // getMaximumValue test
201: // Restrict this test to the fields that matter.
202: Class fieldClass = fieldA.getClass();
203: if (fieldClass == TestGJDayOfYearField.class
204: || fieldClass == TestGJDayOfMonthField.class
205: || fieldClass == TestGJWeekOfWeekyearField.class) {
206:
207: a = fieldA.getMaximumValue(millis);
208: b = fieldB.getMaximumValue(millis);
209: testValue(fieldA, fieldB, "getMaximumValue", millis, a, b);
210: }
211:
212: // set test
213: a = getWrappedValue(value, fieldA.getMinimumValue(millis),
214: fieldA.getMaximumValue(millis));
215: b = getWrappedValue(value, fieldB.getMinimumValue(millis),
216: fieldB.getMaximumValue(millis));
217: if (iMode == JULIAN_MODE
218: && a == 0
219: && (fieldA.getName().equals("year") || fieldA.getName()
220: .equals("weekyear"))) {
221: // Exclude setting Julian year of zero.
222: } else {
223: x = fieldA.set(millis, a);
224: y = fieldB.set(millis, b);
225: testMillis(fieldA, fieldB, "set", millis, x, y, a, b);
226: }
227:
228: // roundFloor test
229: x = fieldA.roundFloor(millis);
230: y = fieldB.roundFloor(millis);
231: testMillis(fieldA, fieldB, "roundFloor", millis, x, y);
232:
233: // roundCeiling test
234: x = fieldA.roundCeiling(millis);
235: y = fieldB.roundCeiling(millis);
236: testMillis(fieldA, fieldB, "roundCeiling", millis, x, y);
237:
238: // roundHalfFloor test
239: x = fieldA.roundHalfFloor(millis);
240: y = fieldB.roundHalfFloor(millis);
241: testMillis(fieldA, fieldB, "roundHalfFloor", millis, x, y);
242:
243: // roundHalfEven test
244: x = fieldA.roundHalfEven(millis);
245: y = fieldB.roundHalfEven(millis);
246: testMillis(fieldA, fieldB, "roundHalfEven", millis, x, y);
247:
248: // remainder test
249: x = fieldA.remainder(millis);
250: y = fieldB.remainder(millis);
251: testMillis(fieldA, fieldB, "remainder", millis, x, y);
252:
253: // add test
254: x = fieldA.add(millis, value);
255: y = fieldB.add(millis, value);
256: testMillis(fieldA, fieldB, "add", millis, x, y);
257:
258: // addWrapField test
259: x = fieldA.addWrapField(millis, value);
260: y = fieldB.addWrapField(millis, value);
261: testMillis(fieldA, fieldB, "addWrapField", millis, x, y);
262:
263: // getDifference test
264: x = fieldA.getDifference(millis, millis2);
265: y = fieldB.getDifference(millis, millis2);
266: try {
267: testValue(fieldA, fieldB, "getDifference", millis, x, y);
268: } catch (RuntimeException e) {
269: System.out.println("Test datetime 2: "
270: + makeDatetime(millis2));
271: throw e;
272: }
273:
274: // isLeap test
275: m = fieldA.isLeap(millis);
276: n = fieldB.isLeap(millis);
277: testBoolean(fieldA, fieldB, "isLeap", millis, m, n);
278:
279: // getLeapAmount test
280: a = fieldA.getLeapAmount(millis);
281: b = fieldB.getLeapAmount(millis);
282: testValue(fieldA, fieldB, "getLeapAmount", millis, a, b);
283: }
284:
285: private int getWrappedValue(int value, int minValue, int maxValue) {
286: if (minValue >= maxValue) {
287: throw new IllegalArgumentException("MIN > MAX");
288: }
289:
290: int wrapRange = maxValue - minValue + 1;
291: value -= minValue;
292:
293: if (value >= 0) {
294: return (value % wrapRange) + minValue;
295: }
296:
297: int remByRange = (-value) % wrapRange;
298:
299: if (remByRange == 0) {
300: return 0 + minValue;
301: }
302: return (wrapRange - remByRange) + minValue;
303: }
304:
305: private void testValue(DateTimeField fieldA, DateTimeField fieldB,
306: String method, long millis, long valueA, long valueB) {
307: if (valueA != valueB) {
308: failValue(fieldA, fieldB, method, millis, valueA, valueB);
309: }
310: }
311:
312: private void testMillis(DateTimeField fieldA, DateTimeField fieldB,
313: String method, long millis, long millisA, long millisB) {
314: if (millisA != millisB) {
315: failMillis(fieldA, fieldB, method, millis, millisA, millisB);
316: }
317: }
318:
319: private void testMillis(DateTimeField fieldA, DateTimeField fieldB,
320: String method, long millis, long millisA, long millisB,
321: int valueA, int valueB) {
322: if (millisA != millisB) {
323: failMillis(fieldA, fieldB, method, millis, millisA,
324: millisB, valueA, valueB);
325: }
326: }
327:
328: private void testBoolean(DateTimeField fieldA,
329: DateTimeField fieldB, String method, long millis,
330: boolean boolA, boolean boolB) {
331: if (boolA != boolB) {
332: failBoolean(fieldA, fieldB, method, millis, boolA, boolB);
333: }
334: }
335:
336: private void failValue(DateTimeField fieldA, DateTimeField fieldB,
337: String method, long millis, long valueA, long valueB) {
338: System.out.println("Failure on " + makeName(fieldA, fieldB)
339: + "." + method);
340: System.out.println(fieldA.getClass().getName() + "\n\tvs. "
341: + fieldB.getClass().getName());
342: System.out.println("Datetime: " + makeDatetime(millis));
343: System.out.println("Millis from 1970: " + millis);
344: System.out.println(valueA + " != " + valueB);
345: throw new RuntimeException();
346: }
347:
348: private void failMillis(DateTimeField fieldA, DateTimeField fieldB,
349: String method, long millis, long millisA, long millisB) {
350: System.out.println("Failure on " + makeName(fieldA, fieldB)
351: + "." + method);
352: System.out.println(fieldA.getClass().getName() + "\n\tvs. "
353: + fieldB.getClass().getName());
354: System.out.println("Datetime: " + makeDatetime(millis));
355: System.out.println("Millis from 1970: " + millis);
356: System.out.println(makeDatetime(millisA) + " != "
357: + makeDatetime(millisB));
358: System.out.println(millisA + " != " + millisB);
359: System.out
360: .println("Original value as reported by first field: "
361: + fieldA.get(millis));
362: System.out
363: .println("Original value as reported by second field: "
364: + fieldB.get(millis));
365: System.out
366: .println("First new value as reported by first field: "
367: + fieldA.get(millisA));
368: System.out
369: .println("First new value as reported by second field: "
370: + fieldB.get(millisA));
371: System.out
372: .println("Second new value as reported by first field: "
373: + fieldA.get(millisB));
374: System.out
375: .println("Second new value as reported by second field: "
376: + fieldB.get(millisB));
377: throw new RuntimeException();
378: }
379:
380: private void failMillis(DateTimeField fieldA, DateTimeField fieldB,
381: String method, long millis, long millisA, long millisB,
382: int valueA, int valueB) {
383: System.out.println("Failure on " + makeName(fieldA, fieldB)
384: + "." + method);
385: System.out.println(fieldA.getClass().getName() + "\n\tvs. "
386: + fieldB.getClass().getName());
387: System.out.println("Datetime: " + makeDatetime(millis));
388: System.out.println("Millis from 1970: " + millis);
389: System.out.println(makeDatetime(millisA) + " != "
390: + makeDatetime(millisB));
391: System.out.println(millisA + " != " + millisB);
392: System.out
393: .println("Original value as reported by first field: "
394: + fieldA.get(millis));
395: System.out
396: .println("Original value as reported by second field: "
397: + fieldB.get(millis));
398: System.out
399: .println("First new value as reported by first field: "
400: + fieldA.get(millisA));
401: System.out
402: .println("First new value as reported by second field: "
403: + fieldB.get(millisA));
404: System.out
405: .println("Second new value as reported by first field: "
406: + fieldA.get(millisB));
407: System.out
408: .println("Second new value as reported by second field: "
409: + fieldB.get(millisB));
410: System.out.println("Value to set for first field: " + valueA);
411: System.out.println("Value to set for second field: " + valueB);
412: throw new RuntimeException();
413: }
414:
415: private void failBoolean(DateTimeField fieldA,
416: DateTimeField fieldB, String method, long millis,
417: boolean boolA, boolean boolB) {
418: System.out.println("Failure on " + makeName(fieldA, fieldB)
419: + "." + method);
420: System.out.println(fieldA.getClass().getName() + "\n\tvs. "
421: + fieldB.getClass().getName());
422: System.out.println("Datetime: " + makeDatetime(millis));
423: System.out.println("Millis from 1970: " + millis);
424: System.out.println(boolA + " != " + boolB);
425: throw new RuntimeException();
426: }
427:
428: private String makeName(DateTimeField fieldA, DateTimeField fieldB) {
429: if (fieldA.getName().equals(fieldB.getName())) {
430: return fieldA.getName();
431: } else {
432: return fieldA.getName() + "/" + fieldB.getName();
433: }
434: }
435:
436: private String makeDatetime(long millis) {
437: return makeDatetime(millis, iActual);
438: }
439:
440: private String makeDatetime(long millis, Chronology chrono) {
441: return chrono.dayOfWeek().getAsShortText(millis) + " "
442: + new DateTime(millis, chrono).toString() + " / "
443: + chrono.weekyear().get(millis) + "-W"
444: + chrono.weekOfWeekyear().get(millis) + "-"
445: + chrono.dayOfWeek().get(millis);
446: }
447:
448: private String makeDate(long millis) {
449: return makeDate(millis, iActual);
450: }
451:
452: private String makeDate(long millis, Chronology chrono) {
453: return chrono.dayOfWeek().getAsShortText(millis) + " "
454: + new DateTime(millis, chrono).toString("yyyy-MM-dd")
455: + " / " + chrono.weekyear().get(millis) + "-W"
456: + chrono.weekOfWeekyear().get(millis) + "-"
457: + chrono.dayOfWeek().get(millis);
458: }
459:
460: //-----------------------------------------------------------------------
461: private static long randomMillis(Random rnd) {
462: long millis = rnd.nextLong();
463: if (millis >= 0) {
464: millis = millis % MAX_MILLIS;
465: } else {
466: millis = millis % -MIN_MILLIS;
467: }
468: return millis;
469: }
470:
471: private static void dump(Chronology chrono, long millis) {
472: System.out.println("year: "
473: + chrono.year().get(millis));
474: System.out.println("monthOfYear: "
475: + chrono.monthOfYear().get(millis));
476: System.out.println("dayOfMonth: "
477: + chrono.dayOfMonth().get(millis));
478: System.out.println("weekyear: "
479: + chrono.weekyear().get(millis));
480: System.out.println("weekOfWeekyear: "
481: + chrono.weekOfWeekyear().get(millis));
482: System.out.println("dayOfWeek: "
483: + chrono.dayOfWeek().get(millis));
484: System.out.println("dayOfYear: "
485: + chrono.dayOfYear().get(millis));
486: }
487:
488: }
|