001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.solr.util;
017:
018: import org.apache.solr.util.DateMathParser;
019:
020: import junit.framework.Test;
021: import junit.framework.TestCase;
022: import junit.framework.TestSuite;
023:
024: import java.text.SimpleDateFormat;
025: import java.text.DateFormat;
026: import java.util.Calendar;
027: import java.util.Date;
028: import java.util.TimeZone;
029: import java.util.Locale;
030:
031: import java.util.Map;
032: import java.util.HashMap;
033: import java.util.Iterator;
034: import java.text.ParseException;
035:
036: /**
037: * Tests that the functions in DateMathParser
038: */
039: public class DateMathParserTest extends TestCase {
040:
041: public static TimeZone UTC = TimeZone.getTimeZone("UTC");
042:
043: /**
044: * A formatter for specifying every last nuance of a Date for easy
045: * refernece in assertion statements
046: */
047: private DateFormat fmt;
048: /**
049: * A parser for reading in explicit dates that are convinient to type
050: * in a test
051: */
052: private DateFormat parser;
053:
054: public DateMathParserTest() {
055: super ();
056: fmt = new SimpleDateFormat(
057: "G yyyyy MM ww WW DD dd F E aa HH hh mm ss SSS z Z",
058: Locale.US);
059: fmt.setTimeZone(UTC);
060:
061: parser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS",
062: Locale.US);
063: parser.setTimeZone(UTC);
064: }
065:
066: /** MACRO: Round: parses s, rounds with u, fmts */
067: protected String r(String s, String u) throws Exception {
068: Date d = parser.parse(s);
069: Calendar c = Calendar.getInstance(UTC, Locale.US);
070: c.setTime(d);
071: DateMathParser.round(c, u);
072: return fmt.format(c.getTime());
073: }
074:
075: /** MACRO: Add: parses s, adds v u, fmts */
076: protected String a(String s, int v, String u) throws Exception {
077: Date d = parser.parse(s);
078: Calendar c = Calendar.getInstance(UTC, Locale.US);
079: c.setTime(d);
080: DateMathParser.add(c, v, u);
081: return fmt.format(c.getTime());
082: }
083:
084: /** MACRO: Expected: parses s, fmts */
085: protected String e(String s) throws Exception {
086: return fmt.format(parser.parse(s));
087: }
088:
089: protected void assertRound(String e, String i, String u)
090: throws Exception {
091: String ee = e(e);
092: String rr = r(i, u);
093: assertEquals(ee + " != " + rr + " round:" + i + ":" + u, ee, rr);
094: }
095:
096: protected void assertAdd(String e, String i, int v, String u)
097: throws Exception {
098:
099: String ee = e(e);
100: String aa = a(i, v, u);
101: assertEquals(
102: ee + " != " + aa + " add:" + i + "+" + v + ":" + u, ee,
103: aa);
104: }
105:
106: protected void assertMath(String e, DateMathParser p, String i)
107: throws Exception {
108:
109: String ee = e(e);
110: String aa = fmt.format(p.parseMath(i));
111: assertEquals(ee + " != " + aa + " math:"
112: + parser.format(p.getNow()) + ":" + i, ee, aa);
113: }
114:
115: public void testCalendarUnitsConsistency() throws Exception {
116: String input = "2001-07-04T12:08:56.235";
117: for (String u : DateMathParser.CALENDAR_UNITS.keySet()) {
118: try {
119: r(input, u);
120: } catch (IllegalStateException e) {
121: assertNotNull("no logic for rounding: " + u, e);
122: }
123: try {
124: a(input, 1, u);
125: } catch (IllegalStateException e) {
126: assertNotNull("no logic for rounding: " + u, e);
127: }
128: }
129: }
130:
131: public void testRound() throws Exception {
132:
133: String input = "2001-07-04T12:08:56.235";
134:
135: assertRound("2001-07-04T12:08:56.000", input, "SECOND");
136: assertRound("2001-07-04T12:08:00.000", input, "MINUTE");
137: assertRound("2001-07-04T12:00:00.000", input, "HOUR");
138: assertRound("2001-07-04T00:00:00.000", input, "DAY");
139: assertRound("2001-07-01T00:00:00.000", input, "MONTH");
140: assertRound("2001-01-01T00:00:00.000", input, "YEAR");
141:
142: }
143:
144: public void testAddZero() throws Exception {
145:
146: String input = "2001-07-04T12:08:56.235";
147:
148: for (String u : DateMathParser.CALENDAR_UNITS.keySet()) {
149: assertAdd(input, input, 0, u);
150: }
151: }
152:
153: public void testAdd() throws Exception {
154:
155: String input = "2001-07-04T12:08:56.235";
156:
157: assertAdd("2001-07-04T12:08:56.236", input, 1, "MILLISECOND");
158: assertAdd("2001-07-04T12:08:57.235", input, 1, "SECOND");
159: assertAdd("2001-07-04T12:09:56.235", input, 1, "MINUTE");
160: assertAdd("2001-07-04T13:08:56.235", input, 1, "HOUR");
161: assertAdd("2001-07-05T12:08:56.235", input, 1, "DAY");
162: assertAdd("2001-08-04T12:08:56.235", input, 1, "MONTH");
163: assertAdd("2002-07-04T12:08:56.235", input, 1, "YEAR");
164:
165: }
166:
167: public void testParseStatelessness() throws Exception {
168:
169: DateMathParser p = new DateMathParser(UTC, Locale.US);
170: p.setNow(parser.parse("2001-07-04T12:08:56.235"));
171:
172: String e = fmt.format(p.parseMath(""));
173:
174: Date trash = p.parseMath("+7YEARS");
175: trash = p.parseMath("/MONTH");
176: trash = p.parseMath("-5DAYS+20MINUTES");
177: Thread.currentThread().sleep(5);
178:
179: String a = fmt.format(p.parseMath(""));
180: assertEquals("State of DateMathParser changed", e, a);
181: }
182:
183: public void testParseMath() throws Exception {
184:
185: DateMathParser p = new DateMathParser(UTC, Locale.US);
186: p.setNow(parser.parse("2001-07-04T12:08:56.235"));
187:
188: // No-Op
189: assertMath("2001-07-04T12:08:56.235", p, "");
190:
191: // simple round
192: assertMath("2001-07-04T12:08:56.000", p, "/SECOND");
193: assertMath("2001-07-04T12:08:00.000", p, "/MINUTE");
194: assertMath("2001-07-04T12:00:00.000", p, "/HOUR");
195: assertMath("2001-07-04T00:00:00.000", p, "/DAY");
196: assertMath("2001-07-01T00:00:00.000", p, "/MONTH");
197: assertMath("2001-01-01T00:00:00.000", p, "/YEAR");
198:
199: // simple addition
200: assertMath("2001-07-04T12:08:56.236", p, "+1MILLISECOND");
201: assertMath("2001-07-04T12:08:57.235", p, "+1SECOND");
202: assertMath("2001-07-04T12:09:56.235", p, "+1MINUTE");
203: assertMath("2001-07-04T13:08:56.235", p, "+1HOUR");
204: assertMath("2001-07-05T12:08:56.235", p, "+1DAY");
205: assertMath("2001-08-04T12:08:56.235", p, "+1MONTH");
206: assertMath("2002-07-04T12:08:56.235", p, "+1YEAR");
207:
208: // simple subtraction
209: assertMath("2001-07-04T12:08:56.234", p, "-1MILLISECOND");
210: assertMath("2001-07-04T12:08:55.235", p, "-1SECOND");
211: assertMath("2001-07-04T12:07:56.235", p, "-1MINUTE");
212: assertMath("2001-07-04T11:08:56.235", p, "-1HOUR");
213: assertMath("2001-07-03T12:08:56.235", p, "-1DAY");
214: assertMath("2001-06-04T12:08:56.235", p, "-1MONTH");
215: assertMath("2000-07-04T12:08:56.235", p, "-1YEAR");
216:
217: // simple '+/-'
218: assertMath("2001-07-04T12:08:56.235", p,
219: "+1MILLISECOND-1MILLISECOND");
220: assertMath("2001-07-04T12:08:56.235", p, "+1SECOND-1SECOND");
221: assertMath("2001-07-04T12:08:56.235", p, "+1MINUTE-1MINUTE");
222: assertMath("2001-07-04T12:08:56.235", p, "+1HOUR-1HOUR");
223: assertMath("2001-07-04T12:08:56.235", p, "+1DAY-1DAY");
224: assertMath("2001-07-04T12:08:56.235", p, "+1MONTH-1MONTH");
225: assertMath("2001-07-04T12:08:56.235", p, "+1YEAR-1YEAR");
226:
227: // simple '-/+'
228: assertMath("2001-07-04T12:08:56.235", p,
229: "-1MILLISECOND+1MILLISECOND");
230: assertMath("2001-07-04T12:08:56.235", p, "-1SECOND+1SECOND");
231: assertMath("2001-07-04T12:08:56.235", p, "-1MINUTE+1MINUTE");
232: assertMath("2001-07-04T12:08:56.235", p, "-1HOUR+1HOUR");
233: assertMath("2001-07-04T12:08:56.235", p, "-1DAY+1DAY");
234: assertMath("2001-07-04T12:08:56.235", p, "-1MONTH+1MONTH");
235: assertMath("2001-07-04T12:08:56.235", p, "-1YEAR+1YEAR");
236:
237: // more complex stuff
238: assertMath("2000-07-04T12:08:56.236", p, "+1MILLISECOND-1YEAR");
239: assertMath("2000-07-04T12:08:57.235", p, "+1SECOND-1YEAR");
240: assertMath("2000-07-04T12:09:56.235", p, "+1MINUTE-1YEAR");
241: assertMath("2000-07-04T13:08:56.235", p, "+1HOUR-1YEAR");
242: assertMath("2000-07-05T12:08:56.235", p, "+1DAY-1YEAR");
243: assertMath("2000-08-04T12:08:56.235", p, "+1MONTH-1YEAR");
244: assertMath("2000-07-04T12:08:56.236", p, "-1YEAR+1MILLISECOND");
245: assertMath("2000-07-04T12:08:57.235", p, "-1YEAR+1SECOND");
246: assertMath("2000-07-04T12:09:56.235", p, "-1YEAR+1MINUTE");
247: assertMath("2000-07-04T13:08:56.235", p, "-1YEAR+1HOUR");
248: assertMath("2000-07-05T12:08:56.235", p, "-1YEAR+1DAY");
249: assertMath("2000-08-04T12:08:56.235", p, "-1YEAR+1MONTH");
250: assertMath("2000-07-01T00:00:00.000", p,
251: "-1YEAR+1MILLISECOND/MONTH");
252: assertMath("2000-07-04T00:00:00.000", p, "-1YEAR+1SECOND/DAY");
253: assertMath("2000-07-04T00:00:00.000", p, "-1YEAR+1MINUTE/DAY");
254: assertMath("2000-07-04T13:00:00.000", p, "-1YEAR+1HOUR/HOUR");
255: assertMath("2000-07-05T12:08:56.000", p, "-1YEAR+1DAY/SECOND");
256: assertMath("2000-08-04T12:08:56.000", p, "-1YEAR+1MONTH/SECOND");
257:
258: // "tricky" cases
259: p.setNow(parser.parse("2006-01-31T17:09:59.999"));
260: assertMath("2006-02-28T17:09:59.999", p, "+1MONTH");
261: assertMath("2008-02-29T17:09:59.999", p, "+25MONTH");
262: assertMath("2006-02-01T00:00:00.000", p, "/MONTH+35DAYS/MONTH");
263: assertMath("2006-01-31T17:10:00.000", p, "+3MILLIS/MINUTE");
264:
265: }
266:
267: public void testParseMathExceptions() throws Exception {
268:
269: DateMathParser p = new DateMathParser(UTC, Locale.US);
270: p.setNow(parser.parse("2001-07-04T12:08:56.235"));
271:
272: Map<String, Integer> badCommands = new HashMap<String, Integer>();
273: badCommands.put("/", 1);
274: badCommands.put("+", 1);
275: badCommands.put("-", 1);
276: badCommands.put("/BOB", 1);
277: badCommands.put("+SECOND", 1);
278: badCommands.put("-2MILLI/", 4);
279: badCommands.put(" +BOB", 0);
280: badCommands.put("+2SECONDS ", 3);
281: badCommands.put("/4", 1);
282: badCommands.put("?SECONDS", 0);
283:
284: for (String command : badCommands.keySet()) {
285: try {
286: Date out = p.parseMath(command);
287: fail("Didn't generate ParseException for: " + command);
288: } catch (ParseException e) {
289: assertEquals("Wrong pos for: " + command + " => "
290: + e.getMessage(), badCommands.get(command)
291: .intValue(), e.getErrorOffset());
292:
293: }
294: }
295:
296: }
297:
298: }
|