/*******************************************************************************
* Copyright (c) 2008, 2009 Nokia 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:
* Nokia - initial implementation. Oct. 2008
*******************************************************************************/
package org.eclipse.cdt.tests.dsf.concurrent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import junit.framework.Assert;
import junit.framework.AssertionFailedError;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Sequence;
import org.eclipse.cdt.tests.dsf.TestDsfExecutor;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.swt.widgets.Display;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Test whether a step in a sequence can control the progress monitor.
*/
public class DsfSequenceProgressTests {
private List<Throwable> fExceptions = Collections.synchronizedList(new ArrayList<Throwable>());
TestDsfExecutor fExecutor;
@Before
public void startExecutor() throws ExecutionException, InterruptedException {
fExecutor = new TestDsfExecutor();
}
@After
public void shutdownExecutor() throws ExecutionException, InterruptedException {
fExecutor.submit(new DsfRunnable() { public void run() {
fExecutor.shutdown();
}}).get();
if (fExecutor.exceptionsCaught()) {
Throwable[] exceptions = fExecutor.getExceptions();
throw new ExecutionException(exceptions[0]);
}
fExecutor = null;
}
// Create a counter for tracking number of steps performed and steps
// rolled back.
class IntegerHolder { int fInteger; }
final IntegerHolder stepCounter = new IntegerHolder();
final IntegerHolder rollBackCounter = new IntegerHolder();
class SleepStep extends Sequence.Step {
@Override
public int getTicks() {
return 6;
}
@Override public void execute(RequestMonitor requestMonitor) {
stepCounter.fInteger++;
sleep(getTicks(), requestMonitor, null);
requestMonitor.done();
}
@Override public void rollBack(RequestMonitor requestMonitor) {
rollBackCounter.fInteger++;
sleep(1, null, null);
requestMonitor.done();
}
}
class SleepStepWithProgress extends Sequence.StepWithProgress {
@Override
public int getTicks() {
return 6;
}
private final static int SUB_TICKS = 3;
@Override
public void execute(RequestMonitor rm, IProgressMonitor pm) {
stepCounter.fInteger++;
// step has its own sub-progress ticks which take the total ticks
// of this step and divides them into subticks
pm.beginTask(getTaskName() + ": ", SUB_TICKS);
sleep(SUB_TICKS, rm, pm);
rm.done();
pm.done();
}
@Override
public void rollBack(RequestMonitor rm) {
rollBackCounter.fInteger++;
sleep(2, null, null);
rm.done();
}
}
@Test
/**
* It's better to run this as a manual interactive test. Run this as a JUnit
* plugin test.<br>
* <br>
* In the test workbench, watch the progress bar in the Progress View.<br>
* <br>
* During execution of a StepWithProgress, you should see the progress bar
* is growing and you can have more responsive cancel.<br>
* <br>
* Meanwhile, during execution of a step without progress, you should see
* that progress bar does not grow and cancel does not work until end of the
* step.<br>
* <br>
* Also watch that when you cancel the progress bar during the execution of
* the sequence, you should see that "Rollback.." appears in the progress bar
* label.<br>
*/
public void sequenceProgressTest() throws InterruptedException, ExecutionException {
final Sequence.Step[] steps = new Sequence.Step[] {
new SleepStepWithProgress() {
@Override
public String getTaskName() {
return "StepWithProgress #1";
}},
new SleepStepWithProgress() {
@Override
public String getTaskName() {
return "StepWithProgress #2";
}},
new SleepStep() {
@Override
public String getTaskName() {
return "Step #3";
}},
new SleepStep() {
@Override
public String getTaskName() {
return "Step #4";
}},
};
fExceptions.clear();
Job myJob = new Job("Run test sequence") {
@Override
protected IStatus run(IProgressMonitor monitor) {
// Create and start.
Sequence sequence = new Sequence(fExecutor, monitor, "Run my sequence", "Rollback my sequence") {
@Override public Step[] getSteps() { return steps; }
};
fExecutor.execute(sequence);
// Block and wait for sequence to complete.
try {
sequence.get();
} catch (InterruptedException e) {
// ignore here.
} catch (ExecutionException e) {
// Expected exception, ignore here.
} finally {
try {
System.out.println("StepCounter: " + stepCounter.fInteger);
System.out.println("RollBackCounter: " + rollBackCounter.fInteger);
if (sequence.isCancelled())
Assert.assertTrue(
"Wrong number of steps were rolled back after cancellation.",
stepCounter.fInteger == rollBackCounter.fInteger);
else {
Assert.assertTrue(
"Wrong number of steps executed.",
stepCounter.fInteger == steps.length);
Assert.assertTrue(
"Some steps are mistakenly rolled back",
rollBackCounter.fInteger == 0);
}
// Check state from Future interface
Assert.assertTrue(sequence.isDone());
} catch (AssertionFailedError e) {
fExceptions.add(e);
}
}
return null;
}};
myJob.schedule();
// Wait for the job to finish
waitForJob(myJob);
// now throw any assertion errors.
if (fExceptions.size() > 0)
throw (AssertionFailedError)fExceptions.get(0);
}
private static void sleep(int seconds, RequestMonitor rm, IProgressMonitor pm) {
try {
for (int i = 0; i < seconds; i++) {
if (pm != null)
pm.subTask("subStep - " + (i+1));
Thread.sleep(1000);
if (pm != null) {
pm.worked(1);
if (pm.isCanceled()) {
return;
}
}
if (rm != null && rm.isCanceled()) {
return;
}
}
} catch (InterruptedException e) {
// ignore
}
}
// Wait for a job to finish without possible blocking of UI thread.
//
private static void waitForJob(Job job) {
Display display = Display.getCurrent();
while (true) {
IStatus status = job.getResult();
if (status != null)
break;
if (display != null) {
while (display.readAndDispatch()) ;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
job.cancel();
break;
}
}
}
}