A long time ago at work I ran into a small problem during some of our tests with fitnesse. We had a few fitnesse tests that tested time based rules, stuff like a 30 day rule that basically goes something like “if the file was received longer than 60 days ago, do not process it.” This was slightly problematic though as we had to update tests from time to time if we were going to hard code dates in our tests, so I decided to take it upon myself to create a wiki plugin to generate the date time dynamically.
Anyhow, I posted it on the fitnesse mailing list on yahoogroups, but I have neglected to take the time to write up instructions on it. So, without furth ado, here it is… the fitnesse sysdate plugin how to (or you can download it now).
Installation
Installation is rather simple… simply place the SysdatePlugin.jar plugin somewhere in your class path and add it to your plugins.properties file (if you don’t already have one, create it in the same directory you run fitnesse from).
Below is what you need to add to your plugins.properties file:
WikiWidgets=fitnesse.wikitext.widgets.SystemDateWikiWidget
Start fitnesse and you’re ready to go! You should see the following at the console once fitnesse starts up.
Custom wiki widgets loaded:
fitnesse.wikitext.widgets.SystemDateWikiWidget
Usage
Using the plugin is quite simple. To start using it right away, simply type:
!sysdate
This will print the current date in MMddYYYY format. If you want to use a different format, you have the option of passing the format in as a parameter… basically it will accept any format string that SimpleDateFormat takes, so refer to the api documentation for SimpleDateFormat for further information.
For example, if I wanted to print the date for today as something like 11/11/2006 21:15:23, I’d use the following:
!sysdate(MM/dd/yyyy hh:mm:ss)
Of course, it’s not usefull without features to generate a date in the future, or in the past. For example, say I want to have a test that alsways tests a boundry condition, say against a date that is always EXACTLY 30 days in the past.
The following are examples of various formats to use. Simply adding a +number or -number to the sysdate will make n days in the past or future.
!sysdate(MM/dd/yyyy hh:mm:ss)-30
!sysdate-30
!sysdate+30
!sysdate(MM/dd/yyyy hh:mm:ss)+5
That’s about it. If there are any features anyone like to have, please feel free to let me know. You can download the plugin here.
The Source
Here is the accompanying unit test:
package fitnesse.wikitext.widgets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import fitnesse.wiki.*;
import fitnesse.wikitext.WidgetBuilder;
public class SystemDateWikiWidgetTest extends WidgetTest
{
private WikiPage root;
private PageCrawler crawler;
private WikiPage page;
private WidgetRoot widgetRoot;
public void setUp() throws Exception
{
root = InMemoryPage.makeRoot("root");
crawler = root.getPageCrawler();
page = crawler.addPage(root, PathParser.parse("MyPage"));
widgetRoot = new WidgetRoot("", page);
}
public void testMatches() throws Exception
{
assertMatches("!sysdate");
assertMatches("!sysdate(mmmddyyyy)");
assertMatches("!sysdate(MM-dd-yy)");
assertMatches("!sysdate(MM-dd-yy)-1");
assertMatches("!sysdate(MM-dd-yy)-22");
assertMatches("!sysdate(MM-dd-yy)-300");
assertMatches("!sysdate-30");
}
protected String getRegexp()
{
return SystemDateWikiWidget.REGEXP;
}
/**
* Allow adding of sysdate special variable
*
* @author jamescarr
* @throws Exception
*/
public void testSysdateVariable() throws Exception{
WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), "!sysdaten");
WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");
WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, "!sysdate");
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat format = new SimpleDateFormat("MMddyyyy");
System.out.println(format.format(date));
assertEquals(format.format(date), w.render());
}
public void testSysdateVariableWithFormat() throws Exception{
WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), "!sysdate(MMM-dd-yyyy)n");
WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");
WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, "!sysdate(MMM-dd-yyyy)");
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat format = new SimpleDateFormat("MMM-dd-yyyy");
System.out.println(format.format(date));
assertEquals(format.format(date), w.render());
}
public void testSysdateVariableWithBadFormat() throws Exception{
WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), "!define var {!sysdate(shithead)}n");
WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");
WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, "!sysdate(shithead)");
// default
SimpleDateFormat format = new SimpleDateFormat("MMddyyyy");
Date date = new Date(System.currentTimeMillis());
assertEquals( format.format(date), w.render());
}
public void testMultipleSysDateCalls() throws Exception{
String dates = "!sysdate(MMMddyyyy)n!sysdaten!sysdate(MM-dd-yyyy)n!sysdate(yyyy-MMM-dd)n!sysdate(MM/dd/yyyy)";
WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), dates);
WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");
WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, dates);
// expect only the first date
SimpleDateFormat format = new SimpleDateFormat("MMMddyyyy");
Date date = new Date(System.currentTimeMillis());
assertEquals(format.format(date), w.render());
}
public void testSubtractZeroDays() throws Exception{
String dates = "!sysdate(MM/dd/yyyy)-0";
WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), dates);
WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");
WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, dates);
// expect only the first date
SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
Calendar cal = Calendar.getInstance();
Date date = new Date(System.currentTimeMillis());
String dateString = format.format(date);
assertEquals(dateString, w.render());
}
public void testSubtractTwentyDays() throws Exception{
String dates = "!sysdate(MM/dd/yyyy)-20";
WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), dates);
WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");
WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, dates);
// expect only the first date
SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
Calendar cal = Calendar.getInstance();
Date date = new Date(System.currentTimeMillis());
cal.setTime(date);
cal.add(Calendar.DATE, -20);
date = cal.getTime();
String dateString = format.format(date);
assertEquals(dateString, w.render());
}
public void testAddTwentyDays() throws Exception{
String dates = "!sysdate(MM/dd/yyyy)+20";
WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), dates);
WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");
WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, dates);
// expect only the first date
SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
Calendar cal = Calendar.getInstance();
Date date = new Date(System.currentTimeMillis());
cal.setTime(date);
cal.add(Calendar.DATE, 20);
date = cal.getTime();
String dateString = format.format(date);
assertEquals(dateString, w.render());
}
public void testAddThreeHundredDays() throws Exception{
String dates = "!sysdate(MM/dd/yyyy)+300";
WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), dates);
WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");
WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, dates);
// expect only the first date
SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
Calendar cal = Calendar.getInstance();
Date date = new Date(System.currentTimeMillis());
cal.setTime(date);
cal.add(Calendar.DATE, 300);
date = cal.getTime();
String dateString = format.format(date);
assertEquals(dateString, w.render());
}
}
And here is the source for the date widget class:
package fitnesse.wikitext.widgets;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import fitnesse.wikitext.WikiWidget;
public class SystemDateWikiWidget extends WikiWidget {
private static final String SUBTRACT = "-";
private static final String ADD = "+";
private static final String DATE_FORMAT_REGEX = "\(\w+.*\)";
public static final String REGEXP = "!sysdate(\(\w+.*?\))?(-\d+|\+\d+)?";
public static final Pattern pattern = Pattern.compile(REGEXP);
private static final Pattern dateMethodPattern = Pattern.compile(DATE_FORMAT_REGEX);
private static final Pattern subtractionPattern = Pattern.compile("(-\d+|\+\d+)$");
private String originalText = "";
private String token = null;
public SystemDateWikiWidget(ParentWidget parent, String text) {
super(parent);
originalText = text;
Matcher match = pattern.matcher(text);
if (match.find()) {
token = match.group(0);
}
}
private String getCurrentDate(String token) {
SimpleDateFormat formatter = getDateFormatter(token);
Date date = getTheDate(token);
return formatter.format(date);
}
private Date getTheDate(String token) {
Matcher match = subtractionPattern.matcher(token);
Date date = new Date(System.currentTimeMillis());
int days = 0;
if (match.find()) {
String result = match.group(0);
if (0 == result.indexOf(SUBTRACT)) {
days = getIntegerValue(result, SUBTRACT);
}
else if (0 == result.indexOf(ADD)) {
days = getIntegerValue(result, ADD);
}
date = getDateDaysFromDate(date, days);
}
return date;
}
private Date getDateDaysFromDate(Date date, int days) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.DATE, days);
return cal.getTime();
}
private SimpleDateFormat getDateFormatter(String token) {
SimpleDateFormat formatter = new SimpleDateFormat("MMddyyyy");
Matcher match = dateMethodPattern.matcher(token);
if (match.find()) {
String format = match.group(0);
format = removeParenthesis(format);
try {
formatter = new SimpleDateFormat(format);
}
catch (IllegalArgumentException e) {
}
}
return formatter;
}
private int getIntegerValue(String result, String modifier) {
result = result.replaceAll("\" + modifier, "");
return (SUBTRACT.equals(modifier)) ? -Integer.parseInt(result) : Integer.parseInt(result);
}
private String removeParenthesis(String format) {
return format.replaceAll("\(", "").replaceAll("\)", "");
}
public String render() throws Exception {
return (token != null) ? getCurrentDate(token) : originalText;
}
}