KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > opensymphony > oscache > util > TestFastCronParser


1 /*
2  * Copyright (c) 2002-2003 by OpenSymphony
3  * All rights reserved.
4  */

5 package com.opensymphony.oscache.util;
6
7 import junit.framework.Test;
8 import junit.framework.TestCase;
9 import junit.framework.TestSuite;
10
11 import java.text.ParseException JavaDoc;
12 import java.text.SimpleDateFormat JavaDoc;
13
14 import java.util.*;
15
16 /**
17  *
18  * @author <a HREF="&#109;a&#105;&#108;&#116;&#111;:chris&#64;swebtec.&#99;&#111;&#109;">Chris Miller</a>
19  * @author $Author: ltorunski $
20  * @version $Revision: 1.2 $
21  */

22 public class TestFastCronParser extends TestCase {
23     public TestFastCronParser(String JavaDoc str) {
24         super(str);
25     }
26
27     /**
28     * This methods returns the name of this test class to JUnit
29     * <p>
30     * @return The name of this class
31     */

32     public static Test suite() {
33         return new TestSuite(TestFastCronParser.class);
34     }
35
36     /**
37     * Tests to see if the cron class can calculate the previous matching
38     * time correctly in various circumstances
39     */

40     public void testEvaluations() {
41         // Minute tests
42
cronCall("01/01/2003 0:00", "45 * * * *", "31/12/2002 23:45", false);
43         cronCall("01/01/2003 0:00", "45-47,48,49 * * * *", "31/12/2002 23:49", false);
44         cronCall("01/01/2003 0:00", "2/5 * * * *", "31/12/2002 23:57", false);
45
46         // Hour tests
47
cronCall("20/12/2003 10:00", "* 3/4 * * *", "20/12/2003 07:59", false);
48         cronCall("20/12/2003 0:00", "* 3 * * *", "19/12/2003 03:59", false);
49
50         // Day of month tests
51
cronCall("07/01/2003 0:00", "30 * 1 * *", "01/01/2003 23:30", false);
52         cronCall("01/01/2003 0:00", "10 * 22 * *", "22/12/2002 23:10", false);
53         cronCall("01/01/2003 0:00", "30 23 19 * *", "19/12/2002 23:30", false);
54         cronCall("01/01/2003 0:00", "30 23 21 * *", "21/12/2002 23:30", false);
55         cronCall("01/01/2003 0:01", "* * 21 * *", "21/12/2002 23:59", false);
56         cronCall("10/07/2003 0:00", "* * 30,31 * *", "30/06/2003 23:59", false);
57
58         // Test month rollovers for months with 28,29,30 and 31 days
59
cronCall("01/03/2002 0:11", "* * * 2 *", "28/02/2002 23:59", false);
60         cronCall("01/03/2004 0:44", "* * * 2 *", "29/02/2004 23:59", false);
61         cronCall("01/04/2002 0:00", "* * * 3 *", "31/03/2002 23:59", false);
62         cronCall("01/05/2002 0:00", "* * * 4 *", "30/04/2002 23:59", false);
63
64         // Other month tests (including year rollover)
65
cronCall("01/01/2003 5:00", "10 * * 6 *", "30/06/2002 23:10", false);
66         cronCall("01/01/2003 5:00", "10 * * February,April-Jun *", "30/06/2002 23:10", false);
67         cronCall("01/01/2003 0:00", "0 12 1 6 *", "01/06/2002 12:00", false);
68         cronCall("11/09/1988 14:23", "* 12 1 6 *", "01/06/1988 12:59", false);
69         cronCall("11/03/1988 14:23", "* 12 1 6 *", "01/06/1987 12:59", false);
70         cronCall("11/03/1988 14:23", "* 2,4-8,15 * 6 *", "30/06/1987 15:59", false);
71         cronCall("11/03/1988 14:23", "20 * * january,FeB,Mar,april,May,JuNE,July,Augu,SEPT-October,Nov,DECEM *", "11/03/1988 14:20", false);
72
73         // Day of week tests
74
cronCall("26/06/2003 10:00", "30 6 * * 0", "22/06/2003 06:30", false);
75         cronCall("26/06/2003 10:00", "30 6 * * sunday", "22/06/2003 06:30", false);
76         cronCall("26/06/2003 10:00", "30 6 * * SUNDAY", "22/06/2003 06:30", false);
77         cronCall("23/06/2003 0:00", "1 12 * * 2", "17/06/2003 12:01", false);
78         cronCall("23/06/2003 0:00", "* * * * 3,0,4", "22/06/2003 23:59", false);
79         cronCall("23/06/2003 0:00", "* * * * 5", "20/06/2003 23:59", false);
80         cronCall("02/06/2003 18:30", "0 12 * * 2", "27/05/2003 12:00", false);
81         cronCall("02/06/2003 18:30", "0 12 * * Tue,Thurs-Sat,2", "31/05/2003 12:00", false);
82         cronCall("02/06/2003 18:30", "0 12 * * Mon-tuesday,wed,THURS-FRiday,Sat-SUNDAY", "02/06/2003 12:00", false);
83
84         // Leap year tests
85
cronCall("01/03/2003 12:00", "1 12 * * *", "28/02/2003 12:01", false); // non-leap year
86
cronCall("01/03/2004 12:00", "1 12 * * *", "29/02/2004 12:01", false); // leap year
87
cronCall("01/03/2003 12:00", "1 23 * * 0", "23/02/2003 23:01", false); // non-leap year
88
cronCall("01/03/2004 12:00", "1 23 * * 0", "29/02/2004 23:01", false); // leap year
89
cronCall("01/03/2003 12:00", "* * 29 2 *", "29/02/2000 23:59", false); // Find the previous leap-day
90
cronCall("01/02/2003 12:00", "* * 29 2 *", "29/02/2000 23:59", false); // Find the previous leap-day
91
cronCall("01/02/2004 12:00", "* * 29 2 *", "29/02/2000 23:59", false); // Find the previous leap-day
92

93         // Interval and range tests
94
cronCall("20/12/2003 10:00", "* */4 * * *", "20/12/2003 08:59", false);
95         cronCall("20/12/2003 10:00", "* 3/2 * * *", "20/12/2003 09:59", false);
96         cronCall("20/12/2003 10:00", "1-30/5 10-20/3 * jan-aug/2 *", "31/07/2003 19:26", false);
97         cronCall("20/12/2003 10:00", "20-25,27-30/2 10/8 * * *", "19/12/2003 18:29", false);
98     }
99
100     /**
101     * Tests a range of invalid cron expressions
102     */

103     public void testInvalidExpressionParsing() {
104         FastCronParser parser = new FastCronParser();
105
106         try {
107             parser.setCronExpression(null);
108             fail("An IllegalArgumentException should have been thrown");
109         } catch (IllegalArgumentException JavaDoc e) {
110             // Expected
111
} catch (ParseException JavaDoc e) {
112             fail("Expected an IllegalArgumentException but received a ParseException instead");
113         }
114
115         /**
116         * Not enough tokens
117         */

118         cronCall("01/01/2003 0:00", "", "", true);
119         cronCall("01/01/2003 0:00", "8 * 8/1 *", "", true);
120
121         /**
122         * Invalid syntax
123         */

124         cronCall("01/01/2003 0:00", "* invalid * * *", "", true);
125         cronCall("01/01/2003 0:00", "* -1 * * *", "", true);
126         cronCall("01/01/2003 0:00", "* * 20 * 0", "", true);
127         cronCall("01/01/2003 0:00", "* * 5-6-7 * *", "", true);
128         cronCall("01/01/2003 0:00", "* * 5/6-7 * *", "", true);
129         cronCall("01/01/2003 0:00", "* * 5-* * *", "", true);
130         cronCall("01/01/2003 0:00", "* * 5-6* * *", "", true);
131         cronCall("01/01/2003 0:00", "* * * * Mo", "", true);
132         cronCall("01/01/2003 0:00", "* * * jxxx *", "", true);
133         cronCall("01/01/2003 0:00", "* * * juxx *", "", true);
134         cronCall("01/01/2003 0:00", "* * * fbr *", "", true);
135         cronCall("01/01/2003 0:00", "* * * mch *", "", true);
136         cronCall("01/01/2003 0:00", "* * * mAh *", "", true);
137         cronCall("01/01/2003 0:00", "* * * arl *", "", true);
138         cronCall("01/01/2003 0:00", "* * * Spteber *", "", true);
139         cronCall("01/01/2003 0:00", "* * * otber *", "", true);
140         cronCall("01/01/2003 0:00", "* * * nvemtber *", "", true);
141         cronCall("01/01/2003 0:00", "* * * Dcmber *", "", true);
142         cronCall("01/01/2003 0:00", "* * * * mnday", "", true);
143         cronCall("01/01/2003 0:00", "* * * * tsdeday", "", true);
144         cronCall("01/01/2003 0:00", "* * * * wdnesday", "", true);
145         cronCall("01/01/2003 0:00", "* * * * frday", "", true);
146         cronCall("01/01/2003 0:00", "* * * * sdhdatr", "", true);
147
148         /**
149         * Values out of range
150         */

151         cronCall("01/01/2003 0:00", "* * 0 * *", "", true);
152         cronCall("01/01/2003 0:00", "* 50 * * *", "", true);
153         cronCall("01/01/2003 0:00", "* * * 1-20 *", "", true);
154         cronCall("01/01/2003 0:00", "* * 0-20 * *", "", true);
155         cronCall("01/01/2003 0:00", "* * 1-40 * *", "", true);
156         cronCall("01/01/2003 0:00", "* * * 1 8", "", true);
157         cronCall("01/01/2003 0:00", "* * 0/3 * *", "", true);
158         cronCall("01/01/2003 0:00", "* * 30 2 *", "", true); // 30th Feb doesn't ever exist!
159
cronCall("01/01/2003 0:00", "* * 31 4 *", "", true); // 31st April doesn't ever exist!
160
}
161
162     /**
163     * This tests the performance of the cron parsing engine. Note that it may take
164     * a couple of minutes o run - by default this test is disabled. Comment out the
165     * <code>return</code> statement at the start of this method to enable the
166     * benchmarking.
167     */

168     public void testPerformance() {
169         if (true) {
170             // return; // Comment out this line to benchmark
171
}
172
173         SimpleDateFormat JavaDoc sdf = new SimpleDateFormat JavaDoc("dd/MM/yyyy HH:mm");
174         Date date = null;
175
176         try {
177             date = sdf.parse("21/01/2003 16:27");
178         } catch (ParseException JavaDoc e) {
179             fail("Failed to parse date. Please check your unit test code!");
180         }
181
182         Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
183         calendar.setTime(date);
184
185         long baseTime = calendar.getTimeInMillis();
186         FastCronParser parser = null;
187
188         long time = 0;
189
190         try {
191             // Give HotSpot a chance to warm up
192
iterate("28 17 22 02 *", baseTime, time, 10000, true);
193
194             // Number of iterations to test
195
int count = 1000000;
196
197             // Test the best-case scenario
198
long bestCaseTime = iterate("* * * * *", baseTime, time, count, true);
199             System.out.println("Best case with parsing took " + bestCaseTime + "ms for " + count + " iterations. (" + (bestCaseTime / (float) count) + "ms per call)");
200
201             // Test a near worst-case scenario
202
long worstCaseTime = iterate("0-59,0-13,2,3,4,5 17-19 22-23,22,23 2,3 *", baseTime, time, count, true);
203             System.out.println("Worst case with parsing took " + worstCaseTime + "ms for " + count + " iterations. (" + (worstCaseTime / (float) count) + "ms per call)");
204
205             // Test the best-case scenario without parsing the expression on each iteration
206
bestCaseTime = iterate("* * * * *", baseTime, time, count, false);
207             System.out.println("Best case without parsing took " + bestCaseTime + "ms for " + count + " iterations. (" + (bestCaseTime / (float) count) + "ms per call)");
208
209             // Test a near worst-case scenario without parsing the expression on each iteration
210
worstCaseTime = iterate("0-59,0-13,2,3,4,5 17-19 22-23,22,23 2,3 *", baseTime, time, count, false);
211             System.out.println("Worst case without parsing took " + worstCaseTime + "ms for " + count + " iterations. (" + (worstCaseTime / (float) count) + "ms per call)");
212         } catch (ParseException JavaDoc e) {
213         }
214     }
215
216     /**
217     * Tests that a range of valid cron expressions get parsed correctly.
218     */

219     public void testValidExpressionParsing() {
220         FastCronParser parser;
221
222         // Check the default constructor
223
parser = new FastCronParser();
224         assertNull(parser.getCronExpression());
225
226         try {
227             parser = new FastCronParser("* * * * *");
228             assertEquals("* * * * *", parser.getCronExpression()); // Should be the same as what we gave it
229
assertEquals("* * * * *", parser.getExpressionSummary());
230
231             parser.setCronExpression("0 * * * *");
232             assertEquals("0 * * * *", parser.getCronExpression()); // Should be the same as what we gave it
233
assertEquals("0 * * * *", parser.getExpressionSummary());
234
235             parser.setCronExpression("5 10 * * 1,4,6");
236             assertEquals("5 10 * * 1,4,6", parser.getExpressionSummary());
237
238             parser.setCronExpression("0,5-20,4-15,24-27 0 * 2-4,5,6-3 *"); // Overlapping ranges, backwards ranges
239
assertEquals("0,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,24,25,26,27 0 * 2,3,4,5,6 *", parser.getExpressionSummary());
240         } catch (ParseException JavaDoc e) {
241             e.printStackTrace();
242             fail("Cron expression should have been valid: " + e);
243         }
244     }
245
246     /**
247     * Makes a call to the FastCronParser.
248     *
249     * @param dateStr The date string to use as the base date. The format must be
250     * <code>"dd/MM/yyyy HH:mm"</code>.
251     * @param cronExpr The cron expression to test.
252     * @param result The expected result. This should be a date in the same format
253     * as <code>dateStr</code>.
254     * @param expectException Pass in <code>true</code> if the {@link FastCronParser} is
255     * expected to throw a <code>ParseException</code>.
256     */

257     private void cronCall(String JavaDoc dateStr, String JavaDoc cronExpr, String JavaDoc result, boolean expectException) {
258         SimpleDateFormat JavaDoc sdf = new SimpleDateFormat JavaDoc("dd/MM/yyyy HH:mm");
259         Date date = null;
260
261         try {
262             date = sdf.parse(dateStr);
263         } catch (ParseException JavaDoc e) {
264             fail("Failed to parse date " + dateStr + ". Please check your unit test code!");
265         }
266
267         Calendar calendar = Calendar.getInstance();
268         calendar.setTime(date);
269
270         long baseTime = calendar.getTimeInMillis();
271         FastCronParser parser = null;
272
273         try {
274             parser = new FastCronParser(cronExpr);
275
276             if (expectException) {
277                 fail("Should have received a ParseException while parsing " + cronExpr);
278             }
279
280             long time = parser.getTimeBefore(baseTime);
281             assertEquals(result, sdf.format(new Date(time)));
282         } catch (ParseException JavaDoc e) {
283             if (!expectException) {
284                 fail("Unexpected ParseException while parsing " + cronExpr + ": " + e);
285             }
286         }
287     }
288
289     /**
290     * Used by the benchmarking
291     */

292     private long iterate(String JavaDoc cronExpr, long baseTime, long time, int count, boolean withParse) throws ParseException JavaDoc {
293         long startTime = System.currentTimeMillis();
294
295         if (withParse) {
296             FastCronParser parser = new FastCronParser();
297
298             for (int i = 0; i < count; i++) {
299                 parser.setCronExpression(cronExpr);
300                 time = parser.getTimeBefore(baseTime);
301             }
302         } else {
303             FastCronParser parser = new FastCronParser(cronExpr);
304
305             for (int i = 0; i < count; i++) {
306                 time = parser.getTimeBefore(baseTime);
307             }
308         }
309
310         long endTime = System.currentTimeMillis();
311         long duration = (endTime - startTime);
312         duration += (time - time); // Use the time variable to prevent it getting optimized away
313
return duration;
314     }
315 }
316
Popular Tags