Commit 2cadbd7b authored by Rajiv Prabhakar's avatar Rajiv Prabhakar
Browse files

v1.14.0: ThreadUtilc.sleep(duration) added. Unit tests cleaned up

parent c7646d51
Pipeline #14868560 passed with stage
in 1 minute and 24 seconds
......@@ -6,7 +6,7 @@
<groupId>com.rajivprab</groupId>
<artifactId>cava</artifactId>
<version>1.13.1</version>
<version>1.14.0</version>
<name>Cava: Clean Java</name>
<description>A library that enables users to write minimal, clean and simple Java</description>
......
......@@ -14,6 +14,10 @@ import java.util.concurrent.*;
public class ThreadUtilc {
private static final Log log = LogFactory.getLog(ThreadUtilc.class);
public static void sleep(Duration duration) {
sleep(duration.toMillis());
}
public static void sleep(long millis) {
try {
Thread.sleep(millis);
......
package org.rajivprab.cava;
import com.google.common.truth.Truth;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Assert;
import org.junit.Test;
import org.rajivprab.cava.ThreadUtilc.InterruptException;
......@@ -10,8 +8,8 @@ import org.rajivprab.cava.ThreadUtilc.TimeException;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
......@@ -24,25 +22,22 @@ import java.util.stream.IntStream;
* Created by rprabhakar on 12/22/15.
*/
public class ThreadUtilcTest extends TestBase {
private static final Log log = LogFactory.getLog(ThreadUtilcTest.class);
private static final Duration SLEEP_TIME = Duration.ofMillis(1000);
private static final Duration SLEEP_TIME_UNCERTAINTY = Duration.ofMillis(300);
private static final Duration TEST_DURATION = Duration.ofMillis(1000);
private static final int RETURN_VALUE = 42;
private static final Random RNG = new Random();
// ----------------- slepp ----------------
@Test
public void threadUtilcSleep_allGood() throws Exception {
Instant start = Instant.now();
int result = getSleepTask(SLEEP_TIME).get();
Duration duration = Duration.between(start, Instant.now());
Truth.assertThat(result).isEqualTo(RETURN_VALUE);
Truth.assertThat(duration).isAtLeast(SLEEP_TIME);
Truth.assertThat(duration).isAtMost(SLEEP_TIME.plus(SLEEP_TIME_UNCERTAINTY));
public void sleep_allGood() {
checkRunTime(() -> {
int result = ThreadUtilc.get(runSleepTask(TEST_DURATION));
Truth.assertThat(result).isEqualTo(RETURN_VALUE);
}, TEST_DURATION);
}
@Test
public void threadUtilcSleep_interrupted_getCallsExecutionException() {
public void sleep_interrupted_getCallsExecutionException() {
FutureTask<Integer> futureTask = new FutureTask<>(() -> ThreadUtilc.sleep(10000), RETURN_VALUE);
Thread thread = new Thread(futureTask);
thread.start();
......@@ -61,7 +56,7 @@ public class ThreadUtilcTest extends TestBase {
}
@Test
public void threadUtilcSleep_interrupted_threadUtilcGet_throwsCauseWithWrapper() {
public void sleep_interrupted_threadUtilcGet_throwsCauseWithWrapper() {
FutureTask<Integer> futureTask = new FutureTask<>(() -> ThreadUtilc.sleep(10000), RETURN_VALUE);
Thread thread = new Thread(futureTask);
thread.start();
......@@ -75,47 +70,40 @@ public class ThreadUtilcTest extends TestBase {
}
}
// ------------------------ Get -----------------------
@Test
public void getNoTimeout_success() {
Instant start = Instant.now();
Future<Integer> future = getSleepTask(SLEEP_TIME);
int result = ThreadUtilc.get(future);
Duration duration = Duration.between(start, Instant.now());
Truth.assertThat(result).isEqualTo(RETURN_VALUE);
Truth.assertThat(duration).isAtLeast(SLEEP_TIME);
Truth.assertThat(duration).isLessThan(SLEEP_TIME.plus(SLEEP_TIME_UNCERTAINTY));
public void get_NoTimeout_success() {
Future<Integer> future = runSleepTask(TEST_DURATION);
checkRunTime(() -> {
int result = ThreadUtilc.get(future);
Truth.assertThat(result).isEqualTo(RETURN_VALUE);
}, TEST_DURATION);
}
@Test
public void futureGet_timeoutNotHit() {
Instant start = Instant.now();
Future<Integer> future = getSleepTask(SLEEP_TIME);
int result = ThreadUtilc.get(future, SLEEP_TIME.multipliedBy(2));
Duration duration = Duration.between(start, Instant.now());
Truth.assertThat(result).isEqualTo(RETURN_VALUE);
Truth.assertThat(duration).isAtLeast(SLEEP_TIME);
Truth.assertThat(duration).isLessThan(SLEEP_TIME.plus(SLEEP_TIME_UNCERTAINTY));
public void get_timeoutSpecifiedButNotHit() {
Future<Integer> future = runSleepTask(TEST_DURATION);
checkRunTime(() -> {
int result = threadUtilcGet(future, TEST_DURATION.multipliedBy(2));
Truth.assertThat(result).isEqualTo(RETURN_VALUE);
}, TEST_DURATION);
}
@Test
public void futureGet_timeoutHit() {
Future<Integer> future = getSleepTask(Duration.ofMinutes(2));
Instant start = Instant.now();
try {
ThreadUtilc.get(future, SLEEP_TIME.toMillis());
Assert.fail("Should have hit timeout");
} catch (TimeException ignored) {
}
Duration duration = Duration.between(start, Instant.now());
Truth.assertThat(duration).isAtLeast(SLEEP_TIME);
Truth.assertThat(duration).isLessThan(SLEEP_TIME.plus(SLEEP_TIME_UNCERTAINTY));
public void get_timeoutHit_shouldThrowTimeException_andReturnAtTimeout() {
Future<Integer> future = runSleepTask(Duration.ofMinutes(2));
checkRunTime(() -> {
try {
threadUtilcGet(future, TEST_DURATION);
Assert.fail("Should have hit timeout");
} catch (TimeException ignored) {
}
}, TEST_DURATION);
}
@Test
public void threadUtilcGet_executionException_causedByRunTimeException_shouldThrowCause() {
public void get_executionException_causedByRunTimeException_shouldThrowCause() {
FutureTask<Integer> future = new FutureTask<>(() -> {
throw new ArithmeticException("test");
});
......@@ -129,7 +117,7 @@ public class ThreadUtilcTest extends TestBase {
}
@Test
public void threadUtilcGet_executionException_causedByCheckedException_shouldThrowCauseWithWrapper() {
public void get_executionException_causedByCheckedException_shouldThrowCauseWithWrapper() {
FutureTask<Integer> future = new FutureTask<>(() -> {
throw new IOException("test");
});
......@@ -143,9 +131,11 @@ public class ThreadUtilcTest extends TestBase {
}
}
// TODO Enhancment: Following two tests expose confusing behavior? Review
@Test
public void get_interrupted_throwsExecutionException() {
FutureTask<Integer> sleepTask = getSleepTask(Duration.ofSeconds(10));
public void threadGet_interrupted_throwsExecutionException() {
FutureTask<Integer> sleepTask = runSleepTask(Duration.ofSeconds(10));
FutureTask<Integer> getTask = new FutureTask<>(() -> ThreadUtilc.get(sleepTask));
Thread thread = new Thread(getTask);
thread.start();
......@@ -164,7 +154,7 @@ public class ThreadUtilcTest extends TestBase {
@Test
public void threadUtilcGet_interrupted_throwsInterruptException() {
FutureTask<Integer> sleepTask = getSleepTask(Duration.ofSeconds(10));
FutureTask<Integer> sleepTask = runSleepTask(Duration.ofSeconds(10));
FutureTask<Integer> getTask = new FutureTask<>(() -> ThreadUtilc.get(sleepTask));
Thread thread = new Thread(getTask);
thread.start();
......@@ -176,46 +166,60 @@ public class ThreadUtilcTest extends TestBase {
}
}
// ---------------- Fork -------------------------
@Test
public void fork_shouldRunInParallel() {
Duration sleep = Duration.ofMillis(1000);
Instant start = Instant.now();
// Getting rid of the intermediate collect will result in all sleeps happening sequentially, because of lazy streams
List<Integer> result = IntStream.range(0, 5)
.mapToObj(i -> ThreadUtilc.fork(() -> {
ThreadUtilc.sleep(sleep.toMillis());
return i;
}))
.collect(Collectors.toList()).stream()
.map(ThreadUtilc::get)
.collect(Collectors.toList());
Instant end = Instant.now();
Truth.assertThat(result).containsExactly(0, 1, 2, 3, 4).inOrder();
Truth.assertThat(Duration.between(start, end)).isGreaterThan(sleep);
Truth.assertThat(Duration.between(start, end)).isLessThan(sleep.multipliedBy(2));
public void forkRunnable() {
checkRunTime(() -> {
FutureTask<Boolean> task = ThreadUtilc.fork(() -> ThreadUtilc.sleep(TEST_DURATION));
Truth.assertThat(task.isDone()).isFalse();
Truth.assertThat(ThreadUtilc.get(task)).isTrue();
}, TEST_DURATION);
}
@Test
public void forkRunnable() throws Exception {
Instant start = Instant.now();
FutureTask<Boolean> task = ThreadUtilc.fork(() -> ThreadUtilc.sleep(1000));
Truth.assertThat(task.isDone()).isFalse();
public void fork_shouldRunInParallel() {
checkRunTime(() -> {
// Getting rid of the intermediate collect will result in all sleeps happening sequentially,
// because of lazy streams
List<Integer> result = IntStream.range(0, 5)
.mapToObj(i -> ThreadUtilc.fork(() -> {
ThreadUtilc.sleep(TEST_DURATION);
return i;
}))
.collect(Collectors.toList()).stream()
.map(ThreadUtilc::get)
.collect(Collectors.toList());
Truth.assertThat(result).containsExactly(0, 1, 2, 3, 4).inOrder();
}, TEST_DURATION);
}
Truth.assertThat(task.get()).isTrue();
Instant end = Instant.now();
// -------------------- Helpers ---------------
Truth.assertThat(task.isDone()).isTrue();
Truth.assertThat(Duration.between(start, end)).isGreaterThan(Duration.ofMillis(1000));
Truth.assertThat(Duration.between(start, end)).isLessThan(Duration.ofMillis(1300));
}
private static void checkRunTime(Runnable runnable, Duration runTime) {
long start = System.nanoTime();
runnable.run();
long end = System.nanoTime();
Duration duration = Duration.ofNanos(end - start);
// --
Truth.assertThat(duration).isAtLeast(runTime);
Truth.assertThat(duration).isLessThan(runTime.multipliedBy(11).dividedBy(10));
}
private static FutureTask<Integer> getSleepTask(Duration sleep) {
FutureTask<Integer> futureTask = new FutureTask<>(() -> ThreadUtilc.sleep(sleep.toMillis()), RETURN_VALUE);
private static FutureTask<Integer> runSleepTask(Duration sleep) {
boolean useDuration = RNG.nextBoolean();
FutureTask<Integer> futureTask = new FutureTask<>(() -> {
if (useDuration) {
ThreadUtilc.sleep(sleep);
} else {
ThreadUtilc.sleep(sleep.toMillis());
}
}, RETURN_VALUE);
new Thread(futureTask).start();
return futureTask;
}
private static <T> T threadUtilcGet(Future<T> future, Duration duration) {
return RNG.nextBoolean() ? ThreadUtilc.get(future, duration) : ThreadUtilc.get(future, duration.toMillis());
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment