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 * 60 * 60 * 1000;
055 private static final long _1000_YEARS = 1000 * MILLIS_PER_YEAR;
056 private static final long _500_YEARS = 500 * MILLIS_PER_YEAR;
057 private static final long MAX_MILLIS = (10000 - 1970) * MILLIS_PER_YEAR;
058 private static final long MIN_MILLIS = (-10000 - 1970) * MILLIS_PER_YEAR;
059
060 // Show progess reports every 5 seconds.
061 private static final long UPDATE_INTERVAL = 5000;
062
063 /**
064 * Arguments: iterations [mode [seed]]
065 */
066 public static void main(String[] args) throws Exception {
067 int iterations = 1000000;
068 int mode = GREGORIAN_MODE;
069 long seed = 1345435247779935L;
070
071 if (args.length > 0) {
072 iterations = Integer.parseInt(args[0]);
073 if (args.length > 1) {
074 if (args[1].startsWith("g")) {
075 mode = GREGORIAN_MODE;
076 } else if (args[1].startsWith("j")) {
077 mode = JULIAN_MODE;
078 } else {
079 throw new IllegalArgumentException
080 ("Unknown mode: " + args[1]);
081 }
082 if (args.length > 2) {
083 seed = Long.parseLong(args[2]);
084 }
085 }
086 }
087
088 new MainTest(iterations, mode, seed).testChronology();
089 }
090
091 //-----------------------------------------------------------------------
092 private final int iIterations;
093 private final int iMode;
094 private final long iSeed;
095 private final Chronology iTest;
096 private final Chronology iActual;
097
098 /**
099 * @param iterations number of test iterations to perform
100 * @param mode GREGORIAN_MODE or JULIAN_MODE,0=Gregorian, 1=Julian
101 * @param seed seed for random number generator
102 */
103 public MainTest(int iterations, int mode, long seed) {
104 super("testChronology");
105 iIterations = iterations;
106 iMode = mode;
107 iSeed = seed;
108 if (mode == GREGORIAN_MODE) {
109 iTest = new TestGregorianChronology();
110 iActual = GregorianChronology.getInstanceUTC();
111 } else {
112 iTest = new TestJulianChronology();
113 iActual = JulianChronology.getInstanceUTC();
114 }
115 }
116
117 //-----------------------------------------------------------------------
118 /**
119 * Main junit test
120 */
121 public void testChronology() {
122 int iterations = iIterations;
123 long seed = iSeed;
124 String modeStr;
125 if (iMode == GREGORIAN_MODE) {
126 modeStr = "Gregorian";
127 } else {
128 modeStr = "Julian";
129 }
130
131 System.out.println("\nTesting " + modeStr + " chronology over " + iterations + " iterations");
132
133 Random rnd = new Random(seed);
134 long updateMillis = System.currentTimeMillis() + UPDATE_INTERVAL;
135
136 for (int i=0; i<iterations; i++) {
137 long now = System.currentTimeMillis();
138 if (now >= updateMillis) {
139 updateMillis = now + UPDATE_INTERVAL;
140 double complete = ((int)((double)i / iterations * 1000.0)) / 10d;
141 if (complete < 100) {
142 System.out.println("" + complete + "% complete (i=" + i + ")");
143 }
144 }
145
146 long millis = randomMillis(rnd);
147 int value = rnd.nextInt(200) - 100;
148 // millis2 is used for difference tests.
149 long millis2 = millis + rnd.nextLong() % _1000_YEARS - _500_YEARS;
150
151 try {
152 testFields(millis, value, millis2);
153 } catch (RuntimeException e) {
154 System.out.println("Failure index: " + i);
155 System.out.println("Test millis: " + millis);
156 System.out.println("Test value: " + value);
157 System.out.println("Test millis2: " + millis2);
158 fail(e.getMessage());
159 }
160 }
161
162 System.out.println("100% complete (i=" + iterations + ")");
163 }
164
165 //-----------------------------------------------------------------------
166 private void testFields(long millis, int value, long millis2) {
167 testField(iTest.year(), iActual.year(), millis, value, millis2);
168 testField(iTest.monthOfYear(), iActual.monthOfYear(), millis, value, millis2);
169 testField(iTest.dayOfMonth(), iActual.dayOfMonth(), millis, value, millis2);
170 testField(iTest.weekyear(), iActual.weekyear(), millis, value, millis2);
171 testField(iTest.weekOfWeekyear(),
172 iActual.weekOfWeekyear(), millis, value, millis2);
173 testField(iTest.dayOfWeek(), iActual.dayOfWeek(), millis, value, millis2);
174 testField(iTest.dayOfYear(), iActual.dayOfYear(), millis, value, millis2);
175 }
176
177 private void testField(DateTimeField fieldA, DateTimeField fieldB, long millis,
178 int value, long millis2)
179 {
180 int a, b;
181 long x, y;
182 boolean m, n;
183
184 // get test
185 a = fieldA.get(millis);
186 b = fieldB.get(millis);
187 testValue(fieldA, fieldB, "get", millis, a, b);
188
189 // getMaximumValue test
190 // Restrict this test to the fields that matter.
191 Class fieldClass = fieldA.getClass();
192 if (fieldClass == TestGJDayOfYearField.class ||
193 fieldClass == TestGJDayOfMonthField.class ||
194 fieldClass == TestGJWeekOfWeekyearField.class) {
195
196 a = fieldA.getMaximumValue(millis);
197 b = fieldB.getMaximumValue(millis);
198 testValue(fieldA, fieldB, "getMaximumValue", millis, a, b);
199 }
200
201 // set test
202 a = getWrappedValue
203 (value, fieldA.getMinimumValue(millis), fieldA.getMaximumValue(millis));
204 b = getWrappedValue
205 (value, fieldB.getMinimumValue(millis), fieldB.getMaximumValue(millis));
206 if (iMode == JULIAN_MODE && a == 0
207 && (fieldA.getName().equals("year") || fieldA.getName().equals("weekyear"))) {
208 // Exclude setting Julian year of zero.
209 } else {
210 x = fieldA.set(millis, a);
211 y = fieldB.set(millis, b);
212 testMillis(fieldA, fieldB, "set", millis, x, y, a, b);
213 }
214
215 // roundFloor test
216 x = fieldA.roundFloor(millis);
217 y = fieldB.roundFloor(millis);
218 testMillis(fieldA, fieldB, "roundFloor", millis, x, y);
219
220 // roundCeiling test
221 x = fieldA.roundCeiling(millis);
222 y = fieldB.roundCeiling(millis);
223 testMillis(fieldA, fieldB, "roundCeiling", millis, x, y);
224
225 // roundHalfFloor test
226 x = fieldA.roundHalfFloor(millis);
227 y = fieldB.roundHalfFloor(millis);
228 testMillis(fieldA, fieldB, "roundHalfFloor", millis, x, y);
229
230 // roundHalfEven test
231 x = fieldA.roundHalfEven(millis);
232 y = fieldB.roundHalfEven(millis);
233 testMillis(fieldA, fieldB, "roundHalfEven", millis, x, y);
234
235 // remainder test
236 x = fieldA.remainder(millis);
237 y = fieldB.remainder(millis);
238 testMillis(fieldA, fieldB, "remainder", millis, x, y);
239
240 // add test
241 x = fieldA.add(millis, value);
242 y = fieldB.add(millis, value);
243 testMillis(fieldA, fieldB, "add", millis, x, y);
244
245 // addWrapField test
246 x = fieldA.addWrapField(millis, value);
247 y = fieldB.addWrapField(millis, value);
248 testMillis(fieldA, fieldB, "addWrapField", millis, x, y);
249
250 // getDifference test
251 x = fieldA.getDifference(millis, millis2);
252 y = fieldB.getDifference(millis, millis2);
253 try {
254 testValue(fieldA, fieldB, "getDifference", millis, x, y);
255 } catch (RuntimeException e) {
256 System.out.println("Test datetime 2: " + makeDatetime(millis2));
257 throw e;
258 }
259
260 // isLeap test
261 m = fieldA.isLeap(millis);
262 n = fieldB.isLeap(millis);
263 testBoolean(fieldA, fieldB, "isLeap", millis, m, n);
264
265 // getLeapAmount test
266 a = fieldA.getLeapAmount(millis);
267 b = fieldB.getLeapAmount(millis);
268 testValue(fieldA, fieldB, "getLeapAmount", millis, a, b);
269 }
270
271 private int getWrappedValue(int value, int minValue, int maxValue) {
272 if (minValue >= maxValue) {
273 throw new IllegalArgumentException("MIN > MAX");
274 }
275
276 int wrapRange = maxValue - minValue + 1;
277 value -= minValue;
278
279 if (value >= 0) {
280 return (value % wrapRange) + minValue;
281 }
282
283 int remByRange = (-value) % wrapRange;
284
285 if (remByRange == 0) {
286 return 0 + minValue;
287 }
288 return (wrapRange - remByRange) + minValue;
289 }
290
291 private void testValue(DateTimeField fieldA, DateTimeField fieldB,
292 String method, long millis, long valueA, long valueB) {
293 if (valueA != valueB) {
294 failValue(fieldA, fieldB, method, millis, valueA, valueB);
295 }
296 }
297
298 private void testMillis(DateTimeField fieldA, DateTimeField fieldB,
299 String method, long millis, long millisA, long millisB) {
300 if (millisA != millisB) {
301 failMillis(fieldA, fieldB, method, millis, millisA, millisB);
302 }
303 }
304
305 private void testMillis(DateTimeField fieldA, DateTimeField fieldB,
306 String method, long millis, long millisA, long millisB,
307 int valueA, int valueB) {
308 if (millisA != millisB) {
309 failMillis(fieldA, fieldB, method, millis, millisA, millisB, valueA, valueB);
310 }
311 }
312
313 private void testBoolean(DateTimeField fieldA, DateTimeField fieldB,
314 String method, long millis, boolean boolA, boolean boolB) {
315 if (boolA != boolB) {
316 failBoolean(fieldA, fieldB, method, millis, boolA, boolB);
317 }
318 }
319
320 private void failValue(DateTimeField fieldA, DateTimeField fieldB,
321 String method, long millis, long valueA, long valueB) {
322 System.out.println("Failure on " + makeName(fieldA, fieldB) + "." + method);
323 System.out.println(fieldA.getClass().getName() + "\n\tvs. "
324 + fieldB.getClass().getName());
325 System.out.println("Datetime: " + makeDatetime(millis));
326 System.out.println("Millis from 1970: " + millis);
327 System.out.println(valueA + " != " + valueB);
328 throw new RuntimeException();
329 }
330
331 private void failMillis(DateTimeField fieldA, DateTimeField fieldB,
332 String method, long millis, long millisA, long millisB) {
333 System.out.println("Failure on " + makeName(fieldA, fieldB) + "." + method);
334 System.out.println(fieldA.getClass().getName() + "\n\tvs. "
335 + fieldB.getClass().getName());
336 System.out.println("Datetime: " + makeDatetime(millis));
337 System.out.println("Millis from 1970: " + millis);
338 System.out.println(makeDatetime(millisA) + " != " + makeDatetime(millisB));
339 System.out.println(millisA + " != " + millisB);
340 System.out.println("Original value as reported by first field: " +
341 fieldA.get(millis));
342 System.out.println("Original value as reported by second field: " +
343 fieldB.get(millis));
344 System.out.println("First new value as reported by first field: " +
345 fieldA.get(millisA));
346 System.out.println("First new value as reported by second field: " +
347 fieldB.get(millisA));
348 System.out.println("Second new value as reported by first field: " +
349 fieldA.get(millisB));
350 System.out.println("Second new value as reported by second field: " +
351 fieldB.get(millisB));
352 throw new RuntimeException();
353 }
354
355 private void failMillis(DateTimeField fieldA, DateTimeField fieldB,
356 String method, long millis, long millisA, long millisB,
357 int valueA, int valueB) {
358 System.out.println("Failure on " + makeName(fieldA, fieldB) + "." + method);
359 System.out.println(fieldA.getClass().getName() + "\n\tvs. "
360 + fieldB.getClass().getName());
361 System.out.println("Datetime: " + makeDatetime(millis));
362 System.out.println("Millis from 1970: " + millis);
363 System.out.println(makeDatetime(millisA) + " != " + makeDatetime(millisB));
364 System.out.println(millisA + " != " + millisB);
365 System.out.println("Original value as reported by first field: " +
366 fieldA.get(millis));
367 System.out.println("Original value as reported by second field: " +
368 fieldB.get(millis));
369 System.out.println("First new value as reported by first field: " +
370 fieldA.get(millisA));
371 System.out.println("First new value as reported by second field: " +
372 fieldB.get(millisA));
373 System.out.println("Second new value as reported by first field: " +
374 fieldA.get(millisB));
375 System.out.println("Second new value as reported by second field: " +
376 fieldB.get(millisB));
377 System.out.println("Value to set for first field: " + valueA);
378 System.out.println("Value to set for second field: " + valueB);
379 throw new RuntimeException();
380 }
381
382 private void failBoolean(DateTimeField fieldA, DateTimeField fieldB,
383 String method, long millis, boolean boolA, boolean boolB) {
384 System.out.println("Failure on " + makeName(fieldA, fieldB) + "." + 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(boolA + " != " + boolB);
390 throw new RuntimeException();
391 }
392
393 private String makeName(DateTimeField fieldA, DateTimeField fieldB) {
394 if (fieldA.getName().equals(fieldB.getName())) {
395 return fieldA.getName();
396 } else {
397 return fieldA.getName() + "/" + fieldB.getName();
398 }
399 }
400
401 private String makeDatetime(long millis) {
402 return makeDatetime(millis, iActual);
403 }
404
405 private String makeDatetime(long millis, Chronology chrono) {
406 return chrono.dayOfWeek().getAsShortText(millis) + " "
407 + new DateTime(millis, chrono).toString() + " / " +
408 chrono.weekyear().get(millis) + "-W" + chrono.weekOfWeekyear().get(millis) +
409 "-" + chrono.dayOfWeek().get(millis);
410 }
411
412 private String makeDate(long millis) {
413 return makeDate(millis, iActual);
414 }
415
416 private String makeDate(long millis, Chronology chrono) {
417 return chrono.dayOfWeek().getAsShortText(millis) + " "
418 + new DateTime(millis, chrono).toString("yyyy-MM-dd") + " / " +
419 chrono.weekyear().get(millis) + "-W" + chrono.weekOfWeekyear().get(millis) +
420 "-" + chrono.dayOfWeek().get(millis);
421 }
422
423 //-----------------------------------------------------------------------
424 private static long randomMillis(Random rnd) {
425 long millis = rnd.nextLong();
426 if (millis >= 0) {
427 millis = millis % MAX_MILLIS;
428 } else {
429 millis = millis % -MIN_MILLIS;
430 }
431 return millis;
432 }
433
434 private static void dump(Chronology chrono, long millis) {
435 System.out.println("year: " + chrono.year().get(millis));
436 System.out.println("monthOfYear: " + chrono.monthOfYear().get(millis));
437 System.out.println("dayOfMonth: " + chrono.dayOfMonth().get(millis));
438 System.out.println("weekyear: " + chrono.weekyear().get(millis));
439 System.out.println("weekOfWeekyear: " + chrono.weekOfWeekyear().get(millis));
440 System.out.println("dayOfWeek: " + chrono.dayOfWeek().get(millis));
441 System.out.println("dayOfYear: " + chrono.dayOfYear().get(millis));
442 }
443
444 }