/** * <copyright> Copyright (c) 2008-2009 Jonas Helming, Maximilian Koegel. 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> */ package org.eclipse.emf.emfstore.client.test.changeTracking.canonization; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.util.List; import org.eclipse.emf.emfstore.client.model.CompositeOperationHandle; import org.eclipse.emf.emfstore.client.model.exceptions.InvalidHandleException; import org.eclipse.emf.emfstore.client.model.util.EMFStoreCommand; import org.eclipse.emf.emfstore.client.test.WorkspaceTest; import org.eclipse.emf.emfstore.client.test.model.UnicaseModelElement; import org.eclipse.emf.emfstore.client.test.model.document.DocumentFactory; import org.eclipse.emf.emfstore.client.test.model.document.LeafSection; import org.eclipse.emf.emfstore.client.test.model.requirement.Actor; import org.eclipse.emf.emfstore.client.test.model.requirement.RequirementFactory; import org.eclipse.emf.emfstore.client.test.model.requirement.UseCase; import org.eclipse.emf.emfstore.common.model.ModelFactory; import org.eclipse.emf.emfstore.common.model.Project; import org.eclipse.emf.emfstore.common.model.util.ModelUtil; import org.eclipse.emf.emfstore.server.model.versioning.operations.AbstractOperation; import org.eclipse.emf.emfstore.server.model.versioning.operations.AttributeOperation; import org.eclipse.emf.emfstore.server.model.versioning.operations.CompositeOperation; import org.eclipse.emf.emfstore.server.model.versioning.operations.CreateDeleteOperation; import org.eclipse.emf.emfstore.server.model.versioning.operations.MultiReferenceOperation; import org.eclipse.emf.emfstore.server.model.versioning.operations.util.OperationsCanonizer; import org.junit.Test; /** * Tests canonization of attribute operations. * * @author chodnick */ public class AttributeTest extends WorkspaceTest { /** * Tests canonization for consecutive attribute changes on a single feature. * * @throws IOException */ @Test public void consecutiveAttributeChangeSingleFeature() throws IOException { final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(useCase); useCase.setName("oldName"); } }.run(false); Project expectedProject = ModelUtil.clone(getProject()); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); new EMFStoreCommand() { @Override protected void doRun() { clearOperations(); useCase.setName("A"); useCase.setName("B"); useCase.setName("C"); useCase.setName("newName"); } }.run(false); assertEquals("newName", useCase.getName()); assertEquals(4, getProjectSpace().getOperations().size()); final List<AbstractOperation> operations = getProjectSpace().getOperations(); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); assertEquals(operations.size(), 1); final AttributeOperation reverse = (AttributeOperation) operations.get(0).reverse(); new EMFStoreCommand() { @Override protected void doRun() { reverse.apply(getProject()); } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Tests canonization for consecutive attribute changes on a single feature. * * @throws IOException */ @Test public void consecutiveAttributeChangeSingleFeatureToNull() throws IOException { final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(useCase); useCase.setName("oldName"); } }.run(false); Project expectedProject = ModelUtil.clone(getProject()); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); new EMFStoreCommand() { @Override protected void doRun() { clearOperations(); useCase.setName("A"); useCase.setName("B"); useCase.setName("C"); useCase.setName(null); } }.run(false); assertEquals(null, useCase.getName()); final List<AbstractOperation> operations = getProjectSpace().getOperations(); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); assertEquals(operations.size(), 1); final AttributeOperation reverse = (AttributeOperation) operations.get(0).reverse(); new EMFStoreCommand() { @Override protected void doRun() { reverse.apply(getProject()); } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject().eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Tests canonization for consecutive attribute changes on a single feature. * * @throws IOException */ @Test public void consecutiveAttributeChangeSingleFeatureNullToValue() throws IOException { final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(useCase); useCase.setName(null); } }.run(false); Project expectedProject = ModelUtil.clone(getProject()); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); new EMFStoreCommand() { @Override protected void doRun() { clearOperations(); useCase.setName("A"); useCase.setName("B"); useCase.setName("C"); } }.run(false); assertEquals("C", useCase.getName()); final List<AbstractOperation> operations = getProjectSpace().getOperations(); assertEquals(3, operations.size()); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); assertEquals(operations.size(), 1); final AttributeOperation reverse = (AttributeOperation) operations.get(0).reverse(); new EMFStoreCommand() { @Override protected void doRun() { reverse.apply(getProject()); } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Tests canonization for consecutive attribute changes, resulting in a noop. * * @throws IOException */ @Test public void attributeChangeNoOp() throws IOException { final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(useCase); useCase.setName("oldName"); } }.run(false); Project expectedProject = ModelUtil.clone(getProject()); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); new EMFStoreCommand() { @Override protected void doRun() { clearOperations(); useCase.setName("A"); useCase.setName("B"); useCase.setName("C"); useCase.setName("oldName"); } }.run(false); assertEquals("oldName", useCase.getName()); final List<AbstractOperation> operations = getProjectSpace().getOperations(); assertEquals(4, operations.size()); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); // should not have created any operations, we were just resetting the name to its original value assertEquals(operations.size(), 0); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Tests canonization for consecutive attribute changes, resulting in a noop. * * @throws IOException */ @Test public void attributeChangeNoOpNull() throws IOException { final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(useCase); useCase.setName(null); } }.run(false); Project expectedProject = ModelUtil.clone(getProject()); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); new EMFStoreCommand() { @Override protected void doRun() { clearOperations(); useCase.setName("A"); useCase.setName("B"); useCase.setName("C"); useCase.setName(null); } }.run(false); assertEquals(null, useCase.getName()); final List<AbstractOperation> operations = getProjectSpace().getOperations(); assertEquals(4, operations.size()); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); // should not have created any operations, we were just resetting the name to its original value assertEquals(operations.size(), 0); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Tests canonization for consecutive attribute changes, resulting in a noop. * * @throws IOException */ @Test public void attributeChangeMultiFeatureNoOp() throws IOException { final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(useCase); useCase.setName("oldName"); useCase.setDescription("oldDescription"); } }.run(false); Project expectedProject = ModelUtil.clone(getProject()); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); new EMFStoreCommand() { @Override protected void doRun() { clearOperations(); useCase.setName("A"); useCase.setDescription("X"); useCase.setName("B"); useCase.setDescription("Y"); useCase.setName("C"); useCase.setDescription("oldDescription"); useCase.setName("oldName"); } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); final List<AbstractOperation> operations = getProjectSpace().getOperations(); assertEquals(7, operations.size()); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); // should not have created any operations, we were just resetting everything to its original value assertEquals(operations.size(), 0); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Tests canonization for consecutive attribute changes on multiple features. * * @throws IOException */ @Test public void consecutiveAttributeChangeMultiFeature() throws IOException { final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(useCase); useCase.setName("oldName"); } }.run(false); Project expectedProject = ModelUtil.clone(getProject()); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); new EMFStoreCommand() { @Override protected void doRun() { clearOperations(); useCase.setName("A"); useCase.setDescription("oldDescription"); useCase.setName("B"); useCase.setName("C"); useCase.setDescription("newDescription"); useCase.setName("newName"); } }.run(false); assertEquals("newName", useCase.getName()); final List<AbstractOperation> operations = getProjectSpace().getOperations(); assertEquals(6, operations.size()); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); assertEquals(operations.size(), 2); new EMFStoreCommand() { @Override protected void doRun() { for (int i = operations.size() - 1; i >= 0; i--) { AbstractOperation reverse = operations.get(i).reverse(); reverse.apply(getProject()); } } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Tests canonization for mixed attribute changes on a single feature. * * @throws IOException */ @Test public void mixedAttributeChangeSingleFeature() throws IOException { final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); final Actor actor = RequirementFactory.eINSTANCE.createActor(); final LeafSection section = DocumentFactory.eINSTANCE.createLeafSection(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(useCase); getProject().addModelElement(actor); getProject().addModelElement(section); useCase.setName("oldName"); section.setName("some section"); actor.setName("homer"); } }.run(false); Project expectedProject = ModelUtil.clone(getProject()); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); new EMFStoreCommand() { @Override protected void doRun() { clearOperations(); useCase.setName("A"); actor.setName("maggie"); useCase.setName("B"); useCase.setInitiatingActor(actor); useCase.setName("C"); section.setName("home"); useCase.setName("newName"); } }.run(false); final List<AbstractOperation> operations = getProjectSpace().getOperations(); assertEquals(7, operations.size()); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); new EMFStoreCommand() { @Override protected void doRun() { for (int i = operations.size() - 1; i >= 0; i--) { AbstractOperation reverse = operations.get(i).reverse(); reverse.apply(getProject()); } } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Tests canonization for mixed attribute changes on a single feature. * * @throws IOException */ @Test public void mixedAttributeChangeMultiFeature() throws IOException { final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); final Actor actor = RequirementFactory.eINSTANCE.createActor(); final LeafSection section = DocumentFactory.eINSTANCE.createLeafSection(); final LeafSection oldSection = DocumentFactory.eINSTANCE.createLeafSection(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(useCase); getProject().addModelElement(actor); getProject().addModelElement(section); getProject().addModelElement(oldSection); useCase.setLeafSection(oldSection); actor.setLeafSection(oldSection); useCase.setName("oldName"); oldSection.setName("oldSection"); section.setName("some section"); actor.setName("homer"); } }.run(false); Project expectedProject = ModelUtil.clone(getProject()); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); new EMFStoreCommand() { @Override protected void doRun() { clearOperations(); useCase.setName("A"); actor.setName("maggie"); useCase.setName("B"); useCase.setDescription("some desc"); useCase.setInitiatingActor(actor); useCase.setName("C"); section.setName("home"); useCase.setDescription("some other desc"); useCase.setName("newName"); useCase.setDescription("final desc"); } }.run(false); final List<AbstractOperation> operations = getProjectSpace().getOperations(); assertEquals("newName", useCase.getName()); assertEquals("final desc", useCase.getDescription()); assertEquals("home", section.getName()); assertEquals("maggie", actor.getName()); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); new EMFStoreCommand() { @Override protected void doRun() { for (int i = operations.size() - 1; i >= 0; i--) { AbstractOperation reverse = operations.get(i).reverse(); reverse.apply(getProject()); } } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Test the creation and completion of a composite operation, that contains attribute changes. * * @throws InvalidHandleException if the test fails * @throws IOException */ @Test public void compositeAttributeChangesACA() throws InvalidHandleException, IOException { final LeafSection section = DocumentFactory.eINSTANCE.createLeafSection(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(section); section.setName("Name"); section.setDescription("oldDescription"); } }.run(false); Project expectedProject = ModelUtil.clone(getProject()); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); new EMFStoreCommand() { @Override protected void doRun() { clearOperations(); section.setDescription("desc 1"); CompositeOperationHandle handle = getProjectSpace().beginCompositeOperation(); section.setDescription("newDescription"); UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); section.getModelElements().add(useCase); try { handle.end("sectionCreation", "description", ModelUtil.getProject(section).getModelElementId(section)); } catch (InvalidHandleException e) { fail(); } section.setDescription("desc 2"); } }.run(false); final List<AbstractOperation> operations = getProjectSpace().getOperations(); assertEquals("desc 2", section.getDescription()); assertEquals(3, operations.size()); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); new EMFStoreCommand() { @Override protected void doRun() { for (int i = operations.size() - 1; i >= 0; i--) { AbstractOperation reverse = operations.get(i).reverse(); reverse.apply(getProject()); } } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Test the creation and completion of a composite operation, that contains attribute changes. * * @throws InvalidHandleException if the test fails * @throws IOException */ @Test public void compositeAttributeChangesAC() throws InvalidHandleException, IOException { final LeafSection section = DocumentFactory.eINSTANCE.createLeafSection(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(section); section.setName("Name"); section.setDescription("oldDescription"); } }.run(false); Project expectedProject = ModelUtil.clone(getProject()); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); new EMFStoreCommand() { @Override protected void doRun() { clearOperations(); section.setDescription("desc 1"); CompositeOperationHandle handle = getProjectSpace().beginCompositeOperation(); section.setDescription("newDescription"); UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); section.getModelElements().add(useCase); try { handle.end("sectionCreation", "description", ModelUtil.getProject(section).getModelElementId(section)); } catch (InvalidHandleException e) { fail(); } } }.run(false); final List<AbstractOperation> operations = getProjectSpace().getOperations(); assertEquals(2, operations.size()); assertEquals("newDescription", section.getDescription()); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); new EMFStoreCommand() { @Override protected void doRun() { for (int i = operations.size() - 1; i >= 0; i--) { AbstractOperation reverse = operations.get(i).reverse(); reverse.apply(getProject()); } } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Test the creation and completion of a composite operation, that contains attribute changes. * * @throws InvalidHandleException if the test fails * @throws IOException */ @Test public void compositeAttributeChangesCA() throws InvalidHandleException, IOException { final LeafSection section = DocumentFactory.eINSTANCE.createLeafSection(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(section); section.setName("Name"); section.setDescription("oldDescription"); } }.run(false); Project expectedProject = ModelUtil.clone(getProject()); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); new EMFStoreCommand() { @Override protected void doRun() { clearOperations(); CompositeOperationHandle handle = getProjectSpace().beginCompositeOperation(); section.setDescription("newDescription"); UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); section.getModelElements().add(useCase); try { handle.end("sectionCreation", "description", ModelUtil.getProject(section).getModelElementId(section)); } catch (InvalidHandleException e) { fail(); } section.setDescription("desc 2"); } }.run(false); final List<AbstractOperation> operations = getProjectSpace().getOperations(); assertEquals(2, operations.size()); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); new EMFStoreCommand() { @Override protected void doRun() { for (int i = operations.size() - 1; i >= 0; i--) { AbstractOperation reverse = operations.get(i).reverse(); reverse.apply(getProject()); } } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Tests canonization for create and consecutive attribute changes. * * @throws IOException */ @Test public void createAndChangeAttributesSimple() throws IOException { Project originalProject = ModelUtil.clone(getProject()); final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(useCase); useCase.setName("NameOfUseCase"); useCase.setDescription("DescriptionOfUseCase"); } }.run(false); assertEquals("NameOfUseCase", useCase.getName()); final List<AbstractOperation> operations = getProjectSpace().getOperations(); // expecting a create and two attribute operations assertEquals(3, operations.size()); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); // now expecting only the create with folded in attributes assertEquals(operations.size(), 1); assertTrue(operations.get(0) instanceof CreateDeleteOperation); final CreateDeleteOperation op = (CreateDeleteOperation) operations.get(0); assertEquals(((UnicaseModelElement) op.getModelElement()).getName(), "NameOfUseCase"); assertEquals(((UnicaseModelElement) op.getModelElement()).getDescription(), "DescriptionOfUseCase"); // test if the create is reversible and re-reversible Project expectedProject = ModelUtil.clone(getProject()); new EMFStoreCommand() { @Override protected void doRun() { op.reverse().apply(getProject()); } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), originalProject)); new EMFStoreCommand() { @Override protected void doRun() { op.reverse().reverse().apply(getProject()); } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Tests canonization for create and consecutive attribute changes. * * @throws IOException */ @Test public void createAndChangeAttributesComplex() throws IOException { Project originalProject = ModelUtil.clone(getProject()); final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); final UseCase useCase2 = RequirementFactory.eINSTANCE.createUseCase(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(useCase); getProject().addModelElement(useCase2); useCase.setName("NameOfUseCase"); useCase.setDescription("DescriptionOfUseCase"); useCase2.setName("NameOfUseCase2"); useCase2.setDescription("DescriptionOfUseCase2"); } }.run(false); assertEquals("NameOfUseCase", useCase.getName()); assertEquals("NameOfUseCase2", useCase2.getName()); final List<AbstractOperation> operations = getProjectSpace().getOperations(); // expecting a create and two attribute operations per usecase assertEquals(6, operations.size()); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); // now expecting only the creates with folded in attributes assertEquals(2, operations.size()); assertTrue(operations.get(0) instanceof CreateDeleteOperation); final CreateDeleteOperation op = (CreateDeleteOperation) operations.get(0); assertEquals(((UnicaseModelElement) op.getModelElement()).getName(), "NameOfUseCase"); assertEquals(((UnicaseModelElement) op.getModelElement()).getDescription(), "DescriptionOfUseCase"); assertTrue(operations.get(1) instanceof CreateDeleteOperation); final CreateDeleteOperation op2 = (CreateDeleteOperation) operations.get(1); assertEquals(((UnicaseModelElement) op2.getModelElement()).getName(), "NameOfUseCase2"); assertEquals(((UnicaseModelElement) op2.getModelElement()).getDescription(), "DescriptionOfUseCase2"); // test reversibility, too new EMFStoreCommand() { @Override protected void doRun() { op2.reverse().apply(getProject()); op.reverse().apply(getProject()); } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), originalProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, originalProject)); } /** * Test the creation and completion of a composite operation, that contains attribute changes. * * @throws InvalidHandleException if the test fails * @throws IOException */ @Test public void createAndAttributeChangesACA() throws InvalidHandleException, IOException { Project originalProject = ModelUtil.clone(getProject()); final LeafSection section = DocumentFactory.eINSTANCE.createLeafSection(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(section); section.setName("Name"); section.setDescription("oldDescription"); CompositeOperationHandle handle = getProjectSpace().beginCompositeOperation(); section.setDescription("newDescription"); UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); section.getModelElements().add(useCase); try { handle.end("sectionCreation", "description", ModelUtil.getProject(section).getModelElementId(section)); } catch (InvalidHandleException e) { fail(); } section.setDescription("desc 2"); } }.run(false); final List<AbstractOperation> operations = getProjectSpace().getOperations(); // expect create, 2 attribute ops, the composite, 1 attribute op assertEquals(5, operations.size()); assertTrue(operations.get(0) instanceof CreateDeleteOperation); assertTrue(operations.get(1) instanceof AttributeOperation); assertTrue(operations.get(2) instanceof AttributeOperation); assertTrue(operations.get(3) instanceof CompositeOperation); assertTrue(operations.get(4) instanceof AttributeOperation); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); // expect create, the composite and 1 attribute op assertEquals(3, operations.size()); assertTrue(operations.get(0) instanceof CreateDeleteOperation); assertTrue(operations.get(1) instanceof CompositeOperation); assertTrue(operations.get(2) instanceof AttributeOperation); Project expectedProject = ModelUtil.clone(getProject()); // test reversibility new EMFStoreCommand() { @Override protected void doRun() { for (int i = operations.size() - 1; i >= 0; i--) { AbstractOperation reverse = operations.get(i).reverse(); reverse.apply(getProject()); } } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), originalProject)); // test redo new EMFStoreCommand() { @Override protected void doRun() { operations.get(0).apply(getProject()); operations.get(1).apply(getProject()); operations.get(2).apply(getProject()); } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject().eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Tests canonization for consecutive attribute changes followed by a delete. * * @throws IOException */ @Test public void changeAttributesAndDeleteSimple() throws IOException { final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(useCase); useCase.setName("originalName"); useCase.setDescription("originalDescription"); clearOperations(); } }.run(false); Project originalProject = ModelUtil.clone(getProject()); new EMFStoreCommand() { @Override protected void doRun() { useCase.setName("NameOfUseCase"); useCase.setDescription("DescriptionOfUseCase"); getProject().deleteModelElement(useCase); } }.run(false); final List<AbstractOperation> operations = getProjectSpace().getOperations(); // expecting two attribute operations and a delete assertEquals(3, operations.size()); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); // now expecting only the delete with folded in attributes assertEquals(1, operations.size()); assertTrue(operations.get(0) instanceof CreateDeleteOperation); final CreateDeleteOperation op = (CreateDeleteOperation) operations.get(0); assertTrue(op.isDelete()); assertEquals(((UnicaseModelElement) op.getModelElement()).getName(), "originalName"); assertEquals(((UnicaseModelElement) op.getModelElement()).getDescription(), "originalDescription"); // test if the delete is reversible and re-reversible Project expectedProject = ModelUtil.clone(getProject()); new EMFStoreCommand() { @Override protected void doRun() { op.reverse().apply(getProject()); } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), originalProject)); new EMFStoreCommand() { @Override protected void doRun() { op.reverse().reverse().apply(getProject()); } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Tests canonization for consecutive attribute changes and delete. * * @throws IOException */ @Test public void changeAttributesAndDeleteComplex() throws IOException { final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); final UseCase useCase2 = RequirementFactory.eINSTANCE.createUseCase(); final LeafSection section = DocumentFactory.eINSTANCE.createLeafSection(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(section); section.getModelElements().add(useCase); section.getModelElements().add(useCase2); useCase.setName("originalName1"); useCase.setDescription("originalDescription1"); useCase2.setName("originalName2"); useCase2.setDescription("originalDescription2"); clearOperations(); } }.run(false); Project originalProject = ModelUtil.clone(getProject()); new EMFStoreCommand() { @Override protected void doRun() { useCase.setName("NameOfUseCase"); useCase.setDescription("DescriptionOfUseCase"); useCase2.setName("NameOfUseCase2"); useCase2.setDescription("DescriptionOfUseCase2"); assertEquals("NameOfUseCase", useCase.getName()); assertEquals("NameOfUseCase2", useCase2.getName()); getProject().deleteModelElement(useCase); } }.run(false); new EMFStoreCommand() { @Override protected void doRun() { getProject().deleteModelElement(useCase2); } }.run(false); final List<AbstractOperation> operations = getProjectSpace().getOperations(); // expecting two attribute operations and a delete per usecase assertEquals(6, operations.size()); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); // now expecting only the deletes with folded in attributes assertEquals(2, operations.size()); assertTrue(operations.get(0) instanceof CreateDeleteOperation); final CreateDeleteOperation op = (CreateDeleteOperation) operations.get(0); assertEquals("originalName1", ((UnicaseModelElement) op.getModelElement()).getName()); assertEquals("originalDescription1", ((UnicaseModelElement) op.getModelElement()).getDescription()); assertTrue(operations.get(1) instanceof CreateDeleteOperation); final CreateDeleteOperation op2 = (CreateDeleteOperation) operations.get(1); assertEquals(((UnicaseModelElement) op2.getModelElement()).getName(), "originalName2"); assertEquals(((UnicaseModelElement) op2.getModelElement()).getDescription(), "originalDescription2"); // test reversibility, too new EMFStoreCommand() { @Override protected void doRun() { op2.reverse().apply(getProject()); op.reverse().apply(getProject()); } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), originalProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, originalProject)); } /** * Tests canonization for consecutive attribute changes and delete on orphans. */ // commented out, orphan behaviour is irrelevant at present. This reversibility test currently fails. // @Test // public void changeAttributesAndDeleteOrphansComplex() { // // UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); // UseCase useCase2 = RequirementFactory.eINSTANCE.createUseCase(); // // getProject().getModelElements().add(useCase); // getProject().getModelElements().add(useCase2); // // useCase.setName("originalName1"); // useCase.setDescription("originalDescription1"); // // useCase2.setName("originalName2"); // useCase2.setDescription("originalDescription2"); // // Project originalProject = ModelUtil.clone(getProject()); // // clearOperations(); // // useCase.setName("NameOfUseCase"); // useCase.setDescription("DescriptionOfUseCase"); // // useCase2.setName("NameOfUseCase2"); // useCase2.setDescription("DescriptionOfUseCase2"); // // assertEquals("NameOfUseCase", useCase.getName()); // assertEquals("NameOfUseCase2", useCase2.getName()); // // getProject().deleteModelElement(useCase); // getProject().deleteModelElement(useCase2); // // List<AbstractOperation> operations = getProjectSpace().getOperations(); // // // expecting two attribute operations and a delete per usecase // assertEquals(operations.size(), 6); // OperationsCanonizer.canonize(operations); // // // now expecting only the deletes with folded in attributes // assertEquals(operations.size(), 2); // assertTrue(operations.get(0) instanceof CreateDeleteOperation); // // CreateDeleteOperation op = (CreateDeleteOperation) operations.get(0); // // assertEquals(op.getModelElement().getName(), "originalName1"); // assertEquals(op.getModelElement().getDescription(), "originalDescription1"); // // assertTrue(operations.get(1) instanceof CreateDeleteOperation); // // CreateDeleteOperation op2 = (CreateDeleteOperation) operations.get(1); // // assertEquals(op2.getModelElement().getName(), "originalName2"); // assertEquals(op2.getModelElement().getDescription(), "originalDescription2"); // // // test reversibility, too // // op2.reverse().apply(getProject()); // op.reverse().apply(getProject()); // // assertTrue(ModelUtil.areEqual(getProject(), originalProject)); // // } /** * Test the creation and completion of a composite operation, that contains attribute changes. * * @throws InvalidHandleException if the test fails * @throws IOException */ @Test public void attributeChangesACAAndDelete() throws InvalidHandleException, IOException { final LeafSection section = DocumentFactory.eINSTANCE.createLeafSection(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(section); section.setName("originalName"); section.setDescription("originalDescription"); } }.run(false); Project originalProject = ModelUtil.clone(getProject()); new EMFStoreCommand() { @Override protected void doRun() { clearOperations(); section.setName("some new Name"); CompositeOperationHandle handle = getProjectSpace().beginCompositeOperation(); section.setDescription("newDescription"); UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); section.getModelElements().add(useCase); try { handle.end("sectionCreation", "description", ModelUtil.getProject(section).getModelElementId(section)); } catch (InvalidHandleException e) { fail(); } section.setDescription("desc 2"); getProject().deleteModelElement(section); } }.run(false); final List<AbstractOperation> operations = getProjectSpace().getOperations(); // expect 1 attribute op, the composite, 1 attribute op, the delete assertEquals(4, operations.size()); assertTrue(operations.get(0) instanceof AttributeOperation); assertTrue(operations.get(1) instanceof CompositeOperation); assertTrue(operations.get(2) instanceof AttributeOperation); assertTrue(operations.get(3) instanceof CreateDeleteOperation); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); // expect 1 attribute op, the composite and the delete with folded in attribute assertEquals(3, operations.size()); assertTrue(operations.get(0) instanceof AttributeOperation); assertTrue(operations.get(1) instanceof CompositeOperation); assertTrue(operations.get(2) instanceof CreateDeleteOperation); CreateDeleteOperation delOp = (CreateDeleteOperation) operations.get(2); assertTrue(delOp.isDelete()); // not folded, interfering composite was inbeetween assertEquals("some new Name", ((UnicaseModelElement) delOp.getModelElement()).getName()); // folded, value is oldValue from "newDescription"-> "desc 2" assertEquals("newDescription", ((UnicaseModelElement) delOp.getModelElement()).getDescription()); Project expectedProject = ModelUtil.clone(getProject()); // test reversibility new EMFStoreCommand() { @Override protected void doRun() { for (int i = operations.size() - 1; i >= 0; i--) { AbstractOperation reverse = operations.get(i).reverse(); reverse.apply(getProject()); } } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), originalProject)); // test redo new EMFStoreCommand() { @Override protected void doRun() { operations.get(0).apply(getProject()); operations.get(1).apply(getProject()); operations.get(2).apply(getProject()); } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), expectedProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, expectedProject)); } /** * Tests canonization for create, attribute changes and delete. * * @throws IOException */ @Test public void createChangeAttributeAndDelete() throws IOException { Project originalProject = ModelUtil.clone(getProject()); final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(useCase); useCase.setName("someName"); useCase.setName("newName"); getProject().deleteModelElement(useCase); } }.run(false); final List<AbstractOperation> operations = getProjectSpace().getOperations(); // expect create, 2 attribute ops, delete assertEquals(4, operations.size()); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); // expect attributes folding into create, and create and delete removed, // as they would be directly adjacent to each other assertEquals(operations.size(), 0); assertTrue(ModelUtil.areEqual(getProject(), originalProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject() .eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, originalProject)); } /** * Tests canonization for create, attribute changes and delete. * * @throws IOException */ @Test public void createChangeReferencesAndDelete() throws IOException { final UseCase useCase2 = RequirementFactory.eINSTANCE.createUseCase(); new EMFStoreCommand() { @Override protected void doRun() { getProject().addModelElement(useCase2); } }.run(false); Project originalProject = ModelUtil.clone(getProject()); new EMFStoreCommand() { @Override protected void doRun() { clearOperations(); UseCase useCase = RequirementFactory.eINSTANCE.createUseCase(); getProject().addModelElement(useCase); useCase.setName("someName"); useCase.getExtendedUseCases().add(useCase2); getProject().deleteModelElement(useCase); } }.run(false); final List<AbstractOperation> operations = getProjectSpace().getOperations(); // expect create, 1 attribute ops, 1 multiref op, the delete assertEquals(4, operations.size()); new EMFStoreCommand() { @Override protected void doRun() { OperationsCanonizer.canonize(operations); } }.run(false); // expect attributes folding into create, the multiref and delete remain assertEquals(operations.size(), 3); assertTrue(operations.get(0) instanceof CreateDeleteOperation); assertTrue(operations.get(1) instanceof MultiReferenceOperation); assertTrue(operations.get(2) instanceof CreateDeleteOperation); // check the folding of the attribute CreateDeleteOperation createOp = (CreateDeleteOperation) operations.get(0); assertEquals("someName", ((UnicaseModelElement) createOp.getModelElement()).getName()); // check reversibility new EMFStoreCommand() { @Override protected void doRun() { operations.get(2).reverse().apply(getProject()); operations.get(1).reverse().apply(getProject()); operations.get(0).reverse().apply(getProject()); } }.run(false); assertTrue(ModelUtil.areEqual(getProject(), originalProject)); Project loadedProject = ModelUtil.loadEObjectFromResource( ModelFactory.eINSTANCE.getModelPackage().getProject(), getProject().eResource().getURI(), false); assertTrue(ModelUtil.areEqual(loadedProject, originalProject)); } }