| /******************************************************************************* |
| * Copyright (c) 2004, 2021 IBM Corporation and others. |
| * |
| * 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 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Remy Chi Jian Suen (Versant Corporation) - bug 255005 |
| * Jeanderson Candido <http://jeandersonbc.github.io> - Bug 444070 |
| *******************************************************************************/ |
| package org.eclipse.ui.tests.leaks; |
| |
| import java.lang.ref.PhantomReference; |
| import java.lang.ref.Reference; |
| import java.lang.ref.ReferenceQueue; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| 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.jface.dialogs.Dialog; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IPageLayout; |
| import org.eclipse.ui.IViewPart; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchPartSite; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.dialogs.SaveAsDialog; |
| import org.eclipse.ui.ide.IDE; |
| import org.eclipse.ui.internal.part.NullEditorInput; |
| import org.eclipse.ui.progress.IWorkbenchSiteProgressService; |
| import org.eclipse.ui.tests.api.MockViewPart; |
| import org.eclipse.ui.tests.harness.util.FileUtil; |
| import org.eclipse.ui.tests.harness.util.UITestCase; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** |
| * Very simple leak tests to determine if any of our heavy objects are not being |
| * disposed properly. Note that the results of these tests will in no way |
| * actually assist you in tracking down the leak. |
| * |
| * @since 3.1 |
| */ |
| @RunWith(JUnit4.class) |
| public class LeakTests extends UITestCase { |
| private IWorkbenchPage fActivePage; |
| |
| private IWorkbenchWindow fWin; |
| |
| private IProject proj; |
| |
| public LeakTests() { |
| super(LeakTests.class.getSimpleName()); |
| } |
| |
| public static void checkRef(ReferenceQueue<?> queue, Reference<?> ref) |
| throws IllegalArgumentException, InterruptedException { |
| boolean flag = false; |
| for (int i = 0; i < 100; i++) { |
| System.runFinalization(); |
| System.gc(); |
| Thread.yield(); |
| processEvents(); |
| Reference<?> checkRef = queue.remove(100); |
| if (checkRef != null && checkRef.equals(ref)) { |
| flag = true; |
| break; |
| } |
| } |
| |
| assertTrue("Reference not enqueued", flag); |
| } |
| |
| /** |
| * @param queue |
| * @param object |
| * @return |
| */ |
| private Reference createReference(ReferenceQueue queue, Object object) { |
| return new PhantomReference<>(object, queue); |
| } |
| |
| @Override |
| protected void doSetUp() throws Exception { |
| super.doSetUp(); |
| fWin = openTestWindow(IDE.RESOURCE_PERSPECTIVE_ID); |
| fActivePage = fWin.getActivePage(); |
| } |
| |
| @Override |
| protected void doTearDown() throws Exception { |
| super.doTearDown(); |
| fWin = null; |
| fActivePage = null; |
| if (proj != null) { |
| FileUtil.deleteProject(proj); |
| proj = null; |
| } |
| } |
| |
| @Test |
| public void testSimpleEditorLeak() throws Exception { |
| proj = FileUtil.createProject("testEditorLeaks"); |
| |
| IFile file = FileUtil.createFile("test.mock1", proj); |
| |
| ReferenceQueue queue = new ReferenceQueue(); |
| IEditorPart editor = IDE.openEditor(fActivePage, file); |
| assertNotNull(editor); |
| Reference ref = createReference(queue, editor); |
| try { |
| fActivePage.closeEditor(editor, false); |
| editor = null; |
| checkRef(queue, ref); |
| } finally { |
| ref.clear(); |
| } |
| } |
| |
| @Test |
| public void testSimpleViewLeak() throws Exception { |
| ReferenceQueue queue = new ReferenceQueue(); |
| IViewPart view = fActivePage.showView(MockViewPart.ID); |
| assertNotNull(view); |
| Reference ref = createReference(queue, view); |
| |
| try { |
| fActivePage.hideView(view); |
| view = null; |
| checkRef(queue, ref); |
| } finally { |
| ref.clear(); |
| } |
| } |
| |
| @Test |
| public void testBug255005ServiceLeak() throws Exception { |
| ReferenceQueue queue = new ReferenceQueue(); |
| IViewPart view = fActivePage.showView(MockViewPart.ID); |
| assertNotNull(view); |
| |
| // create a job to schedule |
| Job doNothingJob = new Job("Does Nothing") { //$NON-NLS-1$ |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| return Status.OK_STATUS; |
| } |
| }; |
| |
| // retrieve the progress service |
| IWorkbenchSiteProgressService service = view |
| .getSite().getService(IWorkbenchSiteProgressService.class); |
| // schedule it |
| service.schedule(doNothingJob); |
| |
| // create a reference for our service |
| Reference ref = createReference(queue, service); |
| |
| // wait for the job to complete |
| doNothingJob.join(); |
| |
| try { |
| // hide the view |
| fActivePage.hideView(view); |
| // remove our references |
| service = null; |
| view = null; |
| // check for leaks |
| checkRef(queue, ref); |
| } finally { |
| ref.clear(); |
| } |
| } |
| |
| @Test |
| public void testBug255005SiteLeak() throws Exception { |
| ReferenceQueue queue = new ReferenceQueue(); |
| IViewPart view = fActivePage.showView(MockViewPart.ID); |
| assertNotNull(view); |
| |
| // create a job to schedule |
| Job doNothingJob = new Job("Does Nothing") { //$NON-NLS-1$ |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| return Status.OK_STATUS; |
| } |
| }; |
| |
| // retrieve the progress service |
| IWorkbenchSiteProgressService service = view |
| .getSite().getService(IWorkbenchSiteProgressService.class); |
| // schedule it |
| service.schedule(doNothingJob); |
| |
| IWorkbenchPartSite site = view.getSite(); |
| // create a reference for our site |
| Reference ref = createReference(queue, site); |
| |
| // wait for the job to complete |
| doNothingJob.join(); |
| |
| try { |
| // hide the view |
| fActivePage.hideView(view); |
| // remove our references |
| service = null; |
| site = null; |
| view = null; |
| // check for leaks |
| checkRef(queue, ref); |
| } finally { |
| ref.clear(); |
| } |
| } |
| |
| @Test |
| public void testBug265449PropertiesLeak() throws Exception { |
| // create a project to be selected by the 'Navigator' |
| proj = FileUtil.createProject("projectToSelect"); |
| |
| // show the 'Navigator' |
| IViewPart navigator = fActivePage.showView(IPageLayout.ID_PROJECT_EXPLORER); |
| // show the 'Properties' view |
| IViewPart propertiesView = fActivePage.showView(IPageLayout.ID_PROP_SHEET); |
| |
| // select the project in the 'Navigator', this will cause the 'Properties' |
| // view to show something, and create a PropertySheetPage, which was leaking |
| navigator.getSite().getSelectionProvider().setSelection(new StructuredSelection(proj)); |
| |
| // create a reference for the 'Properties' view |
| ReferenceQueue queue = new ReferenceQueue(); |
| Reference ref = createReference(queue, propertiesView); |
| |
| try { |
| // hide the views |
| fActivePage.hideView(navigator); |
| fActivePage.hideView(propertiesView); |
| // remove our references |
| navigator = null; |
| propertiesView = null; |
| // check for leaks |
| checkRef(queue, ref); |
| } finally { |
| ref.clear(); |
| } |
| } |
| |
| @Test |
| public void testTextEditorContextMenu() throws Exception { |
| proj = FileUtil.createProject("testEditorLeaks"); |
| |
| IEditorInput input = new NullEditorInput(); |
| ReferenceQueue queue = new ReferenceQueue(); |
| IEditorPart editor = IDE.openEditor(fActivePage, input, "org.eclipse.ui.tests.leak.contextEditor"); |
| assertTrue(editor instanceof ContextEditorPart); |
| Reference ref = createReference(queue, editor); |
| |
| ContextEditorPart contextMenuEditor = (ContextEditorPart) editor; |
| |
| contextMenuEditor.showMenu(); |
| processEvents(); |
| |
| contextMenuEditor.hideMenu(); |
| processEvents(); |
| |
| try { |
| contextMenuEditor = null; |
| fActivePage.closeEditor(editor, false); |
| editor = null; |
| checkRef(queue, ref); |
| } finally { |
| ref.clear(); |
| } |
| } |
| |
| @Test |
| public void testSimpleWindowLeak() throws Exception { |
| // turn off window management so that we dont have a reference to our |
| // new |
| // window in the listener |
| manageWindows(false); |
| try { |
| ReferenceQueue queue = new ReferenceQueue(); |
| IWorkbenchWindow newWindow = openTestWindow(); |
| |
| assertNotNull(newWindow); |
| Reference ref = createReference(queue, newWindow); |
| try { |
| newWindow.close(); |
| newWindow = null; |
| checkRef(queue, ref); |
| } finally { |
| ref.clear(); |
| } |
| } finally { |
| manageWindows(true); |
| } |
| } |
| |
| /** |
| * Test for leaks if dialog is disposed before it is closed. |
| * This is really testing the framework rather than individual |
| * dialogs, since many dialogs or windows will fail if the shell |
| * is destroyed prior to closing them. |
| * See bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=123296 |
| */ |
| @Test |
| public void testDestroyedDialogLeaks() throws Exception { |
| ReferenceQueue queue = new ReferenceQueue(); |
| // Use SaveAs dialog because it's simple to invoke and utilizes |
| // framework function such as storing dialog bounds. |
| // We are really testing the framework itself here. |
| Dialog newDialog = new SaveAsDialog(fWin.getShell()); |
| newDialog.setBlockOnOpen(false); |
| newDialog.open(); |
| assertNotNull(newDialog); |
| Reference ref = createReference(queue, newDialog); |
| try { |
| // Dispose the window before closing it. |
| newDialog.getShell().dispose(); |
| newDialog.close(); |
| newDialog = null; |
| checkRef(queue, ref); |
| } finally { |
| ref.clear(); |
| } |
| } |
| } |