/*
* Copyright 2012 LinkedIn, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.linkedin.parseq;
import com.linkedin.parseq.promise.Promise;
import com.linkedin.parseq.promise.Promises;
import com.linkedin.parseq.promise.SettablePromise;
import org.testng.annotations.Test;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;
/**
* @author Chris Pettitt (cpettitt@linkedin.com)
*/
public class TestContext extends BaseEngineTest {
@Test
public void testCallContextOutsideOfTask() throws InterruptedException {
// Used to capture the context from a task
final AtomicReference<Context> contextRef = new AtomicReference<Context>();
// Used to indicate when the context reference has been captured
final CountDownLatch contextSetLatch = new CountDownLatch(1);
// Used to prevent our task from completing until after we've tried to
// use it's context
final CountDownLatch stopTaskLatch = new CountDownLatch(1);
// Task that we use to capture the context
final Task<?> task = new BaseTask<Object>() {
@Override
protected Promise<Object> run(final Context context) throws Exception {
contextRef.set(context);
contextSetLatch.countDown();
try {
stopTaskLatch.await();
} catch (InterruptedException e) {
// Ignore
}
return Promises.value(null);
}
};
getEngine().run(task);
assertTrue(contextSetLatch.await(5, TimeUnit.SECONDS));
final Context context = contextRef.get();
TestUtil.assertThrows(IllegalStateException.class, new ThrowingRunnable() {
@Override
public void run() throws Exception {
context.run(TestUtil.noop());
}
});
TestUtil.assertThrows(IllegalStateException.class, new ThrowingRunnable() {
@Override
public void run() throws Exception {
context.createTimer(1, TimeUnit.SECONDS, TestUtil.noop());
}
});
TestUtil.assertThrows(IllegalStateException.class, new ThrowingRunnable() {
@Override
public void run() throws Exception {
context.after(TestUtil.noop());
}
});
stopTaskLatch.countDown();
}
@Test
public void testCreateTimer() throws InterruptedException {
final String value = "done";
final SettablePromise<String> promise = Promises.settable();
final Task<?> timerTask = Task.action("timerTask", () -> promise.done(value));
final Task<String> task = new BaseTask<String>() {
@Override
public Promise<String> run(final Context context) throws Exception {
context.createTimer(50, TimeUnit.MILLISECONDS, timerTask);
return promise;
}
};
runAndWait("TestContext.testCreateTimer", task);
assertEquals(value, task.get());
}
@Test
public void testRun() throws InterruptedException {
final String value = "done";
final Task<String> innerTask = Task.callable("innerTask",() -> value);
final Task<String> task = new BaseTask<String>() {
@Override
public Promise<String> run(final Context context) throws Exception {
context.run(innerTask);
return innerTask;
}
};
runAndWait("TestContext.testRun", task);
assertEquals(value, task.get());
}
@Test
public void testAfter() throws InterruptedException {
final String predecessorValue = "predecessor done";
final String successorValue = "successor done";
final AtomicReference<String> predecessorValueRef = new AtomicReference<String>();
final Task<String> predecessorTask = Task.callable("predecessorTask", () -> predecessorValue);
final Task<String> successorTask = Task.callable("successorTask", () -> {
predecessorValueRef.set(predecessorTask.get());
return successorValue;
} );
final Task<String> task = new BaseTask<String>() {
@Override
public Promise<String> run(final Context context) throws Exception {
context.run(predecessorTask);
context.after(predecessorTask).run(successorTask);
return successorTask;
}
};
runAndWait("TestContext.testAfter", task);
assertEquals(successorValue, successorTask.get());
assertEquals(predecessorValue, predecessorValueRef.get());
}
@Test
public void testPrioritizedTasks() throws InterruptedException {
final Task<Queue<Integer>> task = new BaseTask<Queue<Integer>>() {
@Override
public Promise<Queue<Integer>> run(final Context context) throws Exception {
final SettablePromise<Queue<Integer>> promise = Promises.settable();
final Queue<Integer> queue = new LinkedList<Integer>();
final Task<?> t1 = enqueueTask(queue, 1);
t1.setPriority(-5);
final Task<?> t2 = enqueueTask(queue, 2);
t2.setPriority(10);
final Task<?> t3 = enqueueTask(queue, 3);
t3.setPriority(0);
context.run(t1, t2, t3);
context.after(t1, t2, t3).run(Task.action("done", () -> promise.done(queue)));
return promise;
}
};
runAndWait("TestContext.testPrioritizedTasks", task);
final Queue<Integer> ints = task.get();
assertEquals((Integer) 2, ints.poll());
assertEquals((Integer) 3, ints.poll());
assertEquals((Integer) 1, ints.poll());
assertTrue(ints.isEmpty());
}
private static <T> Task<?> enqueueTask(final Queue<T> queue, final T value) {
return Task.action("enqueue", () -> queue.add(value));
}
}