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.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());
	}
}

@@ -254,16 +254,65 @@
 hers.*;%0A
+import static org.hamcrest.CoreMatchers.equalTo;%0A
 import s
@@ -2542,11 +2542,1393 @@
 e());%0A%09%7D
+%0A%0A    @Test%0A    public void testExecute() throws Exception %7B%0A        /**%0A         * NOTE(Blackrush):%0A         *   %60worker%60 here is only single threaded%0A         *   unlike nested %60submit%60, %60execute%60 is freely nestable even on a single thread%0A         */%0A%0A        Future%3CString%3E future = worker.execute(() -%3E %7B%0A            Thread.sleep(100);%0A%0A            return worker.submit(() -%3E %7B%0A                Thread.sleep(100);%0A%0A                return %22abc%22;%0A            %7D);%0A        %7D);%0A%0A        Instant start = Instant.now();%0A        String result = future.get();%0A        Instant end = Instant.now();%0A%0A        assertThat(%22result%22, result, equalTo(%22abc%22));%0A        assertThat(%22duration%22, Duration.between(start, end), about(Duration.ofMillis(200), Duration.ofMillis(50)));%0A    %7D%0A%0A    @Test(expected = TimeoutException.class)%0A    public void testNestedSubmit() throws Exception %7B%0A        /**%0A         * NOTE(Blackrush):%0A         *   %60worker%60 here is only single threaded%0A         *   submitting nested task would starve %60worker%60 and led to a TimeoutException%0A         */%0A%0A        Future%3CString%3E future = worker.submit(() -%3E %7B%0A            Thread.sleep(100);%0A%0A            Future%3CString%3E nested = worker.submit(() -%3E %7B%0A                Thread.sleep(100);%0A                return %22abc%22;%0A            %7D);%0A%0A            return nested.get();%0A        %7D);%0A%0A        future.get(Duration.ofMillis(500));%0A    %7D
 %0A%7D%0A

test execute and nested submit