From d0cdf84936ac0e7908f8d76faa63e8f4c1e69c29 Mon Sep 17 00:00:00 2001 From: Jeroen Meulemeester Date: Tue, 29 Dec 2015 19:28:04 +0100 Subject: [PATCH] Added tests for singleton pattern --- .../singleton/EnumIvoryTowerTest.java | 17 ++++ .../InitializingOnDemandHolderIdiomTest.java | 17 ++++ .../iluwatar/singleton/IvoryTowerTest.java | 17 ++++ .../LazyLoadedSingletonThreadSafetyTest.java | 90 ------------------- .../com/iluwatar/singleton/SingletonTest.java | 88 ++++++++++++++++++ .../ThreadSafeDoubleCheckLockingTest.java | 17 ++++ .../ThreadSafeLazyLoadedIvoryTowerTest.java | 17 ++++ 7 files changed, 173 insertions(+), 90 deletions(-) create mode 100644 singleton/src/test/java/com/iluwatar/singleton/EnumIvoryTowerTest.java create mode 100644 singleton/src/test/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiomTest.java create mode 100644 singleton/src/test/java/com/iluwatar/singleton/IvoryTowerTest.java delete mode 100644 singleton/src/test/java/com/iluwatar/singleton/LazyLoadedSingletonThreadSafetyTest.java create mode 100644 singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java create mode 100644 singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java create mode 100644 singleton/src/test/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTowerTest.java diff --git a/singleton/src/test/java/com/iluwatar/singleton/EnumIvoryTowerTest.java b/singleton/src/test/java/com/iluwatar/singleton/EnumIvoryTowerTest.java new file mode 100644 index 000000000..49c65c716 --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/EnumIvoryTowerTest.java @@ -0,0 +1,17 @@ +package com.iluwatar.singleton; + +/** + * Date: 12/29/15 - 19:20 PM + * + * @author Jeroen Meulemeester + */ +public class EnumIvoryTowerTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public EnumIvoryTowerTest() { + super(() -> EnumIvoryTower.INSTANCE); + } + +} diff --git a/singleton/src/test/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiomTest.java b/singleton/src/test/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiomTest.java new file mode 100644 index 000000000..60ae4798d --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiomTest.java @@ -0,0 +1,17 @@ +package com.iluwatar.singleton; + +/** + * Date: 12/29/15 - 19:22 PM + * + * @author Jeroen Meulemeester + */ +public class InitializingOnDemandHolderIdiomTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public InitializingOnDemandHolderIdiomTest() { + super(InitializingOnDemandHolderIdiom::getInstance); + } + +} \ No newline at end of file diff --git a/singleton/src/test/java/com/iluwatar/singleton/IvoryTowerTest.java b/singleton/src/test/java/com/iluwatar/singleton/IvoryTowerTest.java new file mode 100644 index 000000000..e9a222aef --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/IvoryTowerTest.java @@ -0,0 +1,17 @@ +package com.iluwatar.singleton; + +/** + * Date: 12/29/15 - 19:23 PM + * + * @author Jeroen Meulemeester + */ +public class IvoryTowerTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public IvoryTowerTest() { + super(IvoryTower::getInstance); + } + +} \ No newline at end of file diff --git a/singleton/src/test/java/com/iluwatar/singleton/LazyLoadedSingletonThreadSafetyTest.java b/singleton/src/test/java/com/iluwatar/singleton/LazyLoadedSingletonThreadSafetyTest.java deleted file mode 100644 index 24cd7cf4b..000000000 --- a/singleton/src/test/java/com/iluwatar/singleton/LazyLoadedSingletonThreadSafetyTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.iluwatar.singleton; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -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 org.junit.Test; - -/** - * This class provides several test case that test singleton construction. - * - * The first proves that multiple calls to the singleton getInstance object are the same when called - * in the SAME thread. The second proves that multiple calls to the singleton getInstance object are - * the same when called in the DIFFERENT thread. - * - */ -public class LazyLoadedSingletonThreadSafetyTest { - - private static final int NUM_THREADS = 5; - private List threadObjects = Collections - .synchronizedList(new ArrayList<>()); - - // NullObject class so Callable has to return something - private class NullObject { - private NullObject() {} - } - - @Test - public void testMultipleCallsReturnTheSameObjectInSameThread() { - // Create several instances in the same calling thread - ThreadSafeLazyLoadedIvoryTower instance1 = ThreadSafeLazyLoadedIvoryTower.getInstance(); - ThreadSafeLazyLoadedIvoryTower instance2 = ThreadSafeLazyLoadedIvoryTower.getInstance(); - ThreadSafeLazyLoadedIvoryTower instance3 = ThreadSafeLazyLoadedIvoryTower.getInstance(); - // now check they are equal - assertEquals(instance1, instance1); - assertEquals(instance1, instance2); - assertEquals(instance2, instance3); - assertEquals(instance1, instance3); - } - - @Test - public void testMultipleCallsReturnTheSameObjectInDifferentThreads() - throws InterruptedException, ExecutionException { - { - // create several threads and inside each callable instantiate the singleton class - ExecutorService executorService = Executors.newSingleThreadExecutor(); - - List> threadList = new ArrayList<>(); - for (int i = 0; i < NUM_THREADS; i++) { - threadList.add(new SingletonCreatingThread()); - } - - ExecutorService service = Executors.newCachedThreadPool(); - List> results = service.invokeAll(threadList); - - // wait for all of the threads to complete - for (Future res : results) { - res.get(); - } - - // tidy up the executor - executorService.shutdown(); - } - { - // now check the contents that were added to threadObjects by each thread - assertEquals(NUM_THREADS, threadObjects.size()); - assertEquals(threadObjects.get(0), threadObjects.get(1)); - assertEquals(threadObjects.get(1), threadObjects.get(2)); - assertEquals(threadObjects.get(2), threadObjects.get(3)); - assertEquals(threadObjects.get(3), threadObjects.get(4)); - } - } - - private class SingletonCreatingThread implements Callable { - @Override - public NullObject call() { - // instantiate the thread safety class and add to list to test afterwards - ThreadSafeLazyLoadedIvoryTower instance = ThreadSafeLazyLoadedIvoryTower.getInstance(); - threadObjects.add(instance); - return new NullObject();// return null object (cannot return Void) - } - } -} diff --git a/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java b/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java new file mode 100644 index 000000000..6c6c4a3f4 --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java @@ -0,0 +1,88 @@ +package com.iluwatar.singleton; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.function.Supplier; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +/** + * This class provides several test case that test singleton construction. + * + * The first proves that multiple calls to the singleton getInstance object are the same when called + * in the SAME thread. The second proves that multiple calls to the singleton getInstance object are + * the same when called in the DIFFERENT thread. + * + * Date: 12/29/15 - 19:25 PM + * + * @author Jeroen Meulemeester + * @author Richard Jones + */ +public abstract class SingletonTest { + + /** + * The singleton's getInstance method + */ + private final Supplier singletonInstanceMethod; + + /** + * Create a new singleton test instance using the given 'getInstance' method + * + * @param singletonInstanceMethod The singleton's getInstance method + */ + public SingletonTest(final Supplier singletonInstanceMethod) { + this.singletonInstanceMethod = singletonInstanceMethod; + } + + /** + * Test the singleton in a non-concurrent setting + */ + @Test + public void testMultipleCallsReturnTheSameObjectInSameThread() { + // Create several instances in the same calling thread + S instance1 = this.singletonInstanceMethod.get(); + S instance2 = this.singletonInstanceMethod.get(); + S instance3 = this.singletonInstanceMethod.get(); + // now check they are equal + assertSame(instance1, instance2); + assertSame(instance1, instance3); + assertSame(instance2, instance3); + } + + /** + * Test singleton instance in a concurrent setting + */ + @Test(timeout = 10000) + public void testMultipleCallsReturnTheSameObjectInDifferentThreads() throws Exception { + + // Create 10000 tasks and inside each callable instantiate the singleton class + final List> tasks = new ArrayList<>(); + for (int i = 0; i < 10000; i++) { + tasks.add(this.singletonInstanceMethod::get); + } + + // Use up to 8 concurrent threads to handle the tasks + final ExecutorService executorService = Executors.newFixedThreadPool(8); + final List> results = executorService.invokeAll(tasks); + + // wait for all of the threads to complete + final S expectedInstance = this.singletonInstanceMethod.get(); + for (Future res : results) { + final S instance = res.get(); + assertNotNull(instance); + assertSame(expectedInstance, instance); + } + + // tidy up the executor + executorService.shutdown(); + + } + +} diff --git a/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java new file mode 100644 index 000000000..f40f0cbc7 --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java @@ -0,0 +1,17 @@ +package com.iluwatar.singleton; + +/** + * Date: 12/29/15 - 19:26 PM + * + * @author Jeroen Meulemeester + */ +public class ThreadSafeDoubleCheckLockingTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public ThreadSafeDoubleCheckLockingTest() { + super(ThreadSafeDoubleCheckLocking::getInstance); + } + +} \ No newline at end of file diff --git a/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTowerTest.java b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTowerTest.java new file mode 100644 index 000000000..8f2a5e6e1 --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTowerTest.java @@ -0,0 +1,17 @@ +package com.iluwatar.singleton; + +/** + * Date: 12/29/15 - 19:26 PM + * + * @author Jeroen Meulemeester + */ +public class ThreadSafeLazyLoadedIvoryTowerTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public ThreadSafeLazyLoadedIvoryTowerTest() { + super(ThreadSafeLazyLoadedIvoryTower::getInstance); + } + +}