/******************************************************************************* * Copyright (c) 2012-2013 EclipseSource Muenchen GmbH 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: ******************************************************************************/ package org.eclipse.emf.emfstore.server.test; import static org.eclipse.emf.emfstore.client.test.common.util.ProjectUtil.addElement; import static org.eclipse.emf.emfstore.client.test.common.util.ProjectUtil.checkout; import static org.eclipse.emf.emfstore.client.test.common.util.ProjectUtil.cloneProject; import static org.eclipse.emf.emfstore.client.test.common.util.ProjectUtil.computeChecksum; import static org.eclipse.emf.emfstore.client.test.common.util.ProjectUtil.revert; import static org.eclipse.emf.emfstore.client.test.common.util.ProjectUtil.share; import static org.eclipse.emf.emfstore.client.test.common.util.ProjectUtil.startRecording; import static org.eclipse.emf.emfstore.client.test.common.util.ProjectUtil.stopRecording; import static org.eclipse.emf.emfstore.client.test.common.util.ProjectUtil.update; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.List; import java.util.concurrent.Callable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.emf.emfstore.client.ESLocalProject; import org.eclipse.emf.emfstore.client.ESWorkspaceProvider; import org.eclipse.emf.emfstore.client.callbacks.ESCommitCallback; import org.eclipse.emf.emfstore.client.callbacks.ESUpdateCallback; import org.eclipse.emf.emfstore.client.test.common.cases.ESTestWithLoggedInUserMock; import org.eclipse.emf.emfstore.client.test.common.dsl.Add; import org.eclipse.emf.emfstore.client.test.common.dsl.Create; import org.eclipse.emf.emfstore.client.test.common.dsl.TestElementFeatures; import org.eclipse.emf.emfstore.client.test.common.dsl.Update; import org.eclipse.emf.emfstore.client.test.common.util.ProjectUtil; import org.eclipse.emf.emfstore.client.util.RunESCommand; import org.eclipse.emf.emfstore.common.model.ESModelElementIdToEObjectMapping; import org.eclipse.emf.emfstore.internal.client.model.Configuration; import org.eclipse.emf.emfstore.internal.client.model.ESWorkspaceProviderImpl; import org.eclipse.emf.emfstore.internal.client.model.ProjectSpace; import org.eclipse.emf.emfstore.internal.client.model.impl.api.ESLocalProjectImpl; import org.eclipse.emf.emfstore.internal.client.model.util.ChecksumErrorHandler; import org.eclipse.emf.emfstore.internal.common.model.ModelElementId; import org.eclipse.emf.emfstore.internal.common.model.util.ModelUtil; import org.eclipse.emf.emfstore.internal.common.model.util.SerializationException; import org.eclipse.emf.emfstore.internal.server.model.impl.api.versionspec.ESPrimaryVersionSpecImpl; import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.AbstractOperation; import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.CreateDeleteOperation; import org.eclipse.emf.emfstore.server.ESConflictSet; import org.eclipse.emf.emfstore.server.exceptions.ESException; import org.eclipse.emf.emfstore.server.model.ESChangePackage; import org.eclipse.emf.emfstore.server.model.versionspec.ESPrimaryVersionSpec; import org.eclipse.emf.emfstore.test.model.TestElement; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class ChecksumTest extends ESTestWithLoggedInUserMock { private static final String C = "C"; //$NON-NLS-1$ private static final String SOME_COMMIT_MESSAGE = "SomeCommitMessage"; //$NON-NLS-1$ private static final String BOOL = "BOOL"; //$NON-NLS-1$ private static final String ATTRIBUTE2 = "attribute"; //$NON-NLS-1$ private static final String ATTRIBUTE_NAME = "attributeName"; //$NON-NLS-1$ private static final String VALUE2 = "value"; //$NON-NLS-1$ private static final String A = "A"; //$NON-NLS-1$ @Override @Before public void before() { org.junit.Assume.assumeTrue(transactionalEditingDomainNotInUse()); super.before(); } public boolean transactionalEditingDomainNotInUse() { return !ESWorkspaceProviderImpl.getInstance().getEditingDomain().getClass().getName().contains("Transactional"); //$NON-NLS-1$ } @Override @After public void after() { super.after(); } @BeforeClass public static void beforeClass() { startEMFStore(); } @AfterClass public static void afterClass() { stopEMFStore(); } @Test public void testRevert() throws SerializationException { final TestElement table = Create.testElement(A); final TestElement value = Create.testElement(VALUE2); final TestElement attributeName = Create.testElement(ATTRIBUTE_NAME); final TestElement attribute = Create.testElement(ATTRIBUTE2); Add.toProject(getLocalProject(), table); Add.toProject(getLocalProject(), value); Add.toProject(getLocalProject(), attributeName); Add.toProject(getLocalProject(), attribute); attribute.getContainedElements().add(value); table.getElementMap().put(attributeName, value); final long checksum = computeChecksum(getLocalProject()); clearOperations(); assertTrue(getProjectSpace().getLocalChangePackage().isEmpty()); final ProjectSpace clonedProjectSpace = cloneProjectSpace(getProjectSpace()); final ModelElementId attributeId = getProject().getModelElementId(attribute); // Delete.fromProject(getLocalProject(), attribute); ProjectUtil.removeModelElement(getLocalProject(), attribute); final List<AbstractOperation> forceGetOperations = forceGetOperations(); final CreateDeleteOperation createDeleteOperation = (CreateDeleteOperation) forceGetOperations.get(0); assertEquals(attributeId, createDeleteOperation.getModelElementId()); assertTrue(createDeleteOperation.isDelete()); revert(getLocalProject()); assertTrue(ModelUtil.areEqual(getProject(), clonedProjectSpace.getProject())); final long checksumAfterRevert = computeChecksum(getLocalProject()); assertEquals(checksum, checksumAfterRevert); } @Test public void testOrderOfRootElementsInvariance() throws SerializationException { final TestElement a = Create.testElement(A); final TestElement b = Create.testElement(BOOL); addElement(getLocalProject(), a); addElement(getLocalProject(), b); assertEquals(2, getLocalProject().getModelElements().size()); final long computeChecksum = computeChecksum(getLocalProject()); RunESCommand.run(new Callable<Void>() { public Void call() throws Exception { // we must clear and add again in one command getLocalProject().getModelElements().clear(); getLocalProject().getModelElements().add(b); getLocalProject().getModelElements().add(a); return null; } }); final long checksum = computeChecksum(getLocalProject()); assertEquals(computeChecksum, checksum); } @Test public void testAutocorrectErrorHandlerAtCommit() throws ESException, SerializationException { assertEquals(1, ESWorkspaceProviderImpl.getInstance().getWorkspace().getLocalProjects().size()); Configuration.getClientBehavior().setChecksumErrorHandler(ChecksumErrorHandler.AUTOCORRECT); final TestElement testElement = Create.testElement(); share(getUsersession(), addElement(getLocalProject(), testElement)); assertEquals(0, forceGetOperations().size()); Update.testElement(TestElementFeatures.name(), testElement, A); final long expectedChecksum = computeChecksum(getLocalProject()); final ESLocalProject clonedProject = cloneProject(getLocalProject()); stopRecording(getLocalProject()); Update.testElement(TestElementFeatures.name(), testElement, BOOL); startRecording(getLocalProject()); // FIXME: ugly assertEquals(1, forceGetOperations().size()); // re-checkout should be triggered final ESPrimaryVersionSpec commit = commitWithoutCommand(getLocalProject()); assertEquals(1, ESWorkspaceProviderImpl.getInstance().getWorkspace().getLocalProjects().size()); final ESLocalProject restoredProject = ESWorkspaceProvider.INSTANCE.getWorkspace().getLocalProjects().get(0); final long computedChecksum = computeChecksum(restoredProject); assertTrue(ModelUtil.areEqual( ESLocalProjectImpl.class.cast(restoredProject).toInternalAPI().getProject(), ESLocalProjectImpl.class.cast(clonedProject).toInternalAPI().getProject())); assertEquals(expectedChecksum, ESPrimaryVersionSpecImpl.class.cast(commit).toInternalAPI().getProjectStateChecksum()); assertEquals(computedChecksum, ESPrimaryVersionSpecImpl.class.cast(commit).toInternalAPI().getProjectStateChecksum()); } @Test public void testChangeTrackingAfterAutocorrectErrorHandler() throws ESException, SerializationException { assertEquals(1, ESWorkspaceProviderImpl.getInstance().getWorkspace().getLocalProjects().size()); Configuration.getClientBehavior().setChecksumErrorHandler(ChecksumErrorHandler.AUTOCORRECT); final TestElement testElement = Create.testElement(); share(getUsersession(), getLocalProject()); final ESLocalProject checkout = checkout(getLocalProject()); addElement(getLocalProject(), testElement); Update.testElement(TestElementFeatures.name(), testElement, A); commitWithoutCommand(getLocalProject()); assertEquals(0, forceGetOperations().size()); addElement(checkout, Update.testElement(TestElementFeatures.name(), Create.testElement(), BOOL)); update(checkout); commitWithoutCommand(checkout); assertEquals(0, forceGetOperations(ESLocalProjectImpl.class.cast(checkout).toInternalAPI()).size()); assertEquals(0, forceGetOperations().size()); Update.testElement(TestElementFeatures.name(), testElement, BOOL); stopRecording(getLocalProject()); Update.testElement(TestElementFeatures.name(), testElement, C); startRecording(getLocalProject()); assertEquals(1, forceGetOperations().size()); // cancel should be triggered via exception update(getLocalProject(), new MyUpdateCallback()); assertEquals(1, forceGetOperations().size()); } @Test(expected = ESException.class) public void testCancelErrorHandlerAtCommit() throws ESException, SerializationException { assertEquals(1, ESWorkspaceProviderImpl.getInstance().getWorkspace().getLocalProjects().size()); Configuration.getClientBehavior().setChecksumErrorHandler(ChecksumErrorHandler.CANCEL); final TestElement testElement = Create.testElement(); share(getUsersession(), getLocalProject()); addElement(getLocalProject(), testElement); Update.testElement(TestElementFeatures.name(), testElement, A); stopRecording(getLocalProject()); Update.testElement(TestElementFeatures.name(), testElement, BOOL); startRecording(getLocalProject()); // cancel should be triggered commitWithoutCommand(getLocalProject()); } @Test(expected = ESException.class) public void testCancelErrorHandlerAtUpdateAfterOneCommit() throws ESException, SerializationException { assertEquals(1, ESWorkspaceProviderImpl.getInstance().getWorkspace().getLocalProjects().size()); Configuration.getClientBehavior().setChecksumErrorHandler(ChecksumErrorHandler.CANCEL); final TestElement testElement = Create.testElement(); share(getUsersession(), getLocalProject()); final ESLocalProject checkout = checkout(getLocalProject()); addElement(getLocalProject(), testElement); Update.testElement(TestElementFeatures.name(), testElement, A); commitWithoutCommand(getLocalProject()); addElement(checkout, Create.testElement(BOOL)); update(checkout); commitWithoutCommand(checkout); stopRecording(getLocalProject()); Update.testElement(TestElementFeatures.name(), testElement, BOOL); startRecording(getLocalProject()); // cancel should be triggered via exception update(getLocalProject(), new MyUpdateCallback()); } @Test public void testCorrectChecksumsAtUpdate() throws ESException, SerializationException { assertEquals(1, ESWorkspaceProviderImpl.getInstance().getWorkspace().getLocalProjects().size()); Configuration.getClientBehavior().setChecksumErrorHandler(ChecksumErrorHandler.CANCEL); final TestElement testElement = Create.testElement(); share(getUsersession(), getLocalProject()); final ESLocalProject checkout = checkout(getLocalProject(), getLocalProject().getBaseVersion()); Add.toProject(getLocalProject(), testElement); Update.testElement(TestElementFeatures.name(), testElement, A); commitWithoutCommand(getLocalProject()); Add.toProject(checkout, Create.testElement()); update(checkout); commitWithoutCommand(checkout); final ESPrimaryVersionSpec baseVersion = update(getLocalProject()).getBaseVersion(); assertTrue(ModelUtil.areEqual( ESLocalProjectImpl.class.cast(getLocalProject()).toInternalAPI().getProject(), ESLocalProjectImpl.class.cast(checkout).toInternalAPI().getProject())); assertEquals( computeChecksum(getLocalProject()), ESPrimaryVersionSpecImpl.class.cast(baseVersion).toInternalAPI().getProjectStateChecksum()); } @Test public void testCorruptChecksumsAtUpdateWithLocalOperation() throws ESException, SerializationException { assertEquals(1, ESWorkspaceProviderImpl.getInstance().getWorkspace().getLocalProjects().size()); Configuration.getClientBehavior().setChecksumErrorHandler(ChecksumErrorHandler.AUTOCORRECT); final TestElement testElement = Create.testElement(); share(getUsersession(), getLocalProject()); final ESLocalProject checkout = checkout(getLocalProject()); addElement(getLocalProject(), testElement); Update.testElement(TestElementFeatures.name(), testElement, A); commitWithoutCommand(getLocalProject()); addElement(checkout, Create.testElement()); update(checkout); commitWithoutCommand(checkout); Update.testElement(TestElementFeatures.name(), testElement, BOOL); stopRecording(getLocalProject()); Update.testElement(TestElementFeatures.name(), testElement, C); startRecording(getLocalProject()); assertEquals(1, forceGetOperations().size()); // autocorrect should be triggered, will fail update(getLocalProject()); assertEquals(1, forceGetOperations().size()); } private class MyCommitCallback implements ESCommitCallback { public boolean baseVersionOutOfDate(ESLocalProject projectSpace, IProgressMonitor progressMonitor) { return ESCommitCallback.NOCALLBACK.baseVersionOutOfDate(projectSpace, progressMonitor); } public boolean inspectChanges(ESLocalProject projectSpace, ESChangePackage changePackage, ESModelElementIdToEObjectMapping idToEObjectMapping) { return ESCommitCallback.NOCALLBACK.inspectChanges(projectSpace, changePackage, idToEObjectMapping); } public void noLocalChanges(ESLocalProject projectSpace) { ESCommitCallback.NOCALLBACK.noLocalChanges(projectSpace); } } private class MyUpdateCallback implements ESUpdateCallback { public boolean inspectChanges(ESLocalProject projectSpace, List<ESChangePackage> changes, ESModelElementIdToEObjectMapping idToEObjectMapping) { return ESUpdateCallback.NOCALLBACK.inspectChanges(projectSpace, changes, idToEObjectMapping); } public void noChangesOnServer() { ESUpdateCallback.NOCALLBACK.noChangesOnServer(); } public boolean conflictOccurred(ESConflictSet changeConflictException, IProgressMonitor progressMonitor) { return ESUpdateCallback.NOCALLBACK.conflictOccurred(changeConflictException, progressMonitor); } } protected ESPrimaryVersionSpec commitWithoutCommand(final ESLocalProject project) throws ESException { return project.commit(SOME_COMMIT_MESSAGE, new MyCommitCallback(), new NullProgressMonitor()); } }