blob: 61bbba745dc5b66dcd33360e55dcf4aa873b27f5 [file] [log] [blame]
/*
* Copyright (c) 2016, 2018 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 v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Christian W. Damus - initial API and implementation
* Kenn Hussey - 528925, 535301
*
*/
package org.eclipse.uml2.uml.bug.tests;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
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.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.uml2.common.util.CacheAdapter;
import org.eclipse.uml2.common.util.UML2Util;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.tests.util.StandaloneSupport;
import org.eclipse.uml2.uml.util.UMLUtil;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Tests that we can more efficiently process profile/stereotype operations
* on a large number of packages and stereotype applications using the
* {@link UMLUtil#executeOperation(Runnable)} API.
*
* @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=497359
*/
public class Bug497359Test extends TestCase {
private ResourceSet rset;
private Package fixture;
public Bug497359Test() {
super();
}
public Bug497359Test(String name) {
super(name);
}
public static Test suite() {
return new TestSuite(Bug497359Test.class, "Bug 497359 tests"); //$NON-NLS-1$
}
/**
* Verify performance improvement in profile reapplication within
* a profile operation context.
*/
public void testReapplyProfile() {
// There is only the one
final Profile profile = fixture.getAppliedProfiles().get(0);
runProfileOperationExperiment("reapply profile", 4, new Runnable() {
public void run() {
fixture.applyProfile(profile);
}
});
}
//
// Test framework
//
@Override
protected void setUp() throws Exception {
rset = new ResourceSetImpl();
if (StandaloneSupport.isStandalone()) {
StandaloneSupport.init(rset);
}
// Papyrus and other applications use this option because otherwise
// the profile-application annotation reference to the applied EPackage
// isn't set until after root elements are attached, and then because
// the profile application has no definition when sub-unit root is
// attached to its container, all of its stereotype applications are
// destroyed
rset.getLoadOptions().put(XMLResource.OPTION_DEFER_ATTACHMENT, true);
rset.getLoadOptions().put(XMLResource.OPTION_DEFER_IDREF_RESOLUTION, true);
rset.getLoadOptions().put(XMLResource.OPTION_LAX_FEATURE_PROCESSING, Boolean.TRUE);
rset.getLoadOptions().put(XMLResource.OPTION_RECORD_UNKNOWN_FEATURE, Boolean.TRUE);
rset.getLoadOptions().put(XMLResource.OPTION_USE_PACKAGE_NS_URI_AS_LOCATION, Boolean.FALSE);
fixture = getTestModel();
}
@Override
protected void tearDown() throws Exception {
fixture = null;
// clean up the CacheAdapter as well as we can
for (Resource next : rset.getResources()) {
next.unload();
next.eAdapters().clear();
}
rset.getResources().clear();
rset.eAdapters().clear();
}
Package getTestModel() {
URL url = getClass().getResource("Bug497359.uml"); //$NON-NLS-1$
Package result = (Package) UML2Util.load(rset,
URI.createURI(url.toExternalForm()), UMLPackage.Literals.PACKAGE);
// Load all of the sub-model units and send profile-ish resources
// to the end of the list
EcoreUtil.resolveAll(result);
EList<Resource> resources = rset.getResources();
int last = resources.size() - 1;
for (int i = last; i >= 0; i--) {
if (!resources.get(i).getURI().isPlatform()) {
resources.move(last, i);
last = last - 1; // Keep them in the same relative order
}
}
return result;
}
void runProfileOperationExperiment(String label, int expectedFactor,
final Runnable operation) {
final int NUM_ITERATIONS = 10;
// Take an average of a few runs on the operation as is
long withoutPOC = measure(NUM_ITERATIONS, operation);
System.out.printf(
"Mean time to %s: %02.3fs%n",
label,
(double) withoutPOC / 1000.0);
// Take an average of a few runs on the operation in context
long withPOC = measure(NUM_ITERATIONS, new Runnable() {
public void run() {
UMLUtil.executeOperation(operation);
}
});
System.out.printf(
"Mean time to %s in context: %02.3fs%n",
label,
(double) withPOC / 1000.0);
// Expect at least the expected factor of reduction in time
assertTrue(String.format(
"Expected %d times improvement in profile operation context",
expectedFactor),
withPOC <= (withoutPOC / expectedFactor));
}
long measure(int numSamples, Runnable experiment) {
List<Long> samples = new ArrayList<Long>();
for (int i = 0; i < numSamples; i++) {
long start = System.currentTimeMillis();
experiment.run();
long end = System.currentTimeMillis();
// Exclude the effects of the CacheAdapter
CacheAdapter.getInstance().clear();
samples.add(end - start);
}
// Throw out the extremes
int min = 0;
int max = 0;
for (int i = 1; i < numSamples; i++) {
if (samples.get(i) < samples.get(min)) {
min = i;
}
if (samples.get(i) > samples.get(max)) {
max = i;
}
}
if (max > min) {
samples.remove(max);
samples.remove(min);
} else {
samples.remove(min);
samples.remove(max);
}
// Average what's left
long result = 0;
for (long sample : samples) {
result = result + sample;
}
result = result / samples.size();
return result;
}
void walkAllStereotypeApplications(Element element) {
TreeIterator<EObject> iter = UML2Util.getAllContents(element, true, false);
while (iter.hasNext()) {
EObject next = iter.next();
if (next instanceof Element) {
// Just get the stereotype applications
((Element)next).getStereotypeApplications();
} else {
// There won't be any UML content in here
iter.prune();
}
}
}
}