diff options
Diffstat (limited to 'tests/junit/plugins/junit/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AbstractModelFixture.java')
-rw-r--r-- | tests/junit/plugins/junit/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AbstractModelFixture.java | 214 |
1 files changed, 154 insertions, 60 deletions
diff --git a/tests/junit/plugins/junit/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AbstractModelFixture.java b/tests/junit/plugins/junit/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AbstractModelFixture.java index 5d0da4a751d..dd5fc65b021 100644 --- a/tests/junit/plugins/junit/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AbstractModelFixture.java +++ b/tests/junit/plugins/junit/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AbstractModelFixture.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 CEA and others. + * Copyright (c) 2014 CEA, Christian W. Damus, and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -8,6 +8,7 @@ * * Contributors: * Christian W. Damus (CEA) - Initial API and implementation + * Christian W. Damus - bug 399859 * */ package org.eclipse.papyrus.junit.utils.rules; @@ -16,6 +17,7 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Annotation; @@ -25,8 +27,11 @@ import java.util.Enumeration; import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Queue; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; @@ -47,6 +52,7 @@ import org.eclipse.papyrus.infra.gmfdiag.common.model.NotationModel; import org.eclipse.papyrus.uml.tools.model.UmlModel; import org.eclipse.uml2.uml.Package; import org.eclipse.uml2.uml.Profile; +import org.eclipse.uml2.uml.UMLPackage; import org.junit.Rule; import org.junit.rules.TestWatcher; import org.junit.runner.Description; @@ -54,10 +60,15 @@ import org.junit.runners.model.Statement; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; +import com.google.common.base.Charsets; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.google.common.io.ByteStreams; +import com.google.common.io.CharStreams; +import com.google.common.io.Resources; /** @@ -65,15 +76,12 @@ import com.google.common.io.ByteStreams; * <ul> * <li>an editing domain of some kind (subclasses must create it)</li> * <li>a test project in the workspace, exposed via a nested {@link ProjectFixture} rule</li> - * <li>a test {@link Package} loaded from a resource in the plug-in and saved as <tt>model.uml</tt> in the test project. This model is specified using - * an annotation on the test, as described below</li> + * <li>a test {@link Package} loaded from a resource in the plug-in and saved as <tt>model.uml</tt> in the test project. This model is specified using an annotation on the test, as described below</li> * </ul> * The test model template to load into the editing domain and project must be specified by one of the following annotations: * <ul> - * <li>{@link JavaResource @JavaResource}: specifies the path to a resource to be loaded from the test class's classpath, using the - * {@link Class#getResource(String)} API</li> - * <li>{@link PluginResource @PluginResource}: specifies a path relative to the root of the OSGi bundle containing the test class, to be loaded via - * the {@link Bundle#getEntry(String)} API</li> + * <li>{@link JavaResource @JavaResource}: specifies the path to a resource to be loaded from the test class's classpath, using the {@link Class#getResource(String)} API</li> + * <li>{@link PluginResource @PluginResource}: specifies a path relative to the root of the OSGi bundle containing the test class, to be loaded via the {@link Bundle#getEntry(String)} API</li> * </ul> * The resource annotation may be specified either on the test method, in which case it applies to that test case, or on the test * class, in which case it applies to all test methods in the class that do not have a resource annotation of their own (method @@ -95,6 +103,7 @@ public abstract class AbstractModelFixture<T extends EditingDomain> extends Test super(); } + @Override public Statement apply(Statement base, Description description) { testClass = description.getTestClass(); @@ -124,8 +133,7 @@ public abstract class AbstractModelFixture<T extends EditingDomain> extends Test } /** - * Obtains the test model, which is resident in the <tt>model.uml</tt> file in the test project (as indicated by its - * {@linkplain #getModelResourceURI() URI}). + * Obtains the test model, which is resident in the <tt>model.uml</tt> file in the test project (as indicated by its {@linkplain #getModelResourceURI() URI}). * * @return the test model */ @@ -150,36 +158,46 @@ public abstract class AbstractModelFixture<T extends EditingDomain> extends Test @Override protected void starting(Description description) { domain = createEditingDomain(); - model = (Package)Iterables.getFirst(initModelResources(description), null).getContents().get(0); - + + Resource main = Iterables.getFirst(initModelResources(description), null); + assertThat("No main UML resource in model fixture", main, notNullValue()); + + model = (Package) main.getContents().get(0); + // We have finished initializing initialResourceURIs = null; + + didLoadResourceSet(); + } + + protected void didLoadResourceSet() { + // Pass } protected Iterable<Resource> initModelResources(Description description) { List<Resource> result; // Don't initialize the resources more than once (subclasses such as PapyrusEditorFixture can repeat this) - if(initialResourceURIs == null) { + if (initialResourceURIs == null) { Annotation resourceAnnotation = getResourceAnnotation(description); ResourceKind kind = ResourceKind.getResourceKind(resourceAnnotation); final String[] paths = kind.getResourcePaths(resourceAnnotation); result = Lists.newArrayListWithCapacity(paths.length); - for(String path : paths) { + for (String path : paths) { result.add(initModelResource(new Path(path), kind)); } List<URI> uris = Lists.newArrayListWithCapacity(result.size()); - for(Resource next : result) { + for (Resource next : result) { uris.add(next.getURI()); } initialResourceURIs = uris; } else { ResourceSet rset = getResourceSet(); result = Lists.newArrayList(); - for(URI next : initialResourceURIs) { + for (URI next : initialResourceURIs) { result.add(rset.getResource(next, true)); } } @@ -189,7 +207,7 @@ public abstract class AbstractModelFixture<T extends EditingDomain> extends Test private Resource initModelResource(IPath resourcePath, ResourceKind kind) { String targetResourceName = "model"; - if(isDIModel(resourcePath)) { + if (isDIModel(resourcePath)) { // We will be initializing all three resources, and they have cross-references, so must not change // resource name targetResourceName = resourcePath.removeFileExtension().lastSegment(); @@ -204,61 +222,68 @@ public abstract class AbstractModelFixture<T extends EditingDomain> extends Test } protected Resource initModelResource(String targetPath, ResourceKind resourceKind, String resourcePath) { - Resource result; + Resource result = null; ResourceSet resourceSet = getResourceSet(); final boolean bootstrapResourceSet = resourceSet == null; - if(bootstrapResourceSet) { + if (bootstrapResourceSet) { // Bootstrap the initialization of the test model with a plain resource set resourceSet = new ResourceSetImpl(); resourceSet.getLoadOptions().put(XMLResource.OPTION_RECORD_UNKNOWN_FEATURE, true); resourceSet.getLoadOptions().put(XMLResource.OPTION_LAX_FEATURE_PROCESSING, true); } + Set<Resource> toUnload = Sets.newHashSet(); try { IPath resourceIPath = new Path(resourcePath); - if(isDIModel(resourceIPath)) { - // Try to initialize the triumvirate of files + if (isDIModel(resourceIPath)) { + // Try to initialize the entire collection of files resourceIPath = resourceIPath.removeFileExtension(); - // The UML resource must exist for any sane test - result = doInitModelResource(resourceSet, targetPath, resourceKind, resourceIPath.addFileExtension(UmlModel.UML_FILE_EXTENSION)); + Map<IPath, Boolean> manifest = loadManifest(resourceKind, resourceIPath); + for (Map.Entry<IPath, Boolean> next : manifest.entrySet()) { + Resource resource = doInitModelResource(resourceSet, targetPath, resourceKind, next.getKey()); - // Both of these are optional - IPath notationPath = resourceIPath.addFileExtension(NotationModel.NOTATION_FILE_EXTENSION); - if(resourceKind.exists(testClass, notationPath)) { - doInitModelResource(resourceSet, targetPath, resourceKind, notationPath); - } - IPath diPath = resourceIPath.addFileExtension(DiModel.DI_FILE_EXTENSION); - if(resourceKind.exists(testClass, diPath)) { - doInitModelResource(resourceSet, targetPath, resourceKind, diPath); + if ((result == null) && UmlModel.UML_FILE_EXTENSION.equals(next.getKey().getFileExtension())) { + // We should always have this one, at least, and it's the one we most care about + result = resource; + } + + if (!next.getValue()) { + // Unload this resource + toUnload.add(resource); + } } } else { result = doInitModelResource(resourceSet, targetPath, resourceKind, resourceIPath); } + if (result == null) { + fail("No UML resource in test model"); + } + // Look for any other dependencies (libraries, profiles, etc.) that also need to be copied Queue<Resource> dependents = new LinkedList<Resource>(); Set<Resource> scanned = new HashSet<Resource>(); dependents.add(result); boolean loadedProfiles = false; - for(Resource dependent = dependents.poll(); dependent != null; dependent = dependents.poll()) { - if(scanned.add(dependent)) { + for (Resource dependent = dependents.poll(); dependent != null; dependent = dependents.poll()) { + if (scanned.add(dependent)) { URI baseURI = result.getURI().trimSegments(1); - if(!baseURI.isPrefix()) { + if (!baseURI.isPrefix()) { baseURI = baseURI.appendSegment(""); } - for(EObject proxy : EcoreUtil.UnresolvedProxyCrossReferencer.find(dependent).keySet()) { + for (EObject proxy : EcoreUtil.UnresolvedProxyCrossReferencer.find(dependent).keySet()) { URI dependencyURI = EcoreUtil.getURI(proxy).trimFragment(); - if(dependencyURI.toString().startsWith(baseURI.toString())) { + if (dependencyURI.toString().startsWith(baseURI.toString())) { Resource dependency = resourceSet.getResource(dependencyURI, false); - if((dependency == null) || !dependency.isLoaded() || !dependency.getErrors().isEmpty()) { + if ((dependency == null) || !dependency.isLoaded() || !dependency.getErrors().isEmpty()) { // It should be available in the test bundle. Try to get it URI relative = dependencyURI.deresolve(baseURI); IPath depPath = resourceIPath.removeLastSegments(1).append(URI.decode(relative.toString())); - if(resourceKind.exists(testClass, depPath)) { - if(dependency == null) { + if (resourceKind.exists(testClass, depPath)) { + if (dependency == null) { dependency = resourceSet.createResource(dependencyURI); } else { dependency.unload(); @@ -278,7 +303,7 @@ public abstract class AbstractModelFixture<T extends EditingDomain> extends Test // If we depend on profiles, then we may have stereotype applications that need to resolve against that schema. // In such case, re-load the model resource to resolve the stereotype schema - if(loadedProfiles && Iterables.any(result.getContents(), Predicates.instanceOf(AnyType.class))) { + if (loadedProfiles && Iterables.any(result.getContents(), Predicates.instanceOf(AnyType.class))) { try { result.unload(); result.load(null); @@ -287,8 +312,15 @@ public abstract class AbstractModelFixture<T extends EditingDomain> extends Test fail("Error re-loading resource to resolve stereotype schema: " + e.getLocalizedMessage()); } } + + // Now unload resources that the manifest indicates should not be loaded initially + for (Resource next : toUnload) { + next.unload(); + next.getResourceSet().getResources().remove(next); + next.eAdapters().clear(); + } } finally { - if(bootstrapResourceSet) { + if (bootstrapResourceSet) { EMFHelper.unload(resourceSet); } } @@ -296,23 +328,85 @@ public abstract class AbstractModelFixture<T extends EditingDomain> extends Test return result; } + private Map<IPath, Boolean> loadManifest(ResourceKind resourceKind, IPath resourceIPath) { + Map<IPath, Boolean> result = null; + IPath manifestPath = resourceIPath.addFileExtension("manifest"); + + URL manifestURL = resourceKind.getResourceURL(testClass, manifestPath); + if (manifestURL != null) { + try { + result = parseManifest(manifestPath.removeLastSegments(1), manifestURL); + } catch (IOException e) { + e.printStackTrace(); + // Create a default manifest + } + } + + if (result == null) { + // Default manifest + result = Maps.newHashMap(); + IPath basePath = manifestPath.removeFileExtension(); + result.put(basePath.addFileExtension(DiModel.DI_FILE_EXTENSION), true); + result.put(basePath.addFileExtension(UmlModel.UML_FILE_EXTENSION), true); + result.put(basePath.addFileExtension(NotationModel.NOTATION_FILE_EXTENSION), true); + } + + return result; + } + + private Map<IPath, Boolean> parseManifest(IPath baseResourcePath, URL manifestURL) throws IOException { + Map<IPath, Boolean> result = Maps.newLinkedHashMap(); + + List<String> lines = CharStreams.readLines(CharStreams.newReaderSupplier(Resources.newInputStreamSupplier(manifestURL), Charsets.UTF_8)); + Pattern pattern = Pattern.compile("([^=]+)(?:=(true|false))?"); + Matcher m = pattern.matcher(""); + for (String line : lines) { + m.reset(line); + if (m.matches()) { + IPath path = baseResourcePath.append(m.group(1)); + + boolean load = true; + if (m.group(2) != null) { + load = Boolean.valueOf(m.group(2)); + } + + result.put(path, load); + } + } + + return result; + } + private Resource doInitModelResource(ResourceSet resourceSet, String targetPath, ResourceKind resourceKind, IPath resourceIPath) { IPath targetIPath = new Path(targetPath); - if(!resourceIPath.getFileExtension().equals(targetIPath.getFileExtension())) { + if (!resourceIPath.getFileExtension().equals(targetIPath.getFileExtension())) { targetIPath = targetIPath.addFileExtension(resourceIPath.getFileExtension()); } + // If the file name is different from the core model name, then use it as is. It's an extra resource for some purpose + // (perhaps such as a library model) + if (!targetIPath.lastSegment().equals(resourceIPath.lastSegment())) { + targetIPath = targetIPath.removeLastSegments(1).append(resourceIPath.lastSegment()); + } + final URI modelURI = project.getURI(targetIPath); Resource result = resourceSet.getResource(modelURI, false); - if(result == null) { - result = resourceSet.createResource(modelURI); + if (result == null) { + String extension = modelURI.fileExtension(); + if (DiModel.DI_FILE_EXTENSION.equals(extension) || UmlModel.UML_FILE_EXTENSION.equals(extension) || NotationModel.NOTATION_FILE_EXTENSION.equals(extension)) { + // Default load behaviour + result = resourceSet.createResource(modelURI); + } else { + // Assume it's a fragment of UML content (such as a profile-application resource) + result = resourceSet.createResource(modelURI, UMLPackage.eCONTENT_TYPE); + } } - if(!result.isLoaded()) { - if(resourceSet instanceof ModelSet) { - ModelSet modelSet = (ModelSet)resourceSet; - if(modelSet.getURIWithoutExtension() == null) { + if (!result.isLoaded()) { + if (resourceSet instanceof ModelSet) { + ModelSet modelSet = (ModelSet) resourceSet; + if (modelSet.getURIWithoutExtension() == null) { modelSet.getInternal().setPrimaryModelResourceURI(modelURI); } } @@ -345,12 +439,12 @@ public abstract class AbstractModelFixture<T extends EditingDomain> extends Test initialResourceURIs = null; model = null; - if(domain instanceof TransactionalEditingDomain) { - ((TransactionalEditingDomain)domain).dispose(); + if (domain instanceof TransactionalEditingDomain) { + ((TransactionalEditingDomain) domain).dispose(); } domain = null; - if(resourceSet != null) { + if (resourceSet != null) { EMFHelper.unload(resourceSet); } } @@ -368,15 +462,15 @@ public abstract class AbstractModelFixture<T extends EditingDomain> extends Test fail("Could not access test method via JUnit framework."); } - if(testMethod.isAnnotationPresent(JavaResource.class)) { + if (testMethod.isAnnotationPresent(JavaResource.class)) { result = testMethod.getAnnotation(JavaResource.class); - } else if(testMethod.isAnnotationPresent(PluginResource.class)) { + } else if (testMethod.isAnnotationPresent(PluginResource.class)) { result = testMethod.getAnnotation(PluginResource.class); } else { // The class must have an annotation - if(testClass.isAnnotationPresent(JavaResource.class)) { + if (testClass.isAnnotationPresent(JavaResource.class)) { result = testClass.getAnnotation(JavaResource.class); - } else if(testClass.isAnnotationPresent(PluginResource.class)) { + } else if (testClass.isAnnotationPresent(PluginResource.class)) { result = testClass.getAnnotation(PluginResource.class); } } @@ -394,11 +488,11 @@ public abstract class AbstractModelFixture<T extends EditingDomain> extends Test } String[] getResourcePaths(Annotation resourceAnnotation) { - switch(this) { + switch (this) { case JAVA: - return ((JavaResource)resourceAnnotation).value(); + return ((JavaResource) resourceAnnotation).value(); case BUNDLE: - return ((PluginResource)resourceAnnotation).value(); + return ((PluginResource) resourceAnnotation).value(); } fail("Not a resource annotation: " + resourceAnnotation); @@ -412,7 +506,7 @@ public abstract class AbstractModelFixture<T extends EditingDomain> extends Test URL getResourceURL(Class<?> context, IPath path) { URL result = null; - switch(this) { + switch (this) { case JAVA: result = context.getResource(path.toString()); break; @@ -429,13 +523,13 @@ public abstract class AbstractModelFixture<T extends EditingDomain> extends Test String pattern = resourcePath.lastSegment(); IPath search; - if(resourcePath.segmentCount() > 1) { + if (resourcePath.segmentCount() > 1) { search = resourcePath.removeLastSegments(1); } else { search = Path.ROOT; } Enumeration<URL> urls = FrameworkUtil.getBundle(testClass).findEntries(search.toPortableString(), pattern, false); - if((urls != null) && urls.hasMoreElements()) { + if ((urls != null) && urls.hasMoreElements()) { result = urls.nextElement(); } |