/******************************************************************************** * CruiseControl, a Continuous Integration Toolkit * Copyright (c) 2001, ThoughtWorks, Inc. * 200 E. Randolph, 25th Floor * Chicago, IL 60601 USA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * + Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * + Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the * names of its contributors may be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************************/ package net.sourceforge.cruisecontrol; import java.util.Calendar; import java.util.Date; import java.util.SimpleTimeZone; import java.util.TimeZone; import junit.framework.TestCase; import net.sourceforge.cruisecontrol.builders.MockBuilder; import org.jdom.Element; public class ScheduleTest extends TestCase { private Schedule schedule; private TimeZone defaultTimeZone; private static final long ONE_MINUTE = Schedule.ONE_MINUTE; private static final long ONE_HOUR = 60 * ONE_MINUTE; private static final long TWELVE_HOURS = 12 * ONE_HOUR; private static final long ONE_DAY = Schedule.ONE_DAY; private static final TimeZone LOS_ANGELES; private static final Calendar FRIDAY; private static final Calendar THURSDAY; private static final Date THURSDAY_1001; private static final Date THURSDAY_1101; private static final Date THURSDAY_1201; private static final Date THURSDAY_2301; private static final Date FRIDAY_0000; private static final MockBuilder NOON_BUILDER; private static final MockBuilder MIDNIGHT_BUILDER; private static final MockBuilder MULTIPLE_5; private static final MockBuilder MULTIPLE_1; private static final PauseBuilder PAUSE_11_13; private static final PauseBuilder PAUSE_11_13_FRIDAY; static { LOS_ANGELES = createLosAngelesTimeZone(); NOON_BUILDER = new MockBuilder(); NOON_BUILDER.setTime("1200"); NOON_BUILDER.setBuildLogXML(new Element("builder1")); MIDNIGHT_BUILDER = new MockBuilder(); MIDNIGHT_BUILDER.setTime("0000"); MIDNIGHT_BUILDER.setBuildLogXML(new Element("builder1")); MULTIPLE_5 = new MockBuilder(); MULTIPLE_5.setMultiple(5); MULTIPLE_5.setBuildLogXML(new Element("builder2")); MULTIPLE_1 = new MockBuilder(); MULTIPLE_1.setMultiple(1); MULTIPLE_1.setBuildLogXML(new Element("builder3")); PAUSE_11_13 = new PauseBuilder(); PAUSE_11_13.setStartTime(1100); PAUSE_11_13.setEndTime(1300); PAUSE_11_13_FRIDAY = new PauseBuilder(); PAUSE_11_13_FRIDAY.setStartTime(1100); PAUSE_11_13_FRIDAY.setEndTime(1300); PAUSE_11_13_FRIDAY.setDay("friday"); THURSDAY = Calendar.getInstance(); THURSDAY.set(2001, Calendar.NOVEMBER, 22); FRIDAY = Calendar.getInstance(); FRIDAY.set(2001, Calendar.NOVEMBER, 23); THURSDAY_1001 = getDate(THURSDAY, 10, 01); THURSDAY_1101 = getDate(THURSDAY, 11, 01); THURSDAY_1201 = getDate(THURSDAY, 12, 01); THURSDAY_2301 = getDate(THURSDAY, 23, 01); FRIDAY_0000 = getDate(FRIDAY, 0, 0); } protected void setUp() { schedule = new Schedule(); schedule.add(MULTIPLE_1); schedule.add(NOON_BUILDER); schedule.add(MIDNIGHT_BUILDER); schedule.add(MULTIPLE_5); defaultTimeZone = TimeZone.getDefault(); TimeZone.setDefault(LOS_ANGELES); } protected void tearDown() throws Exception { schedule = null; TimeZone.setDefault(defaultTimeZone); } private static TimeZone createLosAngelesTimeZone() { // taken from SimpleTimeZone javadoc: // Base GMT offset: -8:00 // DST starts: at 2:00am in standard time // on the first Sunday in April // DST ends: at 2:00am in daylight time // on the last Sunday in October // Save: 1 hour return new SimpleTimeZone(-28800000, "America/Los_Angeles", Calendar.APRIL, 1, -Calendar.SUNDAY, 7200000, Calendar.OCTOBER, -1, Calendar.SUNDAY, 7200000, 3600000); } /** * @param calendar Calendar with date set * @param hour * @param min * @return Date with date and time set */ private static Date getDate(Calendar calendar, int hour, int min) { Calendar cal = (Calendar) calendar.clone(); cal.setTimeZone(LOS_ANGELES); cal.set(Calendar.HOUR_OF_DAY, hour); cal.set(Calendar.MINUTE, min); return cal.getTime(); } public void testSelectBuilder() throws CruiseControlException { Builder buildIsMultipleOfOne = schedule.selectBuilder(13, THURSDAY_1001, THURSDAY_1101); assertEquals(MULTIPLE_1, buildIsMultipleOfOne); Builder buildIsMultipleOfFive = schedule.selectBuilder(10, THURSDAY_1001, THURSDAY_1101); assertEquals(MULTIPLE_5, buildIsMultipleOfFive); Builder middayTimeBuild = schedule.selectBuilder(13, THURSDAY_1001, THURSDAY_1201); assertEquals(NOON_BUILDER, middayTimeBuild); Builder midnightTimeBuild = schedule.selectBuilder(13, THURSDAY_1001, FRIDAY_0000); assertEquals(MIDNIGHT_BUILDER, midnightTimeBuild); Calendar wednesday = Calendar.getInstance(); wednesday.set(2001, Calendar.NOVEMBER, 21); Date noonWednesday = getDate(wednesday, 12, 00); Builder timeBuildAcrossDays = schedule.selectBuilder(11, noonWednesday, FRIDAY_0000); assertEquals(MIDNIGHT_BUILDER, timeBuildAcrossDays); Schedule timeBasedSchedule = new Schedule(); timeBasedSchedule.add(NOON_BUILDER); timeBasedSchedule.add(MIDNIGHT_BUILDER); Builder nextTimeBuilder = timeBasedSchedule.selectBuilder(3, THURSDAY_1001, THURSDAY_1101); assertEquals(NOON_BUILDER, nextTimeBuilder); try { Schedule badSchedule = new Schedule(); badSchedule.selectBuilder(1, THURSDAY_1001, THURSDAY_1101); fail("should fail with no builders"); } catch (CruiseControlException expected) { } } public void testSelectBuilder_MultipleBuildersWithDaySet() throws CruiseControlException { Builder thursdayBuilder = new MockBuilder(); thursdayBuilder.setDay("thursday"); Schedule scheduledByDay = new Schedule(); scheduledByDay.add(thursdayBuilder); assertEquals(thursdayBuilder, scheduledByDay.selectBuilder(1, THURSDAY_1001, THURSDAY_1101)); assertEquals(thursdayBuilder, scheduledByDay.selectBuilder(1, THURSDAY_1001, FRIDAY_0000)); Builder fridayBuilder = new MockBuilder(); fridayBuilder.setDay("friday"); scheduledByDay.add(fridayBuilder); assertEquals(thursdayBuilder, scheduledByDay.selectBuilder(1, THURSDAY_1001, THURSDAY_1101)); assertEquals(fridayBuilder, scheduledByDay.selectBuilder(1, THURSDAY_1001, FRIDAY_0000)); } public void testSelectBuilder_ForcedBuildAcrossTimeChangeBoundary() throws CruiseControlException { Builder thursdayBuilder = new MockBuilder(); thursdayBuilder.setTime("1120"); thursdayBuilder.setDay("thursday"); Schedule scheduledByDay = new Schedule(); scheduledByDay.add(thursdayBuilder); Calendar oct272005 = Calendar.getInstance(); oct272005.set(2005, Calendar.OCTOBER, 27); // mimic a JMX trigger Date last = getDate(oct272005, 11, 20); Date now = getDate(oct272005, 11, 25); assertEquals(thursdayBuilder, scheduledByDay.selectBuilder(123, last, now)); } public void testIsPaused() { PauseBuilder pauseBuilder = new PauseBuilder(); pauseBuilder.setStartTime(2300); pauseBuilder.setEndTime(2359); schedule.add(pauseBuilder); assertEquals(schedule.isPaused(THURSDAY_2301), true); assertEquals(schedule.isPaused(THURSDAY_1101), false); } public void testGetTimeToNextBuild() { long elevenHours = 11 * ONE_HOUR; long oneHourFiftyNineMinutes = (2 * ONE_HOUR) - ONE_MINUTE; PauseBuilder pause = new PauseBuilder(); pause.setStartTime(2300); pause.setEndTime(2359); schedule.add(pause); assertEquals( "next time build > build interval", ONE_MINUTE, schedule.getTimeToNextBuild(THURSDAY_1001, ONE_MINUTE)); assertEquals( "next time build < build interval", oneHourFiftyNineMinutes, schedule.getTimeToNextBuild(THURSDAY_1001, elevenHours)); assertEquals( "next time build is tomorrow", TWELVE_HOURS - ONE_MINUTE, schedule.getTimeToNextBuild(THURSDAY_1201, ONE_DAY * 2)); assertEquals( "wait till after pause", TWELVE_HOURS - ONE_MINUTE, schedule.getTimeToNextBuild(THURSDAY_1201, elevenHours)); assertEquals( "wait till after pause we're in", ONE_HOUR - ONE_MINUTE, schedule.getTimeToNextBuild(THURSDAY_2301, ONE_MINUTE)); pause = new PauseBuilder(); pause.setStartTime(0000); pause.setEndTime(100); schedule.add(pause); assertEquals( "wait till after pause on next day", 2 * ONE_HOUR, schedule.getTimeToNextBuild(THURSDAY_2301, ONE_HOUR)); assertEquals( "two back-to-back pauses", 2 * ONE_HOUR, schedule.getTimeToNextBuild(THURSDAY_2301, ONE_MINUTE)); pause = new PauseBuilder(); pause.setStartTime(0000); pause.setEndTime(2359); pause.setDay("friday"); schedule.add(pause); assertEquals( "chained pauses with day specific pause", ONE_DAY + (2 * ONE_HOUR), schedule.getTimeToNextBuild(THURSDAY_2301, ONE_HOUR)); } public void testGetTimeToNextBuild_MultipleBuilderWithDaySet() { Schedule intervalThursdaysSchedule = new Schedule(); Builder intervalThursdays = new MockBuilder(); intervalThursdays.setDay("thursday"); intervalThursdaysSchedule.add(intervalThursdays); assertEquals(ONE_MINUTE, intervalThursdaysSchedule.getTimeToNextBuild(THURSDAY_2301, ONE_MINUTE)); assertEquals(6 * ONE_DAY + ONE_HOUR - ONE_MINUTE, intervalThursdaysSchedule.getTimeToNextBuild(THURSDAY_2301, ONE_HOUR)); assertEquals(6 * ONE_DAY + ONE_HOUR - ONE_MINUTE, intervalThursdaysSchedule.getTimeToNextBuild(THURSDAY_2301, ONE_DAY)); } public void testGetTimeToNextBuild_ShouldUseIntervalWhenThereAreNoBuilders() { Schedule badSchedule = new Schedule(); assertEquals( ONE_DAY, badSchedule.getTimeToNextBuild(THURSDAY_1001, ONE_DAY)); } public void testGetTimeToNextBuild_ShouldNotExceedMaximumInterval() { Schedule badSchedule = new Schedule(); PauseBuilder alwaysPaused = new PauseBuilder(); alwaysPaused.setStartTime(0000); alwaysPaused.setEndTime(2359); badSchedule.add(alwaysPaused); assertEquals( Schedule.MAX_INTERVAL_MILLISECONDS, badSchedule.getTimeToNextBuild(THURSDAY_1001, ONE_DAY)); badSchedule = new Schedule(); badSchedule.add(NOON_BUILDER); badSchedule.add(PAUSE_11_13); assertEquals( Schedule.MAX_INTERVAL_MILLISECONDS, badSchedule.getTimeToNextBuild(THURSDAY_1101, ONE_MINUTE)); } public void testGetTimeToNextBuild_DailyBuild() { Schedule dailyBuildSchedule = new Schedule(); dailyBuildSchedule.add(NOON_BUILDER); assertEquals( "ignore interval when only time builds", ONE_HOUR - ONE_MINUTE, dailyBuildSchedule.getTimeToNextBuild(THURSDAY_1101, ONE_MINUTE)); PauseBuilder pause = new PauseBuilder(); pause.setStartTime(0000); pause.setEndTime(2359); pause.setDay("friday"); dailyBuildSchedule.add(pause); assertEquals( "pause w/only time builder", (ONE_DAY * 2) - ONE_MINUTE, dailyBuildSchedule.getTimeToNextBuild(THURSDAY_1201, ONE_MINUTE)); } public void testGetTimeToNextBuild_WeeklyBuild() { schedule = new Schedule(); Builder weeklyBuilder = new MockBuilder(); weeklyBuilder.setTime("0100"); weeklyBuilder.setDay("Sunday"); schedule.add(weeklyBuilder); assertEquals((ONE_DAY * 2) + ONE_HOUR, schedule.getTimeToNextBuild(FRIDAY_0000, ONE_MINUTE)); } public void testGetTimeToNextBuild_WeeklyBuildAcrossDaylightSavingsTimeBoundary() { Builder thursdayBuilder = new MockBuilder(); thursdayBuilder.setTime("1120"); thursdayBuilder.setDay("thursday"); Schedule scheduledByDay = new Schedule(); scheduledByDay.add(thursdayBuilder); Calendar oct272005 = Calendar.getInstance(); oct272005.set(2005, Calendar.OCTOBER, 27); Date now = getDate(oct272005, 11, 25); long dstEndingAdjustment = ONE_HOUR; assertEquals(((ONE_DAY * 6) + (ONE_HOUR * 23) + (ONE_MINUTE * 55) + dstEndingAdjustment), scheduledByDay.getTimeToNextBuild(now, ONE_MINUTE)); } // http://jira.public.thoughtworks.org/browse/CC-863 // <!-- no activity on weekends --> // <pause day="saturday" starttime="0000" endtime="2359"/> // <pause day="sunday" starttime="0000" endtime="2359"/> // <!-- only run between 11:50pm-midnight Mon-Thu --> // <pause day="monday" starttime="0000" endtime="2349"/> // <pause day="friday" starttime="2350" endtime="2359"/> public void testGetTimeToNextBuild_WithPausesAndEndOfDST() { Schedule scheduleWithPauses = new Schedule(); scheduleWithPauses.setInterval(ONE_MINUTE * 10); Builder builder = new MockBuilder(); scheduleWithPauses.add(builder); PauseBuilder pause = new PauseBuilder(); pause.setDay("saturday"); pause.setStartTime(0); pause.setEndTime(2359); scheduleWithPauses.add(pause); pause = new PauseBuilder(); pause.setDay("sunday"); pause.setStartTime(0); pause.setEndTime(2359); scheduleWithPauses.add(pause); pause = new PauseBuilder(); pause.setDay("monday"); pause.setStartTime(0); pause.setEndTime(2349); scheduleWithPauses.add(pause); pause = new PauseBuilder(); pause.setDay("friday"); pause.setStartTime(2350); pause.setEndTime(2359); scheduleWithPauses.add(pause); Calendar fridayBeforeDstEnd = Calendar.getInstance(); fridayBeforeDstEnd.set(2006, Calendar.OCTOBER, 27); Date now = getDate(fridayBeforeDstEnd, 23, 50); long dstEndingAdjustment = ONE_HOUR; assertEquals(((ONE_DAY * 3) + dstEndingAdjustment), scheduleWithPauses.getTimeToNextBuild(now, ONE_MINUTE)); } public void testGetTimeToNextBuild_MonthlyBuild() { schedule = new Schedule(); Builder monthlyBuilder = new MockBuilder() { private long targetTime = FRIDAY_0000.getTime() + (30 * ONE_DAY); public boolean isValidDay(Date now) { return targetTime <= now.getTime() ? true : false; } }; monthlyBuilder.setTime("0000"); schedule.add(monthlyBuilder); assertEquals(ONE_DAY * 30, schedule.getTimeToNextBuild(FRIDAY_0000, ONE_MINUTE)); } public void testInterval() { assertEquals("default interval", 300 * 1000, schedule.getInterval()); schedule.setInterval(500); assertEquals(500 * 1000, schedule.getInterval()); } public void testValidate() throws CruiseControlException { schedule = new Schedule(); try { schedule.validate(); fail("validate should throw exception if it is configured with no builders"); } catch (CruiseControlException e) { } schedule.add(MULTIPLE_1); schedule.validate(); long oneYearInSeconds = 60 * 60 * 24 * 365; schedule.setInterval(oneYearInSeconds); schedule.validate(); schedule.setInterval(oneYearInSeconds + 1); try { schedule.validate(); fail("maximum allowed interval should be " + oneYearInSeconds); } catch (CruiseControlException e) { } } public void testCannotSetZeroOrLessInterval() { schedule = new Schedule(); assertSetIntervalThrowsException(0); assertSetIntervalThrowsException(-1); assertSetIntervalThrowsException(-100); assertSetIntervalThrowsException(Long.MIN_VALUE); } private void assertSetIntervalThrowsException(long testValue) { try { schedule.setInterval(testValue); fail("Expected an exception"); } catch (IllegalArgumentException expected) { //Good } } public void testValidateShouldFailIfAllTimedBuildersInPauses() throws CruiseControlException { schedule = new Schedule(); schedule.add(PAUSE_11_13_FRIDAY); schedule.add(NOON_BUILDER); schedule.validate(); schedule.add(PAUSE_11_13); try { schedule.validate(); fail(); } catch (CruiseControlException expected) { } schedule.add(MIDNIGHT_BUILDER); schedule.validate(); } public void testGetDayString() { assertEquals("sunday", schedule.getDayString(Calendar.SUNDAY).toLowerCase()); assertEquals("friday", schedule.getDayString(Calendar.FRIDAY).toLowerCase()); try { schedule.getDayString(0); fail(); } catch (IllegalArgumentException e) { assertEquals("valid values of days are between 1 and 7, was 0", e.getMessage()); } } }