blob: 85039eb14db1769d2002ce546dbf447344e8609f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018-2019 Aston University, and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 3.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-3.0
*
* Contributors:
* Antonio Garcia-Dominguez - initial API and implementation
* Horacio Hoyos Rodriguez - Add proper Git support
******************************************************************************/
package org.hawk.git;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.Mockito.mock;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.jgit.api.AddCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.RmCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.hawk.core.IModelIndexer;
import org.hawk.core.VcsChangeType;
import org.hawk.core.VcsCommitItem;
import org.hawk.core.VcsRepositoryDelta;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
public class JGitRepositoryTest {
private static final String NULL_REV = "0000000000000000000000000000000000000000";
private enum Version {
v0("Initial Commit", "one.txt"), v1("Adds file two", "one.txt", "two.txt"),
v2("Fixes file one", "one.txt", "two.txt"), v3("Fixes file two, deletes file one", "two.txt");
private final String[] allFiles = new String[] { "one.txt", "two.txt" };
private final String msg;
private final Set<String> names;
Version(String msg, String... files) {
this.msg = msg;
this.names = new HashSet<>(Arrays.asList(files));
}
public void commit(TemporaryFolder folder, Map<String, File> files) throws IOException {
updateFiles(folder, files);
commitChanges(folder, files);
}
private void commitChanges(TemporaryFolder folder, Map<String, File> files) {
try (org.eclipse.jgit.api.Git repo = org.eclipse.jgit.api.Git.open(folder.getRoot())) {
try {
AddCommand add = repo.add();
RmCommand rm = repo.rm();
for (String name : allFiles) {
if (names.contains(name)) {
add.addFilepattern(name);
} else {
rm.addFilepattern(name);
}
}
// Some commands can be empty
try {
add.call();
} catch (NoFilepatternException e) {
/* Ignore */ }
try {
rm.call();
} catch (NoFilepatternException e) {
/* Ignore */ }
repo.commit().setMessage(msg).call();
} catch (GitAPIException e) {
throw new IllegalStateException("Failed to commit");
}
} catch (IOException e2) {
throw new IllegalStateException("Failed to commit");
}
}
private void updateFiles(TemporaryFolder folder, Map<String, File> files)
throws IOException, FileNotFoundException {
File tempFile;
for (String name : allFiles) {
tempFile = files.get(name);
if (names.contains(name)) {
if (tempFile != null) {
// Replace it
tempFile.delete();
}
tempFile = folder.newFile(name);
copy(name, tempFile);
files.put(name, tempFile);
} else {
if (tempFile != null) {
tempFile.delete();
files.remove(name);
}
}
}
}
private void copy(String name, File tempFile) throws FileNotFoundException, IOException {
InputStream in = JGitRepositoryTest.class.getResourceAsStream(String.format("/%s/%s", this.name(), name));
try (OutputStream out = new FileOutputStream(tempFile)) {
byte[] buf = new byte[1024];
int length;
while ((length = in.read(buf)) > 0) {
out.write(buf, 0, length);
}
}
}
}
@Rule
public TemporaryFolder folder = new TemporaryFolder();
@Rule
public ExpectedException thrown = ExpectedException.none();
private JGitRepository vcs;
private final Map<String, File> files = new HashMap<>();
@Before
public void setup() throws Exception {
org.eclipse.jgit.api.Git.init().setDirectory(folder.getRoot()).call();
final IModelIndexer indexer = mock(IModelIndexer.class);
vcs = new JGitRepository();
vcs.init(folder.getRoot().getCanonicalPath(), indexer);
vcs.run();
}
@After
public void clean() throws Exception {
vcs.shutdown();
files.clear();
}
@Test
public void currentRevisionWithoutCommits() throws Exception {
String currRev = vcs.getCurrentRevision();
// Git repo with no commits has revision 0
assertEquals(NULL_REV, currRev);
}
@Test
public void currentRevisionOneCommit() throws Exception {
Version.v0.commit(folder, files);
String currRev = vcs.getCurrentRevision();
// If there are commits there should be a hash
assertNotEquals(NULL_REV, currRev);
}
@Test
public void currentRevisionTwoCommits() throws Exception {
Version.v0.commit(folder, files);
String v0Rev = vcs.getCurrentRevision();
System.out.println(v0Rev);
assertNotEquals(NULL_REV, v0Rev);
Version.v1.commit(folder, files);
String v1Rev = vcs.getCurrentRevision();
System.out.println(v1Rev);
assertNotEquals(NULL_REV, v1Rev);
assertNotEquals(v0Rev, v1Rev);
}
@Test
public void firstRevisionIsConstant() throws Exception {
Version.v0.commit(folder, files);
String rev0 = vcs.getCurrentRevision();
assertEquals(rev0, vcs.getFirstRevision());
Version.v1.commit(folder, files);
assertEquals(rev0, vcs.getFirstRevision());
Version.v2.commit(folder, files);
assertEquals(rev0, vcs.getFirstRevision());
Version.v3.commit(folder, files);
assertEquals(rev0, vcs.getFirstRevision());
}
@Test
public void delta1ArgWithNullRevision() throws Exception {
Version.v0.commit(folder, files);
List<VcsCommitItem> delta = vcs.getDelta(null);
assertEquals("1-arg delta with null starts from first revision", 1, delta.size());
assertEquals(VcsChangeType.ADDED, delta.get(0).getChangeType());
}
@Test
public void delta1ArgWithCurrent() throws Exception {
Version.v0.commit(folder, files);
Collection<VcsCommitItem> delta = vcs.getDelta(vcs.getCurrentRevision());
assertEquals(0, delta.size());
}
@Test
public void delta1ArgWithTwoCommits() throws Exception {
Version.v0.commit(folder, files);
String rev0 = vcs.getCurrentRevision();
Version.v1.commit(folder, files);
Collection<VcsCommitItem> delta = vcs.getDelta(rev0);
assertEquals(1, delta.size());
assertEquals(1L, delta.stream()
.filter(i -> i.getChangeType().equals(VcsChangeType.ADDED) && i.getPath().equals("/two.txt")).count());
}
@Test
public void delta1ArgWithUpdatedFile() throws Exception {
Version.v0.commit(folder, files);
Version.v1.commit(folder, files);
String rev1 = vcs.getCurrentRevision();
Version.v2.commit(folder, files);
Collection<VcsCommitItem> delta = vcs.getDelta(rev1);
assertEquals(1, delta.size());
assertEquals(1L,
delta.stream()
.filter(i -> i.getChangeType().equals(VcsChangeType.UPDATED) && i.getPath().equals("/one.txt"))
.count());
}
@Test
public void delta1ArgTwoChanges() throws Exception {
Version.v0.commit(folder, files);
Version.v1.commit(folder, files);
Version.v2.commit(folder, files);
final String rev2 = vcs.getCurrentRevision();
Version.v3.commit(folder, files);
Collection<VcsCommitItem> delta = vcs.getDelta(rev2);
assertEquals(2, delta.size());
assertEquals(1L,
delta.stream()
.filter(i -> i.getChangeType().equals(VcsChangeType.DELETED) && i.getPath().equals("/one.txt"))
.count());
assertEquals(1L,
delta.stream()
.filter(i -> i.getChangeType().equals(VcsChangeType.UPDATED) && i.getPath().equals("/two.txt"))
.count());
}
@Test
public void delta2ArgAfterFirstOfTwo() throws Exception {
Version.v0.commit(folder, files);
Version.v1.commit(folder, files);
VcsRepositoryDelta delta = vcs.getDelta(vcs.getFirstRevision(), vcs.getCurrentRevision());
assertEquals(1, delta.getCommits().size());
Collection<VcsCommitItem> commits = delta.getCommits().get(0).getItems();
assertEquals(1, commits.size());
assertEquals(1L, commits.stream()
.filter(i -> i.getChangeType().equals(VcsChangeType.ADDED) && i.getPath().equals("/two.txt")).count());
}
@Test
public void delta2ArgAfterFirstOfThree() throws Exception {
Version.v0.commit(folder, files);
Version.v1.commit(folder, files);
Version.v2.commit(folder, files);
Version.v3.commit(folder, files);
VcsRepositoryDelta delta = vcs.getDelta(vcs.getFirstRevision(), vcs.getCurrentRevision());
assertEquals(3, delta.getCommits().size());
Collection<VcsCommitItem> items = delta.getCompactedCommitItems();
assertEquals(2, items.size());
assertEquals(1L,
items.stream()
.filter(i -> i.getChangeType().equals(VcsChangeType.UPDATED) && i.getPath().equals("/two.txt"))
.count());
assertEquals(1L,
items.stream()
.filter(i -> i.getChangeType().equals(VcsChangeType.DELETED) && i.getPath().equals("/one.txt"))
.count());
}
@Test
public void importFile() throws Exception {
Version.v0.commit(folder, files);
String rev0 = vcs.getCurrentRevision();
Version.v1.commit(folder, files);
String rev1 = vcs.getCurrentRevision();
Version.v2.commit(folder, files);
String rev2 = vcs.getCurrentRevision();
Version.v3.commit(folder, files);
String rev3 = vcs.getCurrentRevision();
File temp = vcs.importFile(rev0, "one.txt", folder.newFile());
List<String> lines = Files.lines(temp.toPath()).collect(Collectors.toList());
assertEquals(1, lines.size());
assertEquals("one", lines.get(0));
temp = vcs.importFile(rev0, "two.txt", folder.newFile());
lines = Files.lines(temp.toPath()).collect(Collectors.toList());
assertEquals(0, lines.size());
temp = vcs.importFile(rev1, "two.txt", folder.newFile());
lines = Files.lines(temp.toPath()).collect(Collectors.toList());
assertEquals(1, lines.size());
assertEquals("two", lines.get(0));
temp = vcs.importFile(rev2, "one.txt", folder.newFile());
lines = Files.lines(temp.toPath()).collect(Collectors.toList());
assertEquals(2, lines.size());
assertEquals("one", lines.get(0));
assertEquals("one", lines.get(1));
temp = vcs.importFile(rev3, "two.txt", folder.newFile());
lines = Files.lines(temp.toPath()).collect(Collectors.toList());
assertEquals(2, lines.size());
assertEquals("two", lines.get(0));
assertEquals("two", lines.get(1));
temp = vcs.importFile(rev3, "one.txt", folder.newFile());
lines = Files.lines(temp.toPath()).collect(Collectors.toList());
assertEquals(0, lines.size());
}
@Test
public void gitHonorsCurrentBranch() throws Exception {
Version.v0.commit(folder, files);
String rev0 = vcs.getCurrentRevision();
Version.v1.commit(folder, files);
String rev1 = vcs.getCurrentRevision();
final String newBranchName = "newbranch";
try (Git repo = Git.open(folder.getRoot())) {
repo.checkout().setName(newBranchName).setCreateBranch(true).call();
Version.v2.commit(folder, files);
}
assertEquals(rev1, vcs.getCurrentRevision());
VcsRepositoryDelta delta = vcs.getDelta(null, vcs.getCurrentRevision());
assertEquals(2, delta.getCommits().size());
assertEquals(rev0, delta.getCommits().get(0).getRevision());
assertEquals(rev1, delta.getCommits().get(1).getRevision());
delta = vcs.getDelta(rev0, vcs.getCurrentRevision());
assertEquals(1, delta.getCommits().size());
assertEquals(rev1, delta.getCommits().get(0).getRevision());
// Now try creating a VCS pointing at the new branch
final JGitRepository repoNewBranch = new JGitRepository();
final IModelIndexer indexer = mock(IModelIndexer.class);
repoNewBranch.init(String.format(
"%s?%s=%s",
folder.getRoot().toURI().toString(),
JGitRepository.BRANCH_QPARAM,
newBranchName), indexer);
repoNewBranch.run();
assertEquals(3, repoNewBranch.getDelta(null,
repoNewBranch.getCurrentRevision()).getCommits().size());
}
}