Created
February 4, 2014 21:10
-
-
Save chronodm/8812453 to your computer and use it in GitHub Desktop.
Unit test for Memoizer, as implemented in Java Concurrency in Practice, pp. 105-106
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.junit.Test; | |
import org.mockito.invocation.InvocationOnMock; | |
import org.mockito.stubbing.Answer; | |
import java.util.*; | |
import java.util.concurrent.*; | |
import static org.fest.assertions.Assertions.*; | |
import static org.mockito.Matchers.any; | |
import static org.mockito.Mockito.*; | |
/** | |
* Unit test for {@link Memoizer}, as implemented in Brian Goetz' | |
* "Java Concurrency in Practice", section 5.6 (listing 5.19, p. 108) | |
*/ | |
public class MemoizerTest { | |
// ------------------------------------------------------------ | |
// Tests | |
@Test | |
@SuppressWarnings("unchecked") | |
public void computeDoesCompute() throws InterruptedException { | |
Computable comp = mock(Computable.class); | |
Object key = new Object(); | |
Object expected = new Object(); | |
when(comp.compute(key)).thenReturn(expected); | |
Memoizer memoizer = new Memoizer(comp); | |
Object actual = memoizer.compute(key); | |
assertThat(actual).isSameAs(expected); | |
} | |
@Test | |
@SuppressWarnings("unchecked") | |
public void computeComputesOncePerKey() throws InterruptedException { | |
final int size = 5; | |
Map<?, ?> expected = new HashMap(){{ | |
for (int i = 0; i < size; i++) { | |
put(new Object(), new Object()); | |
} | |
}}; | |
Computable comp = mock(Computable.class); | |
for (Map.Entry entry : expected.entrySet()) { | |
when(comp.compute(entry.getKey())).thenReturn(entry.getValue()); | |
} | |
Memoizer memoizer = new Memoizer(comp); | |
for (Map.Entry entry : expected.entrySet()) { | |
Object actual = memoizer.compute(entry.getKey()); | |
assertThat(actual).isSameAs(entry.getValue()); | |
assertThat(memoizer.compute(entry.getKey())).isSameAs(actual); | |
} | |
verify(comp, times(size)).compute(any()); | |
verifyNoMoreInteractions(comp); | |
} | |
@Test | |
@SuppressWarnings("unchecked") | |
public void computeOnlyComputesOnce() throws InterruptedException { | |
Computable comp = mock(Computable.class); | |
when(comp.compute(any())).thenReturn(new Object()); | |
Memoizer memoizer = new Memoizer(comp); | |
Object key = new Object(); | |
Object actual = memoizer.compute(key); | |
assertThat(memoizer.compute(key)).isSameAs(actual); | |
verify(comp, times(1)).compute(any()); | |
verifyNoMoreInteractions(comp); | |
} | |
@Test | |
@SuppressWarnings("unchecked") | |
public void concurrentComputeOnlyComputesOnce() throws InterruptedException, ExecutionException, TimeoutException { | |
final int poolSize = 5; | |
final int delay = 50; | |
Computable comp = mock(Computable.class); | |
final Memoizer memoizer = new Memoizer(comp); | |
when(comp.compute(any())).thenAnswer(new Answer<Object>() { | |
@Override | |
public Object answer(InvocationOnMock invocation) throws Throwable { | |
Thread.sleep(delay); | |
return new Object(); | |
} | |
}); | |
ExecutorService exec = Executors.newFixedThreadPool(poolSize); | |
try { | |
final Object key = new Object(); | |
List<Future> futures = new ArrayList<Future>(poolSize); | |
for (int i = 0; i < poolSize; i++) { | |
futures.add(exec.submit(new Callable<Object>() { | |
@Override | |
public Object call() throws Exception { | |
return memoizer.compute(key); | |
} | |
})); | |
} | |
Set<Object> returned = new HashSet<Object>(); | |
for (Future f : futures) { | |
returned.add(f.get(delay * poolSize, TimeUnit.MILLISECONDS)); | |
} | |
assertThat(returned).hasSize(1); | |
verify(comp, times(1)).compute(any()); | |
verifyNoMoreInteractions(comp); | |
} finally { | |
exec.shutdownNow(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment