001: /********************************************************************************
002: * CruiseControl, a Continuous Integration Toolkit
003: * Copyright (c) 2001, ThoughtWorks, Inc.
004: * 200 E. Randolph, 25th Floor
005: * Chicago, IL 60601 USA
006: * All rights reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * + Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * + Redistributions in binary form must reproduce the above
016: * copyright notice, this list of conditions and the following
017: * disclaimer in the documentation and/or other materials provided
018: * with the distribution.
019: *
020: * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
021: * names of its contributors may be used to endorse or promote
022: * products derived from this software without specific prior
023: * written permission.
024: *
025: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
026: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
027: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
028: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
029: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
030: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
031: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
032: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
033: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
034: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
035: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
036: ********************************************************************************/package net.sourceforge.cruisecontrol;
037:
038: import java.io.File;
039: import java.io.FileWriter;
040: import java.io.IOException;
041: import java.util.ArrayList;
042: import java.util.List;
043:
044: import junit.framework.TestCase;
045: import net.sourceforge.cruisecontrol.config.XMLConfigManager;
046: import net.sourceforge.cruisecontrol.listeners.ListenerTestPlugin;
047: import net.sourceforge.cruisecontrol.testutil.TestUtil;
048: import net.sourceforge.cruisecontrol.util.Util;
049:
050: /**
051: *
052: * @author <a href="mailto:robertdw@users.sourceforge.net">Robert Watkins</a>
053: */
054: public final class CruiseControlControllerTest extends TestCase {
055:
056: private final TestUtil.FilesToDelete filesToDelete = new TestUtil.FilesToDelete();
057:
058: private final File dir = new File(TestUtil.getTargetDir(), "target");
059: private final File configFile = new File(dir, "_tempConfigFile");
060: private final File configFile2 = new File(dir, "_tempConfigFile2");
061: private CruiseControlController ccController;
062:
063: protected void setUp() throws Exception {
064: Util.doMkDirs(dir);
065: ccController = new CruiseControlController();
066: ensureFileDoesntExist(configFile);
067: ensureFileDoesntExist(configFile2);
068:
069: filesToDelete.add(new File(TestUtil.getTargetDir(), "logs"));
070: }
071:
072: public void tearDown() {
073: ccController.pause();
074: ccController = null;
075: ensureFileDoesntExist(configFile);
076: ensureFileDoesntExist(configFile2);
077:
078: filesToDelete.delete();
079: }
080:
081: private void ensureFileDoesntExist(File file) {
082: final long start = System.currentTimeMillis();
083: final long oneMinute = 60 * 1000;
084: while (file.exists()) {
085: file.delete();
086: if (System.currentTimeMillis() > (start + oneMinute)) {
087: fail("unable to delete file " + file.getPath());
088: }
089: }
090: assertFalse(file.exists());
091: }
092:
093: public void testSetFileFailsWithNull() {
094: try {
095: ccController.setConfigFile(null);
096: fail("Allowed to not set a config file");
097: } catch (CruiseControlException expected) {
098: assertEquals("No config file", expected.getMessage());
099: }
100: }
101:
102: public void testSetFileFailsIfFileDoesntExist() {
103: try {
104: ccController.setConfigFile(configFile);
105: fail("Config file must exist");
106: } catch (CruiseControlException expected) {
107: assertEquals("Config file not found: "
108: + configFile.getAbsolutePath(), expected
109: .getMessage());
110: }
111: }
112:
113: public void testSetFileFailsIfPassedDirectory() throws IOException {
114: File tempFile = File.createTempFile("temp", ".file");
115: tempFile.deleteOnExit();
116:
117: try {
118: ccController.setConfigFile(tempFile.getParentFile());
119: fail("Config file can't be directory");
120: } catch (CruiseControlException expected) {
121: assertEquals("Config file not found: "
122: + tempFile.getParentFile().getAbsolutePath(),
123: expected.getMessage());
124: }
125: }
126:
127: public void testLoadEmptyProjects() throws IOException,
128: CruiseControlException {
129: FileWriter configOut = new FileWriter(configFile);
130: writeHeader(configOut);
131: writeFooterAndClose(configOut);
132:
133: ccController.setConfigFile(configFile);
134: assertEquals(configFile, ccController.getConfigFile());
135: assertTrue(ccController.getProjects().isEmpty());
136: }
137:
138: public void testLoadThreadCount() throws IOException,
139: CruiseControlException {
140: FileWriter configOut = new FileWriter(configFile);
141: writeHeader(configOut);
142: configOut.write("<system><configuration>"
143: + "<threads count=\"2\"/>"
144: + "</configuration></system>");
145: writeFooterAndClose(configOut);
146:
147: ccController.setConfigFile(configFile);
148: assertEquals(configFile, ccController.getConfigFile());
149: assertTrue(ccController.getProjects().isEmpty());
150: }
151:
152: public void testLoadSomeProjects() throws IOException,
153: CruiseControlException {
154: ccController = new CruiseControlController();
155:
156: FileWriter configOut = new FileWriter(configFile);
157: writeHeader(configOut);
158: writeProjectDetails(configOut, "testProject1", 30);
159: writeProjectDetails(configOut, "testProject2", 30);
160: writeFooterAndClose(configOut);
161:
162: ccController.setConfigFile(configFile);
163: assertEquals(configFile, ccController.getConfigFile());
164: assertEquals(2, ccController.getProjects().size());
165: }
166:
167: public void testSetConfigFileShouldFailWithDuplicateProjects()
168: throws IOException {
169: ccController = new CruiseControlController();
170:
171: FileWriter configOut = new FileWriter(configFile);
172: writeHeader(configOut);
173: writeProjectDetails(configOut, "testProject1", 30);
174: writeProjectDetails(configOut, "testProject1", 30);
175: writeFooterAndClose(configOut);
176:
177: try {
178: ccController.setConfigFile(configFile);
179: fail("duplicate project names should fail");
180: } catch (CruiseControlException expected) {
181: assertEquals(
182: "Duplicate entries in config file for project name testProject1",
183: expected.getMessage());
184: }
185: }
186:
187: // FIXME this is a test for the XMLConfigManager
188: public void testLoadSomeProjectsWithParametrizedNames()
189: throws IOException, CruiseControlException {
190: ccController = new CruiseControlController();
191:
192: FileWriter configOut = new FileWriter(configFile);
193: writeHeader(configOut);
194: // a property that defines the project name.
195: configOut
196: .write(" <property name='name' value='testProject'/>\n");
197: configOut.write(" <property name='encoding' value='utf8'/>\n");
198: // this to test that plugin preconfiguration still works
199: configOut
200: .write(" <plugin name='testlistener' "
201: + "classname='net.sourceforge.cruisecontrol.listeners.ListenerTestPlugin' "
202: + "string='listener for ${project.name}'/>\n");
203:
204: // this to test that project name can be parametrized
205: configOut.write(" <project name='${name}1'>\n");
206: configOut
207: .write(" <modificationset><alwaysbuild/></modificationset>\n");
208: configOut.write(" <schedule><ant/></schedule>\n");
209: configOut.write(" <listeners><testlistener/></listeners>\n");
210: configOut
211: .write(" <log dir='logs/${project.name}' encoding='${encoding}'/>\n");
212: configOut.write(" </project>\n");
213: writeFooterAndClose(configOut);
214:
215: ccController.setConfigFile(configFile);
216: assertEquals(configFile, ccController.getConfigFile());
217: assertEquals(1, ccController.getProjects().size());
218: final Project project = ((ProjectConfig) ccController
219: .getProjects().get(0)).getProject();
220: assertEquals("project name can be resolved", "testProject1",
221: project.getName());
222:
223: assertEquals("project name can be resolved", "testProject1",
224: project.getLog().getProjectName());
225:
226: List listeners = project.getListeners();
227: assertEquals(1, listeners.size());
228: Listener listener = (Listener) listeners.get(0);
229: assertEquals(ListenerTestPlugin.class, listener.getClass());
230: assertEquals("listener for testProject1",
231: ((ListenerTestPlugin) listener).getString());
232:
233: assertEquals("logs/testProject1", project.getLogDir());
234: assertEquals("utf8", project.getLog().getLogXmlEncoding());
235: }
236:
237: public void testConfigReloading() throws IOException,
238: CruiseControlException {
239: MyListener listener = new MyListener();
240:
241: ccController = new CruiseControlController();
242:
243: ccController.addListener(listener);
244: FileWriter configOut = new FileWriter(configFile);
245: writeHeader(configOut);
246: writeProjectDetails(configOut, "testProject1", 30);
247: writeProjectDetails(configOut, "testProject2", 30);
248: writeFooterAndClose(configOut);
249:
250: ccController.setConfigFile(configFile);
251:
252: assertEquals(configFile, ccController.getConfigFile());
253: assertEquals(2, ccController.getProjects().size());
254: assertEquals(2, listener.added.size());
255: assertEquals(0, listener.removed.size());
256:
257: listener.clear();
258:
259: // no change - no reload
260: assertFalse(ccController.parseConfigFileIfNecessary());
261:
262: // nothing happened
263: assertEquals(0, listener.added.size());
264: assertEquals(0, listener.removed.size());
265:
266: // add a project:
267:
268: listener.clear();
269:
270: sleep(1200);
271: configOut = new FileWriter(configFile);
272: writeHeader(configOut);
273: writeProjectDetails(configOut, "testProject1", 30);
274: writeProjectDetails(configOut, "testProject2", 30);
275: writeProjectDetails(configOut, "testProject3", 30);
276: writeFooterAndClose(configOut);
277:
278: assertTrue(ccController.parseConfigFileIfNecessary());
279:
280: assertEquals(3, ccController.getProjects().size());
281: assertEquals(1, listener.added.size());
282: assertEquals(0, listener.removed.size());
283:
284: // remove 2 projects
285:
286: listener.clear();
287:
288: sleep(1200);
289: configOut = new FileWriter(configFile);
290: writeHeader(configOut);
291: writeProjectDetails(configOut, "testProject3", 30);
292: writeFooterAndClose(configOut);
293:
294: // ccController.reloadConfigFile();
295: assertTrue(ccController.parseConfigFileIfNecessary());
296:
297: assertEquals(1, ccController.getProjects().size());
298: assertEquals(0, listener.added.size());
299: assertEquals(2, listener.removed.size());
300: }
301:
302: public void testConfigReloadingWithXmlInclude() throws IOException,
303: CruiseControlException {
304: MyListener listener = new MyListener();
305:
306: ccController = new CruiseControlController();
307:
308: ccController.addListener(listener);
309:
310: FileWriter configOut2 = new FileWriter(configFile2);
311: writeProjectDetails(configOut2, "testProject1", 30);
312: writeProjectDetails(configOut2, "testProject2", 30);
313: configOut2.close();
314:
315: FileWriter wrapperConfigOut = new FileWriter(configFile);
316: wrapperConfigOut.write("<?xml version=\"1.0\" ?>\n");
317: wrapperConfigOut.write("<!DOCTYPE cruisecontrol [ \n");
318: wrapperConfigOut.write("<!ENTITY projects SYSTEM \""
319: + configFile2.getName() + "\"> \n");
320: wrapperConfigOut.write("]> \n");
321: wrapperConfigOut.write("<cruisecontrol>\n");
322: wrapperConfigOut.write("&projects;");
323: writeFooterAndClose(wrapperConfigOut);
324:
325: ccController.setConfigFile(configFile);
326:
327: assertEquals(configFile, ccController.getConfigFile());
328: assertEquals(2, ccController.getProjects().size());
329: assertEquals(2, listener.added.size());
330: assertEquals(0, listener.removed.size());
331:
332: listener.clear();
333:
334: // no change - no reload
335: assertFalse(ccController.parseConfigFileIfNecessary());
336:
337: // nothing happened
338: assertEquals(0, listener.added.size());
339: assertEquals(0, listener.removed.size());
340:
341: // add a project:
342:
343: listener.clear();
344:
345: sleep(1200);
346: configOut2 = new FileWriter(configFile2);
347: writeProjectDetails(configOut2, "testProject1", 30);
348: writeProjectDetails(configOut2, "testProject2", 30);
349: writeProjectDetails(configOut2, "testProject3", 30);
350: configOut2.close();
351:
352: assertTrue(ccController.parseConfigFileIfNecessary());
353:
354: assertEquals(3, ccController.getProjects().size());
355: assertEquals(1, listener.added.size());
356: assertEquals(0, listener.removed.size());
357:
358: // remove 2 projects
359:
360: listener.clear();
361:
362: sleep(1200);
363: configOut2 = new FileWriter(configFile2);
364: writeProjectDetails(configOut2, "testProject3", 30);
365: configOut2.close();
366:
367: // ccController.reloadConfigFile();
368: assertTrue(ccController.parseConfigFileIfNecessary());
369:
370: assertEquals(1, ccController.getProjects().size());
371: assertEquals(0, listener.added.size());
372: assertEquals(2, listener.removed.size());
373: }
374:
375: public void testShouldReloadConfigurationWhenPluginAttributesChange()
376: throws Exception {
377: ccController = new CruiseControlController();
378:
379: FileWriter configOut = new FileWriter(configFile);
380: writeHeader(configOut);
381: writeProjectDetails(configOut, "testProject1", 30);
382: writeFooterAndClose(configOut);
383:
384: ccController.setConfigFile(configFile);
385: ProjectConfig originalProject = (ProjectConfig) ccController
386: .getProjects().get(0);
387:
388: // no change - no reload
389: assertFalse(ccController.parseConfigFileIfNecessary());
390:
391: sleep(1200);
392: configOut = new FileWriter(configFile);
393: writeHeader(configOut);
394: writeProjectDetails(configOut, "testProject1", 60);
395: writeFooterAndClose(configOut);
396:
397: assertTrue(ccController.parseConfigFileIfNecessary());
398: ProjectConfig modifiedProject = (ProjectConfig) ccController
399: .getProjects().get(0);
400:
401: assertEquals(30 * 1000, originalProject.getSchedule()
402: .getInterval());
403: assertEquals(60 * 1000, modifiedProject.getSchedule()
404: .getInterval());
405: assertEquals(60 * 1000, modifiedProject.getProject()
406: .getBuildInterval());
407: }
408:
409: public void testRegisterPlugins() throws IOException,
410: CruiseControlException {
411: FileWriter configOut = new FileWriter(configFile);
412: writeHeader(configOut);
413: configOut
414: .write(" <plugin name='testname' "
415: + "classname='net.sourceforge.cruisecontrol.CruiseControllerTest'/>\n");
416: configOut
417: .write(" <plugin name='labelincrementer' classname='my.global.Incrementer'/>\n");
418: writeFooterAndClose(configOut);
419:
420: ccController.setConfigFile(configFile);
421: XMLConfigManager configManager = ccController
422: .getConfigManager();
423: CruiseControlConfig config = configManager
424: .getCruiseControlConfig();
425:
426: PluginRegistry newRegistry = config.getRootPlugins();
427: assertTrue(newRegistry.isPluginRegistered("testname"));
428: assertFalse(newRegistry.isPluginRegistered("unknown_plugin"));
429: assertEquals(
430: newRegistry.getPluginClassname("labelincrementer"),
431: "my.global.Incrementer");
432: }
433:
434: public void testSetConfigFileShouldValidateAllElements()
435: throws IOException {
436: FileWriter configOut = new FileWriter(configFile);
437: writeHeader(configOut);
438: configOut.write(" <project name='buildlogger'>\n");
439: configOut
440: .write(" <modificationset><alwaysbuild/></modificationset>\n");
441: configOut.write(" <schedule><ant/></schedule>\n");
442: configOut.write(" <log>\n");
443: configOut.write(" <merge/>\n");
444: configOut.write(" </log>\n");
445: configOut.write(" </project>\n");
446: writeFooterAndClose(configOut);
447:
448: try {
449: ccController.setConfigFile(configFile);
450: fail("BuildLogger.validate() was not called");
451: } catch (CruiseControlException ccex) {
452: assertEquals("one of file or dir are required attributes",
453: ccex.getMessage());
454: }
455: }
456:
457: private void writeHeader(FileWriter configOut) throws IOException {
458: configOut.write("<?xml version=\"1.0\" ?>\n");
459: configOut.write("<cruisecontrol>\n");
460: }
461:
462: private void writeFooterAndClose(FileWriter configOut)
463: throws IOException {
464: configOut.write("</cruisecontrol>\n");
465: configOut.close();
466: }
467:
468: private void sleep(long l) {
469: try {
470: Thread.sleep(l);
471: } catch (InterruptedException dontCare) {
472: System.out.println("dontCare happened");
473: }
474: }
475:
476: private void writeProjectDetails(FileWriter configOut,
477: final String projectName, int interval) throws IOException {
478: configOut.write("<project name='" + projectName + "'>\n");
479: configOut
480: .write(" <modificationset><alwaysbuild/></modificationset>\n");
481: configOut.write(" <schedule interval=\"" + interval
482: + "\"><ant/></schedule>\n");
483: configOut.write("</project>\n");
484: }
485:
486: final class MyListener implements CruiseControlController.Listener {
487: private final List added = new ArrayList();
488: private final List removed = new ArrayList();
489:
490: public void clear() {
491: added.clear();
492: removed.clear();
493: }
494:
495: public void projectAdded(ProjectInterface project) {
496: added.add(project);
497: }
498:
499: public void projectRemoved(ProjectInterface project) {
500: removed.add(project);
501: }
502: }
503: }
|