package org.fungsi.concurrent; import org.fungsi.Either; import org.fungsi.Unit; import org.junit.Before; import org.junit.Test; import java.time.Duration; import java.time.Instant; import java.util.concurrent.Executors; import static org.fungsi.Matchers.*; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; public class ConcurrentTest { private Worker worker; private Timer timer; @Before public void setUp() throws Exception { worker = Workers.wrap(Executors.newSingleThreadExecutor()); timer = Timers.wrap(Executors.newSingleThreadScheduledExecutor()); } @Test public void testGet() throws Exception { Future<Unit> fut = worker.cast(() -> Thread.sleep(500)); Instant start = Instant.now(); fut.get(); Instant end = Instant.now(); assertThat(Duration.between(start, end), about(Duration.ofMillis(500), Duration.ofMillis(50))); } @Test public void testGetWithTimeout() throws Exception { Future<Unit> fut = worker.cast(() -> Thread.sleep(500)); Instant start = Instant.now(); fut.get(Duration.ofMillis(600)); Instant end = Instant.now(); assertThat(Duration.between(start, end), about(Duration.ofMillis(500), Duration.ofMillis(10))); } @Test(expected = TimeoutException.class) public void testFailingGetWithTimeout() throws Exception { Future<Unit> fut = worker.cast(() -> Thread.sleep(500)); fut.get(Duration.ofMillis(100)); } @Test public void testSuccess() throws Exception { Future<String> fut = worker.submit(() -> { Thread.sleep(500); return "mdr"; }); assertThat(fut.poll(), notPresent()); Thread.sleep(1000); assertThat(fut.poll(), isPresent()); Either<String, Throwable> result = fut.poll().get(); assertThat(result, isLeft()); assertThat(result.left(), is("mdr")); } @Test(expected = NullPointerException.class) public void testFailure() throws Exception { Future<String> fut = worker.submit(() -> { Thread.sleep(500); throw new NullPointerException(); }); assertThat(fut.poll(), notPresent()); Thread.sleep(1000); assertThat(fut.poll(), isPresent()); Either<String, Throwable> result = fut.poll().get(); assertThat(result, isRight()); Either.unsafe(result); } @Test public void testWithin() throws Exception { Future<Unit> fut = worker.cast(() -> Thread.sleep(750)); Future<Unit> fut2 = fut.within(Duration.ofMillis(500), timer); Thread.sleep(1000); assertThat(fut, isSuccess()); assertThat(fut2, isFailure()); } @Test public void testExecute() throws Exception { /** * NOTE(Blackrush): * `worker` here is only single threaded * unlike nested `submit`, `execute` is freely nestable even on a single thread */ Future<String> future = worker.execute(() -> { Thread.sleep(100); return worker.submit(() -> { Thread.sleep(100); return "abc"; }); }); Instant start = Instant.now(); String result = future.get(); Instant end = Instant.now(); assertThat("result", result, equalTo("abc")); assertThat("duration", Duration.between(start, end), about(Duration.ofMillis(200), Duration.ofMillis(50))); } @Test(expected = TimeoutException.class) public void testNestedSubmit() throws Exception { /** * NOTE(Blackrush): * `worker` here is only single threaded * submitting nested task would starve `worker` and led to a TimeoutException */ Future<String> future = worker.submit(() -> { Thread.sleep(100); Future<String> nested = worker.submit(() -> { Thread.sleep(100); return "abc"; }); return nested.get(); }); future.get(Duration.ofMillis(500)); } }