package net.sourceforge.cruisecontrol.builders; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import net.sourceforge.cruisecontrol.BuilderTest; import net.sourceforge.cruisecontrol.CruiseControlException; import net.sourceforge.cruisecontrol.util.BuildOutputLogger; import net.sourceforge.cruisecontrol.util.Commandline; import net.sourceforge.cruisecontrol.util.Directory; import org.jdom.Element; import org.junit.After; import org.junit.Before; import org.junit.Test; public class XcodeBuilderTest { private XcodeBuilder builder; private Directory directoryDoesntFailValidation; private Map<String, String> buildProperties; @Before public void setUp() throws Exception { directoryDoesntFailValidation = new Directory() { @Override public void validate() { } }; builder = new XcodeBuilder(); builder.directory = directoryDoesntFailValidation; buildProperties = BuilderTest.createPropsWithProjectName("testproject"); } @After public void tearDown() throws Exception { builder = null; } @Test public void validateShouldCallDirectoryValidate() throws CruiseControlException { final Called validate = new Called(); builder.directory = new Directory() { @Override public void validate() { validate.called = true; } }; builder.validate(); assertTrue(validate.called); } @Test(expected = CruiseControlException.class) public void validateShouldMakeSureArgsArentEmpty() throws CruiseControlException { builder.createArg().setValue(" "); builder.validate(); } @Test public void getExitCodeShouldReturnSetExitCode() { builder.setExitCode(17); assertEquals(17, builder.getExitCode()); } @Test public void workingDirectoryShouldBeDirectory() throws CruiseControlException { builder.setDirectory("path"); Commandline cmdLine = builder.buildCommandline(); assertEquals("path", cmdLine.getWorkingDirectory().getPath()); } @Test public void executableShouldBeXcodebuild() throws CruiseControlException { Commandline cmdLine = builder.buildCommandline(); assertEquals("xcodebuild", cmdLine.getExecutable()); } @Test public void shouldBeNoDefaultArguments() throws CruiseControlException { Commandline cmdLine = builder.buildCommandline(); assertArrayEquals(new String[] {}, cmdLine.getArguments()); } @Test public void argsShouldBeOnCommandLine() throws CruiseControlException { String arg1 = "hello"; String arg2 = "world"; builder.createArg().setValue(arg1); builder.createArg().setValue(arg2); Commandline cmdLine = builder.buildCommandline(); String[] args = cmdLine.getArguments(); assertEquals(arg1, args[0]); assertEquals(arg2, args[1]); assertEquals(2, args.length); } @Test public void argsShouldHavePropertiesSubstituted() throws CruiseControlException { Map<String, String> properties = new HashMap<String, String>(); properties.put("key", "value"); builder.setProperties(properties); builder.createArg().setValue("${key}"); Commandline cmdLine = builder.buildCommandline(); String[] args = cmdLine.getArguments(); assertEquals("value", args[0]); assertEquals(1, args.length); } @Test public void compileLineShouldNotCreateAnElement() { Element e = builder.getElementFromLine(COMPILE_LINE); assertNull(e); } @Test public void warningLineShouldReturnWarningElement() { Element e = builder.getElementFromLine(WARNING_LINE); assertMessageAtLevel(WARNING_LINE, e, "warn"); } private void assertMessageAtLevel(String line, Element e, String level) { assertNotNull(e); assertEquals("target", e.getName()); Element task = e.getChild("task"); assertNotNull(task); Element message = task.getChild("message"); assertNotNull(message); assertEquals(level, message.getAttributeValue("priority")); assertEquals(line, message.getText()); } @Test public void errorLineShouldReturnErrorElement() { Element e = builder.getElementFromLine(ERROR_LINE); assertMessageAtLevel(ERROR_LINE, e, "error"); } @Test public void buildFaildShouldReturnErrorElement() { Element e = builder.getElementFromLine(BUILD_FAILED_LINE); assertMessageAtLevel(BUILD_FAILED_LINE, e, "error"); } @Test public void everythingAfterBuildFailedShouldReturnElement() { Element e = builder.getElementFromLine(COMMANDS_FAILED_LINE); assertNull(e); e = builder.getElementFromLine(BUILD_FAILED_LINE); assertMessageAtLevel(BUILD_FAILED_LINE, e, "error"); e = builder.getElementFromLine(COMMANDS_FAILED_LINE); assertMessageAtLevel(COMMANDS_FAILED_LINE, e, "error"); } @Test public void timeoutShouldBePassedToScriptRunner() throws CruiseControlException { final Called runScript = new Called(); final ScriptRunner runner = new ScriptRunner() { @Override public boolean runScript(Script script, long timeout, BuildOutputLogger logger) throws CruiseControlException { runScript.called = true; runScript.with = String.valueOf(timeout); return true; } }; builder = new XcodeBuilder() { @Override ScriptRunner createScriptRunner() { return runner; } @Override Element elementFromFile(OutputFile file) { return null; } }; builder.setTimeout(515); builder.build(buildProperties, null); assertTrue(runScript.called); assertEquals("515", runScript.with); } @Test public void timingOutShouldResultInFailedBuild() throws CruiseControlException { final MockOutputFile outputFile = new MockOutputFile(); outputFile.lines.add("hello world"); final ScriptRunner runner = new ScriptRunner() { @Override public boolean runScript(Script script, long timeout, BuildOutputLogger logger) throws CruiseControlException { return false; // returned when timeout happens } }; builder = new XcodeBuilder() { @Override ScriptRunner createScriptRunner() { return runner; } @Override OutputFile createOutputFile(Directory d, String filename) { return outputFile; } }; builder.setTimeout(515); Element result = builder.build(buildProperties, null); assertNotNull(result.getAttribute("error")); assertEquals("build timed out", result.getAttributeValue("error")); } @Test public void buildWithTargetShouldBePassedToCommandLine() throws CruiseControlException { final Called cmdLine = new Called(); builder = builderForBuildTest(cmdLine); builder.buildWithTarget(buildProperties, "target", null); assertTrue(cmdLine.called); assertTrue(cmdLine.with.contains("-target target")); } @Test public void buildWithTargetShouldBeTransient() throws CruiseControlException { final Called cmdLine = new Called(); builder = builderForBuildTest(cmdLine); builder.buildWithTarget(buildProperties, "target", null); builder.build(buildProperties, null); assertTrue(cmdLine.called); assertFalse(cmdLine.with.contains("-target target")); } @Test public void buildWithTargetShouldReplaceExistingTarget() throws CruiseControlException { final Called cmdLine = new Called(); builder = builderForBuildTest(cmdLine); builder.createArg().setValue("-target oldTarget"); builder.buildWithTarget(buildProperties, "newTarget", null); assertTrue(cmdLine.called); assertTrue(cmdLine.with.contains("-target newTarget")); assertFalse(cmdLine.with.contains("-target oldTarget")); } @Test public void afterBuildWithTargetOriginalTargetShouldBeRestored() throws CruiseControlException { final Called cmdLine = new Called(); builder = builderForBuildTest(cmdLine); builder.createArg().setValue("-target oldTarget"); builder.buildWithTarget(buildProperties, "newTarget", null); builder.build(buildProperties, null); assertTrue(cmdLine.called); assertTrue(cmdLine.with.contains("-target oldTarget")); assertFalse(cmdLine.with.contains("-target newTarget")); } private XcodeBuilder builderForBuildTest(final Called cmdLine) { final ScriptRunner runner = new ScriptRunner() { @Override public boolean runScript(Script script, long timeout, BuildOutputLogger logger) throws CruiseControlException { cmdLine.called = true; cmdLine.with = script.buildCommandline().toStringNoQuoting(); return true; } }; XcodeBuilder builderForBuildTest = new XcodeBuilder() { @Override ScriptRunner createScriptRunner() { return runner; } @Override OutputFile createOutputFile(Directory d, String filename) { return new MockOutputFile(); } }; builderForBuildTest.directory = directoryDoesntFailValidation; return builderForBuildTest; } private class Called { boolean called = false; String with; } private class MockOutputFile extends XcodeBuilder.OutputFile { final List<String> lines = new ArrayList<String>(); private Iterator iterator; MockOutputFile() { super(directoryDoesntFailValidation, "."); } @Override public String nextLine() { return (String) iterator.next(); } @Override public boolean hasMoreLines() { if (iterator == null) { iterator = lines.iterator(); } return iterator.hasNext(); } } private static final String COMPILE_LINE = " /Developer/usr/bin/ibtool --errors --warnings --notices" + " --output-format human-readable-text --compile " + "/Users/jfredrick/projects/App/build/Release-iphoneos/App.app/RegionView.nib " + "/Users/jfredrick/projects/App/src/com/jeffreyfredrick/app/view/RegionView.xib"; private static final String WARNING_LINE = "/Users/jfredrick/projects/" + "App/src/com/jeffreyfredrick/app/view/RegionView.xib:28: " + "warning: Setting a UIScrollView's minimum or maximum zoom to anything other than 1.0 will be ignored " + "on iPhone OS versions prior to 2.1."; private static final String ERROR_LINE = "/Users/jfredrick/projects/" + "App/src/com/jeffreyFredrick/EventDispatcher.h:11:16: " + "error: foo.h: No such file or directory"; private static final String BUILD_FAILED_LINE = "** BUILD FAILED **"; private static final String COMMANDS_FAILED_LINE = "The following build commands failed:"; }