| /******************************************************************************* |
| * 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()); |
| } |
| |
| } |