diff options
author | Andrey Loskutov | 2016-03-14 14:06:55 +0000 |
---|---|---|
committer | Andrey Loskutov | 2016-08-01 08:45:01 +0000 |
commit | c668724f83ea175ecc55095039907e6620bcc13a (patch) | |
tree | 95b289c8d8c385cd7222eddfeca8436bdefafcfb | |
parent | 8124e2bb15eb3d3793ac8f160e83bddaf1a5c7d8 (diff) | |
download | eclipse.platform.debug-c668724f83ea175ecc55095039907e6620bcc13a.tar.gz eclipse.platform.debug-c668724f83ea175ecc55095039907e6620bcc13a.tar.xz eclipse.platform.debug-c668724f83ea175ecc55095039907e6620bcc13a.zip |
Bug 489546 - Call to ConsoleManager.showConsoleView(x) has no effect ifY20160811-1000I20160809-1300I20160809-1100
ShowConsoleViewJob is already running
Change-Id: Iab3d68b31476704a81f75172cb41ef21b62a57e9
Signed-off-by: Andrey Loskutov <loskutov@gmx.de>
4 files changed, 305 insertions, 33 deletions
diff --git a/org.eclipse.debug.tests/META-INF/MANIFEST.MF b/org.eclipse.debug.tests/META-INF/MANIFEST.MF index ce0c92c34..6cec1e3ba 100644 --- a/org.eclipse.debug.tests/META-INF/MANIFEST.MF +++ b/org.eclipse.debug.tests/META-INF/MANIFEST.MF @@ -13,12 +13,14 @@ Require-Bundle: org.eclipse.ui;bundle-version="[3.6.0,4.0.0)", org.eclipse.test.performance;bundle-version="3.6.0", org.eclipse.core.resources;bundle-version="[3.5.0,4.0.0)", org.eclipse.debug.core;bundle-version="[3.9.0,4.0.0)", - org.eclipse.ui.externaltools;bundle-version="[3.3.0,4.0.0)" + org.eclipse.ui.externaltools;bundle-version="[3.3.0,4.0.0)", + org.eclipse.ui.console;bundle-version="[3.7.0,4.0.0)" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-Vendor: %providerName Export-Package: org.eclipse.debug.tests, org.eclipse.debug.tests.breakpoint, + org.eclipse.debug.tests.console, org.eclipse.debug.tests.expressions, org.eclipse.debug.tests.launching, org.eclipse.debug.tests.sourcelookup, diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java index 7af33159d..92efb0d1c 100644 --- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java +++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java @@ -16,6 +16,7 @@ import junit.framework.Test; import junit.framework.TestSuite; import org.eclipse.debug.tests.breakpoint.BreakpointOrderingTests; +import org.eclipse.debug.tests.console.ConsoleManagerTests; import org.eclipse.debug.tests.launching.AcceleratorSubstitutionTests; import org.eclipse.debug.tests.launching.ArgumentParsingTests; import org.eclipse.debug.tests.launching.LaunchConfigurationTests; @@ -97,5 +98,8 @@ public class AutomatedSuite extends TestSuite { // Step filters addTest(new TestSuite(StepFiltersTests.class)); + + // Console view + addTest(new TestSuite(ConsoleManagerTests.class)); } } diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleManagerTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleManagerTests.java new file mode 100644 index 000000000..1761c70e5 --- /dev/null +++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleManagerTests.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2016 Andrey Loskutov and others. + * 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 + * + * Contributors: + * Andrey Loskutov <loskutov@gmx.de> - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.tests.console; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IViewReference; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.console.ConsolePlugin; +import org.eclipse.ui.console.IConsole; +import org.eclipse.ui.console.IConsoleManager; +import org.eclipse.ui.console.IConsoleView; +import org.eclipse.ui.part.IPageBookViewPage; +import org.eclipse.ui.part.MessagePage; + +import junit.framework.TestCase; + +/** + * Tests console manager + */ +public class ConsoleManagerTests extends TestCase { + + private static final String INTROVIEW_ID = "org.eclipse.ui.internal.introview"; //$NON-NLS-1$ + private ExecutorService executorService; + private IConsoleManager manager; + private int count; + private CountDownLatch latch; + private ConsoleMock[] consoles; + ConsoleMock firstConsole; + + @Override + protected void setUp() throws Exception { + super.setUp(); + count = 20; + latch = new CountDownLatch(count); + executorService = Executors.newFixedThreadPool(count); + manager = ConsolePlugin.getDefault().getConsoleManager(); + IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + hideWelcomePage(activePage); + processUIEvents(100); + consoles = new ConsoleMock[count]; + for (int i = 0; i < count; i++) { + final ConsoleMock console = new ConsoleMock(i + 1); + consoles[i] = console; + } + // register consoles (this does *not* show anything) + manager.addConsoles(consoles); + + IViewPart consoleView = activePage.showView("org.eclipse.ui.console.ConsoleView"); //$NON-NLS-1$ + activePage.activate(consoleView); + processUIEvents(100); + + // The test is unstable ("show" event on the the first console seem to + // be not always sent), so make sure console view has shown at least + // one console for real before starting the main test + firstConsole = new ConsoleMock(0); + manager.addConsoles(new ConsoleMock[] { firstConsole }); + manager.showConsoleView(firstConsole); + waitForJobs(); + processUIEvents(100); + } + + @Override + protected void tearDown() throws Exception { + executorService.shutdownNow(); + manager.removeConsoles(consoles); + manager.removeConsoles(new ConsoleMock[] { firstConsole }); + processUIEvents(100); + super.tearDown(); + } + + private void hideWelcomePage(IWorkbenchPage activePage) { + IViewReference[] refs = activePage.getViewReferences(); + IViewPart intro = null; + for (IViewReference ref : refs) { + if (INTROVIEW_ID.equals(ref.getId())) { + intro = ref.getView(false); + } + } + if (intro != null) { + activePage.hideView(intro); + processUIEvents(100); + } + } + + /** + * The test triggers {@link #count} simultaneous calls to the + * {@link IConsoleManager#showConsoleView(IConsole)} and checks if all of + * this calls were properly proceeded by the manager. + * <p> + * See bug 489546. + * + * @throws Exception + */ + public void testShowAllConsoles() throws Exception { + // Create a number of threads which will start and wait for the last one + // created to call ConsoleManager.show. + for (ConsoleMock console : consoles) { + showConsole(console); + } + // Console manager starts a job with delay, let wait for him a bit + waitForJobs(); + + // Give UI a chance to proceed pending console manager jobs + processUIEvents(3000); + + executorService.shutdown(); + executorService.awaitTermination(1, TimeUnit.MINUTES); + List<ConsoleMock> shown = new ArrayList<>(); + for (ConsoleMock console : consoles) { + if (console.showCalled > 0) { + shown.add(console); + } + } + assertEquals("Only " + shown.size() + " consoles were shown from " + count, count, shown.size()); //$NON-NLS-1$ //$NON-NLS-2$ + } + + private void processUIEvents(final long millis) { + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < millis) { + PlatformUI.getWorkbench().getDisplay().readAndDispatch(); + } + } + + private void waitForJobs() throws Exception { + if (Display.getCurrent() == null) { + Thread.sleep(200); + } else { + processUIEvents(200); + } + while (!Job.getJobManager().isIdle()) { + if (Display.getCurrent() == null) { + Thread.sleep(200); + } else { + processUIEvents(200); + } + } + } + + private void showConsole(final ConsoleMock console) { + executorService.execute(new Runnable() { + @Override + public void run() { + // last one arriving here triggers execution for all at same + // time + latch.countDown(); + try { + latch.await(1, TimeUnit.MINUTES); + System.out.println("Requesting to show: " + console); //$NON-NLS-1$ + manager.showConsoleView(console); + waitForJobs(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + /** + * Dummy console page showing mock number and counting the numbers its + * control was shown in the console view. + */ + static final class ConsoleMock implements IConsole { + MessagePage page; + volatile int showCalled; + final int number; + + public ConsoleMock(int number) { + this.number = number; + } + + @Override + public void removePropertyChangeListener(IPropertyChangeListener listener) { + } + + @Override + public String getType() { + return null; + } + + @Override + public String getName() { + return toString(); + } + + @Override + public ImageDescriptor getImageDescriptor() { + return null; + } + + @Override + public void addPropertyChangeListener(IPropertyChangeListener listener) { + } + + /** + * Just a page showing the mock console name + */ + @Override + public IPageBookViewPage createPage(IConsoleView view) { + page = new MessagePage() { + @Override + public void createControl(Composite parent) { + super.createControl(parent); + // This listener is get called if the page is really shown + // in the console view + getControl().addListener(SWT.Show, new Listener() { + @Override + public void handleEvent(Event event) { + showCalled++; + System.out.println("Shown: " + ConsoleMock.this); //$NON-NLS-1$ + } + }); + } + + }; + page.setMessage(toString()); + return page; + } + + @Override + public String toString() { + return "mock #" + number; //$NON-NLS-1$ + } + } +} diff --git a/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/ConsoleManager.java b/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/ConsoleManager.java index db73423cd..1ef4b9830 100644 --- a/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/ConsoleManager.java +++ b/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/ConsoleManager.java @@ -7,12 +7,14 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Andrey Loskutov <loskutov@gmx.de> - bug 489546 *******************************************************************************/ package org.eclipse.ui.internal.console; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.regex.PatternSyntaxException; @@ -280,7 +282,7 @@ public class ConsoleManager implements IConsoleManager { private class ShowConsoleViewJob extends WorkbenchJob { - private IConsole console; + private Set<IConsole> queue = new LinkedHashSet<IConsole>(); ShowConsoleViewJob() { super("Show Console View"); //$NON-NLS-1$ @@ -288,50 +290,66 @@ public class ConsoleManager implements IConsoleManager { setPriority(Job.SHORT); } - void setConsole(IConsole console) { - this.console = console; + void addConsole(IConsole console) { + synchronized (queue) { + queue.add(console); + } } @Override public IStatus runInUIThread(IProgressMonitor monitor) { + Set<IConsole> consolesToShow; + synchronized (queue) { + consolesToShow = new LinkedHashSet<>(queue); + queue.clear(); + } + for (IConsole c : consolesToShow) { + showConsole(c); + } + synchronized (queue) { + if (!queue.isEmpty()) { + schedule(); + } + } + return Status.OK_STATUS; + } + + private void showConsole(IConsole c) { boolean consoleFound = false; - IWorkbenchWindow window= PlatformUI.getWorkbench().getActiveWorkbenchWindow(); - IConsole c = console; + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); if (window != null && c != null) { - IWorkbenchPage page= window.getActivePage(); - if (page != null) { - synchronized (fConsoleViews) { + IWorkbenchPage page = window.getActivePage(); + if (page != null) { + synchronized (fConsoleViews) { for (IConsoleView consoleView : fConsoleViews) { if (consoleView.getSite().getPage().equals(page)) { - boolean consoleVisible = page.isPartVisible(consoleView); - if (consoleVisible) { - consoleFound = true; + boolean consoleVisible = page.isPartVisible(consoleView); + if (consoleVisible) { + consoleFound = true; boolean bringToTop = shouldBringToTop(c, consoleView); - if (bringToTop) { - page.bringToTop(consoleView); - } + if (bringToTop) { + page.bringToTop(consoleView); + } consoleView.display(c); - } - } + } + } } - } + } - if (!consoleFound) { - try { - IConsoleView consoleView = (IConsoleView) page.showView(IConsoleConstants.ID_CONSOLE_VIEW, null, IWorkbenchPage.VIEW_CREATE); + if (!consoleFound) { + try { + IConsoleView consoleView = (IConsoleView) page.showView(IConsoleConstants.ID_CONSOLE_VIEW, null, IWorkbenchPage.VIEW_CREATE); boolean bringToTop = shouldBringToTop(c, consoleView); - if (bringToTop) { - page.bringToTop(consoleView); - } + if (bringToTop) { + page.bringToTop(consoleView); + } consoleView.display(c); - } catch (PartInitException pie) { - ConsolePlugin.log(pie); - } - } - } - } - console = null; - return Status.OK_STATUS; + } catch (PartInitException pie) { + ConsolePlugin.log(pie); + } + } + } + } } } @@ -341,7 +359,7 @@ public class ConsoleManager implements IConsoleManager { */ @Override public void showConsoleView(final IConsole console) { - showJob.setConsole(console); + showJob.addConsole(console); showJob.schedule(100); } |