/* * 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.materials.mercurial; import com.thoughtworks.go.domain.materials.Modification; import com.thoughtworks.go.domain.materials.Revision; import com.thoughtworks.go.util.TestFileUtil; import com.thoughtworks.go.util.command.CommandLine; import com.thoughtworks.go.util.command.InMemoryStreamConsumer; import com.thoughtworks.go.util.command.ProcessOutputStreamConsumer; import com.thoughtworks.go.util.command.UrlArgument; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import static com.thoughtworks.go.util.FileUtil.deleteFolder; import static com.thoughtworks.go.util.command.ProcessOutputStreamConsumer.inMemoryConsumer; import static org.hamcrest.Matchers.not; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; public class HgCommandTest { private static File serverRepo; private static File clientRepo; private HgCommand hgCommand; private List<File> foldersToDelete = new ArrayList<>(); private InMemoryStreamConsumer outputStreamConsumer = inMemoryConsumer(); private File workingDirectory; private static final String REVISION_0 = "b61d12de515d82d3a377ae3aae6e8abe516a2651"; private static final String REVISION_1 = "35ff2159f303ecf986b3650fc4299a6ffe5a14e1"; private static final String REVISION_2 = "ca3ebb67f527c0ad7ed26b789056823d8b9af23f"; private File secondBranchWorkingCopy; @Before public void setUp() throws IOException { serverRepo = createTmpFolder("testHgServerRepo"); clientRepo = createTmpFolder("testHgClientRepo"); secondBranchWorkingCopy = createTmpFolder("second"); setUpServerRepoFromHgBundle(serverRepo, new File("../common/test-resources/unit/data/hgrepo.hgbundle")); workingDirectory = new File(clientRepo.getPath()); hgCommand = new HgCommand(null, workingDirectory, "default", serverRepo.getAbsolutePath(), null); hgCommand.clone(outputStreamConsumer, new UrlArgument(serverRepo.getAbsolutePath())); } @After public void tearDown() throws IOException { for (File folder : foldersToDelete) { if (folder.exists()) { deleteFolder(folder); } } } @Test public void shouldCloneFromRemoteRepo() { assertThat(clientRepo.listFiles().length > 0, is(true)); } @Test public void shouldGetLatestModifications() throws Exception { List<Modification> actual = hgCommand.latestOneModificationAsModifications(); assertThat(actual.size(), is(1)); final Modification modification = actual.get(0); assertThat(modification.getComment(), is("test")); assertThat(modification.getUserName(), is("cruise")); assertThat(modification.getModifiedFiles().size(), is(1)); } @Test public void shouldNotIncludeCommitFromAnotherBranchInGetLatestModifications() throws Exception { Modification lastCommit = hgCommand.latestOneModificationAsModifications().get(0); makeACommitToSecondBranch(); hg(workingDirectory, "pull").runOrBomb(null); Modification actual = hgCommand.latestOneModificationAsModifications().get(0); assertThat(actual, is(lastCommit)); assertThat(actual.getComment(), is(lastCommit.getComment())); } @Test public void shouldGetModifications() throws Exception { List<Modification> actual = hgCommand.modificationsSince(new StringRevision(REVISION_0)); assertThat(actual.size(), is(2)); assertThat(actual.get(0).getRevision(), is(REVISION_2)); assertThat(actual.get(1).getRevision(), is(REVISION_1)); } @Test public void shouldNotGetModificationsFromOtherBranches() throws Exception { makeACommitToSecondBranch(); hg(workingDirectory, "pull").runOrBomb(null); List<Modification> actual = hgCommand.modificationsSince(new StringRevision(REVISION_0)); assertThat(actual.size(), is(2)); assertThat(actual.get(0).getRevision(), is(REVISION_2)); assertThat(actual.get(1).getRevision(), is(REVISION_1)); } @Test public void shouldUpdateToSpecificRevision() { InMemoryStreamConsumer output = ProcessOutputStreamConsumer.inMemoryConsumer(); assertThat(output.getStdOut(), is("")); File newFile = new File(clientRepo, "test.txt"); assertThat(newFile.exists(), is(false)); Revision revision = createNewFileAndCheckIn(serverRepo); hgCommand.updateTo(revision, output); assertThat(output.getStdOut(), is(not(""))); assertThat(newFile.exists(), is(true)); } @Test public void shouldUpdateToSpecificRevisionOnGivenBranch() { makeACommitToSecondBranch(); InMemoryStreamConsumer output = ProcessOutputStreamConsumer.inMemoryConsumer(); File newFile = new File(workingDirectory, "test.txt"); hgCommand.updateTo(new StringRevision("tip"), output); assertThat(newFile.exists(), is(false)); } @Test(expected = RuntimeException.class) public void shouldThrowExceptionIfUpdateFails() throws Exception { InMemoryStreamConsumer output = ProcessOutputStreamConsumer.inMemoryConsumer(); // delete repository in order to fail the hg pull command assertThat(deleteFolder(serverRepo), is(true)); // now hg pull will fail and throw an exception hgCommand.updateTo(new StringRevision("tip"), output); } @Test public void shouldGetWorkingUrl() { String workingUrl = hgCommand.workingRepositoryUrl().outputAsString(); assertThat(workingUrl, is(serverRepo.getAbsolutePath())); } @Test(expected = RuntimeException.class) public void shouldThrowExceptionForBadConnection() throws Exception { String url = "http://not-exists"; HgCommand hgCommand = new HgCommand(null, null, null, null, null); hgCommand.checkConnection(new UrlArgument(url)); } @Test public void shouldCloneOnlyTheSpecifiedBranchAndPointToIt() { String branchName = "second"; HgCommand hg = new HgCommand(null, secondBranchWorkingCopy, branchName, serverRepo.getAbsolutePath(), null); hg.clone(outputStreamConsumer, new UrlArgument(serverRepo.getAbsolutePath() + "#" + branchName)); String currentBranch = hg(secondBranchWorkingCopy, "branch").runOrBomb(null).outputAsString(); assertThat(currentBranch, is(branchName)); List<String> branches = hg(secondBranchWorkingCopy, "branches").runOrBomb(null).output(); ArrayList<String> branchNames = new ArrayList<>(); for (String branchDetails : branches) { branchNames.add(StringUtils.split(branchDetails, " ")[0]); } assertThat(branchNames.size(), is(2)); assertThat(branchNames.contains(branchName), is(true)); assertThat(branchNames.contains("default"), is(true)); } private void addLockTo(File hgRepoRootDir) throws IOException { File lock = new File(hgRepoRootDir, ".hg/store/lock"); FileUtils.touch(lock); } private CommandLine hg(File workingDir, String... arguments) { CommandLine hg = CommandLine.createCommandLine("hg").withArgs(arguments); hg.setWorkingDir(workingDir); return hg; } private File createTmpFolder(String folderName) { return new File(TestFileUtil.createUniqueTempFolder(folderName), "repo"); } private void commit(String message, File workingDir) { CommandLine hg = hg(workingDir, "ci", "-u", "cruise-test", "-m", message); String[] input = new String[]{}; hg.runOrBomb(null, input); } private Revision latestRevisionOf() { CommandLine hg = hg(serverRepo, "log", "--limit", "1", "--template", "{node}"); String[] input = new String[]{}; return new StringRevision(hg.runOrBomb(null, input).outputAsString()); } private void addremove(File workingDir) { CommandLine hg = hg(workingDir, "addremove"); String[] input = new String[]{}; hg.runOrBomb(null, input); } private void createNewFileAndPushUpstream(File workingDir) { createNewFileAndCheckIn(workingDir); String branchName = hg(workingDir, "branch").runOrBomb(null).outputAsString(); hg(workingDir, "push", "--rev", branchName).runOrBomb(null); } private Revision createNewFileAndCheckIn(File directory) { try { new FileOutputStream(new File(directory, "test.txt")).close(); addremove(directory); commit("created test.txt", directory); } catch (IOException e) { e.printStackTrace(); } return latestRevisionOf(); } private void setUpServerRepoFromHgBundle(File serverRepo, File hgBundleFile) { String[] input = new String[]{}; CommandLine.createCommandLine("hg") .withArgs("clone", hgBundleFile.getAbsolutePath(), serverRepo.getAbsolutePath()).runOrBomb(null, input); } private void makeACommitToSecondBranch() { HgCommand hg = new HgCommand(null, secondBranchWorkingCopy, "second", serverRepo.getAbsolutePath(), null); hg.clone(outputStreamConsumer, new UrlArgument(serverRepo.getAbsolutePath())); createNewFileAndPushUpstream(secondBranchWorkingCopy); } }