/* * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.google.gwt.activity.shared; import com.google.gwt.event.shared.EventBus; import com.google.gwt.event.shared.EventHandler; import com.google.gwt.event.shared.GwtEvent; import com.google.gwt.event.shared.UmbrellaException; import com.google.gwt.event.shared.testing.CountingEventBus; import com.google.gwt.place.shared.Place; import com.google.gwt.place.shared.PlaceChangeEvent; import com.google.gwt.place.shared.PlaceChangeRequestEvent; import com.google.gwt.user.client.ui.AcceptsOneWidget; import com.google.gwt.user.client.ui.IsWidget; import com.google.gwt.user.client.ui.Widget; import junit.framework.TestCase; /** * Eponymous unit test. */ public class ActivityManagerTest extends TestCase { private static class AsyncActivity extends SyncActivity { AsyncActivity(MyView view) { super(view); } @Override public void start(AcceptsOneWidget display, EventBus eventBus) { this.display = display; this.bus = eventBus; } void finish() { display.setWidget(view); } } private static class Event extends GwtEvent<Handler> { private static GwtEvent.Type<EventHandler> TYPE = new GwtEvent.Type<EventHandler>(); @Override public GwtEvent.Type<Handler> getAssociatedType() { throw new UnsupportedOperationException("Auto-generated method stub"); } @Override protected void dispatch(Handler handler) { throw new UnsupportedOperationException("Auto-generated method stub"); } } private static class Handler implements EventHandler { }; private static class MyDisplay implements AcceptsOneWidget { IsWidget widget = null; public void setWidget(IsWidget widget) { this.widget = widget; } } private static class MyPlace extends Place { } private static class MyView implements IsWidget { public Widget asWidget() { return null; } } private static class SyncActivity implements Activity { boolean canceled = false; boolean stopped = false; AcceptsOneWidget display; String stopWarning; MyView view; EventBus bus; SyncActivity(MyView view) { this.view = view; } public String mayStop() { return stopWarning; } public void onCancel() { canceled = true; } public void onStop() { stopped = true; } public void start(AcceptsOneWidget display, EventBus eventBus) { this.display = display; this.bus = eventBus; display.setWidget(view); } } private final MyPlace place1 = new MyPlace(); private final MyPlace place2 = new MyPlace(); private SyncActivity activity1 = new SyncActivity(new MyView()); private SyncActivity activity2 = new SyncActivity(new MyView()); private final MyDisplay realDisplay = new MyDisplay(); private final ActivityMapper myMap = new ActivityMapper() { public Activity getActivity(Place place) { if (place.equals(place1)) { return activity1; } if (place.equals(place2)) { return activity2; } return null; } }; private CountingEventBus eventBus = new CountingEventBus(); private ActivityManager manager = new ActivityManager( myMap, eventBus); public void testActiveEventBus() { final AsyncActivity asyncActivity1 = new AsyncActivity(new MyView()); final AsyncActivity asyncActivity2 = new AsyncActivity(new MyView()); ActivityMapper map = new ActivityMapper() { public Activity getActivity(Place place) { if (place.equals(place1)) { return asyncActivity1; } if (place.equals(place2)) { return asyncActivity2; } return null; } }; manager = new ActivityManager(map, eventBus); manager.setDisplay(realDisplay); eventBus.fireEvent(new PlaceChangeEvent(place1)); com.google.web.bindery.event.shared.EventBus activeEventBus = manager.getActiveEventBus(); activeEventBus.addHandler(Event.TYPE, new Handler()); assertEquals(1, eventBus.getCount(Event.TYPE)); eventBus.fireEvent(new PlaceChangeEvent(place2)); assertEquals(0, eventBus.getCount(Event.TYPE)); } public void testAsyncDispatch() { final AsyncActivity asyncActivity1 = new AsyncActivity(new MyView()); final AsyncActivity asyncActivity2 = new AsyncActivity(new MyView()); ActivityMapper map = new ActivityMapper() { public Activity getActivity(Place place) { if (place.equals(place1)) { return asyncActivity1; } if (place.equals(place2)) { return asyncActivity2; } return null; } }; manager = new ActivityManager(map, eventBus); manager.setDisplay(realDisplay); PlaceChangeRequestEvent event = new PlaceChangeRequestEvent( place1); eventBus.fireEvent(event); assertNull(event.getWarning()); assertNull(realDisplay.widget); assertFalse(asyncActivity1.stopped); assertFalse(asyncActivity1.canceled); assertNull(asyncActivity1.display); eventBus.fireEvent(new PlaceChangeEvent(place1)); assertNull(realDisplay.widget); assertFalse(asyncActivity1.stopped); assertFalse(asyncActivity1.canceled); assertNotNull(asyncActivity1.display); asyncActivity1.finish(); assertEquals(asyncActivity1.view, realDisplay.widget); assertFalse(asyncActivity1.stopped); assertFalse(asyncActivity1.canceled); event = new PlaceChangeRequestEvent(place2); eventBus.fireEvent(event); assertNull(event.getWarning()); assertEquals(asyncActivity1.view, realDisplay.widget); assertFalse(asyncActivity1.stopped); assertFalse(asyncActivity1.canceled); assertFalse(asyncActivity2.stopped); assertFalse(asyncActivity2.canceled); assertNull(asyncActivity2.display); eventBus.fireEvent(new PlaceChangeEvent(place2)); assertNull(realDisplay.widget); assertFalse(asyncActivity1.canceled); assertTrue(asyncActivity1.stopped); assertFalse(asyncActivity2.stopped); assertFalse(asyncActivity2.canceled); assertNotNull(asyncActivity2.display); asyncActivity2.finish(); assertEquals(asyncActivity2.view, realDisplay.widget); } public void testCancel() { final AsyncActivity asyncActivity1 = new AsyncActivity(new MyView()); final AsyncActivity ayncActivity2 = new AsyncActivity(new MyView()); ActivityMapper map = new ActivityMapper() { public Activity getActivity(Place place) { if (place.equals(place1)) { return asyncActivity1; } if (place.equals(place2)) { return ayncActivity2; } return null; } }; manager = new ActivityManager(map, eventBus); manager.setDisplay(realDisplay); PlaceChangeRequestEvent event = new PlaceChangeRequestEvent( place1); eventBus.fireEvent(event); assertNull(event.getWarning()); assertNull(realDisplay.widget); assertFalse(asyncActivity1.stopped); assertFalse(asyncActivity1.canceled); assertNull(asyncActivity1.display); eventBus.fireEvent(new PlaceChangeEvent(place1)); assertNull(realDisplay.widget); assertFalse(asyncActivity1.stopped); assertFalse(asyncActivity1.canceled); assertNotNull(asyncActivity1.display); event = new PlaceChangeRequestEvent(place2); eventBus.fireEvent(event); assertNull(event.getWarning()); assertNull(realDisplay.widget); assertFalse(asyncActivity1.stopped); assertFalse(asyncActivity1.canceled); eventBus.fireEvent(new PlaceChangeEvent(place2)); assertNull(realDisplay.widget); assertTrue(asyncActivity1.canceled); assertFalse(asyncActivity1.stopped); assertFalse(ayncActivity2.stopped); assertFalse(ayncActivity2.canceled); assertNotNull(ayncActivity2.display); ayncActivity2.finish(); assertEquals(ayncActivity2.view, realDisplay.widget); asyncActivity1.finish(); assertEquals(ayncActivity2.view, realDisplay.widget); } public void testDropHandlersOnStop() { manager.setDisplay(realDisplay); assertEquals(0, eventBus.getCount(Event.TYPE)); activity1 = new SyncActivity(null) { @Override public void start(AcceptsOneWidget panel, EventBus eventBus) { super.start(panel, eventBus); bus.addHandler(Event.TYPE, new Handler()); } @Override public void onStop() { super.onStop(); bus.addHandler(Event.TYPE, new Handler()); } }; PlaceChangeEvent event = new PlaceChangeEvent(place1); eventBus.fireEvent(event); assertEquals(1, eventBus.getCount(Event.TYPE)); event = new PlaceChangeEvent(place2); eventBus.fireEvent(event); assertEquals(0, eventBus.getCount(Event.TYPE)); // Make sure we didn't nuke the ActivityManager's own handlers assertEquals(1, eventBus.getCount(PlaceChangeEvent.TYPE)); assertEquals(1, eventBus.getCount(PlaceChangeRequestEvent.TYPE)); } public void testEventSetupAndTeardown() { assertEquals(0, eventBus.getCount(PlaceChangeEvent.TYPE)); assertEquals(0, eventBus.getCount(PlaceChangeRequestEvent.TYPE)); manager.setDisplay(realDisplay); assertEquals(1, eventBus.getCount(PlaceChangeEvent.TYPE)); assertEquals(1, eventBus.getCount(PlaceChangeRequestEvent.TYPE)); manager.setDisplay(null); assertEquals(0, eventBus.getCount(PlaceChangeEvent.TYPE)); assertEquals(0, eventBus.getCount(PlaceChangeRequestEvent.TYPE)); } public void testExceptionsOnStartAndCancel() { activity1 = new AsyncActivity(null) { @Override public void start(AcceptsOneWidget panel, EventBus eventBus) { super.start(panel, eventBus); bus.addHandler(Event.TYPE, new Handler()); } @Override public void onCancel() { super.onCancel(); bus.addHandler(Event.TYPE, new Handler()); throw new UnsupportedOperationException("Exception on cancel"); } }; activity2 = new SyncActivity(null) { @Override public void start(AcceptsOneWidget panel, EventBus eventBus) { super.start(panel, eventBus); throw new UnsupportedOperationException("Exception on start"); } }; manager.setDisplay(realDisplay); try { PlaceChangeEvent event = new PlaceChangeEvent(place1); eventBus.fireEvent(event); assertEquals(1, eventBus.getCount(Event.TYPE)); event = new PlaceChangeEvent(place2); eventBus.fireEvent(event); fail("Expected exception"); } catch (UmbrellaException e) { // EventBus throws this one assertEquals(1, e.getCauses().size()); // And this is the one thrown by ActivityManager UmbrellaException nested = (UmbrellaException) e.getCause(); assertEquals(2, nested.getCauses().size()); } assertTrue(activity1.canceled); assertNotNull(activity2.display); assertEquals(0, eventBus.getCount(Event.TYPE)); } public void testExceptionsOnStopAndStart() { activity1 = new SyncActivity(null) { @Override public void start(AcceptsOneWidget panel, EventBus eventBus) { super.start(panel, eventBus); bus.addHandler(Event.TYPE, new Handler()); } @Override public void onStop() { super.onStop(); bus.addHandler(Event.TYPE, new Handler()); throw new UnsupportedOperationException("Exception on stop"); } }; activity2 = new SyncActivity(null) { @Override public void start(AcceptsOneWidget panel, EventBus eventBus) { super.start(panel, eventBus); throw new UnsupportedOperationException("Exception on start"); } }; manager.setDisplay(realDisplay); try { PlaceChangeEvent event = new PlaceChangeEvent(place1); eventBus.fireEvent(event); assertEquals(1, eventBus.getCount(Event.TYPE)); event = new PlaceChangeEvent(place2); eventBus.fireEvent(event); fail("Expected exception"); } catch (UmbrellaException e) { // EventBus throws this one assertEquals(1, e.getCauses().size()); // And this is the one thrown by ActivityManager UmbrellaException nested = (UmbrellaException) e.getCause(); assertEquals(2, nested.getCauses().size()); } assertTrue(activity1.stopped); assertNotNull(activity2.display); assertEquals(0, eventBus.getCount(Event.TYPE)); } /** * @see http://code.google.com/p/google-web-toolkit/issues/detail?id=5375 */ public void testNullDisplayOnPlaceChange() { manager.setDisplay(realDisplay); // Start an activity manager.onPlaceChange(new PlaceChangeEvent(place1)); /* * Now we're going to place2. During PlaceChangeEvent dispatch, * someone kills the manager's display. */ manager.setDisplay(null); // Now the place change event reaches the manager manager.onPlaceChange(new PlaceChangeEvent(place2)); assertNull(activity2.display); assertTrue(activity1.stopped); } public void testNullDisplayBeforeAsyncStart() { final AsyncActivity asyncActivity1 = new AsyncActivity(new MyView()); final AsyncActivity asyncActivity2 = new AsyncActivity(new MyView()); ActivityMapper map = new ActivityMapper() { public Activity getActivity(Place place) { if (place.equals(place1)) { return asyncActivity1; } if (place.equals(place2)) { return asyncActivity2; } return null; } }; manager = new ActivityManager(map, eventBus); manager.setDisplay(realDisplay); // Start an activity manager.onPlaceChange(new PlaceChangeEvent(place1)); // Kill the manager manager.setDisplay(null); // The activity is ready to play asyncActivity1.finish(); // Ta da, no NPE } public void testRejected() { manager.setDisplay(realDisplay); activity1.stopWarning = "Stop fool!"; PlaceChangeRequestEvent event = new PlaceChangeRequestEvent( place1); eventBus.fireEvent(event); assertNull(event.getWarning()); assertNull(realDisplay.widget); eventBus.fireEvent(new PlaceChangeEvent(place1)); assertEquals(activity1.view, realDisplay.widget); event = new PlaceChangeRequestEvent(place2); eventBus.fireEvent(event); assertEquals(activity1.stopWarning, event.getWarning()); assertEquals(activity1.view, realDisplay.widget); assertFalse(activity1.stopped); assertFalse(activity1.canceled); } public void testSyncDispatch() { manager.setDisplay(realDisplay); PlaceChangeRequestEvent event = new PlaceChangeRequestEvent( place1); eventBus.fireEvent(event); assertNull(event.getWarning()); assertNull(realDisplay.widget); assertFalse(activity1.stopped); assertFalse(activity1.canceled); eventBus.fireEvent(new PlaceChangeEvent(place1)); assertEquals(activity1.view, realDisplay.widget); assertFalse(activity1.stopped); assertFalse(activity1.canceled); event = new PlaceChangeRequestEvent(place2); eventBus.fireEvent(event); assertNull(event.getWarning()); assertEquals(activity1.view, realDisplay.widget); assertFalse(activity1.stopped); assertFalse(activity1.canceled); eventBus.fireEvent(new PlaceChangeEvent(place2)); assertEquals(activity2.view, realDisplay.widget); assertTrue(activity1.stopped); assertFalse(activity1.canceled); } /** * Non-regression test: make sure an activity can call {@link AcceptsOneWidget#setWidget(IsWidget)} several times to switch views. */ public void testSetWidgetSeveralTimesPerActivity() { class TwoViewActivity extends SyncActivity { MyView view2; public TwoViewActivity(MyView view1, MyView view2) { super(view1); this.view2 = view2; } void secondView() { display.setWidget(view2); } void firstView() { display.setWidget(view); } } final TwoViewActivity activity = new TwoViewActivity(new MyView(), new MyView()); ActivityMapper map = new ActivityMapper() { public Activity getActivity(Place place) { return activity; } }; manager = new ActivityManager(map, eventBus); manager.setDisplay(realDisplay); // Start an activity manager.onPlaceChange(new PlaceChangeEvent(place1)); assertEquals(activity.view, realDisplay.widget); // Call setWidget on the display several times, just to make sure it's possible activity.secondView(); assertEquals(activity.view2, realDisplay.widget); activity.firstView(); assertEquals(activity.view, realDisplay.widget); activity.secondView(); assertEquals(activity.view2, realDisplay.widget); } }