diff options
8 files changed, 562 insertions, 294 deletions
diff --git a/tests/junit/plugins/infra/emf/org.eclipse.papyrus.infra.emf.tests/org.eclipse.papyrus.infra.emf.tests.launch b/tests/junit/plugins/infra/emf/org.eclipse.papyrus.infra.emf.tests/org.eclipse.papyrus.infra.emf.tests.launch index 9bae1d5e8d5..7016b7df774 100644 --- a/tests/junit/plugins/infra/emf/org.eclipse.papyrus.infra.emf.tests/org.eclipse.papyrus.infra.emf.tests.launch +++ b/tests/junit/plugins/infra/emf/org.eclipse.papyrus.infra.emf.tests/org.eclipse.papyrus.infra.emf.tests.launch @@ -25,7 +25,7 @@ <stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/> <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/> <stringAttribute key="org.eclipse.jdt.launching.JAVA_COMMAND" value="java"/> -<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/> +<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.papyrus.infra.emf.tests.AllTests"/> <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog"/> <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.papyrus.infra.emf.tests"/> diff --git a/tests/junit/plugins/infra/emf/org.eclipse.papyrus.infra.emf.tests/tests/org/eclipse/papyrus/infra/emf/advice/ReadOnlyObjectEditAdviceTest.java b/tests/junit/plugins/infra/emf/org.eclipse.papyrus.infra.emf.tests/tests/org/eclipse/papyrus/infra/emf/advice/ReadOnlyObjectEditAdviceTest.java index d44134d70d0..99b9593258e 100644 --- a/tests/junit/plugins/infra/emf/org.eclipse.papyrus.infra.emf.tests/tests/org/eclipse/papyrus/infra/emf/advice/ReadOnlyObjectEditAdviceTest.java +++ b/tests/junit/plugins/infra/emf/org.eclipse.papyrus.infra.emf.tests/tests/org/eclipse/papyrus/infra/emf/advice/ReadOnlyObjectEditAdviceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 CEA and others. + * Copyright (c) 2014, 2016 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 485156 * */ package org.eclipse.papyrus.infra.emf.advice; @@ -70,6 +71,7 @@ import org.eclipse.gmf.runtime.emf.type.core.requests.ReorientRelationshipReques import org.eclipse.gmf.runtime.emf.type.core.requests.ReorientRequest; import org.eclipse.gmf.runtime.emf.type.core.requests.SetRequest; import org.eclipse.papyrus.infra.emf.utils.EMFHelper; +import org.eclipse.papyrus.junit.framework.classification.ClassificationRunnerWithParametersFactory; import org.eclipse.papyrus.junit.framework.classification.rules.Condition; import org.eclipse.papyrus.junit.framework.classification.rules.ConditionRule; import org.eclipse.papyrus.junit.framework.classification.rules.Conditional; @@ -94,6 +96,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; +import org.junit.runners.Parameterized.UseParametersRunnerFactory; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; @@ -104,6 +107,7 @@ import com.google.common.collect.Iterables; * Test suite for the {@link ReadOnlyObjectEditAdvice} class. */ @RunWith(Parameterized.class) +@UseParametersRunnerFactory(ClassificationRunnerWithParametersFactory.class) public class ReadOnlyObjectEditAdviceTest { private static final String TEST_BUNDLE = "org.eclipse.papyrus.infra.emf.tests"; //$NON-NLS-1$ @@ -235,7 +239,7 @@ public class ReadOnlyObjectEditAdviceTest { @Override protected void doExecute() { - usage[0] = (Usage)writablePackage.createPackagedElement("B-->D", UMLPackage.Literals.USAGE); //$NON-NLS-1$ + usage[0] = (Usage) writablePackage.createPackagedElement("B-->D", UMLPackage.Literals.USAGE); //$NON-NLS-1$ usage[0].getClients().add(classB); usage[0].getSuppliers().add(classD); } @@ -438,8 +442,9 @@ public class ReadOnlyObjectEditAdviceTest { public static Collection<Object[]> parameters() { return ImmutableList.copyOf(Iterables.transform(Arrays.asList(ResourceMode.values()), new Function<Object, Object[]>() { + @Override public Object[] apply(Object input) { - return new Object[]{ input }; + return new Object[] { input }; } })); } @@ -452,7 +457,7 @@ public class ReadOnlyObjectEditAdviceTest { @BeforeClass public static void registerReadOnlyHandlerAdapterFactory() throws Exception { // Cannot add dependency on read-only plug-in because that would induce a cycle - readOnlyHandlerAdapterFactory = (IAdapterFactory)Platform.getBundle("org.eclipse.papyrus.infra.emf.readonly").loadClass("org.eclipse.papyrus.infra.emf.readonly.ReadOnlyAdapterFactory").newInstance(); + readOnlyHandlerAdapterFactory = (IAdapterFactory) Platform.getBundle("org.eclipse.papyrus.infra.emf.readonly").loadClass("org.eclipse.papyrus.infra.emf.readonly.ReadOnlyAdapterFactory").newInstance(); Platform.getAdapterManager().registerAdapters(readOnlyHandlerAdapterFactory, EditingDomain.class); } @@ -471,7 +476,7 @@ public class ReadOnlyObjectEditAdviceTest { URI readOnlyURI; - switch(resourceMode) { + switch (resourceMode) { case WORKSPACE_WRITEABLE: case WORKSPACE_READONLY: workspaceFile = project.getFile("readonly.uml"); //$NON-NLS-1$ @@ -484,7 +489,7 @@ public class ReadOnlyObjectEditAdviceTest { readOnlyPackage = loadPackage(readOnlyURI); - switch(resourceMode) { + switch (resourceMode) { case WORKSPACE_WRITEABLE: // File needs to exist to determine that it is writable readOnlyPackage.eResource().save(null); @@ -502,24 +507,25 @@ public class ReadOnlyObjectEditAdviceTest { project.refreshLocal(IResource.DEPTH_INFINITE, null); // Pick out some model elements - classA = (Class)writablePackage.getOwnedType("A"); //$NON-NLS-1$ - classB = (Class)readOnlyPackage.getOwnedType("B"); //$NON-NLS-1$ + classA = (Class) writablePackage.getOwnedType("A"); //$NON-NLS-1$ + classB = (Class) readOnlyPackage.getOwnedType("B"); //$NON-NLS-1$ } private Package loadPackage(URI uri) throws IOException { Package result = null; - if(domain == null) { + if (domain == null) { // Use an editing domain that doesn't implement its own read-only checking in order not // to interfere with the advice's read-only check domain = houseKeeper.cleanUpLater(WorkspaceEditingDomainFactory.INSTANCE.createEditingDomain(new ResourceSetImpl()), new HouseKeeper.Disposer<TransactionalEditingDomain>() { + @Override public void dispose(TransactionalEditingDomain object) throws Exception { // Make sure that we flush undo contexts so that our commands and the resources they retain may be reclaimed ResourceSet rset = object.getResourceSet(); - IOperationHistory history = ((IWorkspaceCommandStack)object.getCommandStack()).getOperationHistory(); + IOperationHistory history = ((IWorkspaceCommandStack) object.getCommandStack()).getOperationHistory(); - for(Resource next : rset.getResources()) { + for (Resource next : rset.getResources()) { // Purge the resource undo context IUndoContext resourceContext = new ResourceUndoContext(object, next); history.dispose(resourceContext, true, true, true); @@ -531,7 +537,7 @@ public class ReadOnlyObjectEditAdviceTest { }); // Disable the EMF default read-only tracking - ((AdapterFactoryEditingDomain)domain).setResourceToReadOnlyMap(null); + ((AdapterFactoryEditingDomain) domain).setResourceToReadOnlyMap(null); } Resource res = domain.getResourceSet().createResource(uri); @@ -541,9 +547,9 @@ public class ReadOnlyObjectEditAdviceTest { try { input = url.openStream(); res.load(input, null); - result = (Package)EcoreUtil.getObjectByType(res.getContents(), UMLPackage.Literals.PACKAGE); + result = (Package) EcoreUtil.getObjectByType(res.getContents(), UMLPackage.Literals.PACKAGE); } finally { - if(input != null) { + if (input != null) { try { input.close(); } catch (IOException e) { @@ -558,14 +564,14 @@ public class ReadOnlyObjectEditAdviceTest { @After public void destroyFixture() { // Purge the default undo context - if(domain.getCommandStack() instanceof IWorkspaceCommandStack) { - IWorkspaceCommandStack stack = (IWorkspaceCommandStack)domain.getCommandStack(); + if (domain.getCommandStack() instanceof IWorkspaceCommandStack) { + IWorkspaceCommandStack stack = (IWorkspaceCommandStack) domain.getCommandStack(); stack.getOperationHistory().dispose(stack.getDefaultUndoContext(), true, true, true); } } IClientContext getClientContext() { - switch(resourceMode) { + switch (resourceMode) { case PLUGIN_NOADVICE: return ClientContextManager.getDefaultClientContext(); default: @@ -597,7 +603,7 @@ public class ReadOnlyObjectEditAdviceTest { } void assertExecutability(ICommand command) { - switch(resourceMode) { + switch (resourceMode) { case WORKSPACE_WRITEABLE: case WORKSPACE_READONLY: case PLUGIN_NOADVICE: @@ -614,7 +620,7 @@ public class ReadOnlyObjectEditAdviceTest { void assertAdvice(IEditCommandRequest request) { ICommand command = new ReadOnlyObjectEditAdvice().getBeforeEditCommand(request); - switch(resourceMode) { + switch (resourceMode) { case WORKSPACE_WRITEABLE: case WORKSPACE_READONLY: case PLUGIN_NOADVICE: @@ -630,7 +636,7 @@ public class ReadOnlyObjectEditAdviceTest { void executeUnprotected(Command command) { try { - ((TransactionalCommandStack)domain.getCommandStack()).execute(command, Collections.singletonMap(Transaction.OPTION_UNPROTECTED, true)); + ((TransactionalCommandStack) domain.getCommandStack()).execute(command, Collections.singletonMap(Transaction.OPTION_UNPROTECTED, true)); } catch (InterruptedException e) { fail("Unprotected command execution interrupted."); } catch (RollbackException e) { diff --git a/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationConfig.java b/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationConfig.java index f2495e6677a..975fab086b8 100644 --- a/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationConfig.java +++ b/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationConfig.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014, 2015 CEA LIST, Christian W. Damus, and others. + * Copyright (c) 2014, 2016 CEA LIST, 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 @@ -11,6 +11,7 @@ * Christian W. Damus - bug 451230 * Christian W. Damus - bug 464647 * Christian W. Damus - bug 480812 + * Christian W. Damus - bug 485156 *****************************************************************************/ package org.eclipse.papyrus.junit.framework.classification; @@ -34,6 +35,7 @@ import org.eclipse.core.runtime.Platform; import org.eclipse.papyrus.infra.tools.util.ListHelper; import org.junit.Ignore; import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import org.junit.runners.Suite; import com.google.common.collect.ImmutableSet; @@ -212,7 +214,8 @@ public enum ClassificationConfig implements Set<TestCategory> { for (Annotation annotation : annotations) { if (RunWith.class.isInstance(annotation)) { RunWith runWith = (RunWith) annotation; - if (Suite.class.isAssignableFrom(runWith.value())) { + Class<?> runner = runWith.value(); + if (Suite.class.isAssignableFrom(runner) && !Parameterized.class.isAssignableFrom(runner)) { return true; } } diff --git a/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationRunner.java b/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationRunner.java index 7c53cd7bb3e..357298bd6a3 100644 --- a/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationRunner.java +++ b/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationRunner.java @@ -1,5 +1,5 @@ /*****************************************************************************
- * Copyright (c) 2014 CEA LIST and others.
+ * Copyright (c) 2014, 2016 CEA LIST, 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
@@ -12,33 +12,15 @@ * Christian W. Damus (CEA) - bug 432813
* Christian W. Damus (CEA) - bug 434993
* Christian W. Damus (CEA) - bug 436047
+ * Christian W. Damus - bug 485156
*
*****************************************************************************/
package org.eclipse.papyrus.junit.framework.classification;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Field;
-import java.util.Arrays;
import java.util.List;
-import java.util.Set;
-import org.eclipse.core.commands.operations.DefaultOperationHistory;
-import org.eclipse.core.runtime.ListenerList;
-import org.eclipse.core.runtime.Platform;
-import org.eclipse.jface.viewers.BaseLabelProvider;
-import org.eclipse.jface.viewers.IBaseLabelProvider;
-import org.eclipse.jface.viewers.ILabelProviderListener;
-import org.eclipse.jface.viewers.LabelProviderChangedEvent;
-import org.eclipse.papyrus.infra.tools.util.ListHelper;
-import org.eclipse.papyrus.junit.framework.classification.rules.ConditionRule;
import org.eclipse.papyrus.junit.framework.classification.rules.Conditional;
-import org.eclipse.papyrus.junit.framework.classification.rules.MemoryLeakRule;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.IWorkbench;
-import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.PlatformUI;
import org.junit.rules.TestRule;
-import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
@@ -46,12 +28,6 @@ import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
-import com.google.common.base.Predicates;
-import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-
/**
* A Test Runner which is aware of Classification-related annotations and {@link Conditional @Conditional} tests.
*
@@ -68,271 +44,59 @@ import com.google.common.collect.Sets; */
public class ClassificationRunner extends BlockJUnit4ClassRunner {
- private final static long EVENT_LOOP_TIMEOUT = 2L * 60L * 1000L; // 2 minutes in millis
-
- private final static long ONE_MB = 1024L * 1024L; // a megabyte, in bytes
-
- private static final Supplier<TestRule> uiFlusherRuleSupplier = createURIFlusherRuleSupplier();
-
- private final ThreadLocal<Object> preparedTest = new ThreadLocal<Object>();
+ private final ClassificationRunnerImpl impl;
public ClassificationRunner(Class<?> klass) throws InitializationError {
super(klass);
- }
-
- @Override
- protected void runChild(FrameworkMethod method, RunNotifier notifier) {
- List<Annotation> allAnnotations = ListHelper.asList(method.getAnnotations());
- allAnnotations.addAll(Arrays.asList(method.getMethod().getDeclaringClass().getAnnotations()));
- if(ClassificationConfig.shouldRun(allAnnotations.toArray(new Annotation[allAnnotations.size()])) && conditionSatisfied(method)) {
- super.runChild(method, notifier);
- } else {
- Description description = describeChild(method);
- notifier.fireTestIgnored(description);
- }
- }
-
- @Override
- protected Object createTest() throws Exception {
- // Look for a prepared test instance
- Object result = preparedTest.get();
- if(result != null) {
- // We won't need this test instance again
- clearPreparedTest();
- } else {
- result = basicCreateTest();
- }
-
- return result;
- }
-
- protected final Object basicCreateTest() throws Exception {
- return super.createTest();
- }
-
- protected final Object prepareTest() throws Exception {
- // Prepare the test instance and stash it to return on the next invocation
- Object result = basicCreateTest();
- preparedTest.set(result);
- return result;
- }
-
- protected final void clearPreparedTest() {
- preparedTest.remove();
- }
- private boolean conditionSatisfied(FrameworkMethod method) {
- boolean result = true;
+ this.impl = new ClassificationRunnerImpl(new ClassificationRunnerImpl.Delegate() {
- // Does this test declare some precondition?
- Conditional conditional = method.getAnnotation(Conditional.class);
- if(conditional != null) {
- try {
- // We need the test instance to invoke the condition on it, so prepare it now
- Object test = prepareTest();
- result = ConditionRule.testCondition(method.getMethod().getDeclaringClass(), conditional, test);
- } catch (Throwable t) {
- // If we couldn't create the test, then we should just ignore it
- result = false;
- } finally {
- if(!result) {
- // We won't be running the test, so forget the prepared instance (if any)
- clearPreparedTest();
- }
+ @Override
+ public void runChild(FrameworkMethod method, RunNotifier notifier) {
+ ClassificationRunner.super.runChild(method, notifier);
}
- }
- return result;
- }
+ @Override
+ public Description describeChild(FrameworkMethod method) {
+ return ClassificationRunner.super.describeChild(method);
+ }
- @Override
- protected List<TestRule> getTestRules(Object target) {
- // MemoryLeakRules must be the outer-most rules, because leak assertions must only happen after all possible tear-down actions have run
- return reorderForMemoryLeakRules(super.getTestRules(target));
- }
+ @Override
+ public Object createTest() throws Exception {
+ return ClassificationRunner.super.createTest();
+ }
- private List<TestRule> reorderForMemoryLeakRules(List<TestRule> rules) {
- // Quick scan for memory rules
- if(!rules.isEmpty()) {
- int memoryRuleCount = Iterables.size(Iterables.filter(rules, Predicates.instanceOf(MemoryLeakRule.class)));
- if(memoryRuleCount > 0) {
- // Bubble the memory rules to the end
- int limit = rules.size() - memoryRuleCount;
+ @Override
+ public List<TestRule> getTestRules(Object target) {
+ return ClassificationRunner.super.getTestRules(target);
+ }
- for(int i = 0; i < limit; i++) {
- if(rules.get(i) instanceof MemoryLeakRule) {
- // Move the rule to the end and take a step back to get the next element
- rules.add(rules.remove(i--));
- }
- }
+ @Override
+ public Statement classBlock(RunNotifier notifier) {
+ return ClassificationRunner.super.classBlock(notifier);
}
- }
- return rules;
+ });
}
@Override
- protected Statement classBlock(RunNotifier notifier) {
- Statement result = super.classBlock(notifier);
-
- // Wrap the class suite in a rule that flushes the UI thread to release memory referenced by UI runnables
- TestRule uiFlusher = uiFlusherRuleSupplier.get();
- if(uiFlusher != null) {
- // This rule doesn't need any actual test description
- result = uiFlusher.apply(result, Description.EMPTY);
- }
-
- return result;
- }
-
- private static Supplier<TestRule> createURIFlusherRuleSupplier() {
- Supplier<TestRule> result = Suppliers.ofInstance(null);
-
- try {
- if(PlatformUI.isWorkbenchRunning()) {
- result = Suppliers.memoize(new Supplier<TestRule>() {
-
- @Override
- public TestRule get() {
- if(Display.getCurrent() != null) {
- return new TestWatcher() {
-
- @Override
- protected void finished(Description description) {
- final Display display = Display.getCurrent();
- if(display == null) {
- // Can't do UI manipulations and history listener hacking except on the UI thread
- return;
- }
-
- flushUIEventQueue(display);
-
- purgeZombieHistoryListeners();
-
- clearDecorationScheduler();
- }
- };
- }
-
- return null;
- }
- });
- }
- } catch (LinkageError e) {
- // Not running in Eclipse UI context. Fine
- }
-
- return result;
+ protected void runChild(FrameworkMethod method, RunNotifier notifier) {
+ impl.runChild(method, notifier);
}
- private static void flushUIEventQueue(Display display) {
- long base = System.currentTimeMillis();
- long timeout = EVENT_LOOP_TIMEOUT;
-
- // Flush the UI thread's pending events
- while(!display.isDisposed()) {
- try {
- if(!display.readAndDispatch()) {
- break;
- }
- } catch (Exception e) {
- // Ignore it
- }
-
- long now = System.currentTimeMillis();
- if((now - base) > timeout) {
- // This seems to be taking a really long time. What's up?
- base = now;
- timeout = timeout * 3L / 2L; // Exponential back-off to avoid over-reporting
- int freeMB = (int)(Runtime.getRuntime().freeMemory() / ONE_MB);
- System.err.printf("========%nUI event queue clean-up seems to be running long.%nCurrent free memory: %d MB%n========%n%n", freeMB);
- }
- }
+ @Override
+ protected Object createTest() throws Exception {
+ return impl.createTest();
}
- private static void purgeZombieHistoryListeners() {
- // If there are no editors open any longer, then all of the action handlers currently
- // listening to the operation history are leaked, so remove them. This ensures that we
- // do not end up wasting time in notifying thousands of dead/broken/useless listeners
- // every time a test case executes an operation on the history (which happens *a lot*)
- IWorkbench bench = PlatformUI.getWorkbench();
- IWorkbenchWindow window = (bench == null) ? null : bench.getActiveWorkbenchWindow();
- if((window == null) && (bench != null) && (bench.getWorkbenchWindowCount() > 0)) {
- window = bench.getWorkbenchWindows()[0];
- }
- if(window != null && window.getActivePage().getEditorReferences().length == 0) {
- final ListenerList historyListeners = OperationHistoryHelper.getOperationHistoryListeners();
- final Object[] listeners = historyListeners.getListeners();
- for(int i = 0; i < listeners.length; i++) {
- if(OperationHistoryHelper.shouldRemoveHistoryListener(listeners[i])) {
- historyListeners.remove(listeners[i]);
- }
- }
- }
+ @Override
+ protected List<TestRule> getTestRules(Object target) {
+ return impl.getTestRules(target);
}
- private static void clearDecorationScheduler() {
- IWorkbench bench = PlatformUI.getWorkbench();
- if(bench != null) {
- IBaseLabelProvider bogusProvider = new BaseLabelProvider();
-
- try {
- // The DecoratorManager is a label-provider listener and
- // it clears the scheduler on label-provider change events
- ((ILabelProviderListener)bench.getDecoratorManager()).labelProviderChanged(new LabelProviderChangedEvent(bogusProvider));
- } finally {
- bogusProvider.dispose();
- }
- }
+ @Override
+ protected Statement classBlock(RunNotifier notifier) {
+ return impl.classBlock(notifier);
}
- //
- // Nested types
- //
-
- static class OperationHistoryHelper {
-
- static final Field listenersField;
-
- static final Set<Class<?>> historyListenerClasses;
- static {
- try {
- listenersField = DefaultOperationHistory.class.getDeclaredField("listeners");
- listenersField.setAccessible(true);
-
- historyListenerClasses = Sets.<Class<?>> newHashSet( //
- Platform.getBundle("org.eclipse.gmf.runtime.diagram.ui.actions").loadClass("org.eclipse.gmf.runtime.diagram.ui.actions.internal.PropertyChangeContributionItem"), //
- Platform.getBundle("org.eclipse.ui.workbench").loadClass("org.eclipse.ui.operations.OperationHistoryActionHandler$HistoryListener"));
- } catch (Exception e) {
- throw new ExceptionInInitializerError(e);
- }
- }
-
- static ListenerList getOperationHistoryListeners() {
- try {
- return (ListenerList)listenersField.get(PlatformUI.getWorkbench().getOperationSupport().getOperationHistory());
- } catch (Exception e) {
- org.junit.Assert.fail(e.getLocalizedMessage());
- return null; // Unreachable
- }
- }
-
- static boolean shouldRemoveHistoryListener(Object listener) {
- boolean result = historyListenerClasses.contains(listener.getClass().getName());
-
- if(!result) {
- // Maybe it's a subclass
- for(Class<?> next : historyListenerClasses) {
- if(next.isInstance(listener)) {
- // Remember this
- historyListenerClasses.add(listener.getClass());
- result = true;
- break;
- }
- }
- }
-
- return result;
- }
- }
}
diff --git a/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationRunnerImpl.java b/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationRunnerImpl.java new file mode 100644 index 00000000000..f83dd28819e --- /dev/null +++ b/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationRunnerImpl.java @@ -0,0 +1,346 @@ +/***************************************************************************** + * Copyright (c) 2014, 2016 CEA LIST, 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 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation + * Christian W. Damus (CEA) - add support for conditional tests + * Christian W. Damus (CEA) - bug 432813 + * Christian W. Damus (CEA) - bug 434993 + * Christian W. Damus (CEA) - bug 436047 + * Christian W. Damus - bug 485156 + * + *****************************************************************************/ +package org.eclipse.papyrus.junit.framework.classification; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.commands.operations.DefaultOperationHistory; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.viewers.BaseLabelProvider; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.LabelProviderChangedEvent; +import org.eclipse.papyrus.infra.tools.util.ListHelper; +import org.eclipse.papyrus.junit.framework.classification.rules.ConditionRule; +import org.eclipse.papyrus.junit.framework.classification.rules.Conditional; +import org.eclipse.papyrus.junit.framework.classification.rules.MemoryLeakRule; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; + +import com.google.common.base.Predicates; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; + +/** + * Internal implementation of the common classification-sensitive behaviour + * of the {@link ClassificationRunner} and {@link ClassificationRunnerWithParameters} + * test runners. + * + * @author Camille Letavernier + */ +class ClassificationRunnerImpl { + + private final static long EVENT_LOOP_TIMEOUT = 2L * 60L * 1000L; // 2 minutes in millis + + private final static long ONE_MB = 1024L * 1024L; // a megabyte, in bytes + + private static final Supplier<TestRule> uiFlusherRuleSupplier = createUIFlusherRuleSupplier(); + + private final ThreadLocal<Object> preparedTest = new ThreadLocal<Object>(); + + private final Delegate delegate; + + ClassificationRunnerImpl(Delegate delegate) throws InitializationError { + super(); + + this.delegate = delegate; + } + + final void runChild(FrameworkMethod method, RunNotifier notifier) { + List<Annotation> allAnnotations = ListHelper.asList(method.getAnnotations()); + allAnnotations.addAll(Arrays.asList(method.getMethod().getDeclaringClass().getAnnotations())); + if (ClassificationConfig.shouldRun(allAnnotations.toArray(new Annotation[allAnnotations.size()])) && conditionSatisfied(method)) { + delegate.runChild(method, notifier); + } else { + Description description = delegate.describeChild(method); + notifier.fireTestIgnored(description); + } + } + + final Object createTest() throws Exception { + // Look for a prepared test instance + Object result = preparedTest.get(); + if (result != null) { + // We won't need this test instance again + clearPreparedTest(); + } else { + result = delegate.createTest(); + } + + return result; + } + + final Object prepareTest() throws Exception { + // Prepare the test instance and stash it to return on the next invocation + Object result = delegate.createTest(); + preparedTest.set(result); + return result; + } + + final void clearPreparedTest() { + preparedTest.remove(); + } + + private boolean conditionSatisfied(FrameworkMethod method) { + boolean result = true; + + // Does this test declare some precondition? + Conditional conditional = method.getAnnotation(Conditional.class); + if (conditional != null) { + try { + // We need the test instance to invoke the condition on it, so prepare it now + Object test = prepareTest(); + result = ConditionRule.testCondition(method.getMethod().getDeclaringClass(), conditional, test); + } catch (Throwable t) { + // If we couldn't create the test, then we should just ignore it + result = false; + } finally { + if (!result) { + // We won't be running the test, so forget the prepared instance (if any) + clearPreparedTest(); + } + } + } + + return result; + } + + List<TestRule> getTestRules(Object target) { + // MemoryLeakRules must be the outer-most rules, because leak assertions must only happen after all possible tear-down actions have run + return reorderForMemoryLeakRules(delegate.getTestRules(target)); + } + + private List<TestRule> reorderForMemoryLeakRules(List<TestRule> rules) { + // Quick scan for memory rules + if (!rules.isEmpty()) { + int memoryRuleCount = Iterables.size(Iterables.filter(rules, Predicates.instanceOf(MemoryLeakRule.class))); + if (memoryRuleCount > 0) { + // Bubble the memory rules to the end + int limit = rules.size() - memoryRuleCount; + + for (int i = 0; i < limit; i++) { + if (rules.get(i) instanceof MemoryLeakRule) { + // Move the rule to the end and take a step back to get the next element + rules.add(rules.remove(i--)); + } + } + } + } + + return rules; + } + + Statement classBlock(RunNotifier notifier) { + Statement result = delegate.classBlock(notifier); + + // Wrap the class suite in a rule that flushes the UI thread to release memory referenced by UI runnables + TestRule uiFlusher = uiFlusherRuleSupplier.get(); + if (uiFlusher != null) { + // This rule doesn't need any actual test description + result = uiFlusher.apply(result, Description.EMPTY); + } + + return result; + } + + private static Supplier<TestRule> createUIFlusherRuleSupplier() { + Supplier<TestRule> result = Suppliers.ofInstance(null); + + try { + if (PlatformUI.isWorkbenchRunning()) { + result = Suppliers.memoize(new Supplier<TestRule>() { + + @Override + public TestRule get() { + if (Display.getCurrent() != null) { + return new TestWatcher() { + + @Override + protected void finished(Description description) { + final Display display = Display.getCurrent(); + if (display == null) { + // Can't do UI manipulations and history listener hacking except on the UI thread + return; + } + + flushUIEventQueue(display); + + purgeZombieHistoryListeners(); + + clearDecorationScheduler(); + } + }; + } + + return null; + } + }); + } + } catch (LinkageError e) { + // Not running in Eclipse UI context. Fine + } + + return result; + } + + private static void flushUIEventQueue(Display display) { + long base = System.currentTimeMillis(); + long timeout = EVENT_LOOP_TIMEOUT; + + // Flush the UI thread's pending events + while (!display.isDisposed()) { + try { + if (!display.readAndDispatch()) { + break; + } + } catch (Exception e) { + // Ignore it + } + + long now = System.currentTimeMillis(); + if ((now - base) > timeout) { + // This seems to be taking a really long time. What's up? + base = now; + timeout = timeout * 3L / 2L; // Exponential back-off to avoid over-reporting + int freeMB = (int) (Runtime.getRuntime().freeMemory() / ONE_MB); + System.err.printf("========%nUI event queue clean-up seems to be running long.%nCurrent free memory: %d MB%n========%n%n", freeMB); + } + } + } + + private static void purgeZombieHistoryListeners() { + // If there are no editors open any longer, then all of the action handlers currently + // listening to the operation history are leaked, so remove them. This ensures that we + // do not end up wasting time in notifying thousands of dead/broken/useless listeners + // every time a test case executes an operation on the history (which happens *a lot*) + IWorkbench bench = PlatformUI.getWorkbench(); + IWorkbenchWindow window = (bench == null) ? null : bench.getActiveWorkbenchWindow(); + if ((window == null) && (bench != null) && (bench.getWorkbenchWindowCount() > 0)) { + window = bench.getWorkbenchWindows()[0]; + } + if (window != null && window.getActivePage().getEditorReferences().length == 0) { + final ListenerList historyListeners = OperationHistoryHelper.getOperationHistoryListeners(); + final Object[] listeners = historyListeners.getListeners(); + for (int i = 0; i < listeners.length; i++) { + if (OperationHistoryHelper.shouldRemoveHistoryListener(listeners[i])) { + historyListeners.remove(listeners[i]); + } + } + } + } + + private static void clearDecorationScheduler() { + IWorkbench bench = PlatformUI.getWorkbench(); + if (bench != null) { + IBaseLabelProvider bogusProvider = new BaseLabelProvider(); + + try { + // The DecoratorManager is a label-provider listener and + // it clears the scheduler on label-provider change events + ((ILabelProviderListener) bench.getDecoratorManager()).labelProviderChanged(new LabelProviderChangedEvent(bogusProvider)); + } finally { + bogusProvider.dispose(); + } + } + } + + // + // Nested types + // + + static class OperationHistoryHelper { + + static final Field listenersField; + + static final Set<Class<?>> historyListenerClasses; + + static { + try { + listenersField = DefaultOperationHistory.class.getDeclaredField("listeners"); + listenersField.setAccessible(true); + + historyListenerClasses = Sets.<Class<?>> newHashSet( // + Platform.getBundle("org.eclipse.gmf.runtime.diagram.ui.actions").loadClass("org.eclipse.gmf.runtime.diagram.ui.actions.internal.PropertyChangeContributionItem"), // + Platform.getBundle("org.eclipse.ui.workbench").loadClass("org.eclipse.ui.operations.OperationHistoryActionHandler$HistoryListener")); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + static ListenerList getOperationHistoryListeners() { + try { + return (ListenerList) listenersField.get(PlatformUI.getWorkbench().getOperationSupport().getOperationHistory()); + } catch (Exception e) { + org.junit.Assert.fail(e.getLocalizedMessage()); + return null; // Unreachable + } + } + + static boolean shouldRemoveHistoryListener(Object listener) { + boolean result = historyListenerClasses.contains(listener.getClass().getName()); + + if (!result) { + // Maybe it's a subclass + for (Class<?> next : historyListenerClasses) { + if (next.isInstance(listener)) { + // Remember this + historyListenerClasses.add(listener.getClass()); + result = true; + break; + } + } + } + + return result; + } + } + + /** + * Protocol for a delegate that provides the default test framework behaviour + * for the classification runner. These methods are as specified by the + * corresponding APIs of the {@link BlockJUnit4ClassRunner} class. + */ + interface Delegate { + void runChild(FrameworkMethod method, RunNotifier notifier); + + Description describeChild(FrameworkMethod method); + + Object createTest() throws Exception; + + List<TestRule> getTestRules(Object target); + + Statement classBlock(RunNotifier notifier); + } +} diff --git a/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationRunnerWithParameters.java b/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationRunnerWithParameters.java new file mode 100644 index 00000000000..ca3079f3052 --- /dev/null +++ b/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationRunnerWithParameters.java @@ -0,0 +1,102 @@ +/***************************************************************************** + * Copyright (c) 2016 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 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.junit.framework.classification; + +import java.util.List; + +import org.eclipse.papyrus.junit.framework.classification.rules.Conditional; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.junit.runners.Parameterized.UseParametersRunnerFactory; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; +import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters; +import org.junit.runners.parameterized.TestWithParameters; + +/** + * A Test Runner which is aware of Classification-related annotations and {@link Conditional @Conditional} tests, + * for use with test {@link Parameters}. + * + * It ignores the test methods according to their annotations, and the policy defined + * in {@link ClassificationConfig}. + * + * @see Parameterized + * @see UseParametersRunnerFactory + * @see ClassificationRunnerWithParametersFactory + * @see ClassificationConfig + * @see TestCategory + * @see Conditional + * + */ +public class ClassificationRunnerWithParameters extends BlockJUnit4ClassRunnerWithParameters { + + private final ClassificationRunnerImpl impl; + + public ClassificationRunnerWithParameters(TestWithParameters test) throws InitializationError { + super(test); + + this.impl = new ClassificationRunnerImpl(new ClassificationRunnerImpl.Delegate() { + + @Override + public void runChild(FrameworkMethod method, RunNotifier notifier) { + ClassificationRunnerWithParameters.super.runChild(method, notifier); + } + + @Override + public Description describeChild(FrameworkMethod method) { + return ClassificationRunnerWithParameters.super.describeChild(method); + } + + @Override + public Object createTest() throws Exception { + return ClassificationRunnerWithParameters.super.createTest(); + } + + @Override + public List<TestRule> getTestRules(Object target) { + return ClassificationRunnerWithParameters.super.getTestRules(target); + } + + @Override + public Statement classBlock(RunNotifier notifier) { + return ClassificationRunnerWithParameters.super.classBlock(notifier); + } + + }); + } + + @Override + protected void runChild(FrameworkMethod method, RunNotifier notifier) { + impl.runChild(method, notifier); + } + + @Override + public Object createTest() throws Exception { + return impl.createTest(); + } + + @Override + protected List<TestRule> getTestRules(Object target) { + return impl.getTestRules(target); + } + + @Override + protected Statement classBlock(RunNotifier notifier) { + return impl.classBlock(notifier); + } + +} diff --git a/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationRunnerWithParametersFactory.java b/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationRunnerWithParametersFactory.java new file mode 100644 index 00000000000..f305d8b1fd5 --- /dev/null +++ b/tests/junit/plugins/junit/org.eclipse.papyrus.junit.framework/src/org/eclipse/papyrus/junit/framework/classification/ClassificationRunnerWithParametersFactory.java @@ -0,0 +1,44 @@ +/***************************************************************************** + * Copyright (c) 2016 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 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.junit.framework.classification; + +import org.junit.runner.RunWith; +import org.junit.runner.Runner; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.UseParametersRunnerFactory; +import org.junit.runners.model.InitializationError; +import org.junit.runners.parameterized.ParametersRunnerFactory; +import org.junit.runners.parameterized.TestWithParameters; + +/** + * Factory for classification-sensitive parameterized test suites. + * Specify this factory in the {@literal @}{@link UseParametersRunnerFactory} + * annotation on your <tt>{@literal @}{@link RunWith}({@link Parameterized}.class)</tt> + * test class to support the classfication and condition annotations of the Papyrus + * test framework. + * + * @see Parameterized + * @see UseParametersRunnerFactory + */ +public class ClassificationRunnerWithParametersFactory implements ParametersRunnerFactory { + + public ClassificationRunnerWithParametersFactory() { + super(); + } + + @Override + public Runner createRunnerForTestWithParameters(TestWithParameters test) throws InitializationError { + return new ClassificationRunnerWithParameters(test); + } +} diff --git a/tests/junit/plugins/uml/assistants/org.eclipse.papyrus.uml.profile.assistants.generator.tests/src/org/eclipse/papyrus/uml/profile/assistants/generator/tests/ConnectionTypesTest.java b/tests/junit/plugins/uml/assistants/org.eclipse.papyrus.uml.profile.assistants.generator.tests/src/org/eclipse/papyrus/uml/profile/assistants/generator/tests/ConnectionTypesTest.java index 1e3ed5d7f50..20585eb2a33 100644 --- a/tests/junit/plugins/uml/assistants/org.eclipse.papyrus.uml.profile.assistants.generator.tests/src/org/eclipse/papyrus/uml/profile/assistants/generator/tests/ConnectionTypesTest.java +++ b/tests/junit/plugins/uml/assistants/org.eclipse.papyrus.uml.profile.assistants.generator.tests/src/org/eclipse/papyrus/uml/profile/assistants/generator/tests/ConnectionTypesTest.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2015 Christian W. Damus and others. + * Copyright (c) 2015, 2016 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 @@ -15,6 +15,7 @@ package org.eclipse.papyrus.uml.profile.assistants.generator.tests; import java.util.Arrays; +import org.eclipse.papyrus.junit.framework.classification.ClassificationRunnerWithParametersFactory; import org.eclipse.papyrus.junit.utils.rules.PluginResource; import org.eclipse.uml2.uml.Class; import org.eclipse.uml2.uml.Stereotype; @@ -24,12 +25,14 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import org.junit.runners.Parameterized.UseParametersRunnerFactory; /** * Specific regression test cases verifying that connection assistants are inferred correctly for various types of * UML model elements. */ @RunWith(Parameterized.class) +@UseParametersRunnerFactory(ClassificationRunnerWithParametersFactory.class) @PluginResource("/resources/edges.profile.uml") public class ConnectionTypesTest { |