/*
* Copyright (C) 2012 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 interactivespaces.activity.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import interactivespaces.activity.Activity;
import interactivespaces.activity.ActivityListener;
import interactivespaces.activity.ActivityRuntime;
import interactivespaces.activity.ActivityState;
import interactivespaces.activity.ActivityStatus;
import interactivespaces.activity.component.ActivityComponent;
import interactivespaces.activity.component.ActivityComponentContext;
import interactivespaces.activity.component.BaseActivityComponent;
import interactivespaces.activity.execution.ActivityExecutionContext;
import interactivespaces.configuration.Configuration;
import interactivespaces.configuration.SimpleConfiguration;
import interactivespaces.system.InteractiveSpacesEnvironment;
import interactivespaces.time.TimeProvider;
import interactivespaces.util.resource.ManagedResource;
import com.google.common.collect.ImmutableList;
import org.apache.commons.logging.Log;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.ros.concurrent.DefaultScheduledExecutorService;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
/**
* Tests for the {@link BaseActivity}.
*
* @author Keith M. Hughes
*/
public class BaseActivityTest {
private static final long CURRENT_MOCK_TIME = 1000L;
private static final int SAMPLE_TIME = 500;
private BaseActivity activity;
private ScheduledExecutorService executorService;
private Log log;
private ActivityRuntime activityRuntime;
private ActivityComponent component;
private ManagedResource resource;
private SimpleConfiguration configuration;
private ActivityExecutionContext executionContext;
private InOrder activityInOrder;
private InOrder componentInOrder;
private List<ActivityComponent> componentsToAdd;
private List<ManagedResource> resourcesToAdd;
/**
* Setup for all tests.
*/
@Before
public void setup() {
componentsToAdd = new ArrayList<>();
resourcesToAdd = new ArrayList<>();
executorService = new DefaultScheduledExecutorService();
InteractiveSpacesEnvironment spaceEnvironment = Mockito.mock(InteractiveSpacesEnvironment.class);
TimeProvider timeProvider = Mockito.mock(TimeProvider.class);
Mockito.when(spaceEnvironment.getTimeProvider()).thenReturn(timeProvider);
Mockito.when(timeProvider.getCurrentTime()).thenReturn(CURRENT_MOCK_TIME);
Mockito.when(spaceEnvironment.getExecutorService()).thenReturn(executorService);
log = Mockito.mock(Log.class);
activityRuntime = Mockito.mock(ActivityRuntime.class);
configuration = new SimpleConfiguration(null);
executionContext = Mockito.mock(ActivityExecutionContext.class);
component = Mockito.spy(new MyBaseActivityComponent("component1"));
componentsToAdd.add(component);
componentInOrder = Mockito.inOrder(component);
resource = Mockito.mock(ManagedResource.class);
resourcesToAdd.add(resource);
Mockito.when(component.getName()).thenReturn("component");
Mockito.when(component.getDependencies()).thenReturn(new ArrayList<String>());
activity = Mockito.spy(new MyBaseActivity());
activity.setActivityRuntime(activityRuntime);
activity.setSpaceEnvironment(spaceEnvironment);
activity.setConfiguration(configuration);
activity.setExecutionContext(executionContext);
activity.setLog(log);
activity.setUuid("123456789-10-11-12");
activityInOrder = Mockito.inOrder(activity);
}
/**
* Cleanup at the end of all tests.
*/
@After
public void cleanup() {
executorService.shutdown();
}
/**
* Test that a clean startup works.
*/
@Test
public void testCleanStartup() {
activity.startup();
activityInOrder.verify(activity).onActivityPreSetup();
activityInOrder.verify(activity).onActivitySetup();
activityInOrder.verify(activity).onActivityStartup();
activityInOrder.verify(activity).onActivityPostStartup();
componentInOrder.verify(component).setComponentContext(activity.getActivityComponentContext());
componentInOrder.verify(component).configureComponent(configuration);
componentInOrder.verify(component).startupComponent();
assertEquals(ActivityState.RUNNING, activity.getActivityStatus().getState());
assertTrue(activity.getActivityComponentContext().areHandlersAllowed());
Mockito.verify(resource).startup();
}
/**
* Test that a clean activate works.
*/
@Test
public void testCleanActivate() {
activity.startup();
activity.activate();
activityInOrder.verify(activity).onActivityActivate();
assertEquals(ActivityState.ACTIVE, activity.getActivityStatus().getState());
}
/**
* Test that a clean activate works.
*
*/
@Test
public void testCleanDeactivate() {
activity.startup();
activity.activate();
activity.deactivate();
activityInOrder.verify(activity).onActivityDeactivate();
assertEquals(ActivityState.RUNNING, activity.getActivityStatus().getState());
}
/**
* Test that a clean shutdown works.
*/
@Test
public void testCleanShutdown() {
activity.startup();
activity.shutdown();
activityInOrder.verify(activity).onActivityPreShutdown();
activityInOrder.verify(activity).onActivityShutdown();
activityInOrder.verify(activity).onActivityCleanup();
componentInOrder.verify(component).shutdownComponent();
assertEquals(ActivityState.READY, activity.getActivityStatus().getState());
ActivityComponentContext activityComponentContext = activity.getActivityComponentContext();
assertFalse(activityComponentContext.areHandlersAllowed());
assertTrue(activityComponentContext.waitOnNoProcessingHandlings(SAMPLE_TIME, SAMPLE_TIME * 2));
Mockito.verify(resource).shutdown();
}
/**
* Test that a clean shutdown works even if a handler hasn't returned.
*/
@Test
public void testShutdownWithRunningHandler() {
activity.startup();
activity.getActivityComponentContext().enterHandler();
activity.shutdown();
activityInOrder.verify(activity).onActivityPreShutdown();
activityInOrder.verify(activity).onActivityShutdown();
activityInOrder.verify(activity).onActivityCleanup();
componentInOrder.verify(component).shutdownComponent();
assertEquals(ActivityState.READY, activity.getActivityStatus().getState());
ActivityComponentContext activityComponentContext = activity.getActivityComponentContext();
assertFalse(activityComponentContext.areHandlersAllowed());
assertTrue(activityComponentContext.areProcessingHandlers());
Mockito.verify(log, Mockito.times(1)).warn(Mockito.anyString());
Mockito.verify(resource).shutdown();
}
/**
* Test that a broken presetup works.
*/
@Test
public void testBrokenPreSetup() {
Error e = new Error();
Mockito.doThrow(e).when(activity).onActivityPreSetup();
activity.startup();
activityInOrder.verify(activity).onActivityPreSetup();
Mockito.verify(activity, Mockito.never()).onActivitySetup();
Mockito.verify(activity, Mockito.never()).onActivityStartup();
Mockito.verify(activity, Mockito.never()).onActivityPostStartup();
assertEquals(ActivityState.STARTUP_FAILURE, activity.getActivityStatus().getState());
Mockito.verify(log, Mockito.times(1)).error(Mockito.anyString(), Mockito.eq(e));
assertFalse(activity.getActivityComponentContext().areHandlersAllowed());
}
/**
* Test that a broken setup works.
*/
@Test
public void testBrokenSetup() {
Error e = new Error();
Mockito.doThrow(e).when(activity).onActivitySetup();
activity.startup();
activityInOrder.verify(activity).onActivitySetup();
Mockito.verify(activity, Mockito.never()).onActivityStartup();
Mockito.verify(activity, Mockito.never()).onActivityPostStartup();
assertEquals(ActivityState.STARTUP_FAILURE, activity.getActivityStatus().getState());
Mockito.verify(log, Mockito.times(1)).error(Mockito.anyString(), Mockito.eq(e));
assertFalse(activity.getActivityComponentContext().areHandlersAllowed());
}
/**
* Test that a broken component configuring works.
*/
@Test
public void testBrokenComponentConfigure() {
Error e = new Error();
Mockito.doThrow(e).when(component).configureComponent((Configuration) Mockito.anyObject());
activity.startup();
activityInOrder.verify(activity).onActivitySetup();
Mockito.verify(activity, Mockito.never()).onActivityStartup();
Mockito.verify(activity, Mockito.never()).onActivityPostStartup();
assertEquals(ActivityState.STARTUP_FAILURE, activity.getActivityStatus().getState());
Mockito.verify(log, Mockito.times(1)).error(Mockito.anyString(), Mockito.eq(e));
componentInOrder.verify(component).setComponentContext(activity.getActivityComponentContext());
componentInOrder.verify(component).configureComponent(configuration);
assertFalse(activity.getActivityComponentContext().areHandlersAllowed());
Mockito.verify(resource).startup();
Mockito.verify(resource).shutdown();
}
/**
* Test that a broken component startup works.
*/
@Test
public void testBrokenComponentStartup() {
Error e = new Error();
Mockito.doThrow(e).when(component).startupComponent();
activity.startup();
activityInOrder.verify(activity).onActivitySetup();
Mockito.verify(activity, Mockito.never()).onActivityStartup();
Mockito.verify(activity, Mockito.never()).onActivityPostStartup();
assertEquals(ActivityState.STARTUP_FAILURE, activity.getActivityStatus().getState());
Mockito.verify(log, Mockito.atLeast(1)).error(Mockito.anyString(), Mockito.eq(e));
componentInOrder.verify(component).setComponentContext(activity.getActivityComponentContext());
componentInOrder.verify(component).configureComponent(configuration);
componentInOrder.verify(component).startupComponent();
assertFalse(activity.getActivityComponentContext().areHandlersAllowed());
Mockito.verify(resource).startup();
Mockito.verify(resource).shutdown();
}
/**
* Test that a broken component startup works when there are two components and the second one craps out. The first
* one should then shut down.
*/
@Test
public void testBrokenComponentStartupWithComponentShutdown() {
ActivityComponent component2 = Mockito.spy(new MyBaseActivityComponent("component2"));
List<String> dependencies = ImmutableList.of(component.getName());
Mockito.when(component2.getDependencies()).thenReturn(dependencies);
componentInOrder = Mockito.inOrder(component, component2);
componentsToAdd.add(component2);
Error e = new Error();
Mockito.doThrow(e).when(component2).startupComponent();
activity.startup();
activityInOrder.verify(activity).onActivitySetup();
Mockito.verify(activity, Mockito.never()).onActivityStartup();
Mockito.verify(activity, Mockito.never()).onActivityPostStartup();
assertEquals(ActivityState.STARTUP_FAILURE, activity.getActivityStatus().getState());
Mockito.verify(log, Mockito.atLeast(1)).error(Mockito.anyString(), Mockito.eq(e));
Mockito.verify(component).setComponentContext(activity.getActivityComponentContext());
Mockito.verify(component2).setComponentContext(activity.getActivityComponentContext());
componentInOrder.verify(component).configureComponent(configuration);
componentInOrder.verify(component2).configureComponent(configuration);
componentInOrder.verify(component).startupComponent();
componentInOrder.verify(component2).startupComponent();
Mockito.verify(component).shutdownComponent();
Mockito.verify(component2, Mockito.never()).shutdownComponent();
assertFalse(activity.getActivityComponentContext().areHandlersAllowed());
Mockito.verify(resource).startup();
Mockito.verify(resource).shutdown();
}
/**
* Test that a broken resource startup fails the activity startup.
*/
@Test
public void testBrokenManagedResourceFailsStartup() {
Error e = new Error();
Mockito.doThrow(e).when(resource).startup();
activity.startup();
activityInOrder.verify(activity).onActivitySetup();
Mockito.verify(activity, Mockito.never()).onActivityStartup();
Mockito.verify(activity, Mockito.never()).onActivityPostStartup();
assertEquals(ActivityState.STARTUP_FAILURE, activity.getActivityStatus().getState());
Mockito.verify(log, Mockito.atLeast(1)).error(Mockito.anyString(), Mockito.any(Throwable.class));
}
/**
* Test that a broken onStartup works.
*/
@Test
public void testBrokenStartup() {
Error e = new Error();
Mockito.doThrow(e).when(activity).onActivityStartup();
activity.startup();
activityInOrder.verify(activity).onActivitySetup();
activityInOrder.verify(activity).onActivityStartup();
Mockito.verify(activity, Mockito.never()).onActivityPostStartup();
assertEquals(ActivityState.STARTUP_FAILURE, activity.getActivityStatus().getState());
Mockito.verify(log, Mockito.times(1)).error(Mockito.anyString(), Mockito.eq(e));
componentInOrder.verify(component).setComponentContext(activity.getActivityComponentContext());
componentInOrder.verify(component).configureComponent(configuration);
componentInOrder.verify(component).startupComponent();
assertFalse(activity.getActivityComponentContext().areHandlersAllowed());
Mockito.verify(resource).startup();
Mockito.verify(resource).shutdown();
}
/**
* Test that a clean startup with broken Post Startup will actually end up with a running activity.
*/
@Test
public void testBrokenPostStartup() {
Error e = new Error();
Mockito.doThrow(e).when(activity).onActivityPostStartup();
activity.startup();
activityInOrder.verify(activity).onActivitySetup();
activityInOrder.verify(activity).onActivityStartup();
activityInOrder.verify(activity).onActivityPostStartup();
componentInOrder.verify(component).setComponentContext(activity.getActivityComponentContext());
componentInOrder.verify(component).configureComponent(configuration);
componentInOrder.verify(component).startupComponent();
assertEquals(ActivityState.RUNNING, activity.getActivityStatus().getState());
assertTrue(activity.getActivityComponentContext().areHandlersAllowed());
// Make sure the failure is logged.
Mockito.verify(log, Mockito.times(1)).error(Mockito.anyString(), Mockito.eq(e));
Mockito.verify(resource).startup();
Mockito.verify(resource, Mockito.times(0)).shutdown();
}
/**
* Test that a broken onActivate works.
*/
@Test
public void testBrokenActivate() {
Error e = new Error();
Mockito.doThrow(e).when(activity).onActivityActivate();
activity.startup();
activity.activate();
activityInOrder.verify(activity).onActivityActivate();
assertEquals(ActivityState.ACTIVATE_FAILURE, activity.getActivityStatus().getState());
Mockito.verify(log, Mockito.times(1)).error(Mockito.anyString(), Mockito.eq(e));
}
/**
* Test that a broken onDeactivate works.
*/
@Test
public void testBrokenDeactivate() {
Error e = new Error();
Mockito.doThrow(e).when(activity).onActivityDeactivate();
activity.startup();
activity.activate();
activity.deactivate();
activityInOrder.verify(activity).onActivityDeactivate();
assertEquals(ActivityState.DEACTIVATE_FAILURE, activity.getActivityStatus().getState());
Mockito.verify(log, Mockito.times(1)).error(Mockito.anyString(), Mockito.eq(e));
}
/**
* Test that a broken onPreShutdown works.
*/
@Test
public void testBrokenPreShutdown() {
Error e = new Error();
Mockito.doThrow(e).when(activity).onActivityPreShutdown();
activity.startup();
activity.shutdown();
activityInOrder.verify(activity).onActivityPreShutdown();
Mockito.verify(activity, Mockito.never()).onActivityShutdown();
// Cleanup must always be called.
activityInOrder.verify(activity).onActivityCleanup();
assertEquals(ActivityState.SHUTDOWN_FAILURE, activity.getActivityStatus().getState());
Mockito.verify(log, Mockito.times(1)).error(Mockito.anyString(), Mockito.eq(e));
// Everything is shut down
componentInOrder.verify(component).shutdownComponent();
assertFalse(activity.getActivityComponentContext().areHandlersAllowed());
Mockito.verify(resource).shutdown();
}
/**
* Test that a broken onShutdown works.
*/
@Test
public void testBrokenShutdown() {
Error e = new Error();
Mockito.doThrow(e).when(activity).onActivityShutdown();
activity.startup();
activity.shutdown();
activityInOrder.verify(activity).onActivityShutdown();
// Cleanup must always be called.
activityInOrder.verify(activity).onActivityCleanup();
assertEquals(ActivityState.SHUTDOWN_FAILURE, activity.getActivityStatus().getState());
Mockito.verify(log, Mockito.times(1)).error(Mockito.anyString(), Mockito.eq(e));
// Everything is shut down
componentInOrder.verify(component).shutdownComponent();
assertFalse(activity.getActivityComponentContext().areHandlersAllowed());
Mockito.verify(resource).shutdown();
}
/**
* Test that a broken onShutdown works.
*/
@Test
public void testBrokenComponentShutdown() {
Error e = new Error();
Mockito.doThrow(e).when(component).shutdownComponent();
activity.startup();
activity.shutdown();
activityInOrder.verify(activity).onActivityShutdown();
// Cleanup must always be called.
activityInOrder.verify(activity).onActivityCleanup();
assertEquals(ActivityState.SHUTDOWN_FAILURE, activity.getActivityStatus().getState());
Mockito.verify(log, Mockito.times(1)).error(Mockito.anyString(), Mockito.eq(e));
// Everything is shut down
componentInOrder.verify(component).shutdownComponent();
assertFalse(activity.getActivityComponentContext().areHandlersAllowed());
Mockito.verify(resource).shutdown();
}
/**
* Test that status listeners work.
*/
@Test
public void testStatusListener() {
ActivityListener listener = Mockito.mock(ActivityListener.class);
ActivityStatus oldStatus = activity.getActivityStatus();
ActivityStatus newStatus = Mockito.mock(ActivityStatus.class);
activity.addActivityListener(listener);
activity.setActivityStatus(newStatus);
// Need the any() because we are spying on the BaseActivity so the
// object for the listener isn't the same as the spy makes a wrapper
// class
Mockito.verify(listener, Mockito.times(1)).onActivityStatusChange(Mockito.any(Activity.class),
Mockito.eq(oldStatus), Mockito.any(ActivityStatus.class));
}
private class MyBaseActivity extends BaseActivity {
@Override
public void onActivitySetup() {
for (ActivityComponent component : componentsToAdd) {
addActivityComponent(component);
}
for (ManagedResource resource : resourcesToAdd) {
addManagedResource(resource);
}
}
}
private class MyBaseActivityComponent extends BaseActivityComponent {
private String name;
private boolean running;
MyBaseActivityComponent(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void configureComponent(Configuration configuration) {
super.configureComponent(configuration);
}
@Override
public void startupComponent() {
running = true;
}
@Override
public void shutdownComponent() {
running = false;
}
@Override
public boolean isComponentRunning() {
return running;
}
}
}