/*******************************************************************************
* Copyright (c) 2015 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.emf.compare.ide.ui.tests.logical.resolver;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.FutureCallback;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.emf.compare.ide.ui.internal.logical.resolver.IComputation;
import org.eclipse.emf.compare.ide.ui.internal.logical.resolver.ResourceComputationScheduler;
import org.eclipse.emf.compare.ide.ui.internal.logical.resolver.ResourceComputationScheduler.ShutdownStatus;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@SuppressWarnings("nls")
public class ResourceComputationSchedulerTest {
protected ResourceComputationScheduler<String> scheduler;
protected boolean flag;
@Test
public void testInitializeCanBeCalledSeveralTimes() {
scheduler.initialize();
scheduler.initialize();
}
@Test
public void testIsInitializedBeforeInit() {
assertFalse(scheduler.isInitialized());
}
@Test
public void testIsInitializedAfterInit() {
scheduler.initialize();
assertTrue(scheduler.isInitialized());
}
@Test
public void testIsInitializedAfterDispose() {
scheduler.initialize();
scheduler.dispose();
assertFalse(scheduler.isInitialized());
}
@Test
public void testBasicExecution() throws Exception {
scheduler.initialize();
flag = false;
Integer result = scheduler.call(new Callable<Integer>() {
public Integer call() throws Exception {
return Integer.valueOf(42);
}
}, new Runnable() {
public void run() {
flag = true;
}
});
assertEquals(Integer.valueOf(42), result);
// Flag will be true only if the post-treatment has been called...
assertTrue(flag);
}
@Test(expected = OperationCanceledException.class)
public void testInterruptedExceptionInCallCausesOperationCanceledException() throws Exception {
scheduler.initialize();
scheduler.call(new Callable<String>() {
public String call() throws Exception {
throw new InterruptedException();
}
}, null);
}
@Test(expected = OperationCanceledException.class)
public void testOperationCanceledExceptionCall() throws Exception {
scheduler.initialize();
scheduler.call(new Callable<String>() {
public String call() throws Exception {
throw new OperationCanceledException();
}
}, null);
}
@Test
public void testPostTreatmentIsCalledWhenExceptionInTreatment() throws Exception {
scheduler.initialize();
flag = false;
try {
scheduler.call(new Callable<Integer>() {
public Integer call() throws Exception {
throw new Exception("For test");
}
}, new Runnable() {
public void run() {
flag = true;
}
});
} catch (RuntimeException e) {
assertEquals("For test", e.getCause().getMessage());
}
// Flag will be true only if the post-treatment has been called...
assertTrue(flag);
}
@Test
public void testPostTreatmentCanBeNull() throws Exception {
scheduler.initialize();
Integer result = scheduler.call(new Callable<Integer>() {
public Integer call() throws Exception {
return Integer.valueOf(42);
}
}, null);
assertEquals(Integer.valueOf(42), result);
}
@Test(expected = NullPointerException.class)
public void testCallableCannotBeNull() throws Exception {
scheduler.initialize();
scheduler.call(null, new Runnable() {
public void run() {
// Nothing
}
});
}
@Test
public void testComputedElements() {
scheduler.initialize();
assertTrue(scheduler.getComputedElements().isEmpty());
scheduler.setComputedElements(Arrays.asList("a", "b", "c"));
assertEquals(ImmutableSet.of("a", "b", "c"), scheduler.getComputedElements());
scheduler.clearComputedElements();
assertTrue(scheduler.getComputedElements().isEmpty());
}
@Test
public void testComputeOneSuccess() throws Exception {
scheduler.initialize();
final CompStatus desc = new CompStatus();
Integer result = scheduler.call(new Callable<Integer>() {
public Integer call() throws Exception {
scheduler.computeAll(Arrays.asList(new TestSuccessfulComputation(desc, "comp1")));
assertEquals(ImmutableSet.of("comp1"), scheduler.getComputedElements());
return Integer.valueOf(42);
}
}, null);
checkSuccess(desc);
assertEquals(Integer.valueOf(42), result);
assertTrue(scheduler.getComputedElements().isEmpty());
}
@Test
public void testComputeSeveralSuccess() throws Exception {
scheduler.initialize();
final CompStatus[] statuses = new CompStatus[10];
final List<TestSuccessfulComputation> computations = Lists.newArrayList();
for (int i = 0; i < 10; i++) {
statuses[i] = new CompStatus();
computations.add(new TestSuccessfulComputation(statuses[i], "comp" + i));
}
Integer result = scheduler.call(new Callable<Integer>() {
public Integer call() throws Exception {
for (int i = 0; i < 10; i++) {
scheduler.computeAll(computations);
}
assertEquals(ImmutableSet.of("comp0", "comp1", "comp2", "comp3", "comp4", "comp5", "comp6",
"comp7", "comp8", "comp9"), scheduler.getComputedElements());
return Integer.valueOf(42);
}
}, null);
for (int i = 0; i < 10; i++) {
checkSuccess(statuses[i]);
}
assertEquals(Integer.valueOf(42), result);
assertTrue(scheduler.getComputedElements().isEmpty());
}
@Test
public void testPostTreatmentOnFailureIsCalledOnOneFailedComputation() throws Exception {
scheduler.initialize();
final CompStatus cs = new CompStatus();
Integer result = scheduler.call(new Callable<Integer>() {
public Integer call() throws Exception {
scheduler.computeAll(Arrays.asList(new TestFailedComputation(cs, "fail1")));
assertEquals(ImmutableSet.of("fail1"), scheduler.getComputedElements());
return Integer.valueOf(42);
}
}, null);
checkFailure(cs);
assertEquals(Integer.valueOf(42), result);
assertTrue(scheduler.getComputedElements().isEmpty());
}
@Test
public void testPostTreatmentOnFailureIsCalledOnAllFailingComputations() throws Exception {
scheduler.initialize();
final CompStatus[] statuses = new CompStatus[10];
final List<TestFailedComputation> computations = Lists.newArrayList();
for (int i = 0; i < 10; i++) {
statuses[i] = new CompStatus();
computations.add(new TestFailedComputation(statuses[i], "fail" + i));
}
Integer result = scheduler.call(new Callable<Integer>() {
public Integer call() throws Exception {
scheduler.computeAll(computations);
assertEquals(ImmutableSet.of("fail0", "fail1", "fail2", "fail3", "fail4", "fail5", "fail6",
"fail7", "fail8", "fail9"), scheduler.getComputedElements());
return Integer.valueOf(42);
}
}, null);
for (int i = 0; i < 10; i++) {
checkFailure(statuses[i]);
}
assertEquals(Integer.valueOf(42), result);
assertTrue(scheduler.getComputedElements().isEmpty());
}
@Test
public void testRunOneSuccess() throws Exception {
scheduler.initialize();
final CompStatus desc = new CompStatus();
Integer result = scheduler.call(new Callable<Integer>() {
public Integer call() throws Exception {
scheduler.runAll(Arrays.asList(new UninterruptibleRunnable(desc)));
assertTrue(scheduler.getComputedElements().isEmpty());
return Integer.valueOf(42);
}
}, null);
checkSuccess(desc);
assertEquals(Integer.valueOf(42), result);
assertTrue(scheduler.getComputedElements().isEmpty());
}
@Test
public void testRunSeveralSuccess() throws Exception {
scheduler.initialize();
final CompStatus[] statuses = new CompStatus[10];
final List<UninterruptibleRunnable> toBeRun = Lists.newArrayList();
for (int i = 0; i < 10; i++) {
statuses[i] = new CompStatus();
toBeRun.add(new UninterruptibleRunnable(statuses[i]));
}
Integer result = scheduler.call(new Callable<Integer>() {
public Integer call() throws Exception {
scheduler.runAll(toBeRun);
assertTrue(scheduler.getComputedElements().isEmpty());
return Integer.valueOf(42);
}
}, null);
for (int i = 0; i < 10; i++) {
checkSuccess(statuses[i]);
}
assertEquals(Integer.valueOf(42), result);
assertTrue(scheduler.getComputedElements().isEmpty());
}
@Test(expected = NullPointerException.class)
public void testScheduleComputationCannotRunOutsideCall() throws Exception {
scheduler.initialize();
final CompStatus desc = new CompStatus();
scheduler.scheduleComputation(new TestSuccessfulComputation(desc, "comp"));
}
protected void checkSuccess(CompStatus state) {
assertEquals(1, state.getCallCount());
assertFalse(state.isInterrupted());
if (!state.isSuccess() || state.isFailed()) {
fail(state.getMessage());
}
assertEquals("as expected", state.getMessage());
}
protected void checkFailure(CompStatus state) {
assertEquals(1, state.getCallCount());
assertFalse(state.isInterrupted());
if (state.isSuccess() || !state.isFailed()) {
fail(state.getMessage());
}
assertEquals("as expected", state.getMessage());
}
protected void checkInterruptedAndSuccess(CompStatus state) {
assertEquals(1, state.getCallCount());
assertTrue(state.isInterrupted());
if (!state.isSuccess() || state.isFailed()) {
fail(state.getMessage());
}
assertEquals("as expected", state.getMessage());
}
protected void checkInterruptedAndFailure(CompStatus state) {
assertEquals(1, state.getCallCount());
assertTrue(state.isInterrupted());
if (state.isSuccess() || !state.isFailed()) {
fail(state.getMessage());
}
assertEquals("as expected", state.getMessage());
}
@Before
public void setUp() {
scheduler = new ResourceComputationScheduler<String>(10, TimeUnit.MILLISECONDS, null);
}
@After
public void tearDown() {
scheduler.dispose();
}
/**
* A test computation that is successful and updates it {@link CompStatus} accordingly when its
* post-treatment is called.
*
* @author <a href="mailto:laurent.delaigue@obeo.fr">Laurent Delaigue</a>
*/
private final class TestSuccessfulComputation implements IComputation<String> {
private final CompStatus cs;
private final String name;
private TestSuccessfulComputation(CompStatus cs, String name) {
this.cs = cs;
this.name = name;
}
public void run() {
cs.addCall();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
cs.interrupt();
}
}
public FutureCallback<Object> getPostTreatment() {
return new FutureCallback<Object>() {
public void onFailure(Throwable t) {
cs.fail("onFailure() called on computation " + name + ", should have been onSuccess().");
}
public void onSuccess(Object r) {
cs.success("as expected");
}
};
}
public String getKey() {
return name;
}
}
/**
* A test computation that systematically throws an exception when run, and updates its {@link CompStatus}
* accordingly if onFailure() is called on its post-treatment.
*
* @author <a href="mailto:laurent.delaigue@obeo.fr">Laurent Delaigue</a>
*/
private final class TestFailedComputation implements IComputation<String> {
private final CompStatus cs;
private final String name;
private TestFailedComputation(CompStatus desc, String name) {
this.cs = desc;
this.name = name;
}
public void run() {
cs.addCall();
throw new RuntimeException("Error for tests in computation " + name);
}
public FutureCallback<Object> getPostTreatment() {
return new FutureCallback<Object>() {
public void onFailure(Throwable t) {
cs.fail("as expected");
}
public void onSuccess(Object r) {
cs.success(
"onSuccess() called on computation " + name + ", should have been onFailure().");
}
};
}
public String getKey() {
return name;
}
}
/**
* A test computation that is successful and updates it {@link CompStatus} accordingly when its
* post-treatment is called.
*
* @author <a href="mailto:laurent.delaigue@obeo.fr">Laurent Delaigue</a>
*/
private final class UninterruptibleRunnable implements Runnable {
private final CompStatus cs;
private UninterruptibleRunnable(CompStatus desc) {
this.cs = desc;
}
public void run() {
cs.addCall();
cs.success("as expected");
}
}
/**
* Computation Status.
*
* @author <a href="mailto:laurent.delaigue@obeo.fr">Laurent Delaigue</a>
*/
protected static class CompStatus {
private boolean success = false;
private boolean failed = false;
private int callCount;
private boolean interrupted;
private ShutdownStatus shutdownStatus;
private String message;
public String getMessage() {
return message;
}
public boolean isSuccess() {
return success;
}
public synchronized void addCall() {
callCount++;
}
public int getCallCount() {
return callCount;
}
public void success(String msg) {
this.success = true;
this.failed = false;
message = msg;
}
public boolean isFailed() {
return failed;
}
public void fail(String msg) {
this.failed = true;
this.success = false;
message = msg;
}
public void interrupt() {
interrupted = true;
}
public boolean isInterrupted() {
return interrupted;
}
public void setShutdownStatus(ShutdownStatus shutdownStatus) {
this.shutdownStatus = shutdownStatus;
}
public ShutdownStatus getShutdownStatus() {
return shutdownStatus;
}
}
}