diff options
Diffstat (limited to 'org.eclipse.egit.ui.test')
5 files changed, 232 insertions, 15 deletions
diff --git a/org.eclipse.egit.ui.test/META-INF/MANIFEST.MF b/org.eclipse.egit.ui.test/META-INF/MANIFEST.MF index c2eed536e3..88c0eac8ad 100644 --- a/org.eclipse.egit.ui.test/META-INF/MANIFEST.MF +++ b/org.eclipse.egit.ui.test/META-INF/MANIFEST.MF @@ -48,9 +48,10 @@ Import-Package: org.eclipse.egit.core.test;version="[4.3.0,4.4.0)", org.eclipse.swtbot.swt.finder.utils, org.eclipse.swtbot.swt.finder.waits, org.eclipse.swtbot.swt.finder.widgets, - org.junit;version="[4.3.1,5.0.0)", - org.junit.runner;version="[4.3.1,5.0.0)", - org.junit.runners;version="[4.3.1,5.0.0)", + org.junit;version="[4.7.0,5.0.0)", + org.junit.rules;version="[4.7.0,5.0.0)", + org.junit.runner;version="[4.7.0,5.0.0)", + org.junit.runners;version="[4.7.0,5.0.0)", org.mockito;version="[1.8.0,1.9.0)", org.mockito.stubbing;version="[1.8.0,1.9.0)", org.osgi.framework;version="[1.4.0,2.0.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 22c2784ef4..ad75191040 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 @@ -36,6 +36,7 @@ import org.eclipse.egit.core.GitCorePreferences; import org.eclipse.egit.core.GitProvider; import org.eclipse.egit.core.JobFamilies; import org.eclipse.egit.core.RepositoryCache; +import org.eclipse.egit.core.RepositoryUtil; import org.eclipse.egit.core.internal.indexdiff.IndexDiffCache; import org.eclipse.egit.core.internal.util.ResourceUtil; import org.eclipse.egit.core.op.AddToIndexOperation; @@ -47,7 +48,12 @@ import org.eclipse.egit.core.project.GitProjectData; import org.eclipse.egit.core.project.RepositoryMapping; import org.eclipse.egit.core.test.TestUtils; import org.eclipse.egit.ui.UIPreferences; +import org.eclipse.egit.ui.internal.dialogs.CompareTreeView; import org.eclipse.egit.ui.internal.push.PushOperationUI; +import org.eclipse.egit.ui.internal.rebase.RebaseInteractiveView; +import org.eclipse.egit.ui.internal.reflog.ReflogView; +import org.eclipse.egit.ui.internal.repository.RepositoriesView; +import org.eclipse.egit.ui.internal.staging.StagingView; import org.eclipse.egit.ui.test.ContextMenuHelper; import org.eclipse.egit.ui.test.Eclipse; import org.eclipse.egit.ui.test.TestUtil; @@ -69,10 +75,14 @@ import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell; import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree; import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem; import org.eclipse.team.core.RepositoryProvider; +import org.eclipse.team.ui.history.IHistoryView; +import org.eclipse.team.ui.synchronize.ISynchronizeView; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.rules.TestName; /** * Base class for testing with local (file-system based) repositories @@ -140,20 +150,39 @@ public abstract class LocalRepositoryTestCase extends EGitTestCase { protected static final String FILE2 = "test2.txt"; - - protected final static TestUtils testUtils = new TestUtils(); + private static final String[] VIEWS_TO_CLOSE = { // + RebaseInteractiveView.VIEW_ID, // + ISynchronizeView.VIEW_ID, // + IHistoryView.VIEW_ID, // + CompareTreeView.ID, // + ReflogView.VIEW_ID, // + StagingView.VIEW_ID, // + RepositoriesView.VIEW_ID, // + "org.eclipse.search.ui.views.SearchView", // + "org.eclipse.ui.views.PropertySheet" + }; + + @Rule + public TestName testName = new TestName(); + public File getTestDirectory() { return testDirectory; } + protected static void closeGitViews() { + for (String viewId : VIEWS_TO_CLOSE) { + TestUtil.hideView(viewId); + } + } + @Before public void initNewTestDirectory() throws Exception { testMethodNumber++; // create standalone temporary directory testDirectory = testUtils.createTempDir("LocalRepositoriesTests" - + testMethodNumber); + + testMethodNumber + '_' + testName.getMethodName()); if (testDirectory.exists()) FileUtils.delete(testDirectory, FileUtils.RECURSIVE | FileUtils.RETRY); @@ -174,6 +203,7 @@ public abstract class LocalRepositoryTestCase extends EGitTestCase { TestUtil.processUIEvents(); // close all editors/dialogs new Eclipse().reset(); + closeGitViews(); TestUtil.processUIEvents(); // cleanup for (IProject project : ResourcesPlugin.getWorkspace().getRoot() @@ -201,6 +231,7 @@ public abstract class LocalRepositoryTestCase extends EGitTestCase { .getPreferenceStore() .setValue(UIPreferences.SHOW_DETACHED_HEAD_WARNING, false); + closeGitViews(); } @AfterClass @@ -208,11 +239,17 @@ public abstract class LocalRepositoryTestCase extends EGitTestCase { testUtils.deleteTempDirs(); } - protected static void shutDownRepositories() { + protected static void shutDownRepositories() throws Exception { RepositoryCache cache = Activator.getDefault().getRepositoryCache(); for(Repository repository:cache.getAllRepositories()) repository.close(); cache.clear(); + IEclipsePreferences prefs = Activator.getDefault().getRepositoryUtil() + .getPreferences(); + synchronized (prefs) { + prefs.put(RepositoryUtil.PREFS_DIRECTORIES, ""); + prefs.flush(); + } } protected static void deleteAllProjects() throws Exception { diff --git a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/gitflow/AbstractGitflowHandlerTest.java b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/gitflow/AbstractGitflowHandlerTest.java index 03b5ee58e2..e048977827 100644 --- a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/gitflow/AbstractGitflowHandlerTest.java +++ b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/gitflow/AbstractGitflowHandlerTest.java @@ -37,6 +37,7 @@ import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner; +import org.junit.After; import org.junit.Before; import org.junit.runner.RunWith; @@ -59,6 +60,11 @@ public abstract class AbstractGitflowHandlerTest extends LocalRepositoryTestCase resetPreferences(); } + @After + public void teardown() { + repository = null; + } + private void resetPreferences() { IPreferenceStore prefStore = Activator.getDefault() .getPreferenceStore(); diff --git a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/internal/submodules/SubmoduleFolderTest.java b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/internal/submodules/SubmoduleFolderTest.java index 320344a6f9..7b77a7fa40 100644 --- a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/internal/submodules/SubmoduleFolderTest.java +++ b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/internal/submodules/SubmoduleFolderTest.java @@ -50,6 +50,7 @@ import org.eclipse.ui.IEditorReference; import org.eclipse.ui.IViewPart; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -86,6 +87,8 @@ public class SubmoduleFolderTest extends LocalRepositoryTestCase { parentRepositoryGitDir = createProjectAndCommitToRepository(); childRepositoryGitDir = createProjectAndCommitToRepository(CHILDREPO, CHILDPROJECT); + Activator.getDefault().getRepositoryUtil() + .addConfiguredRepository(parentRepositoryGitDir); parentRepository = lookupRepository(parentRepositoryGitDir); childRepository = lookupRepository(childRepositoryGitDir); parentProject = ResourcesPlugin.getWorkspace().getRoot() @@ -137,6 +140,21 @@ public class SubmoduleFolderTest extends LocalRepositoryTestCase { assertNotNull(subRepository); } + @After + public void removeConfiguredRepositories() { + if (parentRepositoryGitDir != null) { + Activator.getDefault().getRepositoryUtil() + .removeDir(parentRepositoryGitDir); + } + if (childRepositoryGitDir != null) { + Activator.getDefault().getRepositoryUtil() + .removeDir(childRepositoryGitDir); + } + childRepository = null; + parentRepository = null; + subRepository = null; + } + @Test public void testChildProjectMapsToSubRepo() { RepositoryMapping mapping = RepositoryMapping.getMapping(childProject); @@ -185,6 +203,7 @@ public class SubmoduleFolderTest extends LocalRepositoryTestCase { TestUtil.joinJobs(JobFamilies.INDEX_DIFF_CACHE_UPDATE); IndexDiffCacheEntry cache = Activator.getDefault().getIndexDiffCache() .getIndexDiffCacheEntry(subRepository); + TestUtil.joinJobs(JobFamilies.INDEX_DIFF_CACHE_UPDATE); IResourceState state = ResourceStateFactory.getInstance() .get(cache.getIndexDiff(), file); assertTrue("File should be staged", state.isStaged()); @@ -261,6 +280,24 @@ public class SubmoduleFolderTest extends LocalRepositoryTestCase { node.getText().contains("[child")); } + /** + * Tests that unrelated changes to the configured repositories do not + * prematurely remove submodules from the cache. + */ + @Test + public void testRepoRemoval() { + Activator.getDefault().getRepositoryUtil() + .addConfiguredRepository(childRepositoryGitDir); + assertTrue("Should still have the subrepo in the cache", + containsRepo(Activator.getDefault().getRepositoryCache() + .getAllRepositories(), subRepository)); + assertTrue("Should have changed the preference", Activator.getDefault() + .getRepositoryUtil().removeDir(childRepositoryGitDir)); + assertTrue("Should still have the subrepo in the cache", + containsRepo(Activator.getDefault().getRepositoryCache() + .getAllRepositories(), subRepository)); + } + @SuppressWarnings("restriction") @Test public void testHistoryFromProjectExplorerIsFromSubRepository() @@ -283,6 +320,15 @@ public class SubmoduleFolderTest extends LocalRepositoryTestCase { 2); } + private boolean containsRepo(Repository[] repositories, Repository needle) { + for (Repository repo : repositories) { + if (needle.equals(repo)) { + return true; + } + } + return false; + } + private void assertRowCountInHistory(String msg, int expected) throws Exception { SWTBotView historyBot = TestUtil.showHistoryView(); diff --git a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/view/repositories/GitRepositoriesViewRepoDeletionTest.java b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/view/repositories/GitRepositoriesViewRepoDeletionTest.java index 730b0739dc..0399bed316 100644 --- a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/view/repositories/GitRepositoriesViewRepoDeletionTest.java +++ b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/view/repositories/GitRepositoriesViewRepoDeletionTest.java @@ -17,11 +17,22 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.File; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.util.Arrays; import java.util.List; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; import org.eclipse.egit.core.internal.indexdiff.IndexDiffCache; import org.eclipse.egit.ui.Activator; import org.eclipse.egit.ui.JobFamilies; +import org.eclipse.egit.ui.internal.RepositoryCacheRule; import org.eclipse.egit.ui.internal.UIText; import org.eclipse.egit.ui.test.ContextMenuHelper; import org.eclipse.egit.ui.test.TestUtil; @@ -32,6 +43,7 @@ import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner; import org.eclipse.swtbot.swt.finder.widgets.SWTBotCheckBox; import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell; import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree; +import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -149,21 +161,109 @@ public class GitRepositoriesViewRepoDeletionTest extends TestUtil.joinJobs(JobFamilies.REPOSITORY_DELETE); refreshAndWait(); assertEmpty(); + assertTrue(repositoryFile.exists()); assertTrue( new File(repositoryFile.getParentFile(), PROJ1).isDirectory()); + waitForDecorations(); + closeGitViews(); + TestUtil.waitForJobs(500, 5000); + // Session properties are stored in the Eclipse resource tree as part of + // the resource info. org.eclipse.core.internal.dtree.DataTreeLookup has + // a static LRU cache of lookup instances to avoid excessive strain on + // the garbage collector due to constantly allocating and then + // forgetting instances. These lookup objects may contain things + // recently queried or modified in the resource tree, such as session + // properties. As a result, the session properties of a deleted resource + // remain around a little longer than expected: to be precise, exactly + // 100 more queries on the Eclipse resource tree until the entry + // containing the session property is recycled. We use session + // properties to store the RepositoryMappings, which reference the + // repository. + // + // Make sure we clear that cache: + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IProject project = root.getProject(PROJ1); + for (int i = 0; i < 101; i++) { + // Number of iterations at least DataTreeLookup.POOL_SIZE! + // Use up one DataTreeLookup instance: + project.create(null); + if (i == 0) { + // Furthermore, the WorkbenchSourceProvider has still a + // reference to the last selection, which is our now long + // removed repository node! Arguably that's a strange thing, but + // strictly speaking, since there is no guarantee _when_ a + // weakly referenced object is removed, not even making + // WorkbenchSourceProvider.lastShowInSelection a WeakReference + // might help. Therefore, let's make sure that the last "show + // in" selection is no longer the RepositoryNode, which also + // still has a reference to the repository. That last "show in" + // selection is set when the "Shown in..." context menu is + // filled, which happens when the project explorer's context + // menu is activated. So we have to open that menu at least once + // with a different selection. + SWTBotTree explorerTree = TestUtil.getExplorerTree(); + SWTBotTreeItem projectNode = TestUtil.navigateTo(explorerTree, + PROJ1); + projectNode.select(); + ContextMenuHelper.isContextMenuItemEnabled(explorerTree, "New"); + } + project.delete(true, true, null); + } + TestUtil.waitForJobs(500, 5000); + // And we may have the RepositoryChangeScanner running: use a job + // with a scheduling rule that ensures we have exclusive access. + final String[] results = { null, null }; + Job verifier = new Job(testName.getMethodName()) { + + @Override + protected IStatus run(IProgressMonitor monitor) { + // Wait for things to definitely quieten down. Note that + // waitForJobs only waits for running and waiting jobs, there + // may still be scheduled jobs that might wake up and run after + // that. TestUtil.joinJobs does really join, which also waits + // for scheduled jobs. + try { + TestUtil.joinJobs( + org.eclipse.egit.core.JobFamilies.INDEX_DIFF_CACHE_UPDATE); + // Is this job doing something when the view is hidden? + TestUtil.joinJobs(JobFamilies.REPO_VIEW_REFRESH); + waitForDecorations(); + } catch (InterruptedException e) { + results[0] = "Interrupted"; + Thread.currentThread().interrupt(); + return Status.CANCEL_STATUS; + } + // Finally... Java does not give any guarantees about when + // exactly an only weakly reachable object is finalized and + // garbage collected. + waitForFinalization(5000); + // Experience shows that an explicit garbage collection run very + // often does reclaim only weakly reachable objects and set the + // weak references' referents to null, but not even that can be + // guaranteed! Whether or not it does may also depend on the + // configuration of the JVM (such as through command-line + // arguments). + Repository[] repositories = org.eclipse.egit.core.Activator + .getDefault().getRepositoryCache().getAllRepositories(); + results[0] = Arrays.asList(repositories).toString(); + IndexDiffCache indexDiffCache = org.eclipse.egit.core.Activator + .getDefault().getIndexDiffCache(); + results[1] = indexDiffCache.currentCacheEntries().toString(); + return Status.OK_STATUS; + } + + }; + verifier.setRule(new RepositoryCacheRule()); + verifier.setSystem(true); + verifier.schedule(); + verifier.join(); List<String> configuredRepos = org.eclipse.egit.core.Activator .getDefault().getRepositoryUtil().getConfiguredRepositories(); assertTrue("Expected no configured repositories", configuredRepos.isEmpty()); - Repository[] repositories = org.eclipse.egit.core.Activator.getDefault() - .getRepositoryCache().getAllRepositories(); - assertEquals("Expected no cached repositories", 0, repositories.length); - // A pity we can't mock the cache. - IndexDiffCache indexDiffCache = org.eclipse.egit.core.Activator - .getDefault().getIndexDiffCache(); - assertTrue("Expected no IndexDiffCache entries", - indexDiffCache.currentCacheEntries().isEmpty()); + assertEquals("Expected no cached repositories", "[]", results[0]); + assertEquals("Expected no IndexDiffCache entries", "[]", results[1]); } @Test @@ -217,4 +317,31 @@ public class GitRepositoriesViewRepoDeletionTest extends assertFalse(subRepo.getWorkTree().exists()); } + @SuppressWarnings("restriction") + private void waitForDecorations() throws InterruptedException { + TestUtil.joinJobs( + org.eclipse.ui.internal.decorators.DecoratorManager.FAMILY_DECORATE); + } + + /** + * Best-effort attempt to get finalization to occur. + * + * @param maxMillis + * maximum amount of time in milliseconds to try getting the + * garbage collector to finalize objects + */ + private void waitForFinalization(int maxMillis) { + long stop = System.currentTimeMillis() + maxMillis; + MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); + do { + System.gc(); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } while (System.currentTimeMillis() < stop + && memoryBean.getObjectPendingFinalizationCount() > 0); + } } |