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