Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian W. Damus2014-02-05 22:07:03 +0000
committerChristian W. Damus2014-02-07 23:55:46 +0000
commit4df3b971ddb0910600402cc93640cbdb538cb036 (patch)
treed1f7b4408353e0e26b2f5419386c5a612d8f7700
parent65af346c9b891ea496ff9e69f8b7c4bf702dff7b (diff)
downloadorg.eclipse.papyrus-4df3b971ddb0910600402cc93640cbdb538cb036.tar.gz
org.eclipse.papyrus-4df3b971ddb0910600402cc93640cbdb538cb036.tar.xz
org.eclipse.papyrus-4df3b971ddb0910600402cc93640cbdb538cb036.zip
402525: [Widgets / Transactions] Papyrus dialogs should be transactional
https://bugs.eclipse.org/bugs/show_bug.cgi?id=402525 JUnit tests for nesting command stacks and related APIs.
-rw-r--r--features/papyrus-tests-features/org.eclipse.papyrus.tests.infra.feature/feature.xml59
-rw-r--r--tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/resource/Bug402525.ecore6
-rw-r--r--tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/resource/NestingTransactionalCommandStackTest.java513
-rw-r--r--tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/tests/AllTests.java6
-rw-r--r--tests/junit/plugins/core/org.eclipse.papyrus.tests/META-INF/MANIFEST.MF3
-rw-r--r--tests/junit/plugins/core/org.eclipse.papyrus.tests/test/org/eclipse/papyrus/tests/AllTests.java5
-rw-r--r--tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/.classpath7
-rw-r--r--tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/.project28
-rw-r--r--tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/.settings/org.eclipse.jdt.core.prefs8
-rw-r--r--tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/META-INF/MANIFEST.MF13
-rw-r--r--tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/build.properties6
-rw-r--r--tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/fragment.properties14
-rw-r--r--tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/org.eclipse.papyrus.infra.gmfdiag.commands.tests.launch42
-rw-r--r--tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/src/org/eclipse/papyrus/commands/Bug402525.ecore6
-rw-r--r--tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/src/org/eclipse/papyrus/commands/NestingNotifyingWorkspaceCommandStackTest.java513
-rw-r--r--tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/src/org/eclipse/papyrus/infra/gmfdiag/commands/tests/AllTests.java29
16 files changed, 1234 insertions, 24 deletions
diff --git a/features/papyrus-tests-features/org.eclipse.papyrus.tests.infra.feature/feature.xml b/features/papyrus-tests-features/org.eclipse.papyrus.tests.infra.feature/feature.xml
index fa00df248a4..c33130f0ba8 100644
--- a/features/papyrus-tests-features/org.eclipse.papyrus.tests.infra.feature/feature.xml
+++ b/features/papyrus-tests-features/org.eclipse.papyrus.tests.infra.feature/feature.xml
@@ -1,20 +1,39 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?><feature id="org.eclipse.papyrus.tests.infra.feature" label="%featureName" provider-name="%providerName" version="1.0.0.qualifier">
-
- <copyright url="http://www.eclipse.org/legal/epl-v10.html">
- Copyright (c) 2008-2012 CEA LIST, Atos Origin, Conselleria de
-Infraestructuras y Transporte, Generalitat de la Comunitat Valenciana
-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
- </copyright>
-
- <license url="%licenseURL">
- %license
- </license>
-
- <plugin download-size="0" fragment="true" id="org.eclipse.papyrus.extendedtypes.tests" install-size="0" unpack="false" version="0.0.0"/>
-
-</feature> \ No newline at end of file
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="org.eclipse.papyrus.tests.infra.feature"
+ label="%featureName"
+ version="1.0.0.qualifier"
+ provider-name="%providerName">
+
+ <copyright url="http://www.eclipse.org/legal/epl-v10.html">
+ Copyright (c) 2008-2014 CEA LIST, Atos Origin, Conselleria de
+Infraestructuras y Transporte, Generalitat de la Comunitat Valenciana
+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
+ </copyright>
+
+ <license url="%licenseURL">
+ %license
+ </license>
+
+ <plugin
+ id="org.eclipse.papyrus.extendedtypes.tests"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.papyrus.infra.gmfdiag.commands.tests"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ fragment="true"
+ unpack="false"/>
+
+</feature>
diff --git a/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/resource/Bug402525.ecore b/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/resource/Bug402525.ecore
new file mode 100644
index 00000000000..8dea18de3e8
--- /dev/null
+++ b/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/resource/Bug402525.ecore
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="bug402525" nsURI="http://www.eclipse.org/schema/Papyrus/tests/bug402525"
+ nsPrefix="bug">
+ <eClassifiers xsi:type="ecore:EClass" name="Foo"/>
+</ecore:EPackage>
diff --git a/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/resource/NestingTransactionalCommandStackTest.java b/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/resource/NestingTransactionalCommandStackTest.java
new file mode 100644
index 00000000000..c64b9923fb7
--- /dev/null
+++ b/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/resource/NestingTransactionalCommandStackTest.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2014 CEA 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 (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.resource;
+
+import static org.hamcrest.CoreMatchers.both;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.net.URL;
+import java.util.concurrent.Callable;
+
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.common.command.CommandStack;
+import org.eclipse.emf.common.notify.AdapterFactory;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EAttribute;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.ENamedElement;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EcoreFactory;
+import org.eclipse.emf.ecore.EcorePackage;
+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.edit.provider.ComposedAdapterFactory;
+import org.eclipse.emf.transaction.RecordingCommand;
+import org.eclipse.emf.transaction.TransactionalCommandStack;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.emf.transaction.impl.TransactionalEditingDomainImpl;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.base.Objects;
+
+
+/**
+ * Test suite for the {@link NestingTransactionalCommandStack} class.
+ */
+public class NestingTransactionalCommandStackTest {
+
+ // No API signatures but the most basic are required for nesting
+ private CommandStack fixture;
+
+ private TransactionalEditingDomain domain;
+
+ private ResourceSet rset;
+
+ private EPackage testPackage;
+
+ private EClass foo;
+
+ public NestingTransactionalCommandStackTest() {
+ super();
+ }
+
+ //
+ // Test cases
+ //
+
+ @Test
+ public void testCompleteUnnestedCommand() {
+ Callable<EAttribute> createAttribute = createAttribute();
+
+ int oldFeatureCount = foo.getEStructuralFeatures().size();
+
+ // Create two attributes
+ EAttribute attr1 = execute(createAttribute);
+ EAttribute attr2 = execute(createAttribute);
+ assertThat(foo.getEStructuralFeatures().size(), is(oldFeatureCount + 2));
+ assertThat(foo.getEStructuralFeatures(), hasItem(attr1));
+ assertThat(foo.getEStructuralFeatures(), hasItem(attr2));
+
+ // Undo only undoes one of them
+ Command undone = undo();
+ assertThat(foo.getEStructuralFeatures().size(), is(oldFeatureCount + 1));
+ assertThat(foo.getEStructuralFeatures(), hasItem(attr1));
+ assertThat(foo.getEStructuralFeatures(), not(hasItem(attr2)));
+
+ // Redo is sane
+ Command redone = redo();
+ assertThat(redone, sameInstance(undone));
+ assertThat(foo.getEStructuralFeatures().size(), is(oldFeatureCount + 2));
+ assertThat(foo.getEStructuralFeatures(), hasItem(attr1));
+ assertThat(foo.getEStructuralFeatures(), hasItem(attr2));
+ }
+
+ @Test
+ public void testCancelUnnestedCommand() {
+ Callable<EAttribute> createAttribute = createAttribute(2);
+
+ int oldFeatureCount = foo.getEStructuralFeatures().size();
+
+ // Try to create two attributes (second one cancels)
+ EAttribute attr1 = execute(createAttribute);
+ EAttribute attr2 = execute(createAttribute);
+ assertThat(attr2, nullValue());
+ assertThat(foo.getEStructuralFeatures().size(), is(oldFeatureCount + 1));
+ assertThat(foo.getEStructuralFeatures(), hasItem(attr1));
+ assertThat(foo.getEStructuralFeature("attribute2"), nullValue()); //$NON-NLS-1$
+
+ // Undo is sane
+ Command undone = undo();
+ assertThat(foo.getEStructuralFeatures().size(), is(oldFeatureCount));
+ assertThat(foo.getEStructuralFeatures(), not(hasItem(attr1)));
+ assertThat(foo.getEStructuralFeature("attribute2"), nullValue()); //$NON-NLS-1$
+
+ // Redo is sane
+ Command redone = redo();
+ assertThat(redone, sameInstance(undone));
+ assertThat(foo.getEStructuralFeatures().size(), is(oldFeatureCount + 1));
+ assertThat(foo.getEStructuralFeatures(), hasItem(attr1));
+ assertThat(foo.getEStructuralFeature("attribute2"), nullValue()); //$NON-NLS-1$
+ }
+
+ @Test
+ public void testCompleteNestedCommand() {
+ final int FEATURES = 2;
+ Callable<EClass> createClass = createClass(FEATURES);
+
+ int oldClassifierCount = testPackage.getEClassifiers().size();
+
+ EClass clas = execute(createClass);
+ assertThat(testPackage.getEClassifiers().size(), is(oldClassifierCount + 1));
+ assertThat(testPackage.getEClassifiers(), hasItem(clas));
+ assertThat(clas.getEStructuralFeatures().size(), is(FEATURES));
+ assertThat(clas.getEStructuralFeature("attribute1"), notNullValue()); //$NON-NLS-1$
+ assertThat(clas.getEStructuralFeature("attribute2"), notNullValue()); //$NON-NLS-1$
+
+ // Undo is sane and there is only one command to undo
+ Command undone = undo();
+ assertThat(fixture.canUndo(), is(false));
+ assertThat(testPackage.getEClassifiers().size(), is(oldClassifierCount));
+ assertThat(testPackage.getEClassifiers(), not(hasItem(clas)));
+
+ // Redo is sane
+ Command redone = redo();
+ assertThat(redone, sameInstance(undone));
+ assertThat(testPackage.getEClassifiers().size(), is(oldClassifierCount + 1));
+ assertThat(testPackage.getEClassifiers(), hasItem(clas));
+ assertThat(clas.getEStructuralFeatures().size(), is(FEATURES));
+ assertThat(clas.getEStructuralFeature("attribute1"), notNullValue()); //$NON-NLS-1$
+ assertThat(clas.getEStructuralFeature("attribute2"), notNullValue()); //$NON-NLS-1$
+ }
+
+ @Test
+ public void testCancelNestedCommand() {
+ final int FEATURES = 2;
+ Callable<EClass> createClass = createClass(FEATURES, 2, FEATURES);
+
+ int oldClassifierCount = testPackage.getEClassifiers().size();
+
+ // Try to create two classes (second one cancels)
+ EClass class1 = execute(createClass);
+ EClass class2 = execute(createClass);
+ assertThat(class2, nullValue());
+ assertThat(testPackage.getEClassifiers().size(), is(oldClassifierCount + 1));
+ assertThat(testPackage.getEClassifiers(), hasItem(class1));
+ assertThat(class1.getEStructuralFeatures().size(), is(FEATURES));
+ assertThat(class1.getEStructuralFeature("attribute1"), notNullValue()); //$NON-NLS-1$
+ assertThat(class1.getEStructuralFeature("attribute2"), notNullValue()); //$NON-NLS-1$
+
+ // Undo is sane and there is only one command to undo
+ Command undone = undo();
+ assertThat(fixture.canUndo(), is(false));
+ assertThat(testPackage.getEClassifiers().size(), is(oldClassifierCount));
+ assertThat(testPackage.getEClassifiers(), not(hasItem(class1)));
+
+ // Redo is sane
+ Command redone = redo();
+ assertThat(redone, sameInstance(undone));
+ assertThat(testPackage.getEClassifiers().size(), is(oldClassifierCount + 1));
+ assertThat(testPackage.getEClassifiers(), hasItem(class1));
+ assertThat(class1.getEStructuralFeatures().size(), is(FEATURES));
+ assertThat(class1.getEStructuralFeature("attribute1"), notNullValue()); //$NON-NLS-1$
+ assertThat(class1.getEStructuralFeature("attribute2"), notNullValue()); //$NON-NLS-1$
+ }
+
+ @Test
+ public void testCompleteTripleNestedCommand() {
+ final int CLASSES = 2;
+ final int FEATURES = 2;
+
+ Callable<EPackage> createPackage = createPackage(CLASSES, FEATURES);
+
+ int oldPackageCount = testPackage.getESubpackages().size();
+
+ EPackage pkg = execute(createPackage);
+ assertThat(testPackage.getESubpackages().size(), is(oldPackageCount + 1));
+ assertThat(testPackage.getESubpackages(), hasItem(pkg));
+ assertThat(pkg.getEClassifiers().size(), is(CLASSES));
+ assertThat(pkg.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class1")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ assertThat(pkg.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class2")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ // Undo is sane and there is only one command to undo
+ Command undone = undo();
+ assertThat(fixture.canUndo(), is(false));
+ assertThat(testPackage.getESubpackages().size(), is(oldPackageCount));
+ assertThat(testPackage.getESubpackages(), not(hasItem(pkg)));
+ assertThat(pkg.getName(), nullValue());
+ assertThat(pkg.getEClassifiers().size(), is(0));
+ assertThat(pkg.getEClassifiers().size(), is(0));
+
+ // Redo is sane
+ Command redone = redo();
+ assertThat(redone, sameInstance(undone));
+ assertThat(fixture.canRedo(), is(false));
+ assertThat(testPackage.getESubpackages().size(), is(oldPackageCount + 1));
+ assertThat(testPackage.getESubpackages(), hasItem(pkg));
+ assertThat(pkg, named("package1")); //$NON-NLS-1$
+ assertThat(pkg.getEClassifiers().size(), is(CLASSES));
+ assertThat(pkg.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class1")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ assertThat(pkg.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class2")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @Test
+ public void testCancelTripleNestedCommand() {
+ final int CLASSES = 2;
+ final int FEATURES = 2;
+
+ Callable<EPackage> createPackage = createPackage(CLASSES, FEATURES, 2, CLASSES, FEATURES);
+
+ int oldPackageCount = testPackage.getESubpackages().size();
+
+ // Try to create two packages (second one cancels)
+ EPackage pkg1 = execute(createPackage);
+ EPackage pkg2 = execute(createPackage);
+
+ // the first package is complete
+ assertThat(testPackage.getESubpackages().size(), is(oldPackageCount + 1));
+ assertThat(testPackage.getESubpackages(), hasItem(pkg1));
+ assertThat(pkg1, named("package1")); //$NON-NLS-1$
+ assertThat(pkg1.getEClassifiers().size(), is(CLASSES));
+ assertThat(pkg1.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class1")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ assertThat(pkg1.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class2")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ // there is no second package
+ assertThat(pkg2, nullValue());
+
+ // Undo is sane and there is only one command to undo
+ Command undone = undo();
+ assertThat(fixture.canUndo(), is(false));
+ assertThat(testPackage.getESubpackages().size(), is(oldPackageCount));
+ assertThat(testPackage.getESubpackages(), not(hasItem(pkg1)));
+ assertThat(pkg1.getName(), nullValue());
+ assertThat(pkg1.getEClassifiers().size(), is(0));
+
+ // Redo is sane
+ Command redone = redo();
+ assertThat(fixture.canRedo(), is(false));
+ assertThat(redone, sameInstance(undone));
+ assertThat(testPackage.getESubpackages().size(), is(oldPackageCount + 1));
+ assertThat(testPackage.getESubpackages(), hasItem(pkg1));
+ assertThat(pkg1, named("package1")); //$NON-NLS-1$
+ assertThat(pkg1.getEClassifiers().size(), is(CLASSES));
+ assertThat(pkg1.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class1")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ assertThat(pkg1.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class2")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ //
+ // Test framework
+ //
+
+ @Before
+ public void createFixture() {
+ rset = new ResourceSetImpl();
+ fixture = new NestingTransactionalCommandStack();
+ AdapterFactory adapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
+ domain = new TransactionalEditingDomainImpl(adapterFactory, (TransactionalCommandStack)fixture, rset);
+
+ URL testModelURL = getClass().getResource("bug402525.ecore"); //$NON-NLS-1$
+ Resource testModel = rset.getResource(URI.createURI(testModelURL.toExternalForm(), true), true);
+ testPackage = (EPackage)testModel.getContents().get(0);
+ foo = (EClass)testPackage.getEClassifier("Foo"); //$NON-NLS-1$
+ }
+
+ @After
+ public void destroyFixture() {
+ // This disposes the command stack for us
+ domain.dispose();
+ domain = null;
+ fixture = null;
+
+ dispose(rset);
+ rset = null;
+ }
+
+ void dispose(ResourceSet rset) {
+ for(Resource next : rset.getResources()) {
+ next.unload();
+ next.eAdapters().clear();
+ }
+
+ rset.getResources().clear();
+ rset.eAdapters().clear();
+ }
+
+ <V> V execute(final Callable<V> operation) {
+ class TestCommand extends RecordingCommand {
+
+ V result;
+
+ TestCommand() {
+ super(domain);
+ }
+
+ @Override
+ protected void doExecute() {
+ try {
+ result = operation.call();
+ } catch (OperationCanceledException e) {
+ // Pass it on
+ throw e;
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Uncaught exception in operation: " + e.getLocalizedMessage()); //$NON-NLS-1$
+ }
+ }
+ }
+
+ TestCommand command = new TestCommand();
+ fixture.execute(command);
+ return command.result;
+ }
+
+ Command undo() {
+ assertThat("Cannot undo", fixture.canUndo()); //$NON-NLS-1$
+ Command result = fixture.getUndoCommand();
+ fixture.undo();
+ return result;
+ }
+
+ Command redo() {
+ assertThat("Cannot redo", fixture.canRedo()); //$NON-NLS-1$
+ Command result = fixture.getRedoCommand();
+ fixture.redo();
+ return result;
+ }
+
+ Callable<EAttribute> createAttribute() {
+ return createAttribute(0);
+ }
+
+ Callable<EAttribute> createAttribute(int cancelOn) {
+ return createAttribute(foo, cancelOn);
+ }
+
+ Callable<EAttribute> createAttribute(final EClass owner, final int cancelOn) {
+ return new Callable<EAttribute>() {
+
+ int i = 0;
+
+ public EAttribute call() throws Exception {
+ String name = nextName();
+
+ EAttribute attr = EcoreFactory.eINSTANCE.createEAttribute();
+ owner.getEStructuralFeatures().add(attr);
+ attr.setName(name);
+ attr.setEType(EcorePackage.Literals.ESTRING);
+
+ checkCancel();
+
+ return attr;
+ }
+
+ private String nextName() {
+ i = i + 1;
+ return "attribute" + i; //$NON-NLS-1$
+ }
+
+ private void checkCancel() {
+ if(i == cancelOn) {
+ throw new OperationCanceledException();
+ }
+ }
+ };
+ }
+
+ Callable<EClass> createClass(int attributes) {
+ return createClass(attributes, 0, 0);
+ }
+
+ Callable<EClass> createClass(int attributes, int cancelOn, int cancelAttributesOn) {
+ return createClass(testPackage, attributes, cancelOn, cancelAttributesOn);
+ }
+
+ Callable<EClass> createClass(final EPackage owner, final int attributes, final int cancelOn, final int cancelAttributesOn) {
+ return new Callable<EClass>() {
+
+ int i = 0;
+
+ public EClass call() throws Exception {
+ String name = nextName();
+
+ EClass clas = EcoreFactory.eINSTANCE.createEClass();
+ owner.getEClassifiers().add(clas);
+ clas.setName(name);
+
+ Callable<?> createAttribute = createAttribute(clas, checkCancelAttributes());
+ for(int i = 0; i < attributes; i++) {
+ // Nested command
+ execute(createAttribute);
+ }
+
+ return clas;
+ }
+
+ private String nextName() {
+ i = i + 1;
+ return "Class" + i; //$NON-NLS-1$
+ }
+
+ private int checkCancelAttributes() {
+ return (i == cancelOn) ? cancelAttributesOn : 0;
+ }
+ };
+ }
+
+ Callable<EPackage> createPackage(int classes, int attributes) {
+ return createPackage(classes, attributes, 0, 0, 0);
+ }
+
+ Callable<EPackage> createPackage(final int classes, final int attributes, final int cancelOn, final int cancelClassesOn, final int cancelAttributesOn) {
+ return new Callable<EPackage>() {
+
+ int i = 0;
+
+ public EPackage call() throws Exception {
+ String name = nextName();
+
+ EPackage pkg = EcoreFactory.eINSTANCE.createEPackage();
+ testPackage.getESubpackages().add(pkg);
+ pkg.setName(name);
+
+ Callable<?> createClass = createClass(pkg, attributes, checkCancelClasses(), cancelAttributesOn);
+ for(int i = 0; i < classes; i++) {
+ // Nested command
+ execute(createClass);
+ }
+
+ return pkg;
+ }
+
+ private String nextName() {
+ i = i + 1;
+ return "package" + i; //$NON-NLS-1$
+ }
+
+ private int checkCancelClasses() {
+ return (i == cancelOn) ? cancelClassesOn : 0;
+ }
+ };
+ }
+
+ Matcher<ENamedElement> named(final String name) {
+ return new BaseMatcher<ENamedElement>() {
+
+ public void describeTo(Description desc) {
+ desc.appendText("is named \"").appendValue(name).appendText("\""); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public boolean matches(Object o) {
+ return (o instanceof ENamedElement) && Objects.equal(((ENamedElement)o).getName(), name);
+ }
+ };
+ }
+
+ Matcher<EClass> featureCount(final int count) {
+ return new BaseMatcher<EClass>() {
+
+ public void describeTo(Description desc) {
+ desc.appendText("has ").appendValue(count).appendText(" features"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public boolean matches(Object o) {
+ return (o instanceof EClass) && (((EClass)o).getEStructuralFeatures().size() == count);
+ }
+ };
+ }
+
+ Matcher<EClass> hasFeature(final String name) {
+ return new BaseMatcher<EClass>() {
+
+ public void describeTo(Description desc) {
+ desc.appendText("has a \"").appendValue(name).appendText("\" feature"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public boolean matches(Object o) {
+ return (o instanceof EClass) && (((EClass)o).getEStructuralFeature(name) != null);
+ }
+ };
+ }
+}
diff --git a/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/tests/AllTests.java b/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/tests/AllTests.java
index 9670eb13bbc..1d389ba9ad7 100644
--- a/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/tests/AllTests.java
+++ b/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/tests/AllTests.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010, 2013 CEA LIST and others.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -9,6 +9,7 @@
* Contributors:
* Remi Schnekenburger (CEA LIST) remi.schnekenburger@cea.fr - Initial API and implementation
* Christian W. Damus (CEA LIST) - add test for AdapterUtils
+ * Christian W. Damus (CEA) - bug 402525
*****************************************************************************/
package org.eclipse.papyrus.infra.core.tests;
@@ -16,6 +17,7 @@ import org.eclipse.papyrus.infra.core.contentoutline.NestedEditorDelegatedOutlin
import org.eclipse.papyrus.infra.core.lifecycleevents.LifeCycleEventsProviderTest;
import org.eclipse.papyrus.infra.core.resource.AbstractModelWithSharedResourceTest;
import org.eclipse.papyrus.infra.core.resource.ModelSetTest;
+import org.eclipse.papyrus.infra.core.resource.NestingTransactionalCommandStackTest;
import org.eclipse.papyrus.infra.core.services.ComposedServiceTest;
import org.eclipse.papyrus.infra.core.services.ServicesRegistryTest;
import org.eclipse.papyrus.infra.core.utils.AdapterUtilsTest;
@@ -28,7 +30,7 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
// {oep.resource}
-ModelSetTest.class, AbstractModelWithSharedResourceTest.class,
+ModelSetTest.class, AbstractModelWithSharedResourceTest.class, NestingTransactionalCommandStackTest.class,
// {oep}.core.services
ComposedServiceTest.class, ServicesRegistryTest.class,
// {oep}.core.lifecycleevents
diff --git a/tests/junit/plugins/core/org.eclipse.papyrus.tests/META-INF/MANIFEST.MF b/tests/junit/plugins/core/org.eclipse.papyrus.tests/META-INF/MANIFEST.MF
index f79b02086aa..085ed5ec9df 100644
--- a/tests/junit/plugins/core/org.eclipse.papyrus.tests/META-INF/MANIFEST.MF
+++ b/tests/junit/plugins/core/org.eclipse.papyrus.tests/META-INF/MANIFEST.MF
@@ -42,7 +42,8 @@ Require-Bundle: org.eclipse.ui,
org.eclipse.papyrus.infra.nattable.model.editor.tests;bundle-version="1.0.0",
org.eclipse.papyrus.uml.diagram.sequence.tests;bundle-version="1.0.0",
org.eclipse.papyrus.uml.diagram.interactionoverview.tests;bundle-version="1.0.0",
- org.eclipse.papyrus.uml.diagram.composite.tests;bundle-version="1.0.0"
+ org.eclipse.papyrus.uml.diagram.composite.tests;bundle-version="1.0.0",
+ org.eclipse.papyrus.infra.gmfdiag.commands;bundle-version="1.0.0"
Bundle-Vendor: %providerName
Bundle-ActivationPolicy: lazy
Bundle-Version: 1.0.0.qualifier
diff --git a/tests/junit/plugins/core/org.eclipse.papyrus.tests/test/org/eclipse/papyrus/tests/AllTests.java b/tests/junit/plugins/core/org.eclipse.papyrus.tests/test/org/eclipse/papyrus/tests/AllTests.java
index baac3e43b0e..58695ed5ec1 100644
--- a/tests/junit/plugins/core/org.eclipse.papyrus.tests/test/org/eclipse/papyrus/tests/AllTests.java
+++ b/tests/junit/plugins/core/org.eclipse.papyrus.tests/test/org/eclipse/papyrus/tests/AllTests.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST 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,8 @@
*
* Contributors:
* Remi Schnekenburger (CEA LIST) remi.schnekenburger@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 402525
+ *
*****************************************************************************/
package org.eclipse.papyrus.tests;
@@ -50,6 +52,7 @@ public class AllTests {
suiteClasses.add(new FragmentTestSuiteClass(org.eclipse.papyrus.infra.emf.Activator.PLUGIN_ID, "org.eclipse.papyrus.infra.emf.utils.ServiceUtilsForResourceTest"));
suiteClasses.add(new FragmentTestSuiteClass(org.eclipse.papyrus.infra.extendedtypes.Activator.PLUGIN_ID, "org.eclipse.papyrus.infra.extendedtypes.AllTests"));
// suiteClasses.add(new PluginTestSuiteClass(org.eclipse.papyrus.infra.services.openelement.tests.AllTests.class));
+ suiteClasses.add(new FragmentTestSuiteClass(org.eclipse.papyrus.commands.Activator.PLUGIN_ID, "org.eclipse.papyrus.infra.gmfdiag.commands.tests.AllTests"));
/* views */
suiteClasses.add(new PluginTestSuiteClass(org.eclipse.papyrus.views.modelexplorer.tests.AllTests.class));
diff --git a/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/.classpath b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/.classpath
new file mode 100644
index 00000000000..64c5e31b7a2
--- /dev/null
+++ b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/.project b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/.project
new file mode 100644
index 00000000000..a7434ea3fe4
--- /dev/null
+++ b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.papyrus.infra.gmfdiag.commands.tests</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/.settings/org.eclipse.jdt.core.prefs b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000000..3eff40b8b33
--- /dev/null
+++ b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Mon Nov 08 19:25:55 CET 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/META-INF/MANIFEST.MF b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..0afee83d834
--- /dev/null
+++ b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/META-INF/MANIFEST.MF
@@ -0,0 +1,13 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-Vendor: %providerName
+Bundle-SymbolicName: org.eclipse.papyrus.infra.gmfdiag.commands.tests
+Bundle-Version: 1.0.0.qualifier
+Bundle-Localization: fragment
+Fragment-Host: org.eclipse.papyrus.infra.gmfdiag.commands;bundle-version="1.0.0"
+Require-Bundle: org.junit;bundle-version="4.10.0",
+ com.google.guava;bundle-version="11.0.0",
+ org.eclipse.papyrus.infra.emf.readonly;bundle-version="1.0.0"
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Export-Package: org.eclipse.papyrus.infra.gmfdiag.commands.tests
diff --git a/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/build.properties b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/build.properties
new file mode 100644
index 00000000000..c61f1594f78
--- /dev/null
+++ b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ fragment.properties,\
+ org.eclipse.papyrus.infra.gmfdiag.commands.tests.launch
diff --git a/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/fragment.properties b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/fragment.properties
new file mode 100644
index 00000000000..6242c967488
--- /dev/null
+++ b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/fragment.properties
@@ -0,0 +1,14 @@
+#################################################################################
+# Copyright (c) 2014 CEA 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 (CEA) - initial API and Implementation.
+#
+##################################################################################
+pluginName=Papyrus GMF Diagram Commands Infrastructure Tests (Incubation)
+providerName=Eclipse Modeling Project
diff --git a/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/org.eclipse.papyrus.infra.gmfdiag.commands.tests.launch b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/org.eclipse.papyrus.infra.gmfdiag.commands.tests.launch
new file mode 100644
index 00000000000..5afacf2f348
--- /dev/null
+++ b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/org.eclipse.papyrus.infra.gmfdiag.commands.tests.launch
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.pde.ui.JunitLaunchConfig">
+<booleanAttribute key="append.args" value="true"/>
+<booleanAttribute key="askclear" value="false"/>
+<booleanAttribute key="automaticAdd" value="true"/>
+<booleanAttribute key="automaticValidate" value="false"/>
+<stringAttribute key="bootstrap" value=""/>
+<stringAttribute key="checked" value="[NONE]"/>
+<booleanAttribute key="clearConfig" value="true"/>
+<booleanAttribute key="clearws" value="true"/>
+<booleanAttribute key="clearwslog" value="false"/>
+<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/pde-junit"/>
+<booleanAttribute key="default" value="true"/>
+<booleanAttribute key="includeOptional" value="true"/>
+<stringAttribute key="location" value="${workspace_loc}/../junit-workspace"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/org.eclipse.papyrus.infra.gmfdiag.commands.tests/src/org/eclipse/papyrus/infra/gmfdiag/commands/tests/AllTests.java"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
+<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+<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.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.papyrus.infra.gmfdiag.commands.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.gmfdiag.commands.tests"/>
+<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dosgi.requiredJavaVersion=1.5 -Xms40m -Xmx1024m -XX:PermSize=256M -XX:MaxPermSize=512M"/>
+<stringAttribute key="pde.version" value="3.3"/>
+<stringAttribute key="product" value="org.eclipse.platform.ide"/>
+<booleanAttribute key="run_in_ui_thread" value="true"/>
+<booleanAttribute key="show_selected_only" value="false"/>
+<booleanAttribute key="tracing" value="false"/>
+<booleanAttribute key="useCustomFeatures" value="false"/>
+<booleanAttribute key="useDefaultConfig" value="true"/>
+<booleanAttribute key="useDefaultConfigArea" value="false"/>
+<booleanAttribute key="useProduct" value="true"/>
+</launchConfiguration>
diff --git a/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/src/org/eclipse/papyrus/commands/Bug402525.ecore b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/src/org/eclipse/papyrus/commands/Bug402525.ecore
new file mode 100644
index 00000000000..8dea18de3e8
--- /dev/null
+++ b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/src/org/eclipse/papyrus/commands/Bug402525.ecore
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="bug402525" nsURI="http://www.eclipse.org/schema/Papyrus/tests/bug402525"
+ nsPrefix="bug">
+ <eClassifiers xsi:type="ecore:EClass" name="Foo"/>
+</ecore:EPackage>
diff --git a/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/src/org/eclipse/papyrus/commands/NestingNotifyingWorkspaceCommandStackTest.java b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/src/org/eclipse/papyrus/commands/NestingNotifyingWorkspaceCommandStackTest.java
new file mode 100644
index 00000000000..925cc1b4c0b
--- /dev/null
+++ b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/src/org/eclipse/papyrus/commands/NestingNotifyingWorkspaceCommandStackTest.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2014 CEA 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 (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.commands;
+
+import static org.hamcrest.CoreMatchers.both;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.net.URL;
+import java.util.concurrent.Callable;
+
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.common.command.CommandStack;
+import org.eclipse.emf.common.notify.AdapterFactory;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EAttribute;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.ENamedElement;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EcoreFactory;
+import org.eclipse.emf.ecore.EcorePackage;
+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.edit.provider.ComposedAdapterFactory;
+import org.eclipse.emf.transaction.RecordingCommand;
+import org.eclipse.emf.transaction.TransactionalCommandStack;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.papyrus.infra.emf.readonly.PapyrusROTransactionalEditingDomain;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.base.Objects;
+
+
+/**
+ * Test suite for the {@link NestingNotifyingWorkspaceCommandStack} class.
+ */
+public class NestingNotifyingWorkspaceCommandStackTest {
+
+ // No API signatures but the most basic are required for nesting
+ private CommandStack fixture;
+
+ private TransactionalEditingDomain domain;
+
+ private ResourceSet rset;
+
+ private EPackage testPackage;
+
+ private EClass foo;
+
+ public NestingNotifyingWorkspaceCommandStackTest() {
+ super();
+ }
+
+ //
+ // Test cases
+ //
+
+ @Test
+ public void testCompleteUnnestedCommand() {
+ Callable<EAttribute> createAttribute = createAttribute();
+
+ int oldFeatureCount = foo.getEStructuralFeatures().size();
+
+ // Create two attributes
+ EAttribute attr1 = execute(createAttribute);
+ EAttribute attr2 = execute(createAttribute);
+ assertThat(foo.getEStructuralFeatures().size(), is(oldFeatureCount + 2));
+ assertThat(foo.getEStructuralFeatures(), hasItem(attr1));
+ assertThat(foo.getEStructuralFeatures(), hasItem(attr2));
+
+ // Undo only undoes one of them
+ Command undone = undo();
+ assertThat(foo.getEStructuralFeatures().size(), is(oldFeatureCount + 1));
+ assertThat(foo.getEStructuralFeatures(), hasItem(attr1));
+ assertThat(foo.getEStructuralFeatures(), not(hasItem(attr2)));
+
+ // Redo is sane
+ Command redone = redo();
+ assertThat(redone, sameInstance(undone));
+ assertThat(foo.getEStructuralFeatures().size(), is(oldFeatureCount + 2));
+ assertThat(foo.getEStructuralFeatures(), hasItem(attr1));
+ assertThat(foo.getEStructuralFeatures(), hasItem(attr2));
+ }
+
+ @Test
+ public void testCancelUnnestedCommand() {
+ Callable<EAttribute> createAttribute = createAttribute(2);
+
+ int oldFeatureCount = foo.getEStructuralFeatures().size();
+
+ // Try to create two attributes (second one cancels)
+ EAttribute attr1 = execute(createAttribute);
+ EAttribute attr2 = execute(createAttribute);
+ assertThat(attr2, nullValue());
+ assertThat(foo.getEStructuralFeatures().size(), is(oldFeatureCount + 1));
+ assertThat(foo.getEStructuralFeatures(), hasItem(attr1));
+ assertThat(foo.getEStructuralFeature("attribute2"), nullValue()); //$NON-NLS-1$
+
+ // Undo is sane
+ Command undone = undo();
+ assertThat(foo.getEStructuralFeatures().size(), is(oldFeatureCount));
+ assertThat(foo.getEStructuralFeatures(), not(hasItem(attr1)));
+ assertThat(foo.getEStructuralFeature("attribute2"), nullValue()); //$NON-NLS-1$
+
+ // Redo is sane
+ Command redone = redo();
+ assertThat(redone, sameInstance(undone));
+ assertThat(foo.getEStructuralFeatures().size(), is(oldFeatureCount + 1));
+ assertThat(foo.getEStructuralFeatures(), hasItem(attr1));
+ assertThat(foo.getEStructuralFeature("attribute2"), nullValue()); //$NON-NLS-1$
+ }
+
+ @Test
+ public void testCompleteNestedCommand() {
+ final int FEATURES = 2;
+ Callable<EClass> createClass = createClass(FEATURES);
+
+ int oldClassifierCount = testPackage.getEClassifiers().size();
+
+ EClass clas = execute(createClass);
+ assertThat(testPackage.getEClassifiers().size(), is(oldClassifierCount + 1));
+ assertThat(testPackage.getEClassifiers(), hasItem(clas));
+ assertThat(clas.getEStructuralFeatures().size(), is(FEATURES));
+ assertThat(clas.getEStructuralFeature("attribute1"), notNullValue()); //$NON-NLS-1$
+ assertThat(clas.getEStructuralFeature("attribute2"), notNullValue()); //$NON-NLS-1$
+
+ // Undo is sane and there is only one command to undo
+ Command undone = undo();
+ assertThat(fixture.canUndo(), is(false));
+ assertThat(testPackage.getEClassifiers().size(), is(oldClassifierCount));
+ assertThat(testPackage.getEClassifiers(), not(hasItem(clas)));
+
+ // Redo is sane
+ Command redone = redo();
+ assertThat(redone, sameInstance(undone));
+ assertThat(testPackage.getEClassifiers().size(), is(oldClassifierCount + 1));
+ assertThat(testPackage.getEClassifiers(), hasItem(clas));
+ assertThat(clas.getEStructuralFeatures().size(), is(FEATURES));
+ assertThat(clas.getEStructuralFeature("attribute1"), notNullValue()); //$NON-NLS-1$
+ assertThat(clas.getEStructuralFeature("attribute2"), notNullValue()); //$NON-NLS-1$
+ }
+
+ @Test
+ public void testCancelNestedCommand() {
+ final int FEATURES = 2;
+ Callable<EClass> createClass = createClass(FEATURES, 2, FEATURES);
+
+ int oldClassifierCount = testPackage.getEClassifiers().size();
+
+ // Try to create two classes (second one cancels)
+ EClass class1 = execute(createClass);
+ EClass class2 = execute(createClass);
+ assertThat(class2, nullValue());
+ assertThat(testPackage.getEClassifiers().size(), is(oldClassifierCount + 1));
+ assertThat(testPackage.getEClassifiers(), hasItem(class1));
+ assertThat(class1.getEStructuralFeatures().size(), is(FEATURES));
+ assertThat(class1.getEStructuralFeature("attribute1"), notNullValue()); //$NON-NLS-1$
+ assertThat(class1.getEStructuralFeature("attribute2"), notNullValue()); //$NON-NLS-1$
+
+ // Undo is sane and there is only one command to undo
+ Command undone = undo();
+ assertThat(fixture.canUndo(), is(false));
+ assertThat(testPackage.getEClassifiers().size(), is(oldClassifierCount));
+ assertThat(testPackage.getEClassifiers(), not(hasItem(class1)));
+
+ // Redo is sane
+ Command redone = redo();
+ assertThat(redone, sameInstance(undone));
+ assertThat(testPackage.getEClassifiers().size(), is(oldClassifierCount + 1));
+ assertThat(testPackage.getEClassifiers(), hasItem(class1));
+ assertThat(class1.getEStructuralFeatures().size(), is(FEATURES));
+ assertThat(class1.getEStructuralFeature("attribute1"), notNullValue()); //$NON-NLS-1$
+ assertThat(class1.getEStructuralFeature("attribute2"), notNullValue()); //$NON-NLS-1$
+ }
+
+ @Test
+ public void testCompleteTripleNestedCommand() {
+ final int CLASSES = 2;
+ final int FEATURES = 2;
+
+ Callable<EPackage> createPackage = createPackage(CLASSES, FEATURES);
+
+ int oldPackageCount = testPackage.getESubpackages().size();
+
+ EPackage pkg = execute(createPackage);
+ assertThat(testPackage.getESubpackages().size(), is(oldPackageCount + 1));
+ assertThat(testPackage.getESubpackages(), hasItem(pkg));
+ assertThat(pkg.getEClassifiers().size(), is(CLASSES));
+ assertThat(pkg.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class1")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ assertThat(pkg.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class2")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ // Undo is sane and there is only one command to undo
+ Command undone = undo();
+ assertThat(fixture.canUndo(), is(false));
+ assertThat(testPackage.getESubpackages().size(), is(oldPackageCount));
+ assertThat(testPackage.getESubpackages(), not(hasItem(pkg)));
+ assertThat(pkg.getName(), nullValue());
+ assertThat(pkg.getEClassifiers().size(), is(0));
+ assertThat(pkg.getEClassifiers().size(), is(0));
+
+ // Redo is sane
+ Command redone = redo();
+ assertThat(redone, sameInstance(undone));
+ assertThat(fixture.canRedo(), is(false));
+ assertThat(testPackage.getESubpackages().size(), is(oldPackageCount + 1));
+ assertThat(testPackage.getESubpackages(), hasItem(pkg));
+ assertThat(pkg, named("package1")); //$NON-NLS-1$
+ assertThat(pkg.getEClassifiers().size(), is(CLASSES));
+ assertThat(pkg.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class1")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ assertThat(pkg.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class2")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @Test
+ public void testCancelTripleNestedCommand() {
+ final int CLASSES = 2;
+ final int FEATURES = 2;
+
+ Callable<EPackage> createPackage = createPackage(CLASSES, FEATURES, 2, CLASSES, FEATURES);
+
+ int oldPackageCount = testPackage.getESubpackages().size();
+
+ // Try to create two packages (second one cancels)
+ EPackage pkg1 = execute(createPackage);
+ EPackage pkg2 = execute(createPackage);
+
+ // the first package is complete
+ assertThat(testPackage.getESubpackages().size(), is(oldPackageCount + 1));
+ assertThat(testPackage.getESubpackages(), hasItem(pkg1));
+ assertThat(pkg1, named("package1")); //$NON-NLS-1$
+ assertThat(pkg1.getEClassifiers().size(), is(CLASSES));
+ assertThat(pkg1.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class1")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ assertThat(pkg1.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class2")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ // there is no second package
+ assertThat(pkg2, nullValue());
+
+ // Undo is sane and there is only one command to undo
+ Command undone = undo();
+ assertThat(fixture.canUndo(), is(false));
+ assertThat(testPackage.getESubpackages().size(), is(oldPackageCount));
+ assertThat(testPackage.getESubpackages(), not(hasItem(pkg1)));
+ assertThat(pkg1.getName(), nullValue());
+ assertThat(pkg1.getEClassifiers().size(), is(0));
+
+ // Redo is sane
+ Command redone = redo();
+ assertThat(fixture.canRedo(), is(false));
+ assertThat(redone, sameInstance(undone));
+ assertThat(testPackage.getESubpackages().size(), is(oldPackageCount + 1));
+ assertThat(testPackage.getESubpackages(), hasItem(pkg1));
+ assertThat(pkg1, named("package1")); //$NON-NLS-1$
+ assertThat(pkg1.getEClassifiers().size(), is(CLASSES));
+ assertThat(pkg1.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class1")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ assertThat(pkg1.getEClassifiers(), hasItem(both(featureCount(FEATURES)).and(named("Class2")).and(hasFeature("attribute1")).and(hasFeature("attribute2")))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ //
+ // Test framework
+ //
+
+ @Before
+ public void createFixture() {
+ rset = new ResourceSetImpl();
+ fixture = new NestingNotifyingWorkspaceCommandStack(CheckedOperationHistory.getInstance());
+ AdapterFactory adapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
+ domain = new PapyrusROTransactionalEditingDomain(adapterFactory, (TransactionalCommandStack)fixture, rset);
+
+ URL testModelURL = getClass().getResource("bug402525.ecore"); //$NON-NLS-1$
+ Resource testModel = rset.getResource(URI.createURI(testModelURL.toExternalForm(), true), true);
+ testPackage = (EPackage)testModel.getContents().get(0);
+ foo = (EClass)testPackage.getEClassifier("Foo"); //$NON-NLS-1$
+ }
+
+ @After
+ public void destroyFixture() {
+ // This disposes the command stack for us
+ domain.dispose();
+ domain = null;
+ fixture = null;
+
+ dispose(rset);
+ rset = null;
+ }
+
+ void dispose(ResourceSet rset) {
+ for(Resource next : rset.getResources()) {
+ next.unload();
+ next.eAdapters().clear();
+ }
+
+ rset.getResources().clear();
+ rset.eAdapters().clear();
+ }
+
+ <V> V execute(final Callable<V> operation) {
+ class TestCommand extends RecordingCommand {
+
+ V result;
+
+ TestCommand() {
+ super(domain);
+ }
+
+ @Override
+ protected void doExecute() {
+ try {
+ result = operation.call();
+ } catch (OperationCanceledException e) {
+ // Pass it on
+ throw e;
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Uncaught exception in operation: " + e.getLocalizedMessage()); //$NON-NLS-1$
+ }
+ }
+ }
+
+ TestCommand command = new TestCommand();
+ fixture.execute(command);
+ return command.result;
+ }
+
+ Command undo() {
+ assertThat("Cannot undo", fixture.canUndo()); //$NON-NLS-1$
+ Command result = fixture.getUndoCommand();
+ fixture.undo();
+ return result;
+ }
+
+ Command redo() {
+ assertThat("Cannot redo", fixture.canRedo()); //$NON-NLS-1$
+ Command result = fixture.getRedoCommand();
+ fixture.redo();
+ return result;
+ }
+
+ Callable<EAttribute> createAttribute() {
+ return createAttribute(0);
+ }
+
+ Callable<EAttribute> createAttribute(int cancelOn) {
+ return createAttribute(foo, cancelOn);
+ }
+
+ Callable<EAttribute> createAttribute(final EClass owner, final int cancelOn) {
+ return new Callable<EAttribute>() {
+
+ int i = 0;
+
+ public EAttribute call() throws Exception {
+ String name = nextName();
+
+ EAttribute attr = EcoreFactory.eINSTANCE.createEAttribute();
+ owner.getEStructuralFeatures().add(attr);
+ attr.setName(name);
+ attr.setEType(EcorePackage.Literals.ESTRING);
+
+ checkCancel();
+
+ return attr;
+ }
+
+ private String nextName() {
+ i = i + 1;
+ return "attribute" + i; //$NON-NLS-1$
+ }
+
+ private void checkCancel() {
+ if(i == cancelOn) {
+ throw new OperationCanceledException();
+ }
+ }
+ };
+ }
+
+ Callable<EClass> createClass(int attributes) {
+ return createClass(attributes, 0, 0);
+ }
+
+ Callable<EClass> createClass(int attributes, int cancelOn, int cancelAttributesOn) {
+ return createClass(testPackage, attributes, cancelOn, cancelAttributesOn);
+ }
+
+ Callable<EClass> createClass(final EPackage owner, final int attributes, final int cancelOn, final int cancelAttributesOn) {
+ return new Callable<EClass>() {
+
+ int i = 0;
+
+ public EClass call() throws Exception {
+ String name = nextName();
+
+ EClass clas = EcoreFactory.eINSTANCE.createEClass();
+ owner.getEClassifiers().add(clas);
+ clas.setName(name);
+
+ Callable<?> createAttribute = createAttribute(clas, checkCancelAttributes());
+ for(int i = 0; i < attributes; i++) {
+ // Nested command
+ execute(createAttribute);
+ }
+
+ return clas;
+ }
+
+ private String nextName() {
+ i = i + 1;
+ return "Class" + i; //$NON-NLS-1$
+ }
+
+ private int checkCancelAttributes() {
+ return (i == cancelOn) ? cancelAttributesOn : 0;
+ }
+ };
+ }
+
+ Callable<EPackage> createPackage(int classes, int attributes) {
+ return createPackage(classes, attributes, 0, 0, 0);
+ }
+
+ Callable<EPackage> createPackage(final int classes, final int attributes, final int cancelOn, final int cancelClassesOn, final int cancelAttributesOn) {
+ return new Callable<EPackage>() {
+
+ int i = 0;
+
+ public EPackage call() throws Exception {
+ String name = nextName();
+
+ EPackage pkg = EcoreFactory.eINSTANCE.createEPackage();
+ testPackage.getESubpackages().add(pkg);
+ pkg.setName(name);
+
+ Callable<?> createClass = createClass(pkg, attributes, checkCancelClasses(), cancelAttributesOn);
+ for(int i = 0; i < classes; i++) {
+ // Nested command
+ execute(createClass);
+ }
+
+ return pkg;
+ }
+
+ private String nextName() {
+ i = i + 1;
+ return "package" + i; //$NON-NLS-1$
+ }
+
+ private int checkCancelClasses() {
+ return (i == cancelOn) ? cancelClassesOn : 0;
+ }
+ };
+ }
+
+ Matcher<ENamedElement> named(final String name) {
+ return new BaseMatcher<ENamedElement>() {
+
+ public void describeTo(Description desc) {
+ desc.appendText("is named \"").appendValue(name).appendText("\""); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public boolean matches(Object o) {
+ return (o instanceof ENamedElement) && Objects.equal(((ENamedElement)o).getName(), name);
+ }
+ };
+ }
+
+ Matcher<EClass> featureCount(final int count) {
+ return new BaseMatcher<EClass>() {
+
+ public void describeTo(Description desc) {
+ desc.appendText("has ").appendValue(count).appendText(" features"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public boolean matches(Object o) {
+ return (o instanceof EClass) && (((EClass)o).getEStructuralFeatures().size() == count);
+ }
+ };
+ }
+
+ Matcher<EClass> hasFeature(final String name) {
+ return new BaseMatcher<EClass>() {
+
+ public void describeTo(Description desc) {
+ desc.appendText("has a \"").appendValue(name).appendText("\" feature"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public boolean matches(Object o) {
+ return (o instanceof EClass) && (((EClass)o).getEStructuralFeature(name) != null);
+ }
+ };
+ }
+}
diff --git a/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/src/org/eclipse/papyrus/infra/gmfdiag/commands/tests/AllTests.java b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/src/org/eclipse/papyrus/infra/gmfdiag/commands/tests/AllTests.java
new file mode 100644
index 00000000000..ecce7998eb4
--- /dev/null
+++ b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.commands.tests/src/org/eclipse/papyrus/infra/gmfdiag/commands/tests/AllTests.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2014 CEA LIST 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 (CEA) - initial API and implementation
+ */
+package org.eclipse.papyrus.infra.gmfdiag.commands.tests;
+
+import org.eclipse.papyrus.commands.NestingNotifyingWorkspaceCommandStackTest;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+
+/**
+ * Master test suite for this test fragment.
+ */
+@RunWith(Suite.class)
+@SuiteClasses({
+// {oep.commands}
+NestingNotifyingWorkspaceCommandStackTest.class })
+public class AllTests {
+
+}

Back to the top