package net.sourceforge.cruisecontrol.sourcecontrols; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.text.ParseException; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.Locale; import java.util.TimeZone; import junit.framework.TestCase; import net.sourceforge.cruisecontrol.CruiseControlException; import net.sourceforge.cruisecontrol.Modification; /** * Tests for Team Foundation Server source control. * * @author <a href="http://www.woodwardweb.com">Martin Woodward</a> */ public class TeamFoundationServerTest extends TestCase { private TimeZone originalTimeZone; private TeamFoundationServer tfs; private Locale originalLocal; private Date minDate; private static final String CHANGESET_DATA = "Changeset: 1645\n" + "User: martin\n" + "Date: 13 December 2006 21:51:50\n" + "\n" + "Comment:\n" + " test line 1!\u90B5\n" + " test line 2\n" + "\n" + "Items:\n" + " edit $/demo/connectfour/src/net/sourceforge/cruisecontrol/sampleproject/\u90B5/connectfour/Cell.java\n" + "\n" + "Check-in Notes:\n" + " Code Reviewer:\n" + " Performance Reviewer:\n" + " Security Reviewer:\n" + "\n" + "\n"; private static final String CHANGESET_DATA_US = "Changeset: 1645\n" + "User: martin\n" + "Date: Wednesday, December 13, 2006 9:51:50 PM\n" + "\n" + "Comment:\n" + " test line 1!\n" + " test line 2\n" + "\n" + "Items:\n" + " edit $/demo/connectfour/src/net/sourceforge/cruisecontrol/sampleproject/connectfour/Cell.java\n" + "\n" + "Check-in Notes:\n" + " Code Reviewer:\n" + " Performance Reviewer:\n" + " Security Reviewer:\n" + "\n" + "\n"; private static final String CHANGESET_DATA_MIN = "Changeset: 1645\n" + "User: martin\n" + "Date: 13 December 2006 21:51:50\n" + "\n" + "Comment:\n" + "\n" + "Items:\n" + " edit $/demo/connectfour/src/net/sourceforge/cruisecontrol/sampleproject/connectfour/Cell.java\n" + "\n" + "\n"; private static final String CHANGESET_DATA_MULTI_ITEM = "Changeset: 1645\n" + "User: martin\n" + "Date: 13 December 2006 21:51:50\n" + "\n" + "Comment:\n" + " test line 1!\n" + " test line 2\n" + "\n" + "Items:\n" + " edit $/demo/connectfour/README.txt\n" + " edit, rename " + "$/demo/connectfour/src/net/sourceforge/cruisecontrol/sampleproject/connectfour/Cell.java\n" + " add $/demo/connectfour/src/net/sourceforge/cruisecontrol/sampleproject/connectfour/text.txt\n" + "\n" + "Check-in Notes:\n" + " Code Reviewer:\n" + " Performance Reviewer:\n" + " Security Reviewer:\n" + "\n" + "\n"; private static final String CHANGESET_DATA_TEAMPRISE = "Changeset: 1650\n" + "User:\tCDESG\\martin\n" + "Date:\t14-Dec-2006 16:06:37\n" + "\n" + "Comment:\n" + " Making a change with a few things in it.\n" + "Lets see what happens.\n\nEspecially with carrage returns in the comments !!\n" + "\n" + "Items:\n" + " edit $/demo/connectfour/README.txt\n" + " edit, rename " + "$/demo/connectfour/src/net/sourceforge/cruisecontrol/sampleproject/connectfour/Cell.java\n" + " add " + "$/demo/connectfour/src/net/sourceforge/cruisecontrol/sampleproject/connectfour/" + "new file with spaces and a very long file name so we can see what happens with wrapping.txt\n" + "\n" + "Check-in Notes:\n" + "\n" + " Code Reviewer:\n" + " Performance Reviewer:\n" + " Security Reviewer:\n" + "\n" + "\n"; /* * (non-Javadoc) * * @see junit.framework.TestCase#setUp() */ protected void setUp() throws Exception { originalTimeZone = TimeZone.getDefault(); originalLocal = Locale.getDefault(); tfs = new TeamFoundationServer(); tfs.setServer("http://tfsserver:8080"); tfs.setProjectPath("$/TeamProjectName/path"); // Set up a default date to be used for the lastBuild date when minDate = (new GregorianCalendar(1976, 3, 10)).getTime(); } /* * (non-Javadoc) * * @see junit.framework.TestCase#tearDown() */ protected void tearDown() throws Exception { TimeZone.setDefault(originalTimeZone); Locale.setDefault(originalLocal); tfs = null; originalTimeZone = null; } /** * Test method for * {@link net.sourceforge.cruisecontrol.sourcecontrols.TeamFoundationServer#formatUTCDate(java.util.Date)}. */ public void testFormatUTCDate() { TimeZone.setDefault(TimeZone.getTimeZone("GMT")); Date janFirstAM2006 = new GregorianCalendar(2006, Calendar.JANUARY, 1, 1, 1, 1).getTime(); assertEquals("2006-01-01T01:01:01Z", TeamFoundationServer.formatUTCDate(janFirstAM2006)); Date decThirtyFirstPM2006 = new GregorianCalendar(2006, Calendar.DECEMBER, 31, 23, 59, 59).getTime(); assertEquals("2006-12-31T23:59:59Z", TeamFoundationServer.formatUTCDate(decThirtyFirstPM2006)); // Test running in diferent timezones. TimeZone.setDefault(TimeZone.getTimeZone("GMT-06:00")); // CST Date marTenth1976 = new GregorianCalendar(1976, Calendar.MARCH, 10, 12, 13, 14).getTime(); assertEquals("1976-03-10T18:13:14Z", TeamFoundationServer.formatUTCDate(marTenth1976)); TimeZone.setDefault(TimeZone.getTimeZone("GMT+10:00")); Date aprilEleventh2004 = new GregorianCalendar(2004, Calendar.APRIL, 11, 18, 37, 5).getTime(); assertEquals("2004-04-11T08:37:05Z", TeamFoundationServer.formatUTCDate(aprilEleventh2004)); } /** * History command should be in the following format * * tf history /noprompt /server:http://tfsserver:8080 $/TeamProjectName/path * /version:D2006-12-01T01:01:01Z~D2006-12-13T20:00:00Z /recursive /user:* * /format:detailed */ public void testBuildHistoryCommand() throws CruiseControlException { Date checkTime = new Date(); long tenMinutes = 10 * 60 * 1000; Date lastBuild = new Date(checkTime.getTime() - tenMinutes); String[] expectedCmd = new String[] { "tf", "history", "-noprompt", "-server:http://tfsserver:8080", "$/TeamProjectName/path", "-version:D" + TeamFoundationServer.formatUTCDate(lastBuild) + "~D" + TeamFoundationServer.formatUTCDate(checkTime), "-recursive", "-format:detailed" }; String[] actualCmd = tfs.buildHistoryCommand(lastBuild, checkTime).getCommandline(); assertArraysEquals(expectedCmd, actualCmd); } /** * History command should be in the following format * * tf history /noprompt /server:http://tfsserver:8080 $/TeamProjectName/path * /version:D2006-12-01T01:01:01Z~D2006-12-13T20:00:00Z /recursive /user:* * /format:detailed /login:"DOMAIN\name","password" */ public void testBuildHistoryCommandWithCredentials() throws CruiseControlException { tfs.setUsername("DOMAIN\\name"); tfs.setPassword("password"); Date checkTime = new Date(); long tenMinutes = 10 * 60 * 1000; Date lastBuild = new Date(checkTime.getTime() - tenMinutes); String[] expectedCmd = new String[] { "tf", "history", "-noprompt", "-server:http://tfsserver:8080", "$/TeamProjectName/path", "-version:D" + TeamFoundationServer.formatUTCDate(lastBuild) + "~D" + TeamFoundationServer.formatUTCDate(checkTime), "-recursive", "-format:detailed", "-login:DOMAIN\\name,password" }; String[] actualCmd = tfs.buildHistoryCommand(lastBuild, checkTime).getCommandline(); assertArraysEquals(expectedCmd, actualCmd); } public void testParseChangesetGetChangeset() throws ParseException { Modification modification = parseChangeset(CHANGESET_DATA); assertEquals("1645", modification.revision); } public void testParseChangesetGetUser() throws ParseException { Modification modification = parseChangeset(CHANGESET_DATA); assertEquals("martin", modification.userName); } public void testParseChangesetGetDate() throws ParseException { Modification modification1 = parseChangeset(CHANGESET_DATA); assertEquals(new GregorianCalendar(2006, Calendar.DECEMBER, 13, 21, 51, 50).getTime(), modification1.modifiedTime); Modification modification2 = parseChangeset(CHANGESET_DATA_US); assertEquals(new GregorianCalendar(2006, Calendar.DECEMBER, 13, 21, 51, 50).getTime(), modification2.modifiedTime); } public void testParseChangesetGetComment() throws ParseException { Modification modification1 = parseChangeset(CHANGESET_DATA); assertEquals("test line 1!\u90B5\ntest line 2", modification1.comment); Modification modification2 = parseChangeset(CHANGESET_DATA_MIN); assertEquals("", modification2.comment); } public void testParseChangesetGetItems() throws ParseException { List list = TeamFoundationServer.TFHistoryParser.parseChangeset(CHANGESET_DATA_MULTI_ITEM, minDate); assertNotNull(list); assertEquals(3, list.size()); assertTrue(list.get(0) instanceof Modification); assertTrue(list.get(1) instanceof Modification); assertTrue(list.get(2) instanceof Modification); assertEquals("$/demo/connectfour/README.txt", ((Modification.ModifiedFile) ((Modification) list.get(0)).files.get(0)).fileName); assertEquals("edit", ((Modification.ModifiedFile) ((Modification) list.get(0)).files.get(0)).action); assertEquals("$/demo/connectfour/src/net/sourceforge/cruisecontrol/sampleproject/connectfour/Cell.java", ((Modification.ModifiedFile) ((Modification) list.get(1)).files.get(0)).fileName); assertEquals("edit, rename", ((Modification.ModifiedFile) ((Modification) list.get(1)).files.get(0)).action); assertEquals("$/demo/connectfour/src/net/sourceforge/cruisecontrol/sampleproject/connectfour/text.txt", ((Modification.ModifiedFile) ((Modification) list.get(2)).files.get(0)).fileName); assertEquals("add", ((Modification.ModifiedFile) ((Modification) list.get(2)).files.get(0)).action); } public void testParseTeampriseChangeset() throws ParseException { List list = TeamFoundationServer.TFHistoryParser.parseChangeset(CHANGESET_DATA_TEAMPRISE, minDate); assertNotNull(list); assertEquals(3, list.size()); assertTrue(list.get(0) instanceof Modification); assertTrue(list.get(1) instanceof Modification); assertTrue(list.get(2) instanceof Modification); assertEquals("$/demo/connectfour/README.txt", ((Modification) list.get(0)).getFileName()); } public void testUnParseableDataFound() throws IOException { String tfOutput = "---------------------------------------------------" + "------------------------------------------------\r\n" + "Changeset: 1234\r\n" + "User: ethomson\r\n" + "Date: 14 December 2006 23:16:59\r\n" + "\r\n" + "Comment:\r\n" + " Should throw a parse error\r\n" + "\r\n" + "Items:\r\n" + " edit Not a valid TFS PATH.\r\n" + " edit $Not a valid TFS PATH.\r\n" + "\r\n" + "Check-in Notes:\r\n" + " Code Reviewer:\r\n" + " Performance Reviewer:\r\n" + " Security Reviewer:\r\n" + "\r\n"; Reader reader = new StringReader(tfOutput); try { TeamFoundationServer.TFHistoryParser.parse(reader, minDate); } catch (ParseException e) { assertEquals("Parse error.", e.getMessage().substring(0, "Parse error.".length())); return; } fail("A parse exception should have been raised."); } public void testParseNoModifications() throws IOException, ParseException { String tfOutput = "No history entries were found for the item and version combination specified." + "\r\n"; Reader reader = new StringReader(tfOutput); List list = TeamFoundationServer.TFHistoryParser.parse(reader, minDate); assertNotNull(list); assertEquals(0, list.size()); } public void testTFS2008Bug() throws IOException, ParseException { // CC-735: Compatibility with TFS2008. // TFS2008 RTM has an issue whereby it will return the latest changeset from a query history // call even when that change happened before the date range passed in the query. // We must therefore check that any returned changesets happened in the window // of time that interests us (i.e. occurred after or equal to our search start date) String tfOutput = "-----------------------------------------------------" + "----------------------------------------------\r\n" + "Changeset: 29\r\n" + "User: ptakale_cp\r\n" + "Date: 11 May 2006 20:23:37\r\n" + "\r\n" + "Comment:\r\n" + " Upgraded to VS8\r\n" + "\r\n" + "Items:\r\n" + " edit $/TestProject7/WindowsApplication1.sln\r\n" + " edit $/TestProject7/WindowsApplication1/WindowsApplication1.vbproj.user\r\n" + " edit $/TestProject7/WindowsApplication1/WindowsApplication1.vbproj\r\n" + "\r\n"; Date lastBuildDate = (new GregorianCalendar(2007, 1, 1)).getTime(); Reader reader = new StringReader(tfOutput); List list = TeamFoundationServer.TFHistoryParser.parse(reader, lastBuildDate); assertNotNull(list); assertEquals(0, list.size()); } public void testFullParseFromCodePlex() throws IOException, ParseException { // A bit of overkill this, but testing parsing routine on real live data // from CodePlex just to be sure String tfOutput = "-----------------------------------------------------" + "----------------------------------------------\r\n" + "Changeset: 29\r\n" + "User: ptakale_cp\r\n" + "Date: 11 May 2006 20:23:37\r\n" + "\r\n" + "Comment:\r\n" + " Upgraded to VS8\r\n" + "\r\n" + "Items:\r\n" + " edit $/TestProject7/WindowsApplication1.sln\r\n" + " edit $/TestProject7/WindowsApplication1/WindowsApplication1.vbproj.user\r\n" + " edit $/TestProject7/WindowsApplication1/WindowsApplication1.vbproj\r\n" + "\r\n" + "-----------------------------------------------------" + "----------------------------------------------\r\n" + "Changeset: 28\r\n" + "User: ptakale_cp\r\n" + "Date: 11 May 2006 20:20:21\r\n" + "\r\n" + "Comment:\r\n" + " Sample Project check-in\r\n" + "\r\n" + "Items:\r\n" + " add $/TestProject7/WindowsApplication1.sln\r\n" + " add $/TestProject7/WindowsApplication1\r\n" + " add $/TestProject7/WindowsApplication1/AssemblyInfo.vb\r\n" + " add $/TestProject7/WindowsApplication1/Form1.resx\r\n" + " add $/TestProject7/WindowsApplication1/Form1.vb\r\n" + " add $/TestProject7/WindowsApplication1/WindowsApplication1.vbproj.user\r\n" + " add $/TestProject7/WindowsApplication1/WindowsApplication1.vbproj\r\n" + "\r\n" + "-------------------------------------------------------" + "--------------------------------------------\r\n" + "Changeset: 7\r\n" + "User: RNO\\_MCLWEB\r\n" + "Date: 20 April 2006 02:20:30\r\n" + "\r\n" + "Comment:\r\n" + " Created team project folder $/TestProject7 via the Team Project Creation Wizard\r\n" + "\r\n" + "Items:\r\n" + " add $/TestProject7\r\n" + "\r\n"; Reader reader = new StringReader(tfOutput); List list = TeamFoundationServer.TFHistoryParser.parse(reader, minDate); assertNotNull(list); assertEquals(11, list.size()); } public void testParseDate() throws ParseException { Date actualDate = TeamFoundationServer.TFHistoryParser.parseDate("20 April 2006 02:20:30"); assertEquals(new GregorianCalendar(2006, Calendar.APRIL, 20, 2, 20, 30).getTime(), actualDate); } public void testParseDateSweden() throws ParseException { // Test a non US or GB locale Locale.setDefault(new Locale("sv", "SE")); TimeZone.setDefault(TimeZone.getTimeZone("CET")); Date actualDate = TeamFoundationServer.TFHistoryParser.parseDate("2007-mar-13 17:11:04 "); assertEquals(new GregorianCalendar(2007, Calendar.MARCH, 13, 17, 11, 4).getTime(), actualDate); } /** * Helper method to return the first changeset from a set of passed data. * * @throws ParseException */ private Modification parseChangeset(String data) throws ParseException { List list = TeamFoundationServer.TFHistoryParser.parseChangeset(data, minDate); assertNotNull(list); assertTrue(list.get(0) instanceof Modification); return (Modification) list.get(0); } /** * Helper method for testing array equality. */ private static void assertArraysEquals(Object[] expected, Object[] actual) { assertEquals("array lengths mismatch!", expected.length, actual.length); for (int i = 0; i < expected.length; i++) { assertEquals(expected[i], actual[i]); } } }