Saturday, May 22, 2010

Testing Stereotype Attribute RegEx Constraints

Goal:


Suppose that we have a UML profile that contains a stereotype with an attribute of a custom data type constrained by a regular expression. This tutorial steps through one way to automate tests that exercise this restriction.

Create a UML Profile:


We can use the instructions from a previous tutorial to create a UML profile with a restricted data type. In our case, a Contact is an Actor who has a US phone number. We can call our profile Contact.profile.uml:

<?xml version="1.0" encoding="UTF-8"?>
<xmi:XMI xmi:version="2.1" xmlns:xmi="http://schema.omg.org/spec/XMI/2.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:Ecore="http://www.eclipse.org/uml2/schemas/Ecore/5" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:uml="http://www.eclipse.org/uml2/3.0.0/UML" xsi:schemaLocation="http://www.eclipse.org/uml2/schemas/Ecore/5 pathmap://UML_PROFILES/Ecore.profile.uml#_z1OFcHjqEdy8S4Cr8Rc_NA">
 <uml:Profile xmi:id="_-8vWsEz2Ed-q5fB8PwONew" name="ContactProfile" metaclassReference="_Q-rN8Ez-Ed-q5fB8PwONew">
  <eAnnotations xmi:id="_TjG80GI3Ed-E2et4hPKZEg" source="http://www.eclipse.org/uml2/2.0.0/UML">
   <contents xmi:type="ecore:EPackage" xmi:id="_TjJZEGI3Ed-E2et4hPKZEg" name="RegexValidation" nsURI="http:///schemas/RegexValidation/_Til_cGI3Ed-E2et4hPKZEg/0" nsPrefix="RegexValidation">
    <eClassifiers xmi:type="ecore:EClass" xmi:id="_TjJZEWI3Ed-E2et4hPKZEg" name="Contact">
     <eAnnotations xmi:id="_TjJZEmI3Ed-E2et4hPKZEg" source="http://www.eclipse.org/uml2/2.0.0/UML" references="_KxZQgEz3Ed-q5fB8PwONew"/>
     <eStructuralFeatures xmi:type="ecore:EReference" xmi:id="_TjJZE2I3Ed-E2et4hPKZEg" name="base_Actor" ordered="false" lowerBound="1">
      <eType xmi:type="ecore:EClass" href="http://www.eclipse.org/uml2/3.0.0/UML#//Actor"/>
     </eStructuralFeatures>
     <eStructuralFeatures xmi:type="ecore:EAttribute" xmi:id="_TjJZFWI3Ed-E2et4hPKZEg" name="PhoneNumber" ordered="false" lowerBound="1" eType="_TjJZF2I3Ed-E2et4hPKZEg"/>
    </eClassifiers>
    <eClassifiers xmi:type="ecore:EDataType" xmi:id="_TjJZF2I3Ed-E2et4hPKZEg" name="USPhoneNumberType" instanceClassName="java.lang.String">
     <eAnnotations xmi:id="_TjJZGGI3Ed-E2et4hPKZEg" source="http://www.eclipse.org/uml2/2.0.0/UML" references="_i7slcE0EEd-q5fB8PwONew"/>
     <eAnnotations xmi:id="_TjJZGWI3Ed-E2et4hPKZEg" source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
      <details xmi:id="_TjJZGmI3Ed-E2et4hPKZEg" key="baseType" value="http://www.eclipse.org/emf/2003/XMLType#string"/>
      <details xmi:id="_TjJZG2I3Ed-E2et4hPKZEg" key="pattern" value="1?\W*([2-9][0-8][0-9])\W*([2-9][0-9]{2})\W*([0-9]{4})(\se?x?t?(\d*))?"/>
      <details xmi:id="_TjJZHGI3Ed-E2et4hPKZEg" key="name" value="USPhoneNumberType"/>
     </eAnnotations>
    </eClassifiers>
    <eClassifiers xmi:type="ecore:EDataType" xmi:id="_TjJZHWI3Ed-E2et4hPKZEg" name="UMLPrimitiveTypes_String" instanceClassName="UMLPrimitiveTypes_String">
     <eAnnotations xmi:id="_TjJZHmI3Ed-E2et4hPKZEg" source="http://www.eclipse.org/uml2/2.0.0/UML">
      <references xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
     </eAnnotations>
    </eClassifiers>
   </contents>
  </eAnnotations>
  <elementImport xmi:id="_-_cFYEz2Ed-q5fB8PwONew">
   <importedElement xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Boolean"/>
  </elementImport>
  <elementImport xmi:id="_-_koQEz2Ed-q5fB8PwONew">
   <importedElement xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
  </elementImport>
  <elementImport xmi:id="_-_koQUz2Ed-q5fB8PwONew">
   <importedElement xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#UnlimitedNatural"/>
  </elementImport>
  <elementImport xmi:id="_-_koQkz2Ed-q5fB8PwONew">
   <importedElement xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Integer"/>
  </elementImport>
  <elementImport xmi:id="_Q-rN8Ez-Ed-q5fB8PwONew">
   <importedElement xmi:type="uml:Class" href="pathmap://UML_METAMODELS/UML.metamodel.uml#Actor"/>
  </elementImport>
  <packagedElement xmi:type="uml:Stereotype" xmi:id="_KxZQgEz3Ed-q5fB8PwONew" name="Contact">
   <ownedAttribute xmi:id="_kIBdYkz-Ed-q5fB8PwONew" name="base_Actor" association="_kIBdYEz-Ed-q5fB8PwONew">
    <type xmi:type="uml:Class" href="pathmap://UML_METAMODELS/UML.metamodel.uml#Actor"/>
   </ownedAttribute>
   <ownedAttribute xmi:id="_XBEfIE0HEd-q5fB8PwONew" name="PhoneNumber" type="_i7slcE0EEd-q5fB8PwONew"/>
  </packagedElement>
  <packagedElement xmi:type="uml:Extension" xmi:id="_kIBdYEz-Ed-q5fB8PwONew" name="Actor_Contact" memberEnd="_kIBdYUz-Ed-q5fB8PwONew _kIBdYkz-Ed-q5fB8PwONew">
   <ownedEnd xmi:type="uml:ExtensionEnd" xmi:id="_kIBdYUz-Ed-q5fB8PwONew" name="extension_Contact" type="_KxZQgEz3Ed-q5fB8PwONew" aggregation="composite" association="_kIBdYEz-Ed-q5fB8PwONew"/>
  </packagedElement>
  <packagedElement xmi:type="uml:PrimitiveType" xmi:id="_i7slcE0EEd-q5fB8PwONew" name="USPhoneNumberType">
   <eAnnotations xmi:id="_xj3IsE0EEd-q5fB8PwONew" source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
    <details xmi:id="_BRrscE0FEd-q5fB8PwONew" key="name" value="USPhoneNumberType"/>
    <details xmi:id="_EECfUE0FEd-q5fB8PwONew" key="baseType" value="http://www.eclipse.org/emf/2003/XMLType#string"/>
    <details xmi:id="_GVdvYE0FEd-q5fB8PwONew" key="pattern" value="1?\W*([2-9][0-8][0-9])\W*([2-9][0-9]{2})\W*([0-9]{4})(\se?x?t?(\d*))?"/>
   </eAnnotations>
   <generalization xmi:id="_oZx-wE0EEd-q5fB8PwONew">
    <general xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
   </generalization>
  </packagedElement>
  <profileApplication xmi:id="_NlBQ4E0GEd-q5fB8PwONew">
   <eAnnotations xmi:id="_NlBQ4U0GEd-q5fB8PwONew" source="http://www.eclipse.org/uml2/2.0.0/UML">
    <references xmi:type="ecore:EPackage" href="pathmap://UML_PROFILES/Ecore.profile.uml#_z1OFcHjqEdy8S4Cr8Rc_NA"/>
   </eAnnotations>
   <appliedProfile href="pathmap://UML_PROFILES/Ecore.profile.uml#_0"/>
  </profileApplication>
 </uml:Profile>
 <Ecore:EDataType xmi:id="_ZFV5cE0GEd-q5fB8PwONew" instanceClassName="java.lang.String" base_PrimitiveType="_i7slcE0EEd-q5fB8PwONew"/>
 <Ecore:EDataType xmi:id="_QE9r0E0LEd-q5fB8PwONew"/>
</xmi:XMI>



Load the Profile Programmatically:


Suppose we need to access our profile from another Eclipse plug-in to create Contacts programmatically. In a plugins parent directory, we will then create a new Java Plug-in project, here called timezra.blog.uml_regex_validation.resources, and it will provide access to this profile from its plug-in Activator.
We can specify metadata for the plug-in in our META-INF/MANIFEST.MF:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: UML Regex Validation Resources
Bundle-SymbolicName: timezra.blog.uml_regex_validation.resources
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: timezra.blog.uml_regex_validation.resources.Activator
Bundle-Vendor: Timezra
Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.5.0,4.0.0)",
 org.eclipse.emf.ecore;bundle-version="[2.5.0,3.0.0)"
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Bundle-ActivationPolicy: lazy



We also want to create an additional source directory called profiles. We can put our Contact.profile.uml file in this directory so that it can be loaded from the classpath in our tests.

Our timezra.blog.uml_regex_validation.resources.Activator.java will handle loading the profile from a resource set:

package timezra.blog.uml_regex_validation.resources;

import org.eclipse.core.runtime.Plugin;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.osgi.framework.BundleContext;

public class Activator extends Plugin {

  public static final String PLUGIN_ID = "timezra.blog.uml_regex_validation.resources";

  private static Activator plugin;

  @Override
  public void start(final BundleContext context) throws Exception {
    super.start(context);
    plugin = this;
  }

  @Override
  public void stop(final BundleContext context) throws Exception {
    plugin = null;
    super.stop(context);
  }

  public static Activator getDefault() {
    return plugin;
  }

  public Resource loadProfile(final ResourceSet resourceSet) {
    return resourceSet.getResource(createURIFor("profiles/Contact.profile.uml"), true);
  }

  public URI createURIFor(final String resource) {
    return URI.createPlatformPluginURI(String.format("/%s/%s", PLUGIN_ID, resource), true);
  }
}



Test with a Fragment:


Now that we have a way to load the UML profile programmatically, we can create a test Fragment as a sibling to the functional bundle in the plugins directory.
We will call this Fragment timezra.blog.uml_regex_validation.resources.tests, and we will again declare plug-in metadata in the META-INF/MANIFEST.MF:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: UML Regex Validation Test
Bundle-SymbolicName: timezra.blog.uml_regex_validation.resources.tests;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: Timezra
Fragment-Host: timezra.blog.uml_regex_validation.resources;bundle-version="[1.0.0,2.0.0)"
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Require-Bundle: org.junit4;bundle-version="[4.5.0,5.0.0)",
 org.eclipse.uml2.uml;bundle-version="[3.0.0,4.0.0)",
 org.eclipse.uml2.uml.resources;bundle-version="[3.0.0,4.0.0)",
 org.eclipse.uml2.uml.edit;bundle-version="[3.0.0,4.0.0)"



The test suite will present a few challenges. For example, suppose we want to run our tests outside OSGi. We will need a way to simulate plug-in activation so that a singleton instance of our Activator is available. We will also need to initialize the resource set used to load the profile, and to redirect requests for platform resources to some other mechanism. Let's dig in our heels and get started by creating a new JUnit file, here called timezra.blog.uml_regex.validation.resources.tests.ContactTest.java:

We can start the framework and activate the resources bundle before each test runs, and dispose of them after each test finishes:

package timezra.blog.uml_regex.validation.resources.tests;

import java.util.Collections;
import org.eclipse.osgi.launch.EquinoxFactory;
import org.junit.After;
import org.junit.Before;
import org.osgi.framework.BundleException;
import org.osgi.framework.launch.Framework;
import timezra.blog.uml_regex_validation.resources.Activator;

public class ContactTest {

  private Framework theFramework;
  private Activator thePlugin;

  @Before
  public void setUp() throws Exception {
    startTheFramework();
    startThePlugin();
  }

  @After
  public void tearDown() throws Exception {
    thePlugin.stop(theFramework.getBundleContext());
    theFramework.stop();
  }

  private void startThePlugin() throws Exception {
    thePlugin = new Activator();
    thePlugin.start(theFramework.getBundleContext());
  }

  private void startTheFramework() throws BundleException {
    theFramework = new EquinoxFactory().newFramework(Collections.emptyMap());
    theFramework.init();
  }
}



As well, we will initialize the resource set during setup;

....
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.resource.impl.URIHandlerImpl;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.resource.UMLResource;

public class ContactTest {

  ....
  private ResourceSet theResourceSet;

  public void setUp() throws Exception {
    ....
    initializeTheResourceSet();
  }

  ....
  
  private void initializeTheResourceSet() {
    theResourceSet = new ResourceSetImpl();
    theResourceSet.getPackageRegistry().put(UMLPackage.eNS_URI, UMLPackage.eINSTANCE);
    theResourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(UMLResource.FILE_EXTENSION,
        UMLResource.Factory.INSTANCE);
    final Map<URI, URI> uriMap = theResourceSet.getURIConverter().getURIMap();
    final URI uri = URI.createURI("classpath:/");
    uriMap.put(URI.createURI(UMLResource.LIBRARIES_PATHMAP), uri.appendSegment("libraries").appendSegment(""));
    uriMap.put(URI.createURI(UMLResource.METAMODELS_PATHMAP), uri.appendSegment("metamodels").appendSegment(""));
    uriMap.put(URI.createURI(UMLResource.PROFILES_PATHMAP), uri.appendSegment("profiles").appendSegment(""));

    uriMap.put(thePlugin.createURIFor(""), uri);

  }
}



We can use a simple mechanism that redirects platform resource and UML-specific URI requests to the classpath for loading the standard profiles as well as our custom profile:

....
import java.io.IOException;
import java.io.InputStream;
import org.eclipse.emf.ecore.resource.impl.URIHandlerImpl;

public class ContactTest {

  ....
  private void initializeTheResourceSet() {
    ....
    theResourceSet.getURIConverter().getURIHandlers().add(0, new ClasspathURIHandler());
  }

  private static final class ClasspathURIHandler extends URIHandlerImpl {

    @Override
    public boolean canHandle(final URI uri) {
      return "classpath".equals(uri.scheme());
    }

    @Override
    public InputStream createInputStream(final URI uri, final Map<?, ?> options) throws IOException {
      return getClass().getResourceAsStream(uri.path());
    }
  }
}



We are now ready to write a few test cases. We can use the org.eclipse.emf.ecore.util.Diagnostician to perform validation for values set on the stereotype application itself or to check whether a value satisfies the stereotype attribute's restrictions. Our final class might look something like this:

package timezra.blog.uml_regex.validation.resources.tests;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Map;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.resource.impl.URIHandlerImpl;
import org.eclipse.emf.ecore.util.Diagnostician;
import org.eclipse.osgi.launch.EquinoxFactory;
import org.eclipse.uml2.uml.Actor;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.UMLFactory;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.resource.UMLResource;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.osgi.framework.BundleException;
import org.osgi.framework.launch.Framework;
import timezra.blog.uml_regex_validation.resources.Activator;

public class ContactTest {

  private static final String STEREOTYPE_QUALIFIED_NAME = "ContactProfile::Contact";
  private static final String PHONE_NUMBER_ATTRIBUTE = "PhoneNumber";

  private static final String VALID_PHONE_NUMBER = "555-555-5555";
  private static final String INVALID_PHONE_NUMBER = "555-555";

  private Framework theFramework;
  private Activator thePlugin;
  private ResourceSet theResourceSet;

  private Profile theProfile;
  private Actor aContact;

  @Before
  public void setUp() throws Exception {
    startTheFramework();
    startThePlugin();
    initializeTheResourceSet();
    loadTheProfile();
    createAContact();
  }

  @After
  public void tearDown() throws Exception {
    thePlugin.stop(theFramework.getBundleContext());
    theFramework.stop();
  }

  @Test
  public void aValidPhoneNumberPassesTheDiagnostics() throws Exception {
    setThePhoneNumber(VALID_PHONE_NUMBER);

    assertEquals(Diagnostic.OK, diagnose(getTheStereotypeApplication()).getSeverity());
  }

  @Test
  public void anInvalidPhoneNumberFailsTheDiagnostics() throws Exception {
    setThePhoneNumber(INVALID_PHONE_NUMBER);

    assertEquals(Diagnostic.ERROR, diagnose(getTheStereotypeApplication()).getSeverity());
  }

  @Test
  public void theDataTypeRestrictsInvalidValues() throws Exception {
    assertEquals(Diagnostic.ERROR, diagnose(INVALID_PHONE_NUMBER).getSeverity());
  }

  @Test
  public void theDataTypeAllowsValidValues() throws Exception {
    assertEquals(Diagnostic.OK, diagnose(VALID_PHONE_NUMBER).getSeverity());
  }

  private EDataType getTheDataType() {
    return (EDataType) getTheStereotypeApplication().eClass().getEStructuralFeature(PHONE_NUMBER_ATTRIBUTE)
        .getEType();
  }

  private Diagnostic diagnose(final String thePhoneNumber) {
    return Diagnostician.INSTANCE.validate(getTheDataType(), thePhoneNumber);
  }

  private Diagnostic diagnose(final EObject theStereotypeApplication) {
    return Diagnostician.INSTANCE.validate(theStereotypeApplication);
  }

  private void setThePhoneNumber(final String thePhoneNumber) {
    aContact.setValue(getTheStereotype(), PHONE_NUMBER_ATTRIBUTE, thePhoneNumber);
  }

  private EObject getTheStereotypeApplication() {
    return aContact.getStereotypeApplication(getTheStereotype());
  }

  private Stereotype getTheStereotype() {
    return aContact.getAppliedStereotype(STEREOTYPE_QUALIFIED_NAME);
  }

  private void createAContact() {
    final Resource theResource = theResourceSet.createResource(URI.createURI("Contacts.uml"));
    final Package contacts = UMLFactory.eINSTANCE.createPackage();
    theResource.getContents().add(contacts);
    contacts.applyProfile(theProfile);
    aContact = UMLFactory.eINSTANCE.createActor();
    contacts.getPackagedElements().add(aContact);
    aContact.applyStereotype(aContact.getApplicableStereotype(STEREOTYPE_QUALIFIED_NAME));
  }

  private void loadTheProfile() {
    theProfile = (Profile) thePlugin.loadProfile(theResourceSet).getContents().get(0);
  }

  private void initializeTheResourceSet() {
    theResourceSet = new ResourceSetImpl();
    theResourceSet.getPackageRegistry().put(UMLPackage.eNS_URI, UMLPackage.eINSTANCE);
    theResourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(UMLResource.FILE_EXTENSION,
        UMLResource.Factory.INSTANCE);
    theResourceSet.getURIConverter().getURIHandlers().add(0, new ClasspathURIHandler());
    final Map<URI, URI> uriMap = theResourceSet.getURIConverter().getURIMap();
    final URI uri = URI.createURI("classpath:/");
    uriMap.put(URI.createURI(UMLResource.LIBRARIES_PATHMAP), uri.appendSegment("libraries").appendSegment(""));
    uriMap.put(URI.createURI(UMLResource.METAMODELS_PATHMAP), uri.appendSegment("metamodels").appendSegment(""));
    uriMap.put(URI.createURI(UMLResource.PROFILES_PATHMAP), uri.appendSegment("profiles").appendSegment(""));

    uriMap.put(thePlugin.createURIFor(""), uri);
  }

  private void startThePlugin() throws Exception {
    thePlugin = new Activator();
    thePlugin.start(theFramework.getBundleContext());
  }

  private void startTheFramework() throws BundleException {
    theFramework = new EquinoxFactory().newFramework(Collections.emptyMap());
    theFramework.init();
  }

  private static final class ClasspathURIHandler extends URIHandlerImpl {

    @Override
    public boolean canHandle(final URI uri) {
      return "classpath".equals(uri.scheme());
    }

    @Override
    public InputStream createInputStream(final URI uri, final Map<?, ?> options) throws IOException {
      return getClass().getResourceAsStream(uri.path());
    }
  }
}



Test With Tycho:


Suppose we want to automate our plug-in build and unit-tests. There are a few different options in the Eclipse eco-system, but my personal preference lately has been tycho. Currently, I am using tycho-0.8.0 and Maven 3-alpha-7 with Eclipse-3.5.2. Be aware that the latest version as of this first posting requires Maven 3-beta-1 and Eclipse-3.6-M7. My environment is configured according to the instructions provided by Mattias Holmqvist.

For our particular plugins, we can generate the poms for the functional resources plug-in, for the test fragment and for the parent plugins directory.

  export M2_HOME=/path/to/apache-maven-3.0-alpha-7
  export MAVEN_OPTS="-Xmx512m"
  export TYCHO_TARGET_PLATFORM=/path/to/eclipse_3.5.2
  cd /path/to/timezra.blog.uml_regex_validation.resources
  mvn org.sonatype.tycho:maven-tycho-plugin:generate-poms -DgroupId=timezra.blog -Dtycho.targetPlatform=$TYCHO_TARGET_PLATFORM
  cd /path/to/timezra.blog.uml_regex_validation.resources.test
  mvn org.sonatype.tycho:maven-tycho-plugin:generate-poms -DgroupId=timezra.blog -Dtycho.targetPlatform=$TYCHO_TARGET_PLATFORM
  cd ..
  mvn org.sonatype.tycho:maven-tycho-plugin:generate-poms -DgroupId=timezra.blog -Dtycho.targetPlatform=$TYCHO_TARGET_PLATFORM


We can modify the pom generated in our fragment to include the following section, which will enable tycho to run microtests outside OSGi:

<?xml version="1.0" encoding="UTF-8"?>
<project ....>
  ....
  <packaging>eclipse-test-plugin</packaging>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <executions>
          <execution>
            <id>test</id>
            <phase>test</phase>
            <configuration>
              <testClassesDirectory>${project.build.outputDirectory}</testClassesDirectory>
            </configuration>
            <goals>
              <goal>test</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.7</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>



Finally we can run our unit-tests from the parent plugins directory:

  mvn clean test -Dtycho.targetPlatform=$TYCHO_TARGET_PLATFORM


Conclusion:


The primary purpose of this tutorial has been to present a way to test Stereotype attributes that use UML Data Types constrained by regular expressions. Along the way, we have also developed techniques for simulating bundle activation outside OSGi, for loading platform plugin resources and resources registered through Eclipse extensions without Eclipse running, and for running microtests with Maven via tycho.