/* $Id: TestTransitionNotationUml.java 18978 2011-01-24 18:25:30Z linus $ ***************************************************************************** * Copyright (c) 2009-2010 Contributors - see below * 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: * tfmorris * mvw ***************************************************************************** * * Some portions of this file was previously release using the BSD License: */ // Copyright (c) 2004-2008 The Regents of the University of California. All // Rights Reserved. Permission to use, copy, modify, and distribute this // software and its documentation without fee, and without a written // agreement is hereby granted, provided that the above copyright notice // and this paragraph appear in all copies. This software program and // documentation are copyrighted by The Regents of the University of // California. The software program and documentation are supplied "AS // IS", without any accompanying services from The Regents. The Regents // does not warrant that the operation of the program will be // uninterrupted or error-free. The end-user understands that the program // was developed for research purposes and is advised not to rely // exclusively on the program for any reason. IN NO EVENT SHALL THE // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. package org.argouml.notation.providers.uml; import java.text.ParseException; import junit.framework.TestCase; import org.argouml.kernel.Project; import org.argouml.kernel.ProjectManager; import org.argouml.model.InitializeModel; import org.argouml.model.Model; import org.argouml.notation.NotationProvider; import org.argouml.notation.NotationSettings; import org.argouml.profile.ProfileFacade; import org.argouml.profile.init.InitProfileSubsystem; /** * Test UML Notation: parsing transitions. * * @author MVW */ public class TestTransitionNotationUml extends TestCase { private Object model; private Object aClass; private Object returnType; private Object aStateMachine; private Object aState; private Object aOper; /** * The constructor. * * @param str the name */ public TestTransitionNotationUml(String str) { super(str); } /* * @see junit.framework.TestCase#setUp() */ protected void setUp() { InitializeModel.initializeDefault(); assertTrue("Model subsystem init failed.", Model.isInitiated()); new InitProfileSubsystem().init(); Project p = ProjectManager.getManager().makeEmptyProject(); returnType = p.getDefaultReturnType(); model = Model.getModelManagementFactory().createModel(); aClass = Model.getCoreFactory().buildClass("A", model); aOper = Model.getCoreFactory().buildOperation2( aClass, returnType, "myOper"); aStateMachine = Model.getStateMachinesFactory().buildStateMachine(aClass); Object top = Model.getFacade().getTop(aStateMachine); aState = Model.getStateMachinesFactory().buildCompositeState(top); } @Override protected void tearDown() throws Exception { ProjectManager.getManager().removeProject( ProjectManager.getManager().getCurrentProject()); ProfileFacade.reset(); super.tearDown(); } /** * Test the reuse of events when a transition notation is parsed. * See issue 5988. */ public void testParseEventsReuse() { // reuse signal event: Object t1s = checkGenerated(aState, "trigger1[guard]/effect", true, true, true, false); Object t2s = checkGenerated(aState, "trigger1[guard]/effect", true, true, true, false); Object t3s = checkGenerated(aState, "trigger2[guard]/effect", true, true, true, false); Object trigger1s = Model.getFacade().getTrigger(t1s); Object trigger2s = Model.getFacade().getTrigger(t2s); Object trigger3s = Model.getFacade().getTrigger(t3s); assertTrue("No reuse of a signal event.", trigger1s == trigger2s); assertTrue("Unexpected reuse of a signal event.", trigger1s != trigger3s); // reuse call event: Object t1c = checkGenerated(aState, "trigger1()[guard]/effect", true, true, true, false); Object t2c = checkGenerated(aState, "trigger1()[guard]/effect", true, true, true, false); Object t3c = checkGenerated(aState, "trigger2()[guard]/effect", true, true, true, false); Object trigger1c = Model.getFacade().getTrigger(t1c); Object trigger2c = Model.getFacade().getTrigger(t2c); Object trigger3c = Model.getFacade().getTrigger(t3c); assertTrue("No reuse of a call event.", trigger1c == trigger2c); assertTrue("Unexpected reuse of a call event.", trigger1c != trigger3c); // reuse time event: Object t1t = checkGenerated(aState, "after(1s)[guard]/effect", true, true, true, false); Object t2t = checkGenerated(aState, "after(1s)[guard]/effect", true, true, true, false); Object t3t = checkGenerated(aState, "after(2s)[guard]/effect", true, true, true, false); Object trigger1t = Model.getFacade().getTrigger(t1t); Object trigger2t = Model.getFacade().getTrigger(t2t); Object trigger3t = Model.getFacade().getTrigger(t3t); assertTrue("No reuse of a time event.", trigger1t == trigger2t); assertTrue("Unexpected reuse of a time event.", trigger1t != trigger3t); // reuse change event: Object t1g = checkGenerated(aState, "when(condition1)[guard]/effect", true, true, true, false); Object t2g = checkGenerated(aState, "when(condition1)[guard]/effect", true, true, true, false); Object t3g = checkGenerated(aState, "when(condition2)[guard]/effect", true, true, true, false); Object trigger1g = Model.getFacade().getTrigger(t1g); Object trigger2g = Model.getFacade().getTrigger(t2g); Object trigger3g = Model.getFacade().getTrigger(t3g); assertTrue("No reuse of a change event.", trigger1g == trigger2g); assertTrue("Unexpected reuse of a change event.", trigger1g != trigger3g); } /** * Test for the parseTransition() method. * These should NOT generate a ParseException. */ public void testParseTransitionCreate() { checkGenerated(aState, "trigger[guard]/effect", true, true, true, false); checkGenerated(aState, "trigger[]/effect", true, false, true, false); checkGenerated(aState, " tri gg er ( w ) [ ] / e ffect ", true, false, true, false); checkGenerated(aState, "trigger / effect", true, false, true, false); checkGenerated(aState, "/effect", false, false, true, false); checkGenerated(aState, "trigger", true, false, false, false); checkGenerated(aState, "t(a:int=3, b=4.0:double)", true, false, false, false); checkGenerated(aState, "", false, false, false, false); checkGenerated(aState, "[]/", false, false, false, false); checkGenerated(aState, "[ guard ]", false, true, false, false); checkGenerated(aState, "trigger()/", true, false, false, false); checkGenerated(aState, "tr/i[gg]er", true, false, true, false); // Inspired by issue 5983: checkGenerated(aState, "ev1 / printf(\"got [i==1]\");", true, false, true, false); } /** * Test for the parseTransition() method. * These should generate a ParseException. */ public void testParseTransitionNotCreate() { checkGenerated(aState, "trigger[guard/effect", false, false, false, true); checkGenerated(aState, "trigger(", false, false, false, true); checkGenerated(aState, "trigger)/eff()", false, false, false, true); checkGenerated(aState, "trigger(/)", false, false, false, true); checkGenerated(aState, "tr]jhgf[ijh", false, false, false, true); checkGenerated(aState, "tr]/e", false, false, false, true); checkGenerated(aState, "tri[g/g]er", false, false, false, true); } /** * Check if the transition parts are generated or not. <p> * * An internal transition is chosen to test these cases, * instead of a transition between states. Reason: simplicity. * * @param st the state in which to create an internal transition * @param text the text to be parsed * @param trigger true if we expect a trigger to be generated * @param guard true if we expect a guard to be generated * @param effect true if we expect an effect to be generated * @param exception true if we expect an exception to be thrown * @return the generated internal transition */ private Object checkGenerated(Object st, String text, boolean trigger, boolean guard, boolean effect, boolean exception) { Object it = Model.getStateMachinesFactory().buildInternalTransition(st); TransitionNotationUml notation = new TransitionNotationUml(it); try { notation.parseTransition(it, text); assertTrue("Expected exception did not happen.", !exception); } catch (ParseException e) { assertTrue("Unexpected exception: " + e.getMessage(), exception); } if (trigger) { assertTrue("Trigger was not generated for " + text, Model.getFacade().getTrigger(it) != null); } else { assertTrue("Trigger was generated for " + text, Model.getFacade().getTrigger(it) == null); } if (guard) { assertTrue("Guard was not generated for " + text, Model.getFacade().getGuard(it) != null); } else { assertTrue("Guard was generated for " + text, Model.getFacade().getGuard(it) == null); } if (effect) { assertTrue("Effect (action) was not generated for " + text, Model.getFacade().getEffect(it) != null); } else { assertTrue("Effect (action) was generated for " + text, Model.getFacade().getEffect(it) == null); } return it; } /** * Test creating modelelements from a given notation, and then * re-generate the notation-string again, and check if they are equal. * Some of these tests are not very usefulll, * since they may fail on white space differences. * TODO: White space should best be ignored. */ public void testRoundTrip() { checkGenerateRoundTrip(aState, "trigger [guard] / effect"); checkGenerateRoundTrip(aState, "trigger / effect"); checkGenerateRoundTrip(aState, "trigger [guard]"); checkGenerateRoundTrip(aState, " [guard] / effect"); checkGenerateRoundTrip(aState, " / effect"); checkGenerateRoundTrip(aState, "trigger"); checkGenerateRoundTrip(aState, " [guard]"); checkGenerateRoundTrip(aState, ""); checkGenerateRoundTrip(aState, "t(a : int = 3, b : double = 4.0)"); checkGenerateRoundTrip(aState, " / effect(a:2,r=6)"); checkGenerateRoundTrip(aState, "trigger [guard] / eff1; eff2; eff3"); /* Issue 5983: */ checkGenerateRoundTrip(aState, "ev1 [i==1] / printf(\"got [i==1\");"); checkGenerateRoundTrip(aState, "ev1 [i==1] / printf(\"got [i==1]\");"); } private void checkGenerateRoundTrip(Object st, String text) { Object it = Model.getStateMachinesFactory().buildInternalTransition(st); TransitionNotationUml notation = new TransitionNotationUml(it); try { notation.parseTransition(it, text); } catch (ParseException e) { assertTrue("Unexpected exception: " + e.getMessage(), true); } // try creating a string from the generated modelelements: String notationStr = notation.toString(it, NotationSettings.getDefaultSettings()); assertTrue("Notation not correctly generated for " + text + "\n" + "Resulted in: '" + notationStr + "'", text.equals(notationStr)); } /** * Test for the parseTrigger() method: TimeEvent. * Also changing triggertype is tested. */ public void testParseTriggerTimeEvent() { Object trans; Object trig; String text; Object expr; //try creating a TimeEvent text = "after(a while)"; trans = checkGenerated(aState, text, true, false, false, false); trig = Model.getFacade().getTrigger(trans); assertTrue("Unexpected triggertype found instead of TimeEvent for " + text, Model.getFacade().isATimeEvent(trig)); expr = Model.getFacade().getExpression(trig); assertTrue("Incorrectly set TimeExpression for" + text, Model.getFacade().getBody(expr).equals("a while")); //try changing the triggertype to ChangeEvent text = "when(it happens)"; NotationProvider notation = new TransitionNotationUml(trans); notation.parse(trans, text); trig = Model.getFacade().getTrigger(trans); assertTrue("Unexpected triggertype found instead of ChangeEvent", Model.getFacade().isAChangeEvent(trig)); } /** * Test for the parseTrigger() method: ChangeEvent. * Testing creation and deletion. */ public void testParseTriggerChangeEvent() { Object trans; Object trig; String text; Object expr; text = "when(it changed)/effect"; trans = checkGenerated(aState, text, true, false, true, false); trig = Model.getFacade().getTrigger(trans); assertTrue("Unexpected triggertype found instead of ChangeEvent for " + text, Model.getFacade().isAChangeEvent(trig)); expr = Model.getFacade().getExpression(trig); assertTrue("Incorrectly set ChangeExpression for" + text, Model.getFacade().getBody(expr).equals("it changed")); text = "/effect"; TransitionNotationUml notation = new TransitionNotationUml(trans); try { notation.parseTransition(trans, text); } catch (ParseException e) { assertTrue("Unexpected exception when removing ChangeEvent trigger", true); } trig = Model.getFacade().getTrigger(trans); assertTrue("Trigger not deleted", trig != null); } /** * Test for the parseTrigger() method: CallEvent. */ public void testParseTriggerCallEvent() { Object trans; Object trig; String text; text = "call(a method)"; trans = checkGenerated(aState, text, true, false, false, false); trig = Model.getFacade().getTrigger(trans); assertTrue("Unexpected triggertype found instead of CallEvent for " + text, Model.getFacade().isACallEvent(trig)); text = "call()"; trans = checkGenerated(aState, text, true, false, false, false); trig = Model.getFacade().getTrigger(trans); assertTrue("Unexpected triggertype found instead of CallEvent for " + text, Model.getFacade().isACallEvent(trig)); } /** * Test for the parseTrigger() method: SignalEvent. */ public void testParseTriggerSignalEvent() { Object trans; Object trig; String text; text = "signal"; trans = checkGenerated(aState, text, true, false, false, false); trig = Model.getFacade().getTrigger(trans); assertTrue("Unexpected triggertype found instead of SignalEvent", Model.getFacade().isASignalEvent(trig)); text = "/effect"; TransitionNotationUml notation = new TransitionNotationUml(trans); try { notation.parseTransition(trans, text); } catch (ParseException e) { assertTrue("Unexpected exception when removing SignalEvent trigger", true); } trig = Model.getFacade().getTrigger(trans); assertTrue("Trigger not deleted", trig != null); } /** * Test for the parseTrigger() method: * linking of an Operation for a CallEvent. * The operation resides on the class that is the context of the * statemachine: */ public void testParseTriggerCallEventOperation1() { checkLinkingOfOperationToCallEvent("myOper()", aOper); } /** * Test for the parseTrigger() method: * linking of an Operation for a CallEvent. * With the operation residing on another class within the same * namespace: */ public void testParseTriggerCallEventOperation2() { Object bClass = Model.getCoreFactory().buildClass("B", model); Object bOper = Model.getCoreFactory().buildOperation2(bClass, returnType, "bOper"); checkLinkingOfOperationToCallEvent("bOper()", bOper); } /** * Test for the parseTrigger() method: * linking of an Operation for a CallEvent. * When the context is a behavioral feature and * the operation = context: */ public void testParseTriggerCallEventOperation3() { Model.getStateMachinesHelper().setContext(aStateMachine, aOper); checkLinkingOfOperationToCallEvent("myOper()", aOper); } /** * Test for the parseTrigger() method: * linking of an Operation for a CallEvent. * When the context is a behavioral feature and the operation differs * from the context: */ public void testParseTriggerCallEventOperation4() { Model.getStateMachinesHelper().setContext(aStateMachine, aOper); Object bClass = Model.getCoreFactory().buildClass("B", model); Model.getCoreFactory().buildOperation2(bClass, returnType, "cOper"); Object dOper = Model.getCoreFactory().buildOperation2(bClass, returnType, "dOper"); checkLinkingOfOperationToCallEvent("dOper()", dOper); } /** * Test for the parseTrigger() method: * linking of an Operation for a CallEvent. * When the context is a package and the operation is on a class * within that package: */ public void testParseTriggerCallEventOperation5() { Object aPack = Model.getModelManagementFactory().buildPackage("pack1"); Model.getCoreHelper().setNamespace(aPack, model); aClass = Model.getCoreFactory().buildClass("A", aPack); aOper = Model.getCoreFactory().buildOperation2( aClass, returnType, "myOper"); aStateMachine = Model.getActivityGraphsFactory().buildActivityGraph(aPack); Object top = Model.getFacade().getTop(aStateMachine); aState = Model.getStateMachinesFactory().buildCompositeState(top); Object bClass = Model.getCoreFactory().buildClass("B", aPack); Model.getCoreFactory().buildOperation2(bClass, returnType, "cOper"); Object dOper = Model.getCoreFactory().buildOperation2(bClass, returnType, "dOper"); checkLinkingOfOperationToCallEvent("dOper()", dOper); } /** * Test for the parseTrigger() method: * linking of an Operation for a CallEvent. * When the context is a nested class and the operation is on a class * within the containing package: */ public void testParseTriggerCallEventOperation6() { Object aPack = Model.getModelManagementFactory().buildPackage("pack1"); Model.getCoreHelper().setNamespace(aPack, model); aClass = Model.getCoreFactory().buildClass("A", aPack); // nested class: Object bClass = Model.getCoreFactory().buildClass("B", aClass); Object cClass = Model.getCoreFactory().buildClass("C", bClass); aStateMachine = Model.getActivityGraphsFactory().buildActivityGraph(cClass); Object top = Model.getFacade().getTop(aStateMachine); aState = Model.getStateMachinesFactory().buildCompositeState(top); Model.getCoreFactory().buildOperation2(cClass, returnType, "cOper"); Object dOper = Model.getCoreFactory().buildOperation2(aClass, returnType, "dOper"); checkLinkingOfOperationToCallEvent("dOper()", dOper); } /** * Test for the parseTrigger() method: * linking of an Operation for a CallEvent. * When the context is a nested class and the operation is on the same * nested class: */ public void testParseTriggerCallEventOperation7() { Object aPack = Model.getModelManagementFactory().buildPackage("pack1"); Model.getCoreHelper().setNamespace(aPack, model); aClass = Model.getCoreFactory().buildClass("A", aPack); // nested class: Object bClass = Model.getCoreFactory().buildClass("B", aClass); Object cClass = Model.getCoreFactory().buildClass("C", bClass); aStateMachine = Model.getActivityGraphsFactory().buildActivityGraph(cClass); Object top = Model.getFacade().getTop(aStateMachine); aState = Model.getStateMachinesFactory().buildCompositeState(top); Model.getCoreFactory().buildOperation2(cClass, returnType, "cOper"); Object dOper = Model.getCoreFactory().buildOperation2(cClass, returnType, "dOper"); checkLinkingOfOperationToCallEvent("dOper()", dOper); } /** * This method only uses the "aState" variable. */ private void checkLinkingOfOperationToCallEvent( String text, Object operation) { Object trans; Object trig; Object myOp; trans = checkGenerated(aState, text, true, false, false, false); trig = Model.getFacade().getTrigger(trans); assertTrue("Unexpected triggertype found instead of CallEvent for " + text, Model.getFacade().isACallEvent(trig)); myOp = Model.getFacade().getOperation(trig); assertTrue("Operation of CallEvent not linked", myOp != null); assertTrue("Wrong operation linked to callevent", myOp == operation); } /** * Test if help is correctly provided. */ public void testGetHelp() { Object it = Model.getStateMachinesFactory().buildInternalTransition(aState); TransitionNotationUml notation = new TransitionNotationUml(it); String help = notation.getParsingHelp(); assertTrue("No help at all given", help.length() > 0); assertTrue("Parsing help not conform for translation", help.startsWith("parsing.")); } /** * Test if the notationProvider refuses to instantiate * without showing it the right UML element. */ public void testValidObjectCheck() { try { new TransitionNotationUml(aState); fail("The NotationProvider did not throw for a wrong UML element."); } catch (IllegalArgumentException e) { /* Everything fine... */ } } }