/******************************************************************************* * Copyright (c) 2007, 2014 compeople AG 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: * compeople AG - initial API and implementation *******************************************************************************/ package org.eclipse.riena.ui.swt.synchronizer; import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import junit.framework.TestCase; import org.easymock.EasyMock; import org.eclipse.swt.graphics.DeviceData; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.PlatformUI; import org.eclipse.riena.core.exception.MurphysLawFailure; import org.eclipse.riena.core.test.RienaTestCase; import org.eclipse.riena.core.test.collect.UITestCase; import org.eclipse.riena.core.util.ReflectionUtils; import org.eclipse.riena.ui.swt.uiprocess.SwtUISynchronizer; /** * {@link TestCase} for {@link SwtUISynchronizer} */ @UITestCase public class SwtUISynchronizerTest extends RienaTestCase { private AtomicBoolean provideDisplay; @Override protected void setUp() throws Exception { super.setUp(); provideDisplay = new AtomicBoolean(false); } private static class MockDisplay extends Display { private int syncExecCalls = 0; private int asyncExecCalls = 0; @Override public void syncExec(final Runnable runnable) { try { runnable.run(); } finally { syncExecCalls++; } } @Override public void asyncExec(final Runnable runnable) { new Thread(runnable).start(); asyncExecCalls++; } @Override protected void checkSubclass() { } @Override protected void create(final DeviceData data) { } @Override protected void checkDevice() { } @Override public boolean isDisposed() { return false; } } private static class DummyJob implements Runnable { private final AtomicBoolean done = new AtomicBoolean(false); private long sleepTime = 0; private final CountDownLatch latch = new CountDownLatch(1); public void run() { try { Thread.sleep(sleepTime); } catch (final InterruptedException e) { throw new MurphysLawFailure("Sleeping failed", e); } done.set(true); latch.countDown(); System.out.println("Job done"); } } public void testDelayedDisplayAsync() { if (!PlatformUI.isWorkbenchRunning()) { System.out.println("Skipping SwtUISynchronizerTest.testDelayedDisplayAsync() - requires PDEJUnit"); return; } final MockDisplay mockDisplay = new MockDisplay(); final DummyJob job = new DummyJob(); job.sleepTime = 4000; provideDisplay.set(false); final SwtUISynchronizer synchronizer = new SwtUISynchronizer() { @Override public Display getDisplay() { return provideDisplay.get() ? mockDisplay : null; } @Override protected boolean hasDisplay() { return true; } }; long start = System.currentTimeMillis(); synchronizer.asyncExec(job); final long end = System.currentTimeMillis(); assertTrue(end - start < job.sleepTime); // as the display does not yet exist the job must not done assertFalse(job.done.get()); final Timer timer = new Timer(); final long taskDelay = 3000; timer.schedule(new TimerTask() { @Override public void run() { provideDisplay.set(true); } }, taskDelay); start = System.currentTimeMillis(); boolean assertCalls = false; try { // wait for the job to concurrently execute assertCalls = job.latch.await(10000, TimeUnit.MILLISECONDS); } catch (final InterruptedException e) { throw new MurphysLawFailure("Waiting failed", e); } // the job should have waked this thread assertTrue(assertCalls); // and therefore the job should be done now assertTrue(System.currentTimeMillis() - start >= job.sleepTime + taskDelay); assertTrue(job.done.get()); assertEquals(1, mockDisplay.asyncExecCalls); } public void testDelayedDisplaySync() { if (!PlatformUI.isWorkbenchRunning()) { System.out.println("Skipping SwtUISynchronizerTest.testDelayedDisplaySync() - requires PDEJunit"); return; } final MockDisplay mockDisplay = new MockDisplay(); DummyJob job = new DummyJob(); job.sleepTime = 4000; provideDisplay.set(false); final SwtUISynchronizer synchronizer = new SwtUISynchronizer() { @Override public Display getDisplay() { return provideDisplay.get() ? mockDisplay : null; } @Override protected boolean hasDisplay() { return true; } }; job = new DummyJob(); job.sleepTime = 4000; provideDisplay.set(false); final Timer timer = new Timer(); final long taskDelay = 5000; timer.schedule(new TimerTask() { @Override public void run() { provideDisplay.set(true); } }, taskDelay); final long start = System.currentTimeMillis(); synchronizer.syncExec(job); // this thread has been waiting for execution of the job (syncExec), // ie the job must be done and syncExec must have been called assertTrue(System.currentTimeMillis() - start >= job.sleepTime + taskDelay); assertTrue(job.done.get()); assertEquals(1, mockDisplay.syncExecCalls); } public void testDelayedDisplayMultiple() { if (!PlatformUI.isWorkbenchRunning()) { System.out.println("Skipping SwtUISynchronizerTest.testDelayedDisplayMultiple() - requires PDEJunit"); return; } final MockDisplay mockDisplay = new MockDisplay(); provideDisplay.set(false); final SwtUISynchronizer synchronizer = new SwtUISynchronizer() { @Override public Display getDisplay() { return provideDisplay.get() ? mockDisplay : null; } @Override protected boolean hasDisplay() { return true; } }; provideDisplay.set(false); final Timer timer = new Timer(); final long taskDelay = 5000; timer.schedule(new TimerTask() { @Override public void run() { provideDisplay.set(true); } }, taskDelay); final List<DummyJob> jobs = new ArrayList<DummyJob>(); final DummyJob exceptionJob = new DummyJob() { @Override public void run() { super.run(); throw new RuntimeException(); } }; jobs.add(exceptionJob); synchronizer.asyncExec(exceptionJob); mockDisplay.asyncExecCalls = 0; for (int i = 0; i < 25; i++) { final DummyJob jobI = new DummyJob(); jobs.add(jobI); synchronizer.asyncExec(jobI); try { Thread.sleep(80); } catch (final InterruptedException e) { throw new MurphysLawFailure("Sleeping failed", e); } } assertNotNull(ReflectionUtils.getHidden(synchronizer, "displayObserver")); for (final DummyJob dummyJob : jobs) { try { dummyJob.latch.await(25 * 80 + taskDelay + 5000, TimeUnit.MILLISECONDS); } catch (final InterruptedException e) { throw new MurphysLawFailure("Waiting failed", e); } } assertTrue(mockDisplay.asyncExecCalls > 1); assertNull(ReflectionUtils.getHidden(synchronizer, "displayObserver")); // all jobs must be done by now for (final DummyJob job : jobs) { assertTrue(job.done.get()); } } public void testSyncExec() { final MockDisplay mockDisplay = new MockDisplay(); final SwtUISynchronizer synchronizer = new SwtUISynchronizer() { @Override public Display getDisplay() { return mockDisplay; } @Override protected boolean hasDisplay() { return true; } }; synchronizer.syncExec(EasyMock.createNiceMock(Runnable.class)); assertEquals(1, mockDisplay.syncExecCalls); assertEquals(0, mockDisplay.asyncExecCalls); } public void testAsyncExec() { final MockDisplay mockDisplay = new MockDisplay(); final SwtUISynchronizer synchronizer = new SwtUISynchronizer() { @Override public Display getDisplay() { return mockDisplay; } @Override protected boolean hasDisplay() { return true; } }; synchronizer.asyncExec(EasyMock.createNiceMock(Runnable.class)); assertEquals(0, mockDisplay.syncExecCalls); assertEquals(1, mockDisplay.asyncExecCalls); } }