blob: f949dab13c1822ff17ba7ec3e10a79003d007653 [file] [log] [blame]
/*
* Copyright (c) 2015 Eike Stepper (Berlin, Germany) 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:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.oomph.setup.sync.tests;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import org.eclipse.oomph.base.util.BaseUtil;
import org.eclipse.oomph.setup.CompoundTask;
import org.eclipse.oomph.setup.PreferenceTask;
import org.eclipse.oomph.setup.SetupFactory;
import org.eclipse.oomph.setup.SetupPackage;
import org.eclipse.oomph.setup.SetupTask;
import org.eclipse.oomph.setup.User;
import org.eclipse.oomph.setup.internal.sync.DataProvider;
import org.eclipse.oomph.setup.internal.sync.DataProvider.NotCurrentException;
import org.eclipse.oomph.setup.internal.sync.LocalDataProvider;
import org.eclipse.oomph.setup.internal.sync.SnychronizerException;
import org.eclipse.oomph.setup.internal.sync.SnychronizerListener;
import org.eclipse.oomph.setup.internal.sync.SyncUtil;
import org.eclipse.oomph.setup.internal.sync.Synchronization;
import org.eclipse.oomph.setup.internal.sync.Synchronizer;
import org.eclipse.oomph.setup.sync.RemoteData;
import org.eclipse.oomph.setup.sync.SyncAction;
import org.eclipse.oomph.setup.sync.SyncActionType;
import org.eclipse.oomph.setup.sync.SyncDelta;
import org.eclipse.oomph.setup.sync.SyncPolicy;
import org.eclipse.oomph.tests.AbstractTest;
import org.eclipse.oomph.util.IOUtil;
import org.eclipse.oomph.util.PropertiesUtil;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.hamcrest.CoreMatchers;
import org.hamcrest.core.IsNull;
import org.junit.Assert;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Eike Stepper
*/
public final class TestWorkstation
{
private static final boolean REUSE_USER_HOMES = true;
private static TestWorkstation lastWorkstation;
private final ResourceSet resourceSet = SyncUtil.createResourceSet();
private final Map<Integer, TestWorkstation> workstations;
private final int id;
private final File userHome;
private final File userSetup;
private final TestSynchronizer synchronizer;
private File remoteFile;
private User user;
public TestWorkstation(Map<Integer, TestWorkstation> workstations, int id) throws Exception
{
this.id = id;
this.workstations = workstations;
userHome = createUserHome();
userSetup = new File(userHome, "user.setup");
DataProvider localDataProvider = new LocalDataProvider(userSetup);
DataProvider remoteDataProvider = TestServer.getRemoteDataProvider();
synchronizer = new TestSynchronizer(localDataProvider, remoteDataProvider, userHome);
log("Create workstation " + userHome);
}
public File getUserHome()
{
return userHome;
}
public User getUser()
{
if (user == null)
{
URI uri = URI.createFileURI(userSetup.getAbsolutePath());
if (resourceSet.getURIConverter().exists(uri, null))
{
user = loadObject(uri, SetupPackage.Literals.USER);
}
else
{
user = SetupFactory.eINSTANCE.createUser();
user.setName("Test User");
Resource resource = resourceSet.createResource(uri);
resource.getContents().add(user);
}
}
return user;
}
public List<PreferenceTask> getPreferenceTasks()
{
List<PreferenceTask> result = new ArrayList<PreferenceTask>();
collectPreferenceTasks(getUser().getSetupTasks(), result);
return result;
}
public String get(String key)
{
return getPreference(getUser().getSetupTasks(), key);
}
public TestWorkstation set(String key, String value)
{
log("Set " + key + " = " + value);
EList<SetupTask> tasks = getUser().getSetupTasks();
for (SetupTask task : tasks)
{
if (task instanceof PreferenceTask)
{
PreferenceTask preferenceTask = (PreferenceTask)task;
if (key.equals(preferenceTask.getKey()))
{
preferenceTask.setValue(value);
return this;
}
}
}
PreferenceTask preferenceTask = SetupFactory.eINSTANCE.createPreferenceTask();
preferenceTask.setKey(key);
preferenceTask.setValue(value);
tasks.add(preferenceTask);
return this;
}
public TestWorkstation remove(String key)
{
log("Remove " + key);
for (SetupTask task : getUser().getSetupTasks())
{
if (task instanceof PreferenceTask)
{
PreferenceTask preferenceTask = (PreferenceTask)task;
if (key.equals(preferenceTask.getKey()))
{
EcoreUtil.remove(preferenceTask);
return this;
}
}
}
return this;
}
public TestWorkstation save()
{
if (user != null)
{
log("Save");
BaseUtil.saveEObject(user);
}
return this;
}
public TestSynchronization synchronize() throws Exception
{
return synchronizer.synchronize();
}
public TestWorkstation commit() throws Exception
{
return synchronize().commitAnd();
}
public TestSynchronization commitFail(FailureHandler handler) throws Exception
{
return synchronize().commitFail(handler);
}
public TestWorkstation assertCount(int count)
{
assertThat(getPreferenceTasks().size(), CoreMatchers.is(count));
return this;
}
public TestWorkstation assertSet(String key, String value)
{
assertThat(get(key), CoreMatchers.is(value));
return this;
}
public TestWorkstation assertRemoved(String key)
{
assertThat(get(key), IsNull.nullValue());
return this;
}
public TestWorkstation assertIncluded(String key) throws IOException
{
Map<String, SyncPolicy> preferencePolicies = getPreferencePolicies();
assertThat(preferencePolicies.get(key), CoreMatchers.is(SyncPolicy.INCLUDE));
return this;
}
public TestWorkstation assertExcluded(String key) throws IOException
{
Map<String, SyncPolicy> preferencePolicies = getPreferencePolicies();
assertThat(preferencePolicies.get(key), CoreMatchers.is(SyncPolicy.EXCLUDE));
return this;
}
public TestWorkstation assertNoPolicy(String key) throws IOException
{
Map<String, SyncPolicy> preferencePolicies = getPreferencePolicies();
assertThat(preferencePolicies.get(key), IsNull.nullValue());
return this;
}
public Map<String, SyncPolicy> getPreferencePolicies() throws IOException
{
RemoteData remoteData = getRemoteData();
EMap<String, SyncPolicy> policies = remoteData.getPolicies();
Map<String, SyncPolicy> preferencePolicies = new HashMap<String, SyncPolicy>();
// Make sure that even policies of remotely removed tasks can be found by preference key.
for (TestWorkstation workstation : workstations.values())
{
if (workstation != this)
{
collectPreferencePolicies(preferencePolicies, policies, workstation.getPreferenceTasks());
}
}
collectPreferencePolicies(preferencePolicies, policies, getPreferenceTasks());
collectPreferencePolicies(preferencePolicies, policies, remoteData.getSetupTasks());
return preferencePolicies;
}
private void collectPreferencePolicies(Map<String, SyncPolicy> preferencePolicies, EMap<String, SyncPolicy> policies, List<? extends SetupTask> tasks)
{
for (SetupTask task : tasks)
{
if (task instanceof PreferenceTask)
{
PreferenceTask preferenceTask = (PreferenceTask)task;
String id = preferenceTask.getID();
SyncPolicy policy = policies.get(id);
if (policy != null)
{
String key = preferenceTask.getKey();
preferencePolicies.put(key, policy);
}
}
}
}
public RemoteData getRemoteData() throws IOException
{
if (remoteFile == null)
{
remoteFile = File.createTempFile("remote-data-", ".tmp");
remoteFile.deleteOnExit();
DataProvider dataProvider = synchronizer.getRemoteSnapshot().getDataProvider();
dataProvider.update(remoteFile);
}
return loadObject(URI.createFileURI(remoteFile.getAbsolutePath()), Synchronization.REMOTE_DATA_TYPE);
}
public TestWorkstation log(Object msg)
{
if (this != lastWorkstation)
{
AbstractTest.log();
AbstractTest.log(this + ":");
}
lastWorkstation = this;
AbstractTest.log(" " + msg);
return this;
}
@Override
public String toString()
{
return "Workstation " + id;
}
private <T extends EObject> T loadObject(URI uri, EClassifier classifier)
{
Resource resource = resourceSet.getResource(uri, true);
return BaseUtil.getObjectByType(resource.getContents(), classifier);
}
private File createUserHome()
{
if (TestWorkstation.REUSE_USER_HOMES)
{
File folder = new File(PropertiesUtil.getTmpDir(), "test-user-" + id);
IOUtil.deleteBestEffort(folder, false);
folder.mkdirs();
return folder;
}
File folder = SynchronizerTests.createTempFolder();
folder.deleteOnExit();
return folder;
}
private String getPreference(EList<SetupTask> tasks, String key)
{
for (SetupTask task : tasks)
{
if (task instanceof PreferenceTask)
{
PreferenceTask preferenceTask = (PreferenceTask)task;
if (key.equals(preferenceTask.getKey()))
{
return preferenceTask.getValue();
}
}
else if (task instanceof CompoundTask)
{
CompoundTask compoundTask = (CompoundTask)task;
String value = getPreference(compoundTask.getSetupTasks(), key);
if (value != null)
{
return value;
}
}
}
return null;
}
private void collectPreferenceTasks(EList<SetupTask> tasks, List<PreferenceTask> result)
{
for (SetupTask task : tasks)
{
if (task instanceof PreferenceTask)
{
PreferenceTask preferenceTask = (PreferenceTask)task;
result.add(preferenceTask);
}
else if (task instanceof CompoundTask)
{
CompoundTask compoundTask = (CompoundTask)task;
collectPreferenceTasks(compoundTask.getSetupTasks(), result);
}
}
}
/**
* @author Eike Stepper
*/
public interface FailureHandler
{
public void handleFailure(Exception ex) throws Exception;
/**
* @author Eike Stepper
*/
public static class Expect implements FailureHandler
{
private final Class<? extends Exception> expectedException;
public Expect(Class<? extends Exception> expectedException)
{
this.expectedException = expectedException;
}
public final void handleFailure(Exception ex) throws Exception
{
assertThat(ex, CoreMatchers.is(instanceOf(expectedException)));
handleFailure();
}
protected void handleFailure() throws Exception
{
}
}
}
/**
* @author Eike Stepper
*/
public final class TestSynchronizer extends Synchronizer
{
private final SnychronizerListener listener = new SnychronizerListener()
{
public void syncStarted(Synchronization synchronization)
{
log("Synchronize");
}
public void actionResolved(Synchronization synchronization, SyncAction action, String id)
{
log("Resolve " + id + " from " + action.getComputedType() + " to " + action.getResolvedType());
}
public void commitStarted(Synchronization synchronization)
{
log("Commit");
}
public void commitFinished(Synchronization synchronization, Throwable t)
{
if (t != null)
{
log(t.getMessage());
}
else
{
if (remoteFile != null)
{
remoteFile.delete();
remoteFile = null;
}
}
user = null;
URI uri = URI.createFileURI(userSetup.getAbsolutePath());
Resource resource = resourceSet.getResource(uri, false);
if (resource != null)
{
resourceSet.getResources().remove(resource);
}
}
public void lockReleased(Synchronization synchronization)
{
}
};
public TestSynchronizer(DataProvider localDataProvider, DataProvider remoteDataProvider, File syncFolder)
{
super(localDataProvider, remoteDataProvider, syncFolder);
addListener(listener);
}
public TestWorkstation ws()
{
return TestWorkstation.this;
}
@Override
protected TestSynchronization createSynchronization() throws IOException
{
return new TestSynchronization(this);
}
@Override
public synchronized TestSynchronization synchronize() throws IOException, SnychronizerException
{
return (TestSynchronization)super.synchronize();
}
}
/**
* @author Eike Stepper
*/
public final class TestSynchronization extends Synchronization
{
public TestSynchronization(TestSynchronizer synchronizer) throws IOException
{
super(synchronizer);
}
@Override
public TestSynchronizer getSynchronizer()
{
return (TestSynchronizer)super.getSynchronizer();
}
public SyncAction getPreferenceAction(String key)
{
for (Map.Entry<String, SyncAction> entry : getActions().entrySet())
{
SyncAction action = entry.getValue();
if (isPreferenceAction(key, action))
{
return action;
}
}
return null;
}
private boolean isPreferenceAction(String key, SyncAction action)
{
if (action != null)
{
if (isPreferenceDelta(key, action.getLocalDelta()))
{
return true;
}
if (isPreferenceDelta(key, action.getRemoteDelta()))
{
return true;
}
}
return false;
}
private boolean isPreferenceDelta(String key, SyncDelta delta)
{
if (delta != null)
{
if (isPreferenceTask(key, delta.getOldTask()))
{
return true;
}
if (isPreferenceTask(key, delta.getNewTask()))
{
return true;
}
}
return false;
}
private boolean isPreferenceTask(String key, SetupTask task)
{
if (task instanceof PreferenceTask)
{
PreferenceTask preferenceTask = (PreferenceTask)task;
if (key.equals(preferenceTask.getKey()))
{
return true;
}
}
return false;
}
@Override
public TestSynchronization resolve(String id, SyncActionType resolvedType)
{
super.resolve(id, resolvedType);
return this;
}
public TestSynchronization resolvePreference(String key, SyncActionType resolvedType)
{
SyncAction action = getPreferenceAction(key);
if (action != null)
{
String id = getID(action);
if (id != null)
{
resolve(id, resolvedType);
}
}
return this;
}
public TestSynchronization exclude(String key)
{
SyncAction action = getPreferenceAction(key);
if (action != null)
{
String id = getID(action);
if (id != null)
{
resolve(id, SyncActionType.EXCLUDE);
}
}
return this;
}
public TestSynchronization pickLocal(String key)
{
SyncAction action = getPreferenceAction(key);
if (action != null)
{
SyncDelta delta = action.getLocalDelta();
SyncActionType location = SyncActionType.SET_LOCAL;
pick(action, delta, location);
}
return this;
}
public TestSynchronization pickRemote(String key)
{
SyncAction action = getPreferenceAction(key);
if (action != null)
{
SyncDelta delta = action.getRemoteDelta();
SyncActionType location = SyncActionType.SET_REMOTE;
pick(action, delta, location);
}
return this;
}
private void pick(SyncAction action, SyncDelta delta, SyncActionType location)
{
String id = getID(action);
if (id != null)
{
if (delta.getNewTask() == null)
{
resolve(id, SyncActionType.REMOVE);
}
else
{
resolve(id, location);
}
}
}
public TestWorkstation commitAnd() throws IOException, NotCurrentException
{
commit();
return TestWorkstation.this;
}
public TestSynchronization commitFail(FailureHandler handler) throws Exception
{
try
{
commit();
}
catch (Exception ex)
{
handler.handleFailure(ex);
return this;
}
Assert.fail("Commit should have failed");
return this;
}
public TestSynchronization assertActions(int count)
{
assertThat(getActions().size(), CoreMatchers.is(count));
return this;
}
public TestSynchronization assertUnresolved(int count)
{
assertThat(getUnresolvedActions().size(), CoreMatchers.is(count));
return this;
}
public TestSynchronization assertComputed(String key, SyncActionType type)
{
assertThat(getPreferenceAction(key).getComputedType(), CoreMatchers.is(type));
return this;
}
public TestSynchronization assertResolved(String key, SyncActionType type)
{
assertThat(getPreferenceAction(key).getResolvedType(), CoreMatchers.is(type));
return this;
}
public TestSynchronization assertEffective(String key, SyncActionType type)
{
assertThat(getPreferenceAction(key).getEffectiveType(), CoreMatchers.is(type));
return this;
}
}
}