/*
* 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 java.io.File;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import com.thoughtworks.go.config.CaseInsensitiveString;
import com.thoughtworks.go.config.materials.Filter;
import com.thoughtworks.go.config.materials.IgnoredFiles;
import com.thoughtworks.go.config.materials.ScmMaterialConfig;
import com.thoughtworks.go.config.materials.SubprocessExecutionContext;
import com.thoughtworks.go.config.materials.dependency.DependencyMaterial;
import com.thoughtworks.go.config.materials.mercurial.HgMaterial;
import com.thoughtworks.go.config.materials.svn.SvnMaterial;
import com.thoughtworks.go.domain.materials.*;
import com.thoughtworks.go.domain.materials.dependency.DependencyMaterialRevision;
import com.thoughtworks.go.domain.materials.mercurial.StringRevision;
import com.thoughtworks.go.helper.*;
import com.thoughtworks.go.util.TestFileUtil;
import com.thoughtworks.go.util.command.InMemoryStreamConsumer;
import org.apache.commons.io.FileUtils;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import static com.thoughtworks.go.helper.ModificationsMother.createHgMaterialWithMultipleRevisions;
import static com.thoughtworks.go.helper.ModificationsMother.multipleModificationsInHg;
import static com.thoughtworks.go.helper.ModificationsMother.oneModifiedFile;
import static com.thoughtworks.go.util.command.ProcessOutputStreamConsumer.inMemoryConsumer;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertThat;
public class MaterialRevisionTest {
private static final StringRevision REVISION_0 = new StringRevision("b61d12de515d82d3a377ae3aae6e8abe516a2651");
private static final StringRevision REVISION_2 = new StringRevision("ca3ebb67f527c0ad7ed26b789056823d8b9af23f");
private HgMaterial hgMaterial;
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
private File workingFolder;
@Before
public void setUp() throws Exception {
HgTestRepo hgTestRepo = new HgTestRepo("hgTestRepo1");
hgMaterial = MaterialsMother.hgMaterial(hgTestRepo.projectRepositoryUrl());
workingFolder = temporaryFolder.newFolder();
}
@After
public void teardown() {
temporaryFolder.delete();
TestRepo.internalTearDown();
}
@Test
public void shouldGetModifiedTimeFromTheLatestModification() throws Exception {
final MaterialRevision materialRevision = new MaterialRevision(MaterialsMother.hgMaterial(), multipleModificationsInHg());
assertThat(materialRevision.getDateOfLatestModification(), is(ModificationsMother.TODAY_CHECKIN));
}
@Test
public void shouldDetectChangesAfterACheckin() throws Exception {
MaterialRevision original = new MaterialRevision(hgMaterial,
hgMaterial.modificationsSince(workingFolder, REVISION_0, new TestSubprocessExecutionContext()));
checkInOneFile(hgMaterial);
checkInOneFile(hgMaterial);
checkInOneFile(hgMaterial);
final MaterialRevision after = findNewRevision(original, hgMaterial, workingFolder, new TestSubprocessExecutionContext());
assertThat(after, not(original));
assertThat(after.numberOfModifications(), is(3));
assertThat(after.getRevision(), is(not(original.getRevision())));
assertThat(after.hasChangedSince(original), is(true));
}
@Test
public void shouldMarkRevisionAsChanged() throws Exception {
MaterialRevision original = new MaterialRevision(hgMaterial, hgMaterial.modificationsSince(workingFolder, REVISION_0, new TestSubprocessExecutionContext()));
checkInFiles(hgMaterial, "user.doc");
MaterialRevision newRev = findNewRevision(original, hgMaterial, workingFolder, new TestSubprocessExecutionContext());
assertThat(newRev.isChanged(), is(true));
}
@Test
public void shouldMarkRevisionAsNotChanged() throws Exception {
List<Modification> modifications = hgMaterial.latestModification(workingFolder, new TestSubprocessExecutionContext());
MaterialRevision original = new MaterialRevision(hgMaterial, modifications);
checkInFiles(hgMaterial, "user.doc");
original = findNewRevision(original, hgMaterial, workingFolder, new TestSubprocessExecutionContext());
MaterialRevision newRev = findNewRevision(original, hgMaterial, workingFolder, new TestSubprocessExecutionContext());
assertThat(newRev.isChanged(), is(false));
}
@Test
public void shouldIgnoreDocumentCheckin() throws Exception {
MaterialRevision previousRevision = new MaterialRevision(hgMaterial, hgMaterial.modificationsSince(workingFolder, REVISION_0, new TestSubprocessExecutionContext()));
Filter filter = new Filter(new IgnoredFiles("**/*.doc"));
hgMaterial.setFilter(filter);
checkInFiles(hgMaterial, "user.doc");
MaterialRevision newRevision = findNewRevision(previousRevision, hgMaterial, workingFolder, new TestSubprocessExecutionContext());
assertThat(newRevision.filter(previousRevision), is(previousRevision));
}
@Test
public void shouldIgnoreDocumentWhenCheckin() throws Exception {
MaterialRevision original = new MaterialRevision(hgMaterial, hgMaterial.modificationsSince(workingFolder, REVISION_0, new TestSubprocessExecutionContext()));
Filter filter = new Filter(new IgnoredFiles("helper/**/*.*"));
hgMaterial.setFilter(filter);
checkInFiles(hgMaterial,
"helper/topics/installing_go_agent.xml",
"helper/topics/installing_go_server.xml");
MaterialRevision newRev = findNewRevision(original, hgMaterial, workingFolder, new TestSubprocessExecutionContext());
assertThat(newRev.filter(original), is(original));
}
@Test
public void shouldIgnoreDocumentsWithSemanticallyEqualsIgnoreFilter() throws Exception {
MaterialRevision original = new MaterialRevision(hgMaterial, hgMaterial.modificationsSince(workingFolder, REVISION_0, new TestSubprocessExecutionContext()));
Filter filter = new Filter(new IgnoredFiles("**/*.doc"), new IgnoredFiles("*.doc"));
hgMaterial.setFilter(filter);
checkInFiles(hgMaterial, "user.doc");
MaterialRevision newRev = findNewRevision(original, hgMaterial, workingFolder, new TestSubprocessExecutionContext());
assertThat(newRev.filter(original), is(original));
}
@Test
public void shouldIncludeJavaFileWithSemanticallyEqualsIgnoreFilter() throws Exception {
MaterialRevision original = new MaterialRevision(hgMaterial,
hgMaterial.modificationsSince(workingFolder, REVISION_0, new TestSubprocessExecutionContext()));
Filter filter = new Filter(new IgnoredFiles("**/*.doc"), new IgnoredFiles("*.doc"));
GoConfigMother.createPipelineConfig(filter, (ScmMaterialConfig) hgMaterial.config());
checkInFiles(hgMaterial, "A.java");
checkInFiles(hgMaterial, "B.doc");
checkInFiles(hgMaterial, "C.pdf");
MaterialRevision newRev = findNewRevision(original, hgMaterial, workingFolder, new TestSubprocessExecutionContext());
assertThat(newRev.filter(original), is(newRev));
}
@Test
public void shouldNotIgnoreJavaFile() throws Exception {
MaterialRevision original = new MaterialRevision(hgMaterial,
hgMaterial.modificationsSince(workingFolder, REVISION_0, new TestSubprocessExecutionContext()));
Filter filter = new Filter(new IgnoredFiles("**/*.doc"));
GoConfigMother.createPipelineConfig(filter, (ScmMaterialConfig) hgMaterial.config());
checkInFiles(hgMaterial, "A.java");
MaterialRevision newRev = findNewRevision(original, hgMaterial, workingFolder, new TestSubprocessExecutionContext());
assertThat(newRev.filter(original), is(newRev));
}
@Test
public void shouldNotIgnoreAnyFileIfFilterIsNotDefinedForTheGivenMaterial() throws Exception {
MaterialRevision original = new MaterialRevision(hgMaterial,
hgMaterial.modificationsSince(workingFolder, REVISION_0, new TestSubprocessExecutionContext()));
Filter filter = new Filter();
GoConfigMother.createPipelineConfig(filter, (ScmMaterialConfig) hgMaterial.config());
checkInFiles(hgMaterial, "A.java");
MaterialRevision newRev = findNewRevision(original, hgMaterial, workingFolder, new TestSubprocessExecutionContext());
assertThat(newRev.filter(original), is(newRev));
}
@Test
public void shouldMarkRevisionChangeFalseIfNoNewChangesAvailable() throws Exception {
Modification modificationForRevisionTip = new Modification(new Date(), REVISION_2.getRevision(), "MOCK_LABEL-12", null);
MaterialRevision revision = new MaterialRevision(hgMaterial, modificationForRevisionTip);
MaterialRevision unchangedRevision = findNewRevision(revision, hgMaterial, workingFolder, new TestSubprocessExecutionContext());
assertThat(unchangedRevision.isChanged(), is(false));
}
@Test
public void shouldReturnOnlyLatestModificationIfNoNewChangesAvailable() throws Exception {
Modification modificationForRevisionTip = new Modification("Unknown", "Unknown", null, new Date(), REVISION_2.getRevision());
Modification olderModification = new Modification("Unknown", "Unknown", null, new Date(), REVISION_0.getRevision());
MaterialRevision revision = new MaterialRevision(hgMaterial, modificationForRevisionTip, olderModification);
MaterialRevision unchangedRevision = findNewRevision(revision, hgMaterial, workingFolder, new TestSubprocessExecutionContext());
assertThat(unchangedRevision.getModifications().size(), is(1));
assertThat(unchangedRevision.getModifications().get(0), is(modificationForRevisionTip));
}
@Test
public void shouldNotConsiderChangedFlagAsPartOfEqualityAndHashCodeCheck() {
Modification modification = oneModifiedFile("revision1");
SvnMaterial material = MaterialsMother.svnMaterial();
MaterialRevision notChanged = new MaterialRevision(material, false, modification);
MaterialRevision changed = new MaterialRevision(material, true, modification);
changed.markAsChanged();
assertThat(changed, is(notChanged));
assertThat(changed.hashCode(), is(notChanged.hashCode()));
}
@Test
public void shouldDetectChangedRevision() {
Modification modification1 = oneModifiedFile("revision1");
Modification modification2 = oneModifiedFile("revision2");
SvnMaterial material = MaterialsMother.svnMaterial();
MaterialRevision materialRevision1 = new MaterialRevision(material, modification1);
MaterialRevision materialRevision2 = new MaterialRevision(material, modification2);
assertThat(materialRevision1.hasChangedSince(materialRevision2), is(true));
}
@Test
public void shouldDisplayRevisionAsBuildCausedByForDependencyMaterial() {
DependencyMaterial dependencyMaterial = new DependencyMaterial(new CaseInsensitiveString("upstream"), new CaseInsensitiveString("stage"));
MaterialRevision materialRevision = new MaterialRevision(dependencyMaterial, new Modification(new Date(), "upstream/2/stage/1", "1.3-2", null));
assertThat(materialRevision.buildCausedBy(), is("upstream/2/stage/1"));
}
@Test
public void shouldUseLatestMaterial() throws Exception {
MaterialRevision original = new MaterialRevision(hgMaterial,
hgMaterial.modificationsSince(workingFolder, REVISION_0, new TestSubprocessExecutionContext()));
HgMaterial newMaterial = MaterialsMother.hgMaterial(hgMaterial.getUrl());
newMaterial.setFilter(new Filter(new IgnoredFiles("**/*.txt")));
final MaterialRevision after = findNewRevision(original, newMaterial, workingFolder, new TestSubprocessExecutionContext());
assertThat(after.getMaterial(), is(newMaterial));
}
@Test
public void shouldDetectLatestAndOldestModification() throws Exception {
MaterialRevision materialRevision = new MaterialRevision(hgMaterial, modification("3"), modification("2"), modification("1"));
assertThat(materialRevision.getLatestModification(), is(modification("3")));
assertThat(materialRevision.getOldestModification(), is(modification("1")));
}
@Test
public void shouldDetectLatestRevision() throws Exception {
MaterialRevision materialRevision = new MaterialRevision(hgMaterial, modification("3"), modification("2"), modification("1"));
assertThat(materialRevision.getRevision(), is(new StringRevision("3")));
}
@Test
public void shouldDetectOldestScmRevision() throws Exception {
MaterialRevision materialRevision = new MaterialRevision(hgMaterial, modification("3"), modification("2"), modification("1"));
assertThat(materialRevision.getOldestRevision(), is(new StringRevision("1")));
}
@Test
public void shouldDetectOldestAndLatestDependencyRevision() throws Exception {
DependencyMaterial dependencyMaterial = new DependencyMaterial(new CaseInsensitiveString("upstream"), new CaseInsensitiveString("stage"));
MaterialRevision materialRevision = new MaterialRevision(dependencyMaterial, new Modification(new Date(), "upstream/3/stage/1", "1.3-3", null),
new Modification(new Date(), "upstream/2/stage/1", "1.3-2", null));
assertThat(materialRevision.getOldestRevision(), is(DependencyMaterialRevision.create("upstream/2/stage/1", "1.3-2")));
assertThat(materialRevision.getRevision(), is(DependencyMaterialRevision.create("upstream/3/stage/1", "1.3-3")));
}
@Test
public void shouldReturnNullRevisionWhenThereIsNoMaterial() throws Exception {
Revision revision = new MaterialRevision(null).getRevision();
assertThat(revision, is(not(nullValue())));
assertThat(revision.getRevision(), is(""));
}
@Test
public void shouldReturnFullRevisionForTheLatestModification() throws Exception {
assertThat(hgRevision().getLatestRevisionString(), is("012345678901234567890123456789"));
}
private MaterialRevision hgRevision() {
return new MaterialRevision(hgMaterial, modification("012345678901234567890123456789"), modification("2"), modification("1"));
}
@Test
public void shouldReturnShortRevisionForTheLatestModification() throws Exception {
assertThat(hgRevision().getLatestShortRevision(), is("012345678901"));
}
@Test
public void shouldReturnMaterialName() throws Exception {
assertThat(hgRevision().getMaterialName(), Matchers.is(hgMaterial.getDisplayName()));
}
@Test
public void shouldReturnTruncatedMaterialName() throws Exception {
assertThat(hgRevision().getTruncatedMaterialName(), Matchers.is(hgMaterial.getTruncatedDisplayName()));
}
@Test
public void shouldReturnMaterialType() throws Exception {
assertThat(hgRevision().getMaterialType(), is("Mercurial"));
}
@Test
public void shouldReturnLatestComments() throws Exception {
assertThat(hgRevision().getLatestComment(), is("Checkin 012345678901234567890123456789"));
}
@Test
public void shouldReturnLatestUser() throws Exception {
assertThat(hgRevision().getLatestUser(), is("user"));
}
@Test
public void shouldRecomputeIsChanged() throws Exception {
MaterialRevision original = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("rev2"), oneModifiedFile("rev1")).getMaterialRevision(0);
MaterialRevision recomputed = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("rev1")).getMaterialRevision(0);
MaterialRevision recomputedAnother = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("rev0")).getMaterialRevision(0);
recomputed.updateRevisionChangedStatus(original);
recomputedAnother.updateRevisionChangedStatus(original);
assertThat(recomputed.isChanged(), is(false));
assertThat(recomputedAnother.isChanged(), is(false));
original.markAsChanged();
recomputed.updateRevisionChangedStatus(original);
recomputedAnother.updateRevisionChangedStatus(original);
assertThat(recomputed.isChanged(), is(true));
assertThat(recomputedAnother.isChanged(), is(false));
}
@Test
public void shouldRemoveFromThisWhateverModificationIsPresentInThePassedInRevision() throws Exception {
MaterialRevision revision = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("rev2"), oneModifiedFile("rev1")).getMaterialRevision(0);
MaterialRevision passedIn = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("rev1")).getMaterialRevision(0);
MaterialRevision expected = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("rev2")).getMaterialRevision(0);
assertThat(revision.subtract(passedIn), is(expected));
}
@Test
public void shouldReturnCurrentIfThePassedInDoesNotHaveAnythingThatCurrentHas() throws Exception {
MaterialRevision revision = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("rev2")).getMaterialRevision(0);
MaterialRevision passedIn = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("rev1")).getMaterialRevision(0);
MaterialRevision expected = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("rev2")).getMaterialRevision(0);
assertThat(revision.subtract(passedIn), is(expected));
}
private Modification modification(String revision) {
return new Modification("user", "Checkin "
+ revision, null, null, revision);
}
private void checkInOneFile(HgMaterial hgMaterial) throws Exception {
checkInFiles(hgMaterial, UUID.randomUUID().toString());
}
private void checkInFiles(HgMaterial hgMaterial, String... fileNames) throws Exception {
final File localDir = TestFileUtil.createTempFolder("foo");
InMemoryStreamConsumer consumer = inMemoryConsumer();
Revision revision = latestRevision(hgMaterial, workingFolder, new TestSubprocessExecutionContext());
hgMaterial.updateTo(consumer, localDir, new RevisionContext(revision), new TestSubprocessExecutionContext());
for (String fileName : fileNames) {
File file = new File(localDir, fileName);
FileUtils.writeStringToFile(file, "");
hgMaterial.add(localDir, consumer, file);
}
hgMaterial.commit(localDir, consumer, "Adding a new file.", "TEST");
hgMaterial.push(localDir, consumer);
}
private Revision latestRevision(HgMaterial material, File workingDir, TestSubprocessExecutionContext execCtx) {
List<Modification> modifications = material.latestModification(workingDir, execCtx);
return new Modifications(modifications).latestRevision(material);
}
public MaterialRevision findNewRevision(MaterialRevision materialRevision, HgMaterial material, File workingFolder, final SubprocessExecutionContext execCtx) {
List<Modification> newModifications = material.modificationsSince(workingFolder, materialRevision.getRevision(), execCtx);
return materialRevision.latestChanges(material, materialRevision.getModifications(), newModifications);
}
}