Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Wolf2015-09-12 17:08:51 -0400
committerAndrey Loskutov2015-09-13 16:55:53 -0400
commit0831deae6fd973df0696b450f274e1a2b6cf73ef (patch)
treeba768981649b0997a038e3e839e06e7a32771078
parentfbb66bdb00b81fc817d9ecd4eed70d4e7d2b4a3e (diff)
downloadegit-0831deae6fd973df0696b450f274e1a2b6cf73ef.tar.gz
egit-0831deae6fd973df0696b450f274e1a2b6cf73ef.tar.xz
egit-0831deae6fd973df0696b450f274e1a2b6cf73ef.zip
Check for objects adaptable to IResource in ShowBlameActionHandler
Includes a new test. Some refactoring in LocalRepositoryTestCase to avoid having to duplicate test repo creation, and to reduce code duplication. Bug: 401156 Change-Id: Ib78d62f11d3a5a52c672bb192e8baba189c49829 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
-rw-r--r--org.eclipse.egit.ui.test/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/common/LocalRepositoryTestCase.java115
-rw-r--r--org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/test/team/actions/ShowBlameActionHandlerTest.java303
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ShowBlameActionHandler.java22
4 files changed, 370 insertions, 73 deletions
diff --git a/org.eclipse.egit.ui.test/META-INF/MANIFEST.MF b/org.eclipse.egit.ui.test/META-INF/MANIFEST.MF
index ce28cd1ef..2cb2ac23f 100644
--- a/org.eclipse.egit.ui.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.egit.ui.test/META-INF/MANIFEST.MF
@@ -15,7 +15,8 @@ Require-Bundle: org.apache.log4j;bundle-version="[1.0.0,2.0.0)",
org.eclipse.ui;bundle-version="[3.4.0,4.0.0)",
org.hamcrest;bundle-version="[1.1.0,2.0.0)",
org.mockito;bundle-version="[1.8.0,1.9.0)",
- org.objenesis;bundle-version="[1.0.0,2.0.0)"
+ org.objenesis;bundle-version="[1.0.0,2.0.0)",
+ org.eclipse.jdt.launching;bundle-version="[3.8.0,4.0.0)"
Import-Package: org.eclipse.egit.core.test;version="[4.1.0,4.2.0)",
org.eclipse.egit.gitflow;version="[4.1.0,4.2.0)",
org.eclipse.egit.gitflow.op;version="[4.1.0,4.2.0)",
diff --git a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/common/LocalRepositoryTestCase.java b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/common/LocalRepositoryTestCase.java
index dfe69aa96..dc7c24768 100644
--- a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/common/LocalRepositoryTestCase.java
+++ b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/common/LocalRepositoryTestCase.java
@@ -237,39 +237,12 @@ public abstract class LocalRepositoryTestCase extends EGitTestCase {
protected File createProjectAndCommitToRepository(String repoName)
throws Exception {
- File gitDir = new File(new File(testDirectory, repoName),
- Constants.DOT_GIT);
- Repository myRepository = new RepositoryBuilder().setGitDir(gitDir)
- .build();
- myRepository.create();
+ Repository myRepository = createLocalTestRepository(repoName);
+ File gitDir = myRepository.getDirectory();
// we need to commit into master first
- IProject firstProject = ResourcesPlugin.getWorkspace().getRoot()
- .getProject(PROJ1);
-
- if (firstProject.exists()) {
- firstProject.delete(true, null);
- TestUtil.waitForJobs(100, 5000);
- }
- IProjectDescription desc = ResourcesPlugin.getWorkspace()
- .newProjectDescription(PROJ1);
- desc.setLocation(new Path(new File(myRepository.getWorkTree(), PROJ1)
- .getPath()));
- firstProject.create(desc, null);
- firstProject.open(null);
- TestUtil.waitForJobs(50, 5000);
-
- assertTrue("Project is not accessible: " + firstProject,
- firstProject.isAccessible());
-
- IFolder folder = firstProject.getFolder(FOLDER);
- folder.create(false, true, null);
- IFile textFile = folder.getFile(FILE1);
- textFile.create(new ByteArrayInputStream("Hello, world"
- .getBytes(firstProject.getDefaultCharset())), false, null);
- IFile textFile2 = folder.getFile(FILE2);
- textFile2.create(new ByteArrayInputStream("Some more content"
- .getBytes(firstProject.getDefaultCharset())), false, null);
+ IProject firstProject = createStandardTestProjectInRepository(
+ myRepository, PROJ1);
try {
new ConnectProviderOperation(firstProject, gitDir).execute(null);
@@ -278,34 +251,8 @@ public abstract class LocalRepositoryTestCase extends EGitTestCase {
}
assertConnected(firstProject);
- IProject secondProject = ResourcesPlugin.getWorkspace().getRoot()
- .getProject(PROJ2);
-
- if (secondProject.exists()) {
- secondProject.delete(true, null);
- TestUtil.waitForJobs(100, 5000);
- }
-
- desc = ResourcesPlugin.getWorkspace().newProjectDescription(PROJ2);
- desc.setLocation(new Path(new File(myRepository.getWorkTree(), PROJ2)
- .getPath()));
- secondProject.create(desc, null);
- secondProject.open(null);
- TestUtil.waitForJobs(50, 5000);
-
- assertTrue("Project is not accessible: " + secondProject,
- secondProject.isAccessible());
-
- IFolder secondfolder = secondProject.getFolder(FOLDER);
- secondfolder.create(false, true, null);
- IFile secondtextFile = secondfolder.getFile(FILE1);
- secondtextFile.create(new ByteArrayInputStream("Hello, world"
- .getBytes(firstProject.getDefaultCharset())), false, null);
- IFile secondtextFile2 = secondfolder.getFile(FILE2);
- secondtextFile2.create(new ByteArrayInputStream("Some more content"
- .getBytes(firstProject.getDefaultCharset())), false, null);
-
- TestUtil.waitForJobs(50, 5000);
+ IProject secondProject = createStandardTestProjectInRepository(
+ myRepository, PROJ2);
// TODO we should be able to hide the .project
// IFile gitignore = secondPoject.getFile(".gitignore");
@@ -322,6 +269,12 @@ public abstract class LocalRepositoryTestCase extends EGitTestCase {
IFile dotProject = firstProject.getFile(".project");
assertTrue(".project is not accessible: " + dotProject,
dotProject.isAccessible());
+ IFolder folder = firstProject.getFolder(FOLDER);
+ IFile textFile = folder.getFile(FILE1);
+ IFile textFile2 = folder.getFile(FILE2);
+ folder = secondProject.getFolder(FOLDER);
+ IFile secondtextFile = folder.getFile(FILE1);
+ IFile secondtextFile2 = folder.getFile(FILE2);
IFile[] commitables = new IFile[] { dotProject,
textFile, textFile2, secondtextFile, secondtextFile2 };
@@ -345,6 +298,50 @@ public abstract class LocalRepositoryTestCase extends EGitTestCase {
return gitDir;
}
+ protected Repository createLocalTestRepository(String repoName)
+ throws IOException {
+ File gitDir = new File(new File(testDirectory, repoName),
+ Constants.DOT_GIT);
+ Repository myRepository = new RepositoryBuilder().setGitDir(gitDir)
+ .build();
+ myRepository.create();
+ return myRepository;
+ }
+
+ protected IProject createStandardTestProjectInRepository(
+ Repository repository, String name) throws Exception {
+ IProject project = ResourcesPlugin.getWorkspace().getRoot()
+ .getProject(name);
+
+ if (project.exists()) {
+ project.delete(true, null);
+ TestUtil.waitForJobs(100, 5000);
+ }
+ IProjectDescription desc = ResourcesPlugin.getWorkspace()
+ .newProjectDescription(name);
+ desc.setLocation(
+ new Path(new File(repository.getWorkTree(), name).getPath()));
+ project.create(desc, null);
+ project.open(null);
+ TestUtil.waitForJobs(50, 5000);
+
+ assertTrue("Project is not accessible: " + project,
+ project.isAccessible());
+
+ IFolder folder = project.getFolder(FOLDER);
+ folder.create(false, true, null);
+ IFile textFile = folder.getFile(FILE1);
+ textFile.create(
+ new ByteArrayInputStream(
+ "Hello, world".getBytes(project.getDefaultCharset())),
+ false, null);
+ IFile textFile2 = folder.getFile(FILE2);
+ textFile2.create(new ByteArrayInputStream(
+ "Some more content".getBytes(project.getDefaultCharset())),
+ false, null);
+ return project;
+ }
+
protected RepositoryMapping assertConnected(IProject project) {
RepositoryProvider provider = RepositoryProvider.getProvider(project,
GitProvider.ID);
diff --git a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/test/team/actions/ShowBlameActionHandlerTest.java b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/test/team/actions/ShowBlameActionHandlerTest.java
new file mode 100644
index 000000000..70dfdcd6c
--- /dev/null
+++ b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/test/team/actions/ShowBlameActionHandlerTest.java
@@ -0,0 +1,303 @@
+/*******************************************************************************
+ * Copyright (C) 2015, 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 v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.ui.test.team.actions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceDescription;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.egit.core.Activator;
+import org.eclipse.egit.core.internal.indexdiff.IndexDiffCache;
+import org.eclipse.egit.core.op.CommitOperation;
+import org.eclipse.egit.core.op.ConnectProviderOperation;
+import org.eclipse.egit.ui.common.LocalRepositoryTestCase;
+import org.eclipse.egit.ui.internal.actions.ShowBlameActionHandler;
+import org.eclipse.egit.ui.test.TestUtil;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.launching.JavaRuntime;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.runners.MockitoJUnitRunner;
+
+/**
+ * JUnit plugin test (non-SWTBot) to verify that {@link ShowBlameActionHandler}
+ * is enabled not only for {@link IResource} but also for other things that
+ * might be visible in the package explorer and that adapt to {@link IResource}.
+ * We verify by setting up a Java project with a simple test class and then
+ * checking for {@link IFile}, {@link ICompilationUnit}, and {@link IType}.
+ *
+ * @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=401156">Bug
+ * 401156</a>
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ShowBlameActionHandlerTest extends LocalRepositoryTestCase {
+
+ private static final String JAVA_PROJECT_NAME = "javatestProject";
+
+ private static final String SRC_FOLDER_NAME = "src";
+
+ private static final String BIN_FOLDER_NAME = "bin";
+
+ private static final String PACKAGE_NAME = "p";
+
+ private static final String JAVA_CLASS_NAME = "A";
+
+ private static final String JAVA_FILE_NAME = JAVA_CLASS_NAME + ".java";
+
+ private static final int MAX_DELETE_RETRY = 5;
+
+ private static final int DELETE_RETRY_DELAY = 1000; // ms
+
+ private static boolean initialAutobuild;
+
+ private IJavaProject javaProject = null;
+
+ @BeforeClass
+ public static void setupAutobuildOff() throws CoreException {
+ // Switch off autobuild -- we don't need it, and a build job might
+ // interfere with our removing the Java project at the end.
+ initialAutobuild = setAutobuild(false);
+ }
+
+ @AfterClass
+ public static void teardownAutobuildReset() throws CoreException {
+ setAutobuild(initialAutobuild);
+ }
+
+ @Before
+ public void setup() throws Exception {
+ javaProject = createJavaProjectAndCommitToRepository();
+ }
+
+ @After
+ public void teardown() throws CoreException {
+ removeJavaProject();
+ }
+
+ @Test
+ public void testShowAnnotationsFromProjectExplorer() throws Exception {
+ IProject project = javaProject.getProject();
+ // Find the file
+ IFile file = project.getFolder(SRC_FOLDER_NAME).getFolder(PACKAGE_NAME)
+ .getFile(JAVA_FILE_NAME);
+ assertBlameEnabled(file, true);
+ // Now repeat the same with the ICompilationUnit.
+ IJavaElement element = JavaCore.create(file, javaProject);
+ assertTrue("Expected an ICompilationUnit",
+ element instanceof ICompilationUnit);
+ assertBlameEnabled(element, true);
+ // And with IType...
+ IType type = javaProject.findType(PACKAGE_NAME, JAVA_CLASS_NAME);
+ assertBlameEnabled(type, true);
+ // ... and finally with something that doesn't adapt to IResource:
+ assertBlameEnabled(this, false);
+ }
+
+ @SuppressWarnings("boxing")
+ private void assertBlameEnabled(Object selected, boolean expected) {
+ assertNotNull("Nothing selected", selected);
+ IStructuredSelection selection = mock(IStructuredSelection.class);
+ when(selection.getFirstElement()).thenReturn(selected);
+ when(selection.size()).thenReturn(1);
+ ShowBlameActionHandler blame = new ShowBlameActionHandler();
+ blame.setSelection(selection);
+ assertEquals("Unexpected enablement of blame action", expected,
+ blame.isEnabled());
+ }
+
+ // Java stuff below
+
+ private static boolean setAutobuild(boolean value) throws CoreException {
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ IWorkspaceDescription desc = workspace.getDescription();
+ boolean isAutoBuilding = desc.isAutoBuilding();
+ if (isAutoBuilding != value) {
+ desc.setAutoBuilding(value);
+ workspace.setDescription(desc);
+ }
+ return isAutoBuilding;
+ }
+
+ private IJavaProject createJavaProjectAndCommitToRepository()
+ throws Exception {
+ Repository myRepository = createLocalTestRepository(REPO1);
+ File gitDir = myRepository.getDirectory();
+ IJavaProject jProject = createJavaProject(myRepository,
+ JAVA_PROJECT_NAME);
+ IProject project = jProject.getProject();
+ try {
+ new ConnectProviderOperation(project, gitDir).execute(null);
+ } catch (Exception e) {
+ Activator.logError("Failed to connect project to repository", e);
+ }
+ assertConnected(project);
+ // Check in at least the java file
+ IFolder folder = project.getFolder(SRC_FOLDER_NAME)
+ .getFolder(PACKAGE_NAME);
+ IFile file = folder.getFile(JAVA_FILE_NAME);
+
+ IFile[] commitables = new IFile[] { file };
+ ArrayList<IFile> untracked = new ArrayList<IFile>();
+ untracked.addAll(Arrays.asList(commitables));
+ // commit to master
+ CommitOperation op = new CommitOperation(commitables, untracked,
+ TestUtil.TESTAUTHOR, TestUtil.TESTCOMMITTER, "Initial commit");
+ op.execute(null);
+
+ // Make sure cache entry is already listening for changes
+ IndexDiffCache cache = Activator.getDefault().getIndexDiffCache();
+ cache.getIndexDiffCacheEntry(lookupRepository(gitDir));
+ return jProject;
+ }
+
+ private IJavaProject createJavaProject(final Repository repository,
+ final String projectName) throws Exception {
+ final IJavaProject[] jProjectHolder = new IJavaProject[] { null };
+ IWorkspaceRunnable runnable = new IWorkspaceRunnable() {
+ @Override
+ public void run(IProgressMonitor monitor) throws CoreException {
+ IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+ IProject project = root.getProject(projectName);
+ if (project.exists()) {
+ project.delete(true, null);
+ TestUtil.waitForJobs(100, 5000);
+ }
+ IProjectDescription desc = ResourcesPlugin.getWorkspace()
+ .newProjectDescription(projectName);
+ desc.setLocation(
+ new Path(new File(repository.getWorkTree(), projectName)
+ .getPath()));
+ project.create(desc, null);
+ project.open(null);
+ TestUtil.waitForJobs(50, 5000);
+ // Create a "bin" folder
+ IFolder bin = project.getFolder(BIN_FOLDER_NAME);
+ if (!bin.exists()) {
+ bin.create(IResource.FORCE | IResource.DERIVED, true, null);
+ }
+ IPath outputLocation = bin.getFullPath();
+ // Create a "src" folder
+ IFolder src = project.getFolder(SRC_FOLDER_NAME);
+ if (!src.exists()) {
+ src.create(IResource.FORCE, true, null);
+ }
+ addNatureToProject(project, JavaCore.NATURE_ID);
+ // Set up the IJavaProject
+ IJavaProject jProject = JavaCore.create(project);
+ IPackageFragmentRoot srcContainer = jProject
+ .getPackageFragmentRoot(src);
+ IClasspathEntry srcEntry = JavaCore
+ .newSourceEntry(srcContainer.getPath());
+ // Create a JRE classpath entry using the default JRE
+ IClasspathEntry jreEntry = JavaRuntime
+ .getDefaultJREContainerEntry();
+ jProject.setRawClasspath(
+ new IClasspathEntry[] { srcEntry, jreEntry },
+ outputLocation, true, null);
+ // Create a package with a single test class
+ IPackageFragment javaPackage = srcContainer
+ .createPackageFragment(PACKAGE_NAME, true, null);
+ javaPackage
+ .createCompilationUnit(JAVA_FILE_NAME,
+ "package " + PACKAGE_NAME + ";\nclass "
+ + JAVA_CLASS_NAME + " {\n\n}",
+ true, null);
+ jProjectHolder[0] = jProject;
+ }
+ };
+ ResourcesPlugin.getWorkspace().run(runnable, null);
+ return jProjectHolder[0];
+ }
+
+ private void addNatureToProject(IProject proj, String natureId)
+ throws CoreException {
+ IProjectDescription description = proj.getDescription();
+ String[] prevNatures = description.getNatureIds();
+ String[] newNatures = new String[prevNatures.length + 1];
+ System.arraycopy(prevNatures, 0, newNatures, 0, prevNatures.length);
+ newNatures[prevNatures.length] = natureId;
+ description.setNatureIds(newNatures);
+ proj.setDescription(description, null);
+ }
+
+ private void removeJavaProject() throws CoreException {
+ if (javaProject == null) {
+ return;
+ }
+ final IProject project = javaProject.getProject();
+ IWorkspaceRunnable runnable = new IWorkspaceRunnable() {
+ @Override
+ public void run(IProgressMonitor monitor) throws CoreException {
+ // Following code inspired by {@link
+ // org.eclipse.jdt.testplugin.JavaProjectHelper#delete(IResource)}.
+ // I don't like all this sleeping at all, but apparently it's
+ // needed because the Java indexer might still run and hold on
+ // to some resources.
+ for (int i = 0; i < MAX_DELETE_RETRY; i++) {
+ try {
+ project.delete(
+ IResource.FORCE
+ | IResource.ALWAYS_DELETE_PROJECT_CONTENT,
+ null);
+ break;
+ } catch (CoreException e) {
+ if (i == MAX_DELETE_RETRY - 1) {
+ throw e;
+ }
+ try {
+ Activator.logInfo(
+ "Sleep before retrying to delete project "
+ + project.getLocationURI());
+ // Give other threads the time to close and release
+ // the resource.
+ Thread.sleep(DELETE_RETRY_DELAY);
+ } catch (InterruptedException e1) {
+ // Ignore and retry to delete
+ }
+ }
+ }
+
+ }
+ };
+ ResourcesPlugin.getWorkspace().run(runnable, null);
+ }
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ShowBlameActionHandler.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ShowBlameActionHandler.java
index 4a4911e1c..0cebf261b 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ShowBlameActionHandler.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ShowBlameActionHandler.java
@@ -17,6 +17,7 @@ import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.egit.core.AdapterUtils;
import org.eclipse.egit.core.internal.job.JobUtil;
import org.eclipse.egit.core.internal.storage.CommitFileRevision;
import org.eclipse.egit.core.project.RepositoryMapping;
@@ -66,20 +67,15 @@ public class ShowBlameActionHandler extends RepositoryActionHandler {
return null;
Object element = selection.getFirstElement();
- if (element instanceof IResource) {
- IResource resource = (IResource) element;
+ IResource resource = AdapterUtils.adapt(element, IResource.class);
+ if (resource instanceof IStorage) {
+ IStorage storage = (IStorage) resource;
+ RepositoryMapping mapping = RepositoryMapping.getMapping(resource);
- if (resource instanceof IStorage) {
- IStorage storage = (IStorage) resource;
- RepositoryMapping mapping = RepositoryMapping
- .getMapping(resource);
-
- if (mapping != null) {
- String repoRelativePath = mapping
- .getRepoRelativePath(resource);
- return new Data(mapping.getRepository(), repoRelativePath,
- storage, null);
- }
+ if (mapping != null) {
+ String repoRelativePath = mapping.getRepoRelativePath(resource);
+ return new Data(mapping.getRepository(), repoRelativePath,
+ storage, null);
}
} else if (element instanceof CommitFileRevision) {
CommitFileRevision revision = (CommitFileRevision) element;

Back to the top