/*
 * This file has been developed at the University of Munich, Chair for Programming & Software Engineering.
 * 
 * This file is licensed under the Eclipse Public License (EPL) 1.0
 * 
 */
package eu.uml4soa.utbm.u2m.test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import org.junit.Test;

import eu.mdd4soa.smm.behaviour.ExceptionHandler;
import eu.mdd4soa.smm.behaviour.ISMBehaviourFactory;
import eu.mdd4soa.smm.behaviour.ServiceActivity;
import eu.mdd4soa.smm.statik.ExceptionType;
import eu.mdd4soa.smm.statik.ISMStatikFactory;
import eu.uml4soa.utbm.u2m.SemanticsException;
import eu.uml4soa.utbm.u2m.model.CAutomaton;
import eu.uml4soa.utbm.u2m.model.CState;
import eu.uml4soa.utbm.u2m.semantics.OrchestrationSemanticsFunction;

/**
 * This class tests exceptions.
 * 
 * @author Philip Mayer, mayer@pst.ifi.lmu.de
 * 
 */
public class TestException extends AbstractSemanticTests {

	/**
	 * Tests that when only using a throw, we get one error end and nothing
	 * else.
	 * 
	 * @throws SemanticsException
	 */
	@Test
	public void testExceptionThrowOnly() throws SemanticsException {

		ServiceActivity sa= createActivity("Test Snd");
		sa.getChildren().add(createThrow("someException"));

		CAutomaton c= OrchestrationSemanticsFunction.transform(sa);
		assertEquals("Must have two states", 2, c.getStates().size());
		assertEquals("Must have one transition", 1, c.getTransitions().size());
		assertEquals("No NonError end", 0, c.getNonErrorEndStates().size());
		assertEquals("One Error end", 1, c.getErrorEndStates().size());

		CState ee= getFirstAndOnly(c.getErrorEndStates());
		assertEquals("Start state must match", "start throw someException(1)", c.getStart().getName());
		assertEquals("End state must match", "end throw someException(1)", ee.getName());
		assertTrue("End state mut be ERROR", c.getErrorEndStates().contains(ee));
	}

	/**
	 * Test that everything on a path behind a throw is removed.
	 * 
	 * @throws SemanticsException
	 */
	@Test
	public void testExceptionThrowAndSomethingBehind() throws SemanticsException {

		ServiceActivity sa= createActivity("Test Snd");
		sa.getChildren().add(createThrow("someException"));
		sa.getChildren().add(createSend("sendSomething"));

		CAutomaton c= OrchestrationSemanticsFunction.transform(sa);
		assertEquals("Must have two states", 2, c.getStates().size());
		assertEquals("Must have one transition", 1, c.getTransitions().size());
		assertEquals("no nonerror end", 0, c.getNonErrorEndStates().size());
		assertEquals("One error end", 1, c.getErrorEndStates().size());

		CState ee= getFirstAndOnly(c.getErrorEndStates());
		assertEquals("Start state must match", "start throw someException(1)", c.getStart().getName());
		assertEquals("End state must match", "end throw someException(1)", ee.getName());
		assertTrue("End state mut be ERROR", c.getErrorEndStates().contains(ee));
	}

	/**
	 * Tests that when a throw is matched, there is no error end anymore.
	 * 
	 * @throws Exception
	 */
	@Test
	public void testExceptionThrowAndCatch() throws Exception {

		ServiceActivity sa= createActivity("Test Snd");

		ExceptionType exceptionType= ISMStatikFactory.eINSTANCE.createExceptionType();
		exceptionType.setName("someException");

		ServiceActivity inner= createActivity("Inner", createThrow("someException"));
		ExceptionHandler handler= ISMBehaviourFactory.eINSTANCE.createExceptionHandler();
		handler.setName("ExcHandler1");
		handler.setExceptionType(exceptionType);
		handler.getChildren().add(createSend("alarm"));

		sa.getChildren().add(inner);
		sa.getHandlers().add(handler);

		CAutomaton c= OrchestrationSemanticsFunction.transform(sa);
		// store("resources/testThrowAndCatch", c);
		assertEquals("State Number Match", 3, c.getStates().size());
		assertEquals("Transition Number Match", 2, c.getTransitions().size());

		assertEquals("Non-Error End State", 1, c.getNonErrorEndStates().size());
		assertEquals("Error End State", 0, c.getErrorEndStates().size());

		CState nonErr= getFirstAndOnly(c.getNonErrorEndStates());

		checkStateMatch("Nonerr state must match", "Merged[end exchandler ExcHandler1+end snd alarm]", nonErr);
	}

	/**
	 * Tests a throw with handler and no throw. Both should properly end at the
	 * same place (!!)
	 * 
	 * @throws Exception
	 */
	@Test
	public void testExceptionSendThrowAndCatch() throws Exception {

		ServiceActivity sa= createActivity("Test Snd");

		ExceptionType exceptionType= ISMStatikFactory.eINSTANCE.createExceptionType();
		exceptionType.setName("someException");

		ServiceActivity inner= createActivity("Inner", createDecision(createSend("someSend"), createThrow("someException")));
		ExceptionHandler handler= ISMBehaviourFactory.eINSTANCE.createExceptionHandler();
		handler.setName("ExcHandler1");
		handler.setExceptionType(exceptionType);
		handler.getChildren().add(createSend("alarm"));

		sa.getChildren().add(inner);
		inner.getHandlers().add(handler);

		CAutomaton c= OrchestrationSemanticsFunction.transform(sa);
		// store("resources/testExceptionSendThrowAndCatch",c);
		compareMios(c, "testExceptionSendThrowAndCatch");
	}

	/**
	 * Same as above, but with an action behind the scope.
	 * 
	 * @throws SemanticsException
	 */
	@Test
	public void testExceptionSendThrowAndCatchAndBehind() throws SemanticsException {

		ServiceActivity sa= createActivity("Test Snd");

		ExceptionType exceptionType= ISMStatikFactory.eINSTANCE.createExceptionType();
		exceptionType.setName("someException");

		ServiceActivity inner= createActivity("Inner", createDecision(createSend("someSend"), createThrow("someException")));
		ExceptionHandler handler= ISMBehaviourFactory.eINSTANCE.createExceptionHandler();
		handler.setName("ExcHandler1");
		handler.setExceptionType(exceptionType);
		handler.getChildren().add(createSend("alarm"));
		inner.getHandlers().add(handler);

		sa.getChildren().add(inner);
		sa.getChildren().add(createSend("sendAfterTheFact"));

		CAutomaton c= OrchestrationSemanticsFunction.transform(sa);

		assertEquals("State Number Match", 4, c.getStates().size());
		assertEquals("Transition Number Match", 4, c.getTransitions().size());

		assertEquals("Non-Error End State", 1, c.getNonErrorEndStates().size());
		assertEquals("Error End State", 0, c.getErrorEndStates().size());

		CState nonErr= getFirstAndOnly(c.getNonErrorEndStates());
		checkStateMatch("Nonerr state must match", "end snd sendAfterTheFact", nonErr);
	}

	/**
	 * Same as above, but with an action behind the scope.
	 * 
	 * @throws Exception
	 */
	@Test
	public void testExceptionLoop() throws Exception {

		ServiceActivity sa= createActivity("Test Snd");

		ExceptionType exceptionType= ISMStatikFactory.eINSTANCE.createExceptionType();
		exceptionType.setName("someException");

		ServiceActivity inner= createActivity("Inner", createThrow("someException"));
		ExceptionHandler handler= ISMBehaviourFactory.eINSTANCE.createExceptionHandler();
		handler.setName("ExcHandler1");
		handler.setExceptionType(exceptionType);
		handler.getChildren().add(createLoop(createSend("inHandler")));

		sa.getChildren().add(inner);
		sa.getHandlers().add(handler);

		CAutomaton c= OrchestrationSemanticsFunction.transform(sa);

		// store("resources/testExceptionLoop", c);
		compareMios(c, "testExceptionLoop");
	}

}
