| /******************************************************************************* |
| * Copyright (C) 2017, Thomas Wolf <thomas.wolf@paranor.ch> |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| *******************************************************************************/ |
| package org.eclipse.egit.ui.internal; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assume.assumeTrue; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.nio.ByteBuffer; |
| import java.nio.charset.StandardCharsets; |
| |
| import org.eclipse.compare.ITypedElement; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.egit.core.RepositoryCache; |
| import org.eclipse.egit.core.op.CommitOperation; |
| import org.eclipse.egit.core.op.ResetOperation; |
| import org.eclipse.egit.ui.common.LocalRepositoryTestCase; |
| import org.eclipse.egit.ui.internal.revision.EditableRevision; |
| import org.eclipse.egit.ui.test.TestUtil; |
| import org.eclipse.jgit.api.ResetCommand.ResetType; |
| import org.eclipse.jgit.attributes.FilterCommand; |
| import org.eclipse.jgit.attributes.FilterCommandFactory; |
| import org.eclipse.jgit.attributes.FilterCommandRegistry; |
| import org.eclipse.jgit.dircache.DirCache; |
| import org.eclipse.jgit.dircache.DirCacheIterator; |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.lib.FileMode; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.lib.StoredConfig; |
| import org.eclipse.jgit.treewalk.TreeWalk; |
| import org.eclipse.jgit.treewalk.filter.PathFilterGroup; |
| import org.eclipse.jgit.util.IO; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| /** |
| * Tests for CompareUtils; in particular editing index content as it can be done |
| * in the compare editor. |
| */ |
| public class CompareUtilsTest extends LocalRepositoryTestCase { |
| |
| private Repository repository; |
| |
| @Before |
| public void setup() throws Exception { |
| File repoFile = createProjectAndCommitToRepository(); |
| assertNotNull(repoFile); |
| repository = RepositoryCache.INSTANCE.lookupRepository(repoFile); |
| assertNotNull(repository); |
| } |
| |
| private String get(InputStream in) throws IOException { |
| ByteBuffer buffer = IO.readWholeStream(in, 1); |
| return new String(buffer.array(), 0, buffer.limit(), |
| StandardCharsets.UTF_8); |
| } |
| |
| @Test |
| public void testIndexEdit() throws Exception { |
| IFile testFile = touch("a"); |
| stage(testFile); |
| // Get the index file revision. |
| ITypedElement element = CompareUtils.getIndexTypedElement(testFile); |
| assert (element instanceof EditableRevision); |
| EditableRevision revision = (EditableRevision) element; |
| // Check that its contents are 'a'. |
| try (InputStream in = revision.getContents()) { |
| assertEquals("a", get(in)); |
| } |
| // Change the contents to 'xx' |
| revision.setContent("xx".getBytes(StandardCharsets.UTF_8)); |
| // Commit the index |
| CommitOperation op = new CommitOperation(repository, |
| TestUtil.TESTAUTHOR, TestUtil.TESTCOMMITTER, |
| "Commit modified index"); |
| op.execute(null); |
| TestUtil.waitForJobs(50, 5000); |
| // Do a reset --hard |
| ResetOperation reset = new ResetOperation(repository, Constants.HEAD, |
| ResetType.HARD); |
| reset.execute(null); |
| TestUtil.waitForJobs(50, 5000); |
| // Should have 'xx' now |
| try (InputStream in = testFile.getContents()) { |
| assertEquals("xx", get(in)); |
| } |
| } |
| |
| @Test |
| public void testIndexEditExecutable() throws Exception { |
| assumeTrue(repository.getFS().supportsExecute()); |
| IFile testFile = touch("a"); |
| File rawFile = new File(testFile.getLocation().toOSString()); |
| repository.getFS().setExecute(rawFile, true); |
| testFile.refreshLocal(IResource.DEPTH_ZERO, null); |
| stage(testFile); |
| assertEquals("Executable bit should be set", FileMode.EXECUTABLE_FILE, |
| getIndexEntryMode(FILE1_PATH)); |
| ITypedElement element = CompareUtils.getIndexTypedElement(testFile); |
| assert (element instanceof EditableRevision); |
| EditableRevision revision = (EditableRevision) element; |
| try (InputStream in = revision.getContents()) { |
| assertEquals("a", get(in)); |
| } |
| revision.setContent("xx".getBytes(StandardCharsets.UTF_8)); |
| // Get the index entry again and check the executable bit |
| assertEquals("Executable bit should be set", FileMode.EXECUTABLE_FILE, |
| getIndexEntryMode(FILE1_PATH)); |
| } |
| |
| private FileMode getIndexEntryMode(String path) throws Exception { |
| DirCache dc = repository.readDirCache(); |
| try (TreeWalk w = new TreeWalk(repository)) { |
| w.addTree(new DirCacheIterator(dc)); |
| w.setFilter(PathFilterGroup.createFromStrings(path)); |
| w.setRecursive(true); |
| assertTrue(path + " not in index", w.next()); |
| return w.getFileMode(); |
| } |
| } |
| |
| @Test |
| public void testIndexEditWithAttributes() throws Exception { |
| IFile testFile = touch("a"); |
| stage(testFile); |
| // Set up .gitattributes such that 'a's are changed to 'x' on smudge |
| IFile gitAttributes = touch(PROJ1, FOLDER + "/.gitattributes", |
| FILE1 + " filter=test"); |
| try { |
| FilterCommandRegistry.register("egitui://builtin/test/smudge", |
| new TestCommandFactory('a', 'x')); |
| StoredConfig config = repository.getConfig(); |
| config.setString("filter", "test", "smudge", |
| "egitui://builtin/test/smudge"); |
| config.save(); |
| // Get the index file revision. |
| ITypedElement element = CompareUtils.getIndexTypedElement(testFile); |
| assert (element instanceof EditableRevision); |
| EditableRevision revision = (EditableRevision) element; |
| // Check that its contents are 'x'. |
| try (InputStream in = revision.getContents()) { |
| assertEquals("x", get(in)); |
| } |
| // Modify the filter to transform 'x' to 'a' on clean |
| FilterCommandRegistry.register("egitui://builtin/test/clean", |
| new TestCommandFactory('x', 'a')); |
| config.setString("filter", "test", "clean", |
| "egitui://builtin/test/clean"); |
| config.save(); |
| // Change the contents to 'xx'. This should apply the above clean |
| // filter. |
| revision.setContent("xx".getBytes(StandardCharsets.UTF_8)); |
| // Commit the index |
| CommitOperation op = new CommitOperation(repository, |
| TestUtil.TESTAUTHOR, TestUtil.TESTCOMMITTER, |
| "Commit modified index"); |
| op.execute(null); |
| TestUtil.waitForJobs(50, 5000); |
| // Remove filters |
| gitAttributes.delete(true, true, new NullProgressMonitor()); |
| config.unsetSection("filter", "test"); |
| config.save(); |
| // Do a reset --hard |
| ResetOperation reset = new ResetOperation(repository, |
| Constants.HEAD, ResetType.HARD); |
| reset.execute(null); |
| TestUtil.waitForJobs(50, 5000); |
| // Should have 'aa' now since we never committed the .gitattributes |
| try (InputStream in = testFile.getContents()) { |
| assertEquals("aa", get(in)); |
| } |
| } finally { |
| FilterCommandRegistry.unregister("egitui://builtin/test/smudge"); |
| FilterCommandRegistry.unregister("egitui://builtin/test/clean"); |
| } |
| } |
| |
| private static class TestCommandFactory implements FilterCommandFactory { |
| private final int toReplace; |
| |
| private final int replacement; |
| |
| public TestCommandFactory(int toReplace, int replacement) { |
| this.toReplace = toReplace; |
| this.replacement = replacement; |
| } |
| |
| @Override |
| public FilterCommand create(Repository repo, InputStream in, |
| final OutputStream out) { |
| FilterCommand cmd = new FilterCommand(in, out) { |
| |
| @Override |
| public int run() throws IOException { |
| int b = in.read(); |
| if (b == -1) { |
| return b; |
| } else if (b == toReplace) { |
| out.write(replacement); |
| } else { |
| out.write(b); |
| } |
| return 1; |
| } |
| }; |
| return cmd; |
| } |
| } |
| } |