| /******************************************************************************* |
| * Copyright (c) 2006, 2018 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v20.html |
| * |
| * Contributors: |
| * IBM - Initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.ocl.ecore.tests; |
| |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EClassifier; |
| import org.eclipse.emf.ecore.EEnumLiteral; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EOperation; |
| import org.eclipse.emf.ecore.EPackage; |
| import org.eclipse.emf.ecore.EParameter; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.EcoreFactory; |
| import org.eclipse.emf.ecore.InternalEObject; |
| import org.eclipse.ocl.Environment; |
| import org.eclipse.ocl.ecore.CallOperationAction; |
| import org.eclipse.ocl.ecore.Constraint; |
| import org.eclipse.ocl.ecore.EcoreEnvironment; |
| import org.eclipse.ocl.ecore.EcoreEnvironmentFactory; |
| import org.eclipse.ocl.ecore.OCL; |
| import org.eclipse.ocl.ecore.SendSignalAction; |
| import org.eclipse.ocl.ecore.internal.UMLReflectionImpl; |
| import org.eclipse.ocl.helper.Choice; |
| import org.eclipse.ocl.helper.ChoiceKind; |
| import org.eclipse.ocl.helper.ConstraintKind; |
| import org.eclipse.ocl.utilities.UMLReflection; |
| |
| /** |
| * Tests for state expressions. |
| * |
| * @author Christian W. Damus (cdamus) |
| */ |
| @SuppressWarnings("nls") |
| public class StatesTest |
| extends AbstractTestSuite { |
| |
| /** |
| * Tests the parsing of the oclIsInState() expression. |
| */ |
| public void test_isInState() { |
| helper.setContext(fruit); |
| |
| try { |
| // test implicit and explicit source |
| helper.createInvariant( |
| "oclIsInState(Ripe) implies not self.oclIsInState(Bad)"); |
| |
| // test source of different type than context (and also implicit) |
| helper.createInvariant( |
| "Apple.allInstances()->forAll(not oclIsInState(Bad::Rotten))"); |
| } catch (Exception e) { |
| fail("Failed to parse: " + e.getLocalizedMessage()); |
| } |
| |
| try { |
| // just to make sure that the second test, above, wasn't a fluke. |
| // Fruit doesn't have this state |
| helper.createInvariant( |
| "Fruit.allInstances()->forAll(not oclIsInState(Bad::Rotten))"); |
| |
| fail("Should have failed to parse non-existent state"); |
| } catch (Exception e) { |
| // success |
| debugPrintln("Got expected error: " + e.getLocalizedMessage()); |
| } |
| } |
| |
| /** |
| * Tests some validation of the oclIsInState() expression. |
| */ |
| public void test_isInState_validation() { |
| helper.setContext(fruit); |
| |
| try { |
| // not enough arguments |
| helper.createInvariant( |
| "self.oclIsInState()"); |
| |
| fail("Should have failed to parse empty arglist"); |
| } catch (Exception e) { |
| // success |
| debugPrintln("Got expected error: " + e.getLocalizedMessage()); |
| } |
| |
| try { |
| // too many arguments |
| helper.createInvariant( |
| "Apple.allInstances()->forAll(oclIsInState(Bad, Rotten))"); |
| |
| fail("Should have failed to parse overabundant arglist"); |
| } catch (Exception e) { |
| // success |
| debugPrintln("Got expected error: " + e.getLocalizedMessage()); |
| } |
| |
| try { |
| // wrong kind of argument |
| helper.createInvariant( |
| "self.oclIsInState(color <> Color::black)"); |
| |
| fail("Should have failed to parse arg of wrong type"); |
| } catch (Exception e) { |
| // success |
| debugPrintln("Got expected error: " + e.getLocalizedMessage()); |
| } |
| |
| try { |
| // another kind of wrong kind of argument |
| helper.createInvariant( |
| "self.oclIsInState(OclTest::Fruit)"); |
| |
| fail("Should have failed to parse arg of wrong type"); |
| } catch (Exception e) { |
| // success |
| debugPrintln("Got expected error: " + e.getLocalizedMessage()); |
| } |
| } |
| |
| /** |
| * Tests the content-assist for states. |
| */ |
| public void test_stateContentAssist() { |
| helper.setContext(fruit); |
| |
| try { |
| // simplest case of first path name part completion |
| List<Choice> choices = helper.getSyntaxHelp( |
| ConstraintKind.INVARIANT, "self.oclIsInState("); |
| assertNotNull(choices); |
| assertChoice(choices, ChoiceKind.STATE, "Ripe"); |
| assertChoice(choices, ChoiceKind.STATE, "Bad"); |
| |
| // case of no more completions (path is already complete) |
| choices = helper.getSyntaxHelp( |
| ConstraintKind.INVARIANT, "self.oclIsInState(Ripe::"); |
| assertNotNull(choices); |
| assertTrue(choices.isEmpty()); |
| |
| // explicit source of non-self type |
| choices = helper.getSyntaxHelp( |
| ConstraintKind.INVARIANT, |
| "Apple.allInstances()->forAll(a : Apple | a.oclIsInState("); |
| assertNotNull(choices); |
| assertChoice(choices, ChoiceKind.STATE, "Ripe"); |
| assertChoice(choices, ChoiceKind.STATE, "Bad"); |
| |
| // implicit source of non-self type |
| // choices = helper.getSyntaxHelp( |
| // ConstraintType.INVARIANT, |
| // "Apple.allInstances()->forAll(oclIsInState("); |
| // assertNotNull(choices); |
| // assertChoice(choices, ChoiceType.STRUCTURAL_FEATURE, "Ripe"); |
| // assertChoice(choices, ChoiceType.STRUCTURAL_FEATURE, "Bad"); |
| |
| // available second-level completions |
| choices = helper.getSyntaxHelp( |
| ConstraintKind.INVARIANT, |
| "Apple.allInstances()->forAll(a : Apple | a.oclIsInState(Bad::"); |
| assertNotNull(choices); |
| assertChoice(choices, ChoiceKind.STATE, "Bruised"); |
| assertChoice(choices, ChoiceKind.STATE, "Rotten"); |
| } catch (Exception e) { |
| fail("Parse failed: " + e.getLocalizedMessage()); |
| } |
| } |
| |
| // |
| // Test framework |
| // |
| |
| @Override |
| protected OCL createOCL() { |
| return OCL.newInstance(new StatefulFruitEnvironmentFactory(this)); |
| } |
| |
| private static final List<String> FRUIT_BAD = Arrays.asList(new String[] {"Bad"}); |
| |
| public static class StatefulFruitEnvironmentFactory extends EcoreEnvironmentFactory { |
| protected final AbstractTestSuite suite; |
| |
| public StatefulFruitEnvironmentFactory(AbstractTestSuite suite) { |
| this.suite = suite; |
| } |
| |
| @Override |
| public EcoreEnvironment createEnvironment() { |
| return new StatefulFruitEnvironment(this, suite); |
| } |
| |
| @Override |
| public EcoreEnvironment createEnvironment( |
| Environment<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> parent) { |
| return new StatefulFruitEnvironment(this, parent, suite); |
| } |
| } |
| |
| private static class StatefulFruitEnvironment extends EcoreEnvironment { |
| protected final AbstractTestSuite suite; |
| private EObject fruitRipe; |
| private EObject fruitBad; |
| private EObject appleBruised; |
| private EObject appleRotten; |
| |
| public StatefulFruitEnvironment(StatefulFruitEnvironmentFactory factory, AbstractTestSuite suite) { |
| super(factory, null); |
| this.suite = suite; |
| setContextPackage(suite.fruitPackage); |
| |
| initStates(); |
| } |
| |
| public StatefulFruitEnvironment( |
| StatefulFruitEnvironmentFactory factory, |
| Environment<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> parent, AbstractTestSuite suite) { |
| super(parent); |
| this.suite = suite; |
| |
| initStates(); |
| } |
| |
| private void initStates() { |
| fruitRipe = EcoreFactory.eINSTANCE.createEObject(); |
| ((InternalEObject) fruitRipe).eSetClass( |
| (EClass) getOCLStandardLibrary().getState()); |
| fruitBad = EcoreFactory.eINSTANCE.createEObject(); |
| ((InternalEObject) fruitBad).eSetClass( |
| (EClass) getOCLStandardLibrary().getState()); |
| appleBruised = EcoreFactory.eINSTANCE.createEObject(); |
| ((InternalEObject) appleBruised).eSetClass( |
| (EClass) getOCLStandardLibrary().getState()); |
| appleRotten = EcoreFactory.eINSTANCE.createEObject(); |
| ((InternalEObject) appleRotten).eSetClass( |
| (EClass) getOCLStandardLibrary().getState()); |
| } |
| |
| @Override |
| protected void collectStates(EClassifier owner, List<String> pathPrefix, List<EObject> states) { |
| if (owner == suite.fruit) { |
| if (pathPrefix.isEmpty()) { |
| states.add(fruitRipe); |
| states.add(fruitBad); |
| } |
| } else if (owner == suite.apple) { |
| if (pathPrefix.isEmpty()) { |
| states.add(fruitBad); |
| } else if (pathPrefix.equals(FRUIT_BAD)) { |
| states.add(appleBruised); |
| states.add(appleRotten); |
| } |
| } |
| } |
| |
| |
| @Override |
| public UMLReflection<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> getUMLReflection() { |
| return new UMLReflectionImpl() { |
| @Override |
| public String getName(Object namedElement) { |
| if (namedElement == fruitRipe) { |
| return "Ripe"; |
| } else if (namedElement == fruitBad) { |
| return "Bad"; |
| } else if (namedElement == appleBruised) { |
| return "Bruised"; |
| } else if (namedElement == appleRotten) { |
| return "Rotten"; |
| } else { |
| return super.getName(namedElement); |
| } |
| }}; |
| } |
| } |
| } |