/*
* Copyright (C) 2007 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.base;
import static com.google.common.testing.SerializableTester.reserialize;
import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.collect.Lists;
import com.google.common.testing.NullPointerTester;
import junit.framework.TestCase;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
* Tests com.google.common.base.Suppliers.
*
* @author Laurence Gonsalves
* @author Harry Heymann
*/
@GwtCompatible(emulated = true)
public class SuppliersTest extends TestCase {
public void testCompose() {
Supplier<Integer> fiveSupplier = new Supplier<Integer>() {
@Override
public Integer get() {
return 5;
}
};
Function<Number, Integer> intValueFunction =
new Function<Number, Integer>() {
@Override
public Integer apply(Number x) {
return x.intValue();
}
};
Supplier<Integer> squareSupplier = Suppliers.compose(intValueFunction,
fiveSupplier);
assertEquals(Integer.valueOf(5), squareSupplier.get());
}
public void testComposeWithLists() {
Supplier<ArrayList<Integer>> listSupplier
= new Supplier<ArrayList<Integer>>() {
@Override
public ArrayList<Integer> get() {
return Lists.newArrayList(0);
}
};
Function<List<Integer>, List<Integer>> addElementFunction =
new Function<List<Integer>, List<Integer>>() {
@Override
public List<Integer> apply(List<Integer> list) {
ArrayList<Integer> result = Lists.newArrayList(list);
result.add(1);
return result;
}
};
Supplier<List<Integer>> addSupplier = Suppliers.compose(addElementFunction,
listSupplier);
List<Integer> result = addSupplier.get();
assertEquals(Integer.valueOf(0), result.get(0));
assertEquals(Integer.valueOf(1), result.get(1));
}
static class CountingSupplier implements Supplier<Integer>, Serializable {
private static final long serialVersionUID = 0L;
transient int calls = 0;
@Override
public Integer get() {
calls++;
return calls * 10;
}
}
public void testMemoize() {
CountingSupplier countingSupplier = new CountingSupplier();
Supplier<Integer> memoizedSupplier = Suppliers.memoize(countingSupplier);
checkMemoize(countingSupplier, memoizedSupplier);
}
public void testMemoize_redudantly() {
CountingSupplier countingSupplier = new CountingSupplier();
Supplier<Integer> memoizedSupplier = Suppliers.memoize(countingSupplier);
assertSame(memoizedSupplier, Suppliers.memoize(memoizedSupplier));
}
@GwtIncompatible("SerializableTester")
public void testMemoizeSerialized() {
CountingSupplier countingSupplier = new CountingSupplier();
Supplier<Integer> memoizedSupplier = Suppliers.memoize(countingSupplier);
checkMemoize(countingSupplier, memoizedSupplier);
// Calls to the original memoized supplier shouldn't affect its copy.
memoizedSupplier.get();
Supplier<Integer> copy = reserialize(memoizedSupplier);
memoizedSupplier.get();
CountingSupplier countingCopy = (CountingSupplier)
((Suppliers.MemoizingSupplier<Integer>) copy).delegate;
checkMemoize(countingCopy, copy);
}
private void checkMemoize(
CountingSupplier countingSupplier, Supplier<Integer> memoizedSupplier) {
// the underlying supplier hasn't executed yet
assertEquals(0, countingSupplier.calls);
assertEquals(10, (int) memoizedSupplier.get());
// now it has
assertEquals(1, countingSupplier.calls);
assertEquals(10, (int) memoizedSupplier.get());
// it still should only have executed once due to memoization
assertEquals(1, countingSupplier.calls);
}
public void testMemoizeExceptionThrown() {
Supplier<Integer> exceptingSupplier = new Supplier<Integer>() {
@Override
public Integer get() {
throw new NullPointerException();
}
};
Supplier<Integer> memoizedSupplier = Suppliers.memoize(exceptingSupplier);
// call get() twice to make sure that memoization doesn't interfere
// with throwing the exception
for (int i = 0; i < 2; i++) {
try {
memoizedSupplier.get();
fail("failed to throw NullPointerException");
} catch (NullPointerException e) {
// this is what should happen
}
}
}
@GwtIncompatible("Thread.sleep")
public void testMemoizeWithExpiration() throws InterruptedException {
CountingSupplier countingSupplier = new CountingSupplier();
Supplier<Integer> memoizedSupplier = Suppliers.memoizeWithExpiration(
countingSupplier, 75, TimeUnit.MILLISECONDS);
checkExpiration(countingSupplier, memoizedSupplier);
}
@GwtIncompatible("Thread.sleep, SerializationTester")
public void testMemoizeWithExpirationSerialized()
throws InterruptedException {
CountingSupplier countingSupplier = new CountingSupplier();
Supplier<Integer> memoizedSupplier = Suppliers.memoizeWithExpiration(
countingSupplier, 75, TimeUnit.MILLISECONDS);
// Calls to the original memoized supplier shouldn't affect its copy.
memoizedSupplier.get();
Supplier<Integer> copy = reserialize(memoizedSupplier);
memoizedSupplier.get();
CountingSupplier countingCopy = (CountingSupplier)
((Suppliers.ExpiringMemoizingSupplier<Integer>) copy).delegate;
checkExpiration(countingCopy, copy);
}
@GwtIncompatible("Thread.sleep")
private void checkExpiration(
CountingSupplier countingSupplier, Supplier<Integer> memoizedSupplier)
throws InterruptedException {
// the underlying supplier hasn't executed yet
assertEquals(0, countingSupplier.calls);
assertEquals(10, (int) memoizedSupplier.get());
// now it has
assertEquals(1, countingSupplier.calls);
assertEquals(10, (int) memoizedSupplier.get());
// it still should only have executed once due to memoization
assertEquals(1, countingSupplier.calls);
Thread.sleep(150);
assertEquals(20, (int) memoizedSupplier.get());
// old value expired
assertEquals(2, countingSupplier.calls);
assertEquals(20, (int) memoizedSupplier.get());
// it still should only have executed twice due to memoization
assertEquals(2, countingSupplier.calls);
}
public void testOfInstanceSuppliesSameInstance() {
Object toBeSupplied = new Object();
Supplier<Object> objectSupplier = Suppliers.ofInstance(toBeSupplied);
assertSame(toBeSupplied,objectSupplier.get());
assertSame(toBeSupplied,objectSupplier.get()); // idempotent
}
public void testOfInstanceSuppliesNull() {
Supplier<Integer> nullSupplier = Suppliers.ofInstance(null);
assertNull(nullSupplier.get());
}
@GwtIncompatible("Thread")
public void testExpiringMemoizedSupplierThreadSafe() throws Throwable {
Function<Supplier<Boolean>, Supplier<Boolean>> memoizer =
new Function<Supplier<Boolean>, Supplier<Boolean>>() {
@Override public Supplier<Boolean> apply(Supplier<Boolean> supplier) {
return Suppliers.memoizeWithExpiration(
supplier, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
}
};
testSupplierThreadSafe(memoizer);
}
@GwtIncompatible("Thread")
public void testMemoizedSupplierThreadSafe() throws Throwable {
Function<Supplier<Boolean>, Supplier<Boolean>> memoizer =
new Function<Supplier<Boolean>, Supplier<Boolean>>() {
@Override public Supplier<Boolean> apply(Supplier<Boolean> supplier) {
return Suppliers.memoize(supplier);
}
};
testSupplierThreadSafe(memoizer);
}
@GwtIncompatible("Thread")
public void testSupplierThreadSafe(
Function<Supplier<Boolean>, Supplier<Boolean>> memoizer)
throws Throwable {
final AtomicInteger count = new AtomicInteger(0);
final AtomicReference<Throwable> thrown =
new AtomicReference<Throwable>(null);
final int numThreads = 3;
final Thread[] threads = new Thread[numThreads];
final long timeout = TimeUnit.SECONDS.toNanos(60);
final Supplier<Boolean> supplier = new Supplier<Boolean>() {
boolean isWaiting(Thread thread) {
switch (thread.getState()) {
case BLOCKED:
case WAITING:
case TIMED_WAITING:
return true;
default:
return false;
}
}
int waitingThreads() {
int waitingThreads = 0;
for (Thread thread : threads) {
if (isWaiting(thread)) {
waitingThreads++;
}
}
return waitingThreads;
}
@Override
public Boolean get() {
// Check that this method is called exactly once, by the first
// thread to synchronize.
long t0 = System.nanoTime();
while (waitingThreads() != numThreads - 1) {
if (System.nanoTime() - t0 > timeout) {
thrown.set(new TimeoutException(
"timed out waiting for other threads to block" +
" synchronizing on supplier"));
break;
}
Thread.yield();
}
count.getAndIncrement();
return Boolean.TRUE;
}
};
final Supplier<Boolean> memoizedSupplier = memoizer.apply(supplier);
for (int i = 0; i < numThreads; i++) {
threads[i] = new Thread() {
@Override public void run() {
assertSame(Boolean.TRUE, memoizedSupplier.get());
}
};
}
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
}
if (thrown.get() != null) {
throw thrown.get();
}
assertEquals(1, count.get());
}
@GwtIncompatible("Thread")
public void testSynchronizedSupplierThreadSafe()
throws InterruptedException {
final Supplier<Integer> nonThreadSafe = new Supplier<Integer>() {
int counter = 0;
@Override
public Integer get() {
int nextValue = counter + 1;
Thread.yield();
counter = nextValue;
return counter;
}
};
final int numThreads = 10;
final int iterations = 1000;
Thread[] threads = new Thread[numThreads];
for (int i = 0; i < numThreads; i++) {
threads[i] = new Thread() {
@Override public void run() {
for (int j = 0; j < iterations; j++) {
Suppliers.synchronizedSupplier(nonThreadSafe).get();
}
}
};
}
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
}
assertEquals(numThreads * iterations + 1, (int) nonThreadSafe.get());
}
public void testSupplierFunction() {
Supplier<Integer> supplier = Suppliers.ofInstance(14);
Function<Supplier<Integer>, Integer> supplierFunction =
Suppliers.supplierFunction();
assertEquals(14, (int) supplierFunction.apply(supplier));
}
@GwtIncompatible("SerializationTester")
public void testSerialization() {
assertEquals(
Integer.valueOf(5), reserialize(Suppliers.ofInstance(5)).get());
assertEquals(Integer.valueOf(5), reserialize(Suppliers.compose(
Functions.identity(), Suppliers.ofInstance(5))).get());
assertEquals(Integer.valueOf(5),
reserialize(Suppliers.memoize(Suppliers.ofInstance(5))).get());
assertEquals(Integer.valueOf(5),
reserialize(Suppliers.memoizeWithExpiration(
Suppliers.ofInstance(5), 30, TimeUnit.SECONDS)).get());
assertEquals(Integer.valueOf(5), reserialize(
Suppliers.synchronizedSupplier(Suppliers.ofInstance(5))).get());
}
@GwtIncompatible("NullPointerTest")
public void testNullPointers() throws Exception {
NullPointerTester tester = new NullPointerTester();
tester.testAllPublicStaticMethods(Suppliers.class);
}
}