Added tests for singleton pattern

This commit is contained in:
Jeroen Meulemeester 2015-12-29 19:28:04 +01:00
parent a375b2d28b
commit d0cdf84936
7 changed files with 173 additions and 90 deletions

View File

@ -0,0 +1,17 @@
package com.iluwatar.singleton;
/**
* Date: 12/29/15 - 19:20 PM
*
* @author Jeroen Meulemeester
*/
public class EnumIvoryTowerTest extends SingletonTest<EnumIvoryTower> {
/**
* Create a new singleton test instance using the given 'getInstance' method
*/
public EnumIvoryTowerTest() {
super(() -> EnumIvoryTower.INSTANCE);
}
}

View File

@ -0,0 +1,17 @@
package com.iluwatar.singleton;
/**
* Date: 12/29/15 - 19:22 PM
*
* @author Jeroen Meulemeester
*/
public class InitializingOnDemandHolderIdiomTest extends SingletonTest<InitializingOnDemandHolderIdiom> {
/**
* Create a new singleton test instance using the given 'getInstance' method
*/
public InitializingOnDemandHolderIdiomTest() {
super(InitializingOnDemandHolderIdiom::getInstance);
}
}

View File

@ -0,0 +1,17 @@
package com.iluwatar.singleton;
/**
* Date: 12/29/15 - 19:23 PM
*
* @author Jeroen Meulemeester
*/
public class IvoryTowerTest extends SingletonTest<IvoryTower> {
/**
* Create a new singleton test instance using the given 'getInstance' method
*/
public IvoryTowerTest() {
super(IvoryTower::getInstance);
}
}

View File

@ -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<ThreadSafeLazyLoadedIvoryTower> 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<Callable<NullObject>> threadList = new ArrayList<>();
for (int i = 0; i < NUM_THREADS; i++) {
threadList.add(new SingletonCreatingThread());
}
ExecutorService service = Executors.newCachedThreadPool();
List<Future<NullObject>> 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<NullObject> {
@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)
}
}
}

View File

@ -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<S> {
/**
* The singleton's getInstance method
*/
private final Supplier<S> singletonInstanceMethod;
/**
* Create a new singleton test instance using the given 'getInstance' method
*
* @param singletonInstanceMethod The singleton's getInstance method
*/
public SingletonTest(final Supplier<S> 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<Callable<S>> 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<Future<S>> results = executorService.invokeAll(tasks);
// wait for all of the threads to complete
final S expectedInstance = this.singletonInstanceMethod.get();
for (Future<S> res : results) {
final S instance = res.get();
assertNotNull(instance);
assertSame(expectedInstance, instance);
}
// tidy up the executor
executorService.shutdown();
}
}

View File

@ -0,0 +1,17 @@
package com.iluwatar.singleton;
/**
* Date: 12/29/15 - 19:26 PM
*
* @author Jeroen Meulemeester
*/
public class ThreadSafeDoubleCheckLockingTest extends SingletonTest<ThreadSafeDoubleCheckLocking> {
/**
* Create a new singleton test instance using the given 'getInstance' method
*/
public ThreadSafeDoubleCheckLockingTest() {
super(ThreadSafeDoubleCheckLocking::getInstance);
}
}

View File

@ -0,0 +1,17 @@
package com.iluwatar.singleton;
/**
* Date: 12/29/15 - 19:26 PM
*
* @author Jeroen Meulemeester
*/
public class ThreadSafeLazyLoadedIvoryTowerTest extends SingletonTest<ThreadSafeLazyLoadedIvoryTower> {
/**
* Create a new singleton test instance using the given 'getInstance' method
*/
public ThreadSafeLazyLoadedIvoryTowerTest() {
super(ThreadSafeLazyLoadedIvoryTower::getInstance);
}
}