blob: 2fa5cf6a7653b90f2c30920d740161483ac44761 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008-2011 Chair for Applied Software Engineering,
* Technische Universitaet Muenchen.
* 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:
* chodnick
******************************************************************************/
package org.eclipse.emf.emfstore.client.changetracking.test.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.test.common.cases.ComparingESTest;
import org.eclipse.emf.emfstore.client.test.common.dsl.Create;
import org.eclipse.emf.emfstore.internal.client.model.CompositeOperationHandle;
import org.eclipse.emf.emfstore.internal.client.model.exceptions.InvalidHandleException;
import org.eclipse.emf.emfstore.internal.client.model.util.EMFStoreCommand;
import org.eclipse.emf.emfstore.internal.common.model.Project;
import org.eclipse.emf.emfstore.internal.common.model.util.ModelUtil;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.AbstractOperation;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.AttributeOperation;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.CompositeOperation;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.CreateDeleteOperation;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.MultiReferenceOperation;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.util.OperationsCanonizer;
import org.eclipse.emf.emfstore.test.model.TestElement;
import org.junit.Test;
/**
* Tests canonization of attribute operations.
*
* @author chodnick
*/
public class AttributeTest extends ComparingESTest {
private static final String SOME_NAME = "someName"; //$NON-NLS-1$
private static final String SOME_NEW_NAME = "some new Name"; //$NON-NLS-1$
private static final String ORIGINAL_DESCRIPTION2 = "originalDescription2"; //$NON-NLS-1$
private static final String ORIGINAL_NAME2 = "originalName2"; //$NON-NLS-1$
private static final String ORIGINAL_DESCRIPTION1 = "originalDescription1"; //$NON-NLS-1$
private static final String ORIGINAL_NAME1 = "originalName1"; //$NON-NLS-1$
private static final String ORIGINAL_DESCRIPTION = "originalDescription"; //$NON-NLS-1$
private static final String ORIGINAL_NAME = "originalName"; //$NON-NLS-1$
private static final String DESCRIPTION_OF_TEST_ELEMENT2 = "DescriptionOfTestElement2"; //$NON-NLS-1$
private static final String NAME_OF_TEST_ELEMENT2 = "NameOfTestElement2"; //$NON-NLS-1$
private static final String DESCRIPTION_OF_TEST_ELEMENT = "DescriptionOfTestElement"; //$NON-NLS-1$
private static final String NAME_OF_TEST_ELEMENT = "NameOfTestElement"; //$NON-NLS-1$
private static final String DESC_2 = "desc 2"; //$NON-NLS-1$
private static final String DESCRIPTION2 = "description"; //$NON-NLS-1$
private static final String FINAL_DESC = "final desc"; //$NON-NLS-1$
private static final String SOME_OTHER_DESC = "some other desc"; //$NON-NLS-1$
private static final String SOME_DESC = "some desc";//$NON-NLS-1$
private static final String OLD_SECTION = "oldSection";//$NON-NLS-1$
private static final String HOME = "home";//$NON-NLS-1$
private static final String MAGGIE = "maggie";//$NON-NLS-1$
private static final String HOMER = "homer";//$NON-NLS-1$
private static final String SOME_SECTION = "some section";//$NON-NLS-1$
private static final String Y_NAME = "Y";//$NON-NLS-1$
private static final String X_NAME = "X";//$NON-NLS-1$
private static final String SECTION_CREATION = "sectionCreation";//$NON-NLS-1$
private static final String DESC_1 = "desc 1";//$NON-NLS-1$
private static final String NAME = "Name";//$NON-NLS-1$
private static final String NEW_DESCRIPTION = "newDescription";//$NON-NLS-1$
private static final String OLD_DESCRIPTION = "oldDescription";//$NON-NLS-1$
private static final String A_NAME = "A"; //$NON-NLS-1$
private static final String B_NAME = "B"; //$NON-NLS-1$
private static final String C_NAME = "C"; //$NON-NLS-1$
private static final String NEW_NAME = "newName"; //$NON-NLS-1$
private static final String OLD_NAME = "oldName"; //$NON-NLS-1$
/**
* Tests canonization for consecutive attribute changes on a single feature.
*
* @throws IOException
*/
@Test
public void consecutiveAttributeChangeSingleFeature() throws IOException {
final TestElement useCase = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(useCase);
useCase.setName(OLD_NAME);
}
}.run(false);
final Project expectedProject = ModelUtil.clone(getProject());
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
new EMFStoreCommand() {
@Override
protected void doRun() {
clearOperations();
useCase.setName(A_NAME);
useCase.setName(B_NAME);
useCase.setName(C_NAME);
useCase.setName(NEW_NAME);
}
}.run(false);
assertEquals(NEW_NAME, useCase.getName());
assertEquals(4, forceGetOperations().size());
final List<AbstractOperation> operations = forceGetOperations();
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));
}
/**
* Tests canonization for consecutive attribute changes on a single feature.
*
* @throws IOException
*/
@Test
public void consecutiveAttributeChangeSingleFeatureToNull() throws IOException {
final TestElement useCase = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(useCase);
useCase.setName(OLD_NAME);
}
}.run(false);
final Project expectedProject = ModelUtil.clone(getProject());
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
new EMFStoreCommand() {
@Override
protected void doRun() {
clearOperations();
useCase.setName(A_NAME);
useCase.setName(B_NAME);
useCase.setName(C_NAME);
useCase.setName(null);
}
}.run(false);
assertEquals(null, useCase.getName());
final List<AbstractOperation> operations = forceGetOperations();
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));
}
/**
* Tests canonization for consecutive attribute changes on a single feature.
*
* @throws IOException
*/
@Test
public void consecutiveAttributeChangeSingleFeatureNullToValue() throws IOException {
final TestElement useCase = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(useCase);
useCase.setName(null);
}
}.run(false);
final Project expectedProject = ModelUtil.clone(getProject());
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
new EMFStoreCommand() {
@Override
protected void doRun() {
clearOperations();
useCase.setName(A_NAME);
useCase.setName(B_NAME);
useCase.setName(C_NAME);
}
}.run(false);
assertEquals(C_NAME, useCase.getName());
final List<AbstractOperation> operations = forceGetOperations();
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));
}
/**
* Tests canonization for consecutive attribute changes, resulting in a noop.
*
* @throws IOException
*/
@Test
public void attributeChangeNoOp() throws IOException {
final TestElement useCase = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(useCase);
useCase.setName(OLD_NAME);
}
}.run(false);
final Project expectedProject = ModelUtil.clone(getProject());
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
new EMFStoreCommand() {
@Override
protected void doRun() {
clearOperations();
useCase.setName(A_NAME);
useCase.setName(B_NAME);
useCase.setName(C_NAME);
useCase.setName(OLD_NAME);
}
}.run(false);
assertEquals(OLD_NAME, useCase.getName());
final List<AbstractOperation> operations = forceGetOperations();
assertEquals(4, operations.size());
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
canonize(operations);
// should not have created any operations, we were just resetting the name to its original value
assertEquals(operations.size(), 0);
}
/**
* Tests canonization for consecutive attribute changes, resulting in a noop.
*
* @throws IOException
*/
@Test
public void attributeChangeNoOpNull() throws IOException {
final TestElement useCase = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(useCase);
useCase.setName(null);
}
}.run(false);
final Project expectedProject = ModelUtil.clone(getProject());
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
new EMFStoreCommand() {
@Override
protected void doRun() {
clearOperations();
useCase.setName(A_NAME);
useCase.setName(B_NAME);
useCase.setName(C_NAME);
useCase.setName(null);
}
}.run(false);
assertEquals(null, useCase.getName());
final List<AbstractOperation> operations = forceGetOperations();
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);
}
/**
* Tests canonization for consecutive attribute changes, resulting in a noop.
*
* @throws IOException
*/
@Test
public void attributeChangeMultiFeatureNoOp() throws IOException {
final TestElement useCase = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(useCase);
useCase.setName(OLD_NAME);
useCase.setDescription(OLD_DESCRIPTION);
}
}.run(false);
final Project expectedProject = ModelUtil.clone(getProject());
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
new EMFStoreCommand() {
@Override
protected void doRun() {
clearOperations();
useCase.setName(A_NAME);
useCase.setDescription(X_NAME);
useCase.setName(B_NAME);
useCase.setDescription(Y_NAME);
useCase.setName(C_NAME);
useCase.setDescription(OLD_DESCRIPTION);
useCase.setName(OLD_NAME);
}
}.run(false);
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
final List<AbstractOperation> operations = forceGetOperations();
assertEquals(7, operations.size());
canonize(operations);
// should not have created any operations, we were just resetting everything to its original value
assertEquals(operations.size(), 0);
}
/**
* Tests canonization for consecutive attribute changes on multiple features.
*
* @throws IOException
*/
@Test
public void consecutiveAttributeChangeMultiFeature() throws IOException {
final TestElement useCase = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(useCase);
useCase.setName(OLD_NAME);
}
}.run(false);
final Project expectedProject = ModelUtil.clone(getProject());
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
new EMFStoreCommand() {
@Override
protected void doRun() {
clearOperations();
useCase.setName(A_NAME);
useCase.setDescription(OLD_DESCRIPTION);
useCase.setName(B_NAME);
useCase.setName(C_NAME);
useCase.setDescription(NEW_DESCRIPTION);
useCase.setName(NEW_NAME);
}
}.run(false);
assertEquals(NEW_NAME, useCase.getName());
final List<AbstractOperation> operations = forceGetOperations();
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--) {
final AbstractOperation reverse = operations.get(i).reverse();
reverse.apply(getProject());
}
}
}.run(false);
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
}
/**
* Tests canonization for mixed attribute changes on a single feature.
*
* @throws IOException
*/
@Test
public void mixedAttributeChangeSingleFeature() throws IOException {
final TestElement useCase = Create.testElement();
final TestElement actor = Create.testElement();
final TestElement section = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(useCase);
getProject().addModelElement(actor);
getProject().addModelElement(section);
useCase.setName(OLD_NAME);
section.setName(SOME_SECTION);
actor.setName(HOMER);
}
}.run(false);
final Project expectedProject = ModelUtil.clone(getProject());
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
new EMFStoreCommand() {
@Override
protected void doRun() {
clearOperations();
useCase.setName(A_NAME);
actor.setName(MAGGIE);
useCase.setName(B_NAME);
useCase.setNonContained_NTo1(actor);
useCase.setName(C_NAME);
section.setName(HOME);
useCase.setName(NEW_NAME);
}
}.run(false);
final List<AbstractOperation> operations = forceGetOperations();
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--) {
final AbstractOperation reverse = operations.get(i).reverse();
reverse.apply(getProject());
}
}
}.run(false);
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
}
/**
* Tests canonization for mixed attribute changes on a single feature.
*
* @throws IOException
*/
@Test
public void mixedAttributeChangeMultiFeature() throws IOException {
final TestElement useCase = Create.testElement();
final TestElement actor = Create.testElement();
final TestElement section = Create.testElement();
final TestElement oldSection = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(useCase);
getProject().addModelElement(actor);
getProject().addModelElement(section);
getProject().addModelElement(oldSection);
useCase.setContainer(oldSection);
actor.setContainer(oldSection);
useCase.setName(OLD_NAME);
oldSection.setName(OLD_SECTION);
section.setName(SOME_SECTION);
actor.setName(HOMER);
}
}.run(false);
final Project expectedProject = ModelUtil.clone(getProject());
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
new EMFStoreCommand() {
@Override
protected void doRun() {
clearOperations();
useCase.setName(A_NAME);
actor.setName(MAGGIE);
useCase.setName(B_NAME);
useCase.setDescription(SOME_DESC);
useCase.setNonContained_NTo1(actor);
useCase.setName(C_NAME);
section.setName(HOME);
useCase.setDescription(SOME_OTHER_DESC);
useCase.setName(NEW_NAME);
useCase.setDescription(FINAL_DESC);
}
}.run(false);
final List<AbstractOperation> operations = forceGetOperations();
assertEquals(NEW_NAME, 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--) {
final AbstractOperation reverse = operations.get(i).reverse();
reverse.apply(getProject());
}
}
}.run(false);
assertTrue(ModelUtil.areEqual(getProject(), 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 TestElement section = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(section);
section.setName(NAME);
section.setDescription(OLD_DESCRIPTION);
}
}.run(false);
final Project expectedProject = ModelUtil.clone(getProject());
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
new EMFStoreCommand() {
@Override
protected void doRun() {
clearOperations();
section.setDescription(DESC_1);
final CompositeOperationHandle handle = getProjectSpace().beginCompositeOperation();
section.setDescription(NEW_DESCRIPTION);
final TestElement useCase = Create.testElement();
section.getContainedElements().add(useCase);
try {
handle.end(SECTION_CREATION, DESCRIPTION2,
ModelUtil.getProject(section).getModelElementId(section));
} catch (final InvalidHandleException e) {
fail();
}
section.setDescription(DESC_2);
}
}.run(false);
final List<AbstractOperation> operations = forceGetOperations();
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--) {
final AbstractOperation reverse = operations.get(i).reverse();
reverse.apply(getProject());
}
}
}.run(false);
assertTrue(ModelUtil.areEqual(getProject(), 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 TestElement section = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(section);
section.setName(NAME);
section.setDescription(OLD_DESCRIPTION);
}
}.run(false);
final Project expectedProject = ModelUtil.clone(getProject());
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
new EMFStoreCommand() {
@Override
protected void doRun() {
clearOperations();
section.setDescription(DESC_1);
final CompositeOperationHandle handle = getProjectSpace().beginCompositeOperation();
section.setDescription(NEW_DESCRIPTION);
final TestElement useCase = Create.testElement();
section.getContainedElements().add(useCase);
try {
handle.end(SECTION_CREATION, DESCRIPTION2,
ModelUtil.getProject(section).getModelElementId(section));
} catch (final InvalidHandleException e) {
fail();
}
}
}.run(false);
final List<AbstractOperation> operations = forceGetOperations();
assertEquals(2, operations.size());
assertEquals(NEW_DESCRIPTION, 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--) {
final AbstractOperation reverse = operations.get(i).reverse();
reverse.apply(getProject());
}
}
}.run(false);
assertTrue(ModelUtil.areEqual(getProject(), 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 TestElement section = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(section);
section.setName(NAME);
section.setDescription(OLD_DESCRIPTION);
}
}.run(false);
final Project expectedProject = ModelUtil.clone(getProject());
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
new EMFStoreCommand() {
@Override
protected void doRun() {
clearOperations();
final CompositeOperationHandle handle = getProjectSpace().beginCompositeOperation();
section.setDescription(NEW_DESCRIPTION);
final TestElement useCase = Create.testElement();
section.getContainedElements().add(useCase);
try {
handle.end(SECTION_CREATION, DESCRIPTION2,
ModelUtil.getProject(section).getModelElementId(section));
} catch (final InvalidHandleException e) {
fail();
}
section.setDescription(DESC_2);
}
}.run(false);
final List<AbstractOperation> operations = forceGetOperations();
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--) {
final AbstractOperation reverse = operations.get(i).reverse();
reverse.apply(getProject());
}
}
}.run(false);
assertTrue(ModelUtil.areEqual(getProject(), expectedProject));
}
/**
* Tests canonization for create and consecutive attribute changes.
*
* @throws IOException
*/
@Test
public void createAndChangeAttributesSimple() throws IOException {
final Project originalProject = ModelUtil.clone(getProject());
final TestElement useCase = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(useCase);
useCase.setName(NAME_OF_TEST_ELEMENT);
useCase.setDescription(DESCRIPTION_OF_TEST_ELEMENT);
}
}.run(false);
assertEquals(NAME_OF_TEST_ELEMENT, useCase.getName());
final List<AbstractOperation> operations = forceGetOperations();
// 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(((TestElement) op.getModelElement()).getName(), NAME_OF_TEST_ELEMENT);
assertEquals(((TestElement) op.getModelElement()).getDescription(), DESCRIPTION_OF_TEST_ELEMENT);
// test if the create is reversible and re-reversible
final 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));
}
/**
* Tests canonization for create and consecutive attribute changes.
*
* @throws IOException
*/
@Test
public void createAndChangeAttributesComplex() throws IOException {
final Project originalProject = ModelUtil.clone(getProject());
final TestElement useCase = Create.testElement();
final TestElement useCase2 = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(useCase);
getProject().addModelElement(useCase2);
useCase.setName(NAME_OF_TEST_ELEMENT);
useCase.setDescription(DESCRIPTION_OF_TEST_ELEMENT);
useCase2.setName(NAME_OF_TEST_ELEMENT2);
useCase2.setDescription(DESCRIPTION_OF_TEST_ELEMENT2);
}
}.run(false);
assertEquals(NAME_OF_TEST_ELEMENT, useCase.getName());
assertEquals(NAME_OF_TEST_ELEMENT2, useCase2.getName());
final List<AbstractOperation> operations = forceGetOperations();
// 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(((TestElement) op.getModelElement()).getName(), NAME_OF_TEST_ELEMENT);
assertEquals(((TestElement) op.getModelElement()).getDescription(), DESCRIPTION_OF_TEST_ELEMENT);
assertTrue(operations.get(1) instanceof CreateDeleteOperation);
final CreateDeleteOperation op2 = (CreateDeleteOperation) operations.get(1);
assertEquals(((TestElement) op2.getModelElement()).getName(), NAME_OF_TEST_ELEMENT2);
assertEquals(((TestElement) op2.getModelElement()).getDescription(), DESCRIPTION_OF_TEST_ELEMENT2);
// test reversibility, too
new EMFStoreCommand() {
@Override
protected void doRun() {
op2.reverse().apply(getProject());
op.reverse().apply(getProject());
}
}.run(false);
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 createAndAttributeChangesACA() throws InvalidHandleException, IOException {
final Project originalProject = ModelUtil.clone(getProject());
final TestElement section = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(section);
section.setName(NAME);
section.setDescription(OLD_DESCRIPTION);
final CompositeOperationHandle handle = getProjectSpace().beginCompositeOperation();
section.setDescription(NEW_DESCRIPTION);
final TestElement useCase = Create.testElement();
section.getContainedElements().add(useCase);
try {
handle.end(SECTION_CREATION, DESCRIPTION2,
ModelUtil.getProject(section).getModelElementId(section));
} catch (final InvalidHandleException e) {
fail();
}
section.setDescription(DESC_2);
}
}.run(false);
final List<AbstractOperation> operations = forceGetOperations();
// 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);
final Project expectedProject = ModelUtil.clone(getProject());
// test reversibility
new EMFStoreCommand() {
@Override
protected void doRun() {
for (int i = operations.size() - 1; i >= 0; i--) {
final 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));
}
/**
* Tests canonization for consecutive attribute changes followed by a delete.
*
* @throws IOException
*/
@Test
public void changeAttributesAndDeleteSimple() throws IOException {
final TestElement useCase = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(useCase);
useCase.setName(ORIGINAL_NAME);
useCase.setDescription(ORIGINAL_DESCRIPTION);
}
}.run(false);
clearOperations();
assertTrue(getProjectSpace().getLocalChangePackage().isEmpty());
assertTrue(forceGetOperations().isEmpty());
final Project originalProject = ModelUtil.clone(getProject());
new EMFStoreCommand() {
@Override
protected void doRun() {
useCase.setName(NAME_OF_TEST_ELEMENT);
useCase.setDescription(DESCRIPTION_OF_TEST_ELEMENT);
getProject().deleteModelElement(useCase);
}
}.run(false);
final List<AbstractOperation> operations = forceGetOperations();
// 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(((TestElement) op.getModelElement()).getName(), ORIGINAL_NAME);
assertEquals(((TestElement) op.getModelElement()).getDescription(), ORIGINAL_DESCRIPTION);
// test if the delete is reversible and re-reversible
final 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));
}
/**
* Tests canonization for consecutive attribute changes and delete.
*
* @throws IOException
*/
@Test
public void changeAttributesAndDeleteComplex() throws IOException {
final TestElement useCase = Create.testElement();
final TestElement useCase2 = Create.testElement();
final TestElement section = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(section);
section.getContainedElements().add(useCase);
section.getContainedElements().add(useCase2);
useCase.setName(ORIGINAL_NAME1);
useCase.setDescription(ORIGINAL_DESCRIPTION1);
useCase2.setName(ORIGINAL_NAME2);
useCase2.setDescription(ORIGINAL_DESCRIPTION2);
}
}.run(false);
clearOperations();
final Project originalProject = ModelUtil.clone(getProject());
new EMFStoreCommand() {
@Override
protected void doRun() {
useCase.setName(NAME_OF_TEST_ELEMENT);
useCase.setDescription(DESCRIPTION_OF_TEST_ELEMENT);
useCase2.setName(NAME_OF_TEST_ELEMENT2);
useCase2.setDescription(DESCRIPTION_OF_TEST_ELEMENT2);
assertEquals(NAME_OF_TEST_ELEMENT, useCase.getName());
assertEquals(NAME_OF_TEST_ELEMENT2, useCase2.getName());
getProject().deleteModelElement(useCase);
}
}.run(false);
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().deleteModelElement(useCase2);
}
}.run(false);
final List<AbstractOperation> operations = forceGetOperations();
// 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(ORIGINAL_NAME1, ((TestElement) op.getModelElement()).getName());
assertEquals(ORIGINAL_DESCRIPTION1, ((TestElement) op.getModelElement()).getDescription());
assertTrue(operations.get(1) instanceof CreateDeleteOperation);
final CreateDeleteOperation op2 = (CreateDeleteOperation) operations.get(1);
assertEquals(((TestElement) op2.getModelElement()).getName(), ORIGINAL_NAME2);
assertEquals(((TestElement) op2.getModelElement()).getDescription(), ORIGINAL_DESCRIPTION2);
// test reversibility, too
new EMFStoreCommand() {
@Override
protected void doRun() {
op2.reverse().apply(getProject());
op.reverse().apply(getProject());
}
}.run(false);
assertTrue(ModelUtil.areEqual(getProject(), 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() {
//
// TestElement useCase = Create.testElement();
// TestElement useCase2 = Create.testElement();
//
// 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("NameOfTestElement");
// useCase.setDescription("DescriptionOfTestElement");
//
// useCase2.setName("NameOfTestElement2");
// useCase2.setDescription("DescriptionOfTestElement2");
//
// assertEquals("NameOfTestElement", useCase.getName());
// assertEquals("NameOfTestElement2", useCase2.getName());
//
// getProject().deleteModelElement(useCase);
// getProject().deleteModelElement(useCase2);
//
// List<AbstractOperation> operations = forceGetOperations();
//
// // 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
*/
// BEGIN COMPLEX CODE
@Test
public void attributeChangesACAAndDelete() throws InvalidHandleException, IOException {
final TestElement section = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(section);
section.setName(ORIGINAL_NAME);
section.setDescription(ORIGINAL_DESCRIPTION);
}
}.run(false);
final Project originalProject = ModelUtil.clone(getProject());
new EMFStoreCommand() {
@Override
protected void doRun() {
clearOperations();
section.setName(SOME_NEW_NAME);
final CompositeOperationHandle handle = getProjectSpace().beginCompositeOperation();
section.setDescription(NEW_DESCRIPTION);
final TestElement useCase = Create.testElement();
section.getContainedElements().add(useCase);
try {
handle.end(SECTION_CREATION, DESCRIPTION2,
ModelUtil.getProject(section).getModelElementId(section));
} catch (final InvalidHandleException e) {
fail();
}
section.setDescription(DESC_2);
getProject().deleteModelElement(section);
}
}.run(false);
final List<AbstractOperation> operations = forceGetOperations();
// 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);
final CreateDeleteOperation delOp = (CreateDeleteOperation) operations.get(2);
assertTrue(delOp.isDelete());
// not folded, interfering composite was inbeetween
assertEquals(SOME_NEW_NAME, ((TestElement) delOp.getModelElement()).getName());
// folded, value is oldValue from "newDescription"-> "desc 2"
assertEquals(NEW_DESCRIPTION, ((TestElement) delOp.getModelElement()).getDescription());
final Project expectedProject = ModelUtil.clone(getProject());
// test reversibility
new EMFStoreCommand() {
@Override
protected void doRun() {
for (int i = operations.size() - 1; i >= 0; i--) {
final 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));
}
// END COMPLEX CODE
/**
* Tests canonization for create, attribute changes and delete.
*
* @throws IOException
*/
@Test
public void createChangeAttributeAndDelete() throws IOException {
final Project originalProject = ModelUtil.clone(getProject());
final TestElement useCase = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(useCase);
useCase.setName(SOME_NAME);
useCase.setName(NEW_NAME);
getProject().deleteModelElement(useCase);
}
}.run(false);
final List<AbstractOperation> operations = forceGetOperations();
// 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));
}
/**
* Tests canonization for create, attribute changes and delete.
*
* @throws IOException
*/
@Test
public void createChangeReferencesAndDelete() throws IOException {
final TestElement useCase2 = Create.testElement();
new EMFStoreCommand() {
@Override
protected void doRun() {
getProject().addModelElement(useCase2);
}
}.run(false);
final Project originalProject = ModelUtil.clone(getProject());
new EMFStoreCommand() {
@Override
protected void doRun() {
clearOperations();
final TestElement useCase = Create.testElement();
getProject().addModelElement(useCase);
useCase.setName(SOME_NAME);
useCase.getReferences().add(useCase2);
getProject().deleteModelElement(useCase);
}
}.run(false);
final List<AbstractOperation> operations = forceGetOperations();
// expect create, 1 attribute ops, 1 multiref op, the delete
assertEquals(4, operations.size());
canonize(operations);
// 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
final CreateDeleteOperation createOp = (CreateDeleteOperation) operations.get(0);
assertEquals(SOME_NAME, ((TestElement) 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));
}
}