From fd8c05846f9cca82dbc49c0da3c405699515a73b Mon Sep 17 00:00:00 2001 From: Jeroen Meulemeester Date: Wed, 30 Dec 2015 19:49:47 +0100 Subject: [PATCH] Added tests for thread-pool pattern Fixed concurrency problem in id generation of Task --- thread-pool/pom.xml | 5 + .../iluwatar/threadpool/CoffeeMakingTask.java | 2 +- .../threadpool/PotatoPeelingTask.java | 2 +- .../java/com/iluwatar/threadpool/Task.java | 8 +- .../threadpool/CoffeeMakingTaskTest.java | 17 +++ .../threadpool/PotatoPeelingTaskTest.java | 17 +++ .../com/iluwatar/threadpool/TaskTest.java | 121 ++++++++++++++++++ .../com/iluwatar/threadpool/WorkerTest.java | 31 +++++ 8 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 thread-pool/src/test/java/com/iluwatar/threadpool/CoffeeMakingTaskTest.java create mode 100644 thread-pool/src/test/java/com/iluwatar/threadpool/PotatoPeelingTaskTest.java create mode 100644 thread-pool/src/test/java/com/iluwatar/threadpool/TaskTest.java create mode 100644 thread-pool/src/test/java/com/iluwatar/threadpool/WorkerTest.java diff --git a/thread-pool/pom.xml b/thread-pool/pom.xml index c50140237..ed6f8f7f4 100644 --- a/thread-pool/pom.xml +++ b/thread-pool/pom.xml @@ -14,5 +14,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/CoffeeMakingTask.java b/thread-pool/src/main/java/com/iluwatar/threadpool/CoffeeMakingTask.java index f1247101c..3a8464092 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/CoffeeMakingTask.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/CoffeeMakingTask.java @@ -7,7 +7,7 @@ package com.iluwatar.threadpool; */ public class CoffeeMakingTask extends Task { - private static final int TIME_PER_CUP = 300; + private static final int TIME_PER_CUP = 100; public CoffeeMakingTask(int numCups) { super(numCups * TIME_PER_CUP); diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/PotatoPeelingTask.java b/thread-pool/src/main/java/com/iluwatar/threadpool/PotatoPeelingTask.java index a90bf4bec..2be941406 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/PotatoPeelingTask.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/PotatoPeelingTask.java @@ -7,7 +7,7 @@ package com.iluwatar.threadpool; */ public class PotatoPeelingTask extends Task { - private static final int TIME_PER_POTATO = 500; + private static final int TIME_PER_POTATO = 200; public PotatoPeelingTask(int numPotatoes) { super(numPotatoes * TIME_PER_POTATO); diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/Task.java b/thread-pool/src/main/java/com/iluwatar/threadpool/Task.java index 12fecbbd0..2426948b3 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/Task.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/Task.java @@ -1,19 +1,21 @@ package com.iluwatar.threadpool; +import java.util.concurrent.atomic.AtomicInteger; + /** - * + * * Abstract base class for tasks * */ public abstract class Task { - private static int nextId = 1; + private static final AtomicInteger ID_GENERATOR = new AtomicInteger(); private final int id; private final int timeMs; public Task(final int timeMs) { - this.id = nextId++; + this.id = ID_GENERATOR.incrementAndGet(); this.timeMs = timeMs; } diff --git a/thread-pool/src/test/java/com/iluwatar/threadpool/CoffeeMakingTaskTest.java b/thread-pool/src/test/java/com/iluwatar/threadpool/CoffeeMakingTaskTest.java new file mode 100644 index 000000000..ab3d47d9a --- /dev/null +++ b/thread-pool/src/test/java/com/iluwatar/threadpool/CoffeeMakingTaskTest.java @@ -0,0 +1,17 @@ +package com.iluwatar.threadpool; + +/** + * Date: 12/30/15 - 18:23 PM + * + * @author Jeroen Meulemeester + */ +public class CoffeeMakingTaskTest extends TaskTest { + + /** + * Create a new test instance + */ + public CoffeeMakingTaskTest() { + super(CoffeeMakingTask::new, 100); + } + +} diff --git a/thread-pool/src/test/java/com/iluwatar/threadpool/PotatoPeelingTaskTest.java b/thread-pool/src/test/java/com/iluwatar/threadpool/PotatoPeelingTaskTest.java new file mode 100644 index 000000000..4f9b1496c --- /dev/null +++ b/thread-pool/src/test/java/com/iluwatar/threadpool/PotatoPeelingTaskTest.java @@ -0,0 +1,17 @@ +package com.iluwatar.threadpool; + +/** + * Date: 12/30/15 - 18:23 PM + * + * @author Jeroen Meulemeester + */ +public class PotatoPeelingTaskTest extends TaskTest { + + /** + * Create a new test instance + */ + public PotatoPeelingTaskTest() { + super(PotatoPeelingTask::new, 200); + } + +} \ No newline at end of file diff --git a/thread-pool/src/test/java/com/iluwatar/threadpool/TaskTest.java b/thread-pool/src/test/java/com/iluwatar/threadpool/TaskTest.java new file mode 100644 index 000000000..f1ef8160f --- /dev/null +++ b/thread-pool/src/test/java/com/iluwatar/threadpool/TaskTest.java @@ -0,0 +1,121 @@ +package com.iluwatar.threadpool; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/30/15 - 18:22 PM + * + * @author Jeroen Meulemeester + */ +public abstract class TaskTest { + + /** + * The number of tasks used during the concurrency test + */ + private static final int TASK_COUNT = 128 * 1024; + + /** + * The number of threads used during the concurrency test + */ + private static final int THREAD_COUNT = 8; + + /** + * The task factory, used to create new test items + */ + private final Function factory; + + /** + * The expected time needed to run the task 1 single time, in milli seconds + */ + private final int expectedExecutionTime; + + /** + * Create a new test instance + * + * @param factory The task factory, used to create new test items + * @param expectedExecutionTime The expected time needed to run the task 1 time, in milli seconds + */ + public TaskTest(final Function factory, final int expectedExecutionTime) { + this.factory = factory; + this.expectedExecutionTime = expectedExecutionTime; + } + + /** + * Verify if the generated id is unique for each task, even if the tasks are created in separate + * threads + */ + @Test(timeout = 10000) + public void testIdGeneration() throws Exception { + final ExecutorService service = Executors.newFixedThreadPool(THREAD_COUNT); + + final List> tasks = new ArrayList<>(); + for (int i = 0; i < TASK_COUNT; i++) { + tasks.add(() -> factory.apply(1).getId()); + } + + final List ids = service.invokeAll(tasks) + .stream() + .map(TaskTest::get) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + service.shutdownNow(); + + final long uniqueIdCount = ids.stream() + .distinct() + .count(); + + assertEquals(TASK_COUNT, ids.size()); + assertEquals(TASK_COUNT, uniqueIdCount); + + } + + /** + * Verify if the time per execution of a task matches the actual time required to execute the task + * a given number of times + */ + @Test + public void testTimeMs() { + for (int i = 0; i < 10; i++) { + assertEquals(this.expectedExecutionTime * i, this.factory.apply(i).getTimeMs()); + } + } + + /** + * Verify if the task has some sort of {@link T#toString()}, different from 'null' + */ + @Test + public void testToString() { + assertNotNull(this.factory.apply(0).toString()); + } + + /** + * Extract the result from a future or returns 'null' when an exception occurred + * + * @param future The future we want the result from + * @param The result type + * @return The result or 'null' when a checked exception occurred + */ + private static O get(Future future) { + try { + return future.get(); + } catch (InterruptedException | ExecutionException e) { + return null; + } + } + +} \ No newline at end of file diff --git a/thread-pool/src/test/java/com/iluwatar/threadpool/WorkerTest.java b/thread-pool/src/test/java/com/iluwatar/threadpool/WorkerTest.java new file mode 100644 index 000000000..53a1d8694 --- /dev/null +++ b/thread-pool/src/test/java/com/iluwatar/threadpool/WorkerTest.java @@ -0,0 +1,31 @@ +package com.iluwatar.threadpool; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/30/15 - 18:21 PM + * + * @author Jeroen Meulemeester + */ +public class WorkerTest { + + /** + * Verify if a worker does the actual job + */ + @Test + public void testRun() { + final Task task = mock(Task.class); + final Worker worker = new Worker(task); + verifyZeroInteractions(task); + + worker.run(); + verify(task).getTimeMs(); + verifyNoMoreInteractions(task); + } + +} \ No newline at end of file