/******************************************************************************* * Copyright (c) 2016, 2017 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.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; 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(); assertNotNull("Must run in UI thread, but was in: " + Thread.currentThread().getName(), //$NON-NLS-1$ Display.getCurrent()); count = 20; latch = new CountDownLatch(count); executorService = Executors.newFixedThreadPool(count); manager = ConsolePlugin.getDefault().getConsoleManager(); IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); hideWelcomePage(activePage); TestHelper.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); TestHelper.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); TestHelper.waitForJobs(); TestHelper.processUIEvents(100); ConsoleMock.allShownConsoles.set(0); } @Override protected void tearDown() throws Exception { executorService.shutdownNow(); manager.removeConsoles(consoles); manager.removeConsoles(new ConsoleMock[] { firstConsole }); TestHelper.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); TestHelper.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); } System.out.println("All tasks scheduled, processing UI events now..."); //$NON-NLS-1$ TestHelper.processUIEvents(1000); // Console manager starts a job with delay, let wait for him a bit System.out.println("Waiting on jobs now..."); //$NON-NLS-1$ TestHelper.waitForJobs(); // Give UI a chance to proceed pending console manager jobs System.out.println("Done with jobs, processing UI events again..."); //$NON-NLS-1$ TestHelper.processUIEvents(3000); executorService.shutdown(); System.out.println("Waiting on execution service to finish..."); //$NON-NLS-1$ boolean OK = waitForExecutorService(); if (!OK) { System.out.println("Timed out..."); //$NON-NLS-1$ TestHelper.processUIEvents(10000); // timeout? assertTrue("Timeout occurred while waiting on console to be shown", //$NON-NLS-1$ waitForExecutorService()); } else { System.out.println("Done waiting on execution service to finish"); //$NON-NLS-1$ } int shown = ConsoleMock.allShownConsoles.intValue(); assertEquals("Only " + shown + " consoles were shown from " + count, count, shown); //$NON-NLS-1$ //$NON-NLS-2$ } private boolean waitForExecutorService() throws InterruptedException { for (int i = 0; i < 60; i++) { if (executorService.awaitTermination(1, TimeUnit.SECONDS)) { return true; } TestHelper.processUIEvents(100); } return false; } 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); TestHelper.waitForJobs(); } catch (InterruptedException e) { e.printStackTrace(); Thread.interrupted(); } } }); } /** * 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; final AtomicInteger showCalled; final int number; final static AtomicInteger allShownConsoles = new AtomicInteger(); public ConsoleMock(int number) { this.number = number; showCalled = new AtomicInteger(); } @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) { int count = showCalled.incrementAndGet(); if (count == 1) { count = allShownConsoles.incrementAndGet(); System.out.println("Shown: " + ConsoleMock.this + ", overall: " + count); //$NON-NLS-1$ //$NON-NLS-2$ } } }); } }; page.setMessage(toString()); return page; } @Override public String toString() { return "mock #" + number; //$NON-NLS-1$ } } }