/*
* Copyright 2017 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.thoughtworks.go.domain;
import com.thoughtworks.go.helper.JobInstanceMother;
import com.thoughtworks.go.remote.BuildRepositoryRemote;
import com.thoughtworks.go.util.TimeProvider;
import org.apache.commons.lang.time.DateUtils;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JMock;
import org.jmock.lib.legacy.ClassImposteriser;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Date;
import static com.thoughtworks.go.util.TestUtils.sizeIs;
import static java.text.MessageFormat.format;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsCollectionContaining.hasItem;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(JMock.class)
public class JobInstanceTest {
private Mockery context = new Mockery() {
{
setImposteriser(ClassImposteriser.INSTANCE);
}
};
private BuildRepositoryRemote remoteBuildRepository;
public TimeProvider timeProvider;
@Before
public void setup() throws Exception {
timeProvider = mock(TimeProvider.class);
remoteBuildRepository = context.mock(BuildRepositoryRemote.class);
}
@Test
public void shouldDetermineMostRecentPassedBeforeWithNullBuildInstances() {
JobInstance mostRecent = JobInstanceMother.building("mostRecent");
mostRecent.completing(JobResult.Passed, new Date());
mostRecent.completed(new Date());
assertEquals(mostRecent, mostRecent.mostRecentPassed(JobInstance.NULL));
assertEquals(mostRecent, JobInstance.NULL.mostRecentPassed(mostRecent));
}
@Test
public void shouldDetermineMostRecentPassed() {
JobInstance oldestPassed = JobInstanceMother.building("oldestPassed");
oldestPassed.completing(JobResult.Passed, DateUtils.addHours(new Date(), -1));
oldestPassed.completed(DateUtils.addHours(new Date(), -1));
JobInstance newestPassed = JobInstanceMother.building("newestPassed");
newestPassed.completing(JobResult.Passed, new Date());
newestPassed.completed(new Date());
assertEquals(newestPassed, newestPassed.mostRecentPassed(oldestPassed));
assertEquals(newestPassed, oldestPassed.mostRecentPassed(newestPassed));
JobInstance newestFailed = JobInstanceMother.building("newestFailed");
newestFailed.completing(JobResult.Failed, DateUtils.addHours(new Date(), +1));
newestFailed.completed(DateUtils.addHours(new Date(), +1));
assertEquals(newestPassed, newestPassed.mostRecentPassed(newestFailed));
}
@Test
public void shouldGetDisplayStatusFailedIfStateIsCompletedAndResultIsFailed() {
JobInstance job = JobInstanceMother.completed("test", JobResult.Failed);
assertThat(job.displayStatusWithResult(), is("failed"));
}
@Test
public void shouldGetDisplayStatusScheduledIfStateIsScheduled() {
JobInstance job = JobInstanceMother.scheduled("test");
assertThat(job.displayStatusWithResult(), is("scheduled"));
}
@Test
public void shouldChangeStatus() throws Exception {
JobInstance instance = JobInstanceMother.scheduled("jobConfig1");
instance.assign("1234", timeProvider.currentTime());
assertThat(instance.getState(), is(JobState.Assigned));
assertThat(instance.getTransitions().byState(JobState.Assigned), not(nullValue()));
}
@Test
public void shouldSetCompletingTimeAndResult() throws Exception {
JobInstance instance = JobInstanceMother.scheduled("jobConfig1");
final Date completionDate = new Date();
instance.completing(JobResult.Passed, completionDate);
assertThat(instance.getResult(), is(JobResult.Passed));
assertThat(instance.getState(), is(JobState.Completing));
}
@Test
public void shouldSetCompletedTimeOnComplete() throws Exception {
JobInstance instance = JobInstanceMother.scheduled("jobConfig1");
final Date completionDate = new Date();
instance.completing(JobResult.Passed, completionDate);
instance.completed(completionDate);
assertThat(instance.getResult(), is(JobResult.Passed));
assertThat(instance.getStartedDateFor(JobState.Completed), is(completionDate));
assertThat(instance.getState(), is(JobState.Completed));
}
@Test
public void shouldIncreaseElapsedTimeWhileBuilding() throws Exception {
JobInstance instance = JobInstanceMother.building("jobConfig1");
instance.setClock(timeProvider);
when(timeProvider.currentTime()).thenReturn(new Date(1000000));
long before = Long.parseLong(instance.getCurrentBuildDuration());
when(timeProvider.currentTime()).thenReturn(new Date(5000000));
long after = Long.parseLong(instance.getCurrentBuildDuration());
assertTrue("after " + after + " should bigger than " + before, after > before);
}
@Test
public void shouldReturnTotalDurationOfBuild() throws Exception {
JobInstance instance = JobInstanceMother.completed("jobConfig1");
assertThat(instance.getCurrentBuildDuration(), is(instance.durationOfCompletedBuildInSeconds().toString()));
}
@Test
public void shouldReturnBuildLocatorAsTitle() throws Exception {
JobInstance instance = JobInstanceMother.completed("jobConfig1");
assertThat(instance.getTitle(), is("pipeline/label-1/stage/1/jobConfig1"));
}
@Test
public void shouldCreateATransitionOnStateChange() throws Exception {
JobInstance instance = JobInstanceMother.scheduled("jobConfig1");
instance.completing(JobResult.Passed);
final JobStateTransition scheduledState = new JobStateTransition(JobState.Scheduled, new Date());
final JobStateTransition completedState = new JobStateTransition(JobState.Completing, new Date());
assertThat(instance.getTransitions(), hasItem(scheduledState));
assertThat(instance.getTransitions(), hasItem(completedState));
assertThat(instance.getTransitions().first(), not(isTransitionWithState(JobState.Preparing)));
}
@Test
public void shouldNotCreateATransitionWhenPreviousStateIsTheSame() throws Exception {
JobInstance instance = JobInstanceMother.scheduled("jobConfig1");
instance.changeState(JobState.Scheduled);
final JobStateTransition scheduledState = new JobStateTransition(JobState.Scheduled, new Date());
assertThat(instance.getTransitions(), hasItem(scheduledState));
assertThat(instance.getTransitions(), sizeIs(1));
assertThat(instance.getTransitions().first(), not(isTransitionWithState(JobState.Preparing)));
}
@Test
public void shouldReturnBuildingTransitionTimeAsStartBuildingDate() {
final Date date = new Date();
JobInstance instance = JobInstanceMother.scheduled("jobConfig1");
JobStateTransitions transitions = new JobStateTransitions(
new JobStateTransition(JobState.Building, date));
instance.setTransitions(transitions);
assertThat(instance.getStartedDateFor(JobState.Building), is(date));
}
public static JobStateTransitionMatcher isTransitionWithState(final JobState expectedState) {
return new JobStateTransitionMatcher(expectedState);
}
@Test
public void shouldCancelBuild() {
final JobInstance instance = JobInstanceMother.scheduled("plan1");
instance.cancel();
assertThat(instance.getState(), is(JobState.Completed));
assertThat(instance.getResult(), is(JobResult.Cancelled));
}
@Test
public void shouldNotCancelCompletedBuild() {
final JobInstance instance = JobInstanceMother.completed("plan1", JobResult.Passed);
instance.cancel();
assertThat(instance.getResult(), is(JobResult.Passed));
}
@Test
public void shouldDetermineDurationOfCompletedBuild() throws Exception {
JobInstance testJob = JobInstanceMother.completed("testJob");
Long duration = testJob.durationOfCompletedBuildInSeconds();
assertThat(duration, is(120L));
}
@Test public void durationShouldBeZeroForIncompleteBuild() throws Exception {
JobInstance building = JobInstanceMother.scheduled("building");
Long duration = building.durationOfCompletedBuildInSeconds();
assertThat(duration, is(0L));
}
@Test
public void shouldCleanAgentIdAndResultAfterRescheduled() throws Exception {
JobInstance instance = JobInstanceMother.assignedWithAgentId("testBuild", "uuid");
instance.completing(JobResult.Failed);
instance.reschedule();
assertThat(instance.getState(), is(JobState.Scheduled));
assertThat(instance.getAgentUuid(), is(nullValue()));
assertThat(instance.getResult(), is(JobResult.Unknown));
}
@Test public void shouldReturnDateForLatestTransition() throws Exception {
JobInstance instance = JobInstanceMother.scheduled("jobConfig1");
instance.setClock(timeProvider);
when(timeProvider.currentTime()).thenReturn(new DateTime().plusDays(1).toDate());
instance.completing(JobResult.Passed);
assertThat(instance.latestTransitionDate(),is(greaterThan(instance.getScheduledDate())));
}
@Test
public void shouldConsiderJobARerunWhenHasOriginalId() {
JobInstance instance = new JobInstance();
assertThat(instance.isCopy(), is(false));
instance.setOriginalJobId(10l);
assertThat(instance.isCopy(), is(true));
}
@Test
public void shouldReturnJobDurationForACompletedJob() {
int fiveSeconds = 5000;
JobInstance instance = JobInstanceMother.passed("first", new Date(), fiveSeconds);
assertThat(instance.getDuration(), is(new RunDuration.ActualDuration(new Duration(5 * fiveSeconds))));
}
@Test
public void shouldReturnJobDurationForABuildingJob() {
int fiveSeconds = 5000;
JobInstance instance = JobInstanceMother.building("first", new Date(), fiveSeconds);
assertThat(instance.getDuration(), is(RunDuration.IN_PROGRESS_DURATION));
}
@Test
public void shouldReturnJobTypeCorrectly() {
JobInstance jobInstance = new JobInstance();
jobInstance.setRunOnAllAgents(true);
assertThat(jobInstance.jobType(), instanceOf(RunOnAllAgents.class));
jobInstance = new JobInstance();
jobInstance.setRunMultipleInstance(true);
assertThat(jobInstance.jobType(), instanceOf(RunMultipleInstance.class));
jobInstance = new JobInstance();
assertThat(jobInstance.jobType(), instanceOf(SingleJobInstance.class));
}
private static class JobStateTransitionMatcher extends BaseMatcher<JobStateTransition> {
private JobState actualState;
private final JobState expectedState;
public JobStateTransitionMatcher(JobState expectedState) {
this.expectedState = expectedState;
}
public boolean matches(Object o) {
JobStateTransition transition = (JobStateTransition) o;
actualState = transition.getCurrentState();
return actualState == expectedState;
}
public void describeTo(Description description) {
description.appendText(format("Expect to get a state {0} but was {1}", expectedState, actualState));
}
}
}