/******************************************************************************* * Copyright (c) 2011, 2015 EclipseSource 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: * EclipseSource - initial API and implementation ******************************************************************************/ package org.eclipse.rap.rwt.internal.lifecycle; import static org.eclipse.rap.rwt.internal.service.ContextProvider.getApplicationContext; import static org.eclipse.rap.rwt.testfixture.internal.ConcurrencyTestUtil.runInThread; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.io.IOException; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.application.EntryPoint; import org.eclipse.rap.rwt.client.WebClient; import org.eclipse.rap.rwt.internal.service.ContextProvider; import org.eclipse.rap.rwt.internal.service.UISessionImpl; import org.eclipse.rap.rwt.service.UISession; import org.eclipse.rap.rwt.service.UISessionEvent; import org.eclipse.rap.rwt.service.UISessionListener; import org.eclipse.rap.rwt.service.UIThreadListener; import org.eclipse.rap.rwt.testfixture.internal.Fixture; import org.eclipse.rap.rwt.testfixture.internal.LoggingPhaseListener; import org.eclipse.rap.rwt.testfixture.internal.TestRequest; import org.eclipse.rap.rwt.testfixture.internal.LoggingPhaseListener.PhaseEventInfo; import org.eclipse.swt.widgets.Display; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; public class SimpleLifeCycle_Test { private LifeCycle lifeCycle; @Before public void setUp() { Fixture.setUp(); lifeCycle = new SimpleLifeCycle( getApplicationContext() ); } @After public void tearDown() { Fixture.tearDown(); } @Test public void testPhaseOrderForInitialRequest() throws Exception { registerEntryPoint( TestEntryPoint.class ); LoggingPhaseListener phaseListener = new LoggingPhaseListener( PhaseId.ANY ); lifeCycle.addPhaseListener( phaseListener ); lifeCycle.execute(); PhaseEventInfo[] loggedEvents = phaseListener.getLoggedEvents(); assertEquals( 4, loggedEvents.length ); assertBeforePhaseEvent( loggedEvents[ 0 ], PhaseId.PREPARE_UI_ROOT ); assertAfterPhaseEvent( loggedEvents[ 1 ], PhaseId.PREPARE_UI_ROOT ); assertBeforePhaseEvent( loggedEvents[ 2 ], PhaseId.RENDER ); assertAfterPhaseEvent( loggedEvents[ 3 ], PhaseId.RENDER ); } @Test public void testPhaseOrderForSubsequentRequest() throws Exception { new Display(); LoggingPhaseListener phaseListener = new LoggingPhaseListener( PhaseId.ANY ); lifeCycle.addPhaseListener( phaseListener ); lifeCycle.execute(); PhaseEventInfo[] loggedEvents = phaseListener.getLoggedEvents(); assertEquals( 8, loggedEvents.length ); assertBeforePhaseEvent( loggedEvents[ 0 ], PhaseId.PREPARE_UI_ROOT ); assertAfterPhaseEvent( loggedEvents[ 1 ], PhaseId.PREPARE_UI_ROOT ); assertBeforePhaseEvent( loggedEvents[ 2 ], PhaseId.READ_DATA ); assertAfterPhaseEvent( loggedEvents[ 3 ], PhaseId.READ_DATA ); assertBeforePhaseEvent( loggedEvents[ 4 ], PhaseId.PROCESS_ACTION ); assertAfterPhaseEvent( loggedEvents[ 5 ], PhaseId.PROCESS_ACTION ); assertBeforePhaseEvent( loggedEvents[ 6 ], PhaseId.RENDER ); assertAfterPhaseEvent( loggedEvents[ 7 ], PhaseId.RENDER ); } @Test public void testThreadIsAttachedInInitialRequest() throws IOException { registerEntryPoint( TestEntryPoint.class ); ThreadRecordingPhaseListener phaseListener = new ThreadRecordingPhaseListener( ); lifeCycle.addPhaseListener( phaseListener ); lifeCycle.execute(); Thread[] threads = phaseListener.getThreads(); for( int i = 0; i < threads.length; i++ ) { assertSame( Thread.currentThread(), threads[ i ] ); } } @Test public void testThreadIsDetachedInInitialRequest() throws IOException { registerEntryPoint( TestEntryPoint.class ); lifeCycle.execute(); assertNull( Display.getCurrent() ); assertNull( LifeCycleUtil.getSessionDisplay().getThread() ); } @Test public void testThreadIsAttachedInSubsequentRequest() throws IOException { registerEntryPoint( TestEntryPoint.class ); lifeCycle.execute(); Fixture.fakeNewRequest(); ThreadRecordingPhaseListener phaseListener = new ThreadRecordingPhaseListener( ); lifeCycle.addPhaseListener( phaseListener ); lifeCycle.execute(); Thread[] threads = phaseListener.getThreads(); for( int i = 0; i < threads.length; i++ ) { assertSame( Thread.currentThread(), threads[ i ] ); } } @Test public void testThreadIsDetachedInSubsequentRequest() throws IOException { registerEntryPoint( TestEntryPoint.class ); TestRequest request = ( TestRequest )RWT.getRequest(); request.setServerName( "/rap" ); lifeCycle.execute(); Fixture.fakeNewRequest(); lifeCycle.execute(); assertNull( Display.getCurrent() ); assertNull( LifeCycleUtil.getSessionDisplay().getThread() ); } // bug 361753 @Test public void testDefaultDisplayIsAvailableInInitialRequest() throws IOException { registerEntryPoint( DefaultDisplayEntryPoint.class ); Fixture.fakeNewRequest(); lifeCycle.execute(); assertNotNull( LifeCycleUtil.getSessionDisplay( ContextProvider.getUISession() ) ); } @Test public void testPhaseListenersHaveApplicationScope() throws Exception { registerEntryPoint( TestEntryPoint.class ); LoggingPhaseListener phaseListener = new LoggingPhaseListener( PhaseId.ANY ); lifeCycle.addPhaseListener( phaseListener ); newSession(); lifeCycle.execute(); assertTrue( phaseListener.getLoggedEvents().length > 0 ); } @Test public void testAddPhaseListener() throws Exception { registerEntryPoint( TestEntryPoint.class ); LoggingPhaseListener phaseListener = new LoggingPhaseListener( PhaseId.ANY ); lifeCycle.addPhaseListener( phaseListener ); lifeCycle.execute(); assertTrue( phaseListener.getLoggedEvents().length > 0 ); } @Test public void testRemovePhaseListener() throws Exception { registerEntryPoint( TestEntryPoint.class ); LoggingPhaseListener phaseListener = new LoggingPhaseListener( PhaseId.ANY ); lifeCycle.addPhaseListener( phaseListener ); lifeCycle.removePhaseListener( phaseListener ); lifeCycle.execute(); assertEquals( 0, phaseListener.getLoggedEvents().length ); } @Test public void testRequestThreadExecRunsRunnableOnCallingThread() { final AtomicReference<Thread> invocationThread = new AtomicReference<>(); Runnable runnable = new Runnable() { @Override public void run() { invocationThread.set( Thread.currentThread() ); } }; lifeCycle.requestThreadExec( runnable ); assertSame( Thread.currentThread(), invocationThread.get() ); } @Test public void testGetUIThreadWhileLifeCycleInExecute() throws IOException { new Display(); final AtomicReference<Thread> uiThread = new AtomicReference<>(); lifeCycle.addPhaseListener( new PhaseListener() { private static final long serialVersionUID = 1L; @Override public PhaseId getPhaseId() { return PhaseId.PREPARE_UI_ROOT; } @Override public void beforePhase( PhaseEvent event ) { } @Override public void afterPhase( PhaseEvent event ) { uiThread.set( LifeCycleUtil.getUIThread( ContextProvider.getUISession() ).getThread() ); } } ); lifeCycle.execute(); assertSame( Thread.currentThread(), uiThread.get() ); } @Test public void testGetUIThreadAfterLifeCycleExecuted() throws IOException { registerEntryPoint( TestEntryPoint.class ); lifeCycle.execute(); IUIThreadHolder threadHolder = LifeCycleUtil.getUIThread( ContextProvider.getUISession() ); assertNull( threadHolder ); } @Test public void testInvalidateDisposesDisplay() throws Throwable { final UISession uiSession = ContextProvider.getUISession(); Display display = new Display(); lifeCycle.execute(); runInThread( new Runnable() { @Override public void run() { uiSession.getHttpSession().invalidate(); } } ); assertTrue( display.isDisposed() ); } @Test public void testSessionRestartDisposesDisplay() throws IOException { final UISession uiSession = ContextProvider.getUISession(); Display display = new Display(); lifeCycle.execute(); uiSession.getHttpSession().invalidate(); assertTrue( display.isDisposed() ); } @Test public void testSleep() { try { lifeCycle.sleep(); fail(); } catch( UnsupportedOperationException expected ) { assertTrue( expected.getMessage().contains( "Display#sleep()" ) ); } } @Test public void testNotifyUIThreadListeners() throws IOException { UIThreadListener listener = mock( UIThreadListener.class ); ContextProvider.getApplicationContext().addUIThreadListener( listener ); new Display(); ArgumentCaptor<UISessionEvent> captor = ArgumentCaptor.forClass( UISessionEvent.class ); InOrder inOrder = inOrder( listener ); lifeCycle.execute(); inOrder.verify( listener ).enterUIThread( captor.capture() ); inOrder.verify( listener ).leaveUIThread( captor.capture() ); inOrder.verifyNoMoreInteractions(); for( UISessionEvent event : captor.getAllValues() ) { assertSame( ContextProvider.getUISession(), event.getUISession() ); } } @Test public void testNotifyUIThreadListeners_twice() throws IOException { UIThreadListener listener = mock( UIThreadListener.class ); ContextProvider.getApplicationContext().addUIThreadListener( listener ); new Display(); lifeCycle.execute(); lifeCycle.execute(); verify( listener, times( 2 ) ).enterUIThread( any( UISessionEvent.class ) ); verify( listener, times( 2 ) ).leaveUIThread( any( UISessionEvent.class ) ); } @Test public void testNotifyUIThreadListeners_haveAccessToUISession() throws IOException { UIThreadListener listener = new UIThreadListener() { @Override public void enterUIThread( UISessionEvent event ) { assertNotNull( RWT.getUISession() ); } @Override public void leaveUIThread( UISessionEvent event ) { assertNotNull( RWT.getUISession() ); } }; ContextProvider.getApplicationContext().addUIThreadListener( listener ); new Display(); lifeCycle.execute(); lifeCycle.execute(); } @Test public void testContextOnShutdownFromBackgroundThread() throws Exception { final AtomicBoolean hasContext = new AtomicBoolean(); // Activate SimpleLifeCycle getApplicationContext().getLifeCycleFactory().deactivate(); getApplicationContext().getLifeCycleFactory().activate(); registerEntryPoint( TestEntryPoint.class ); final UISessionImpl uiSession = ( UISessionImpl )RWT.getUISession(); uiSession.addUISessionListener( new UISessionListener() { @Override public void beforeDestroy( UISessionEvent event ) { hasContext.set( ContextProvider.hasContext() ); } } ); // Initialize shutdown adapter getApplicationContext().getLifeCycleFactory().getLifeCycle().execute(); Thread thread = new Thread( new Runnable() { @Override public void run() { uiSession.getShutdownAdapter().interceptShutdown(); // Prevents NPE in tearDown uiSession.setShutdownAdapter( null ); } } ); thread.setDaemon( true ); thread.start(); thread.join(); assertTrue( hasContext.get() ); } private void assertBeforePhaseEvent( PhaseEventInfo beforePrepareUIRoot, PhaseId phaseId ) { assertTrue( beforePrepareUIRoot.before ); assertEquals( phaseId, beforePrepareUIRoot.phaseId ); assertSame( lifeCycle, beforePrepareUIRoot.source ); } private void assertAfterPhaseEvent( PhaseEventInfo beforePrepareUIRoot, PhaseId phaseId ) { assertFalse( beforePrepareUIRoot.before ); assertEquals( phaseId, beforePrepareUIRoot.phaseId ); assertSame( lifeCycle, beforePrepareUIRoot.source ); } private static void registerEntryPoint( Class<? extends EntryPoint> type ) { getApplicationContext().getEntryPointManager().register( "/rap", type, null ); } private static void newSession() { ContextProvider.disposeContext(); Fixture.createServiceContext(); Fixture.fakeClient( new WebClient() ); } private static class ThreadRecordingPhaseListener implements PhaseListener { private static final long serialVersionUID = 1L; private final List<Thread> threads; private ThreadRecordingPhaseListener() { threads = new LinkedList<Thread>(); } @Override public void beforePhase( PhaseEvent event ) { threads.add( Display.getCurrent().getThread() ); } @Override public void afterPhase( PhaseEvent event ) { threads.add( Display.getCurrent().getThread() ); } @Override public PhaseId getPhaseId() { return PhaseId.ANY; } Thread[] getThreads() { Thread[] result = new Thread[ threads.size() ]; threads.toArray( result ); return result; } } private static class TestEntryPoint implements EntryPoint { @Override public int createUI() { new Display(); return 0; } } private static class DefaultDisplayEntryPoint implements EntryPoint { @Override public int createUI() { Display.getDefault(); return 0; } } }