/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.falcon.state; import org.apache.falcon.FalconException; import org.apache.falcon.cluster.util.EmbeddedCluster; import org.apache.falcon.entity.v0.EntityType; import org.apache.falcon.entity.v0.cluster.Cluster; import org.apache.falcon.entity.v0.feed.Feed; import org.apache.falcon.entity.v0.process.Process; import org.apache.falcon.exception.InvalidStateTransitionException; import org.apache.falcon.exception.StateStoreException; import org.apache.falcon.state.store.AbstractStateStore; import org.apache.falcon.util.StateStoreProperties; import org.mockito.Mockito; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; /** * Tests to ensure entity state changes happen correctly. */ public class EntityStateServiceTest extends AbstractSchedulerTestBase{ private EntityStateChangeHandler listener = Mockito.mock(EntityStateChangeHandler.class); @BeforeClass public void setup() throws Exception { StateStoreProperties.get().setProperty("falcon.state.store.impl", "org.apache.falcon.state.store.InMemoryStateStore"); super.setup(); this.dfsCluster = EmbeddedCluster.newCluster("testCluster"); this.conf = dfsCluster.getConf(); } @AfterMethod public void setUp() throws StateStoreException { AbstractStateStore.get().clear(); } // Tests a schedulable entity's lifecycle : Submit -> run -> suspend -> resume @Test public void testLifeCycle() throws Exception { Process mockEntity = new Process(); mockEntity.setName("test"); storeEntity(EntityType.PROCESS, "test"); StateService.get().handleStateChange(mockEntity, EntityState.EVENT.SUBMIT, listener); EntityState entityFromStore = AbstractStateStore.get().getAllEntities().iterator().next(); Mockito.verify(listener).onSubmit(mockEntity); Assert.assertTrue(entityFromStore.getCurrentState().equals(EntityState.STATE.SUBMITTED)); StateService.get().handleStateChange(mockEntity, EntityState.EVENT.SCHEDULE, listener); Mockito.verify(listener).onSchedule(mockEntity); entityFromStore = AbstractStateStore.get().getAllEntities().iterator().next(); Assert.assertTrue(entityFromStore.getCurrentState().equals(EntityState.STATE.SCHEDULED)); StateService.get().handleStateChange(mockEntity, EntityState.EVENT.SUSPEND, listener); Mockito.verify(listener).onSuspend(mockEntity); entityFromStore = AbstractStateStore.get().getAllEntities().iterator().next(); Assert.assertTrue(entityFromStore.getCurrentState().equals(EntityState.STATE.SUSPENDED)); StateService.get().handleStateChange(mockEntity, EntityState.EVENT.RESUME, listener); Mockito.verify(listener).onResume(mockEntity); entityFromStore = AbstractStateStore.get().getAllEntities().iterator().next(); Assert.assertTrue(entityFromStore.getCurrentState().equals(EntityState.STATE.SCHEDULED)); } @Test public void testInvalidTransitions() throws Exception { Feed mockEntity = new Feed(); mockEntity.setName("test"); storeEntity(EntityType.FEED, "test"); StateService.get().handleStateChange(mockEntity, EntityState.EVENT.SUBMIT, listener); // Attempt suspending a submitted entity try { StateService.get().handleStateChange(mockEntity, EntityState.EVENT.SUSPEND, listener); Assert.fail("Exception expected"); } catch (InvalidStateTransitionException e) { // Do nothing } StateService.get().handleStateChange(mockEntity, EntityState.EVENT.SCHEDULE, listener); // Attempt resuming a scheduled entity try { StateService.get().handleStateChange(mockEntity, EntityState.EVENT.RESUME, listener); Assert.fail("Exception expected"); } catch (InvalidStateTransitionException e) { // Do nothing } // Attempt scheduling a cluster Cluster mockCluster = new Cluster(); mockCluster.setName("test"); StateService.get().handleStateChange(mockCluster, EntityState.EVENT.SUBMIT, listener); try { StateService.get().handleStateChange(mockCluster, EntityState.EVENT.SCHEDULE, listener); Assert.fail("Exception expected"); } catch (FalconException e) { // Do nothing } } @Test(dataProvider = "state_and_events") public void testIdempotency(EntityState.STATE state, EntityState.EVENT event) throws Exception { Process mockEntity = new Process(); mockEntity.setName("test"); storeEntity(EntityType.PROCESS, "test"); EntityState entityState = new EntityState(mockEntity).setCurrentState(state); entityState.nextTransition(event); Assert.assertEquals(entityState.getCurrentState(), state); } @DataProvider(name = "state_and_events") public Object[][] stateAndEvents() { return new Object[][]{ {EntityState.STATE.SCHEDULED, EntityState.EVENT.SCHEDULE}, {EntityState.STATE.SUBMITTED, EntityState.EVENT.SUBMIT}, {EntityState.STATE.SUSPENDED, EntityState.EVENT.SUSPEND}, }; } }