SuppliersTest.java revision 1d580d0f6ee4f21eb309ba7b509d2c6d671c4044
1/*
2 * Copyright (C) 2007 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.base;
18
19import static com.google.common.testing.SerializableTester.reserialize;
20
21import com.google.common.annotations.GwtCompatible;
22import com.google.common.annotations.GwtIncompatible;
23import com.google.common.collect.Lists;
24import com.google.common.testing.NullPointerTester;
25
26import junit.framework.TestCase;
27
28import java.io.Serializable;
29import java.util.ArrayList;
30import java.util.List;
31import java.util.concurrent.TimeUnit;
32import java.util.concurrent.TimeoutException;
33import java.util.concurrent.atomic.AtomicInteger;
34import java.util.concurrent.atomic.AtomicReference;
35
36/**
37 * Tests com.google.common.base.Suppliers.
38 *
39 * @author Laurence Gonsalves
40 * @author Harry Heymann
41 */
42@GwtCompatible(emulated = true)
43public class SuppliersTest extends TestCase {
44  public void testCompose() {
45    Supplier<Integer> fiveSupplier = new Supplier<Integer>() {
46      @Override
47      public Integer get() {
48        return 5;
49      }
50    };
51
52    Function<Number, Integer> intValueFunction =
53        new Function<Number, Integer>() {
54          @Override
55          public Integer apply(Number x) {
56            return x.intValue();
57          }
58        };
59
60    Supplier<Integer> squareSupplier = Suppliers.compose(intValueFunction,
61        fiveSupplier);
62
63    assertEquals(Integer.valueOf(5), squareSupplier.get());
64  }
65
66  public void testComposeWithLists() {
67    Supplier<ArrayList<Integer>> listSupplier
68        = new Supplier<ArrayList<Integer>>() {
69      @Override
70      public ArrayList<Integer> get() {
71        return Lists.newArrayList(0);
72      }
73    };
74
75    Function<List<Integer>, List<Integer>> addElementFunction =
76        new Function<List<Integer>, List<Integer>>() {
77          @Override
78          public List<Integer> apply(List<Integer> list) {
79            ArrayList<Integer> result = Lists.newArrayList(list);
80            result.add(1);
81            return result;
82          }
83        };
84
85    Supplier<List<Integer>> addSupplier = Suppliers.compose(addElementFunction,
86        listSupplier);
87
88    List<Integer> result = addSupplier.get();
89    assertEquals(Integer.valueOf(0), result.get(0));
90    assertEquals(Integer.valueOf(1), result.get(1));
91  }
92
93  static class CountingSupplier implements Supplier<Integer>, Serializable {
94    private static final long serialVersionUID = 0L;
95    transient int calls = 0;
96    @Override
97    public Integer get() {
98      calls++;
99      return calls * 10;
100    }
101  }
102
103  public void testMemoize() {
104    CountingSupplier countingSupplier = new CountingSupplier();
105    Supplier<Integer> memoizedSupplier = Suppliers.memoize(countingSupplier);
106    checkMemoize(countingSupplier, memoizedSupplier);
107  }
108
109  public void testMemoize_redudantly() {
110    CountingSupplier countingSupplier = new CountingSupplier();
111    Supplier<Integer> memoizedSupplier = Suppliers.memoize(countingSupplier);
112    assertSame(memoizedSupplier, Suppliers.memoize(memoizedSupplier));
113  }
114
115  @GwtIncompatible("SerializableTester")
116  public void testMemoizeSerialized() {
117    CountingSupplier countingSupplier = new CountingSupplier();
118    Supplier<Integer> memoizedSupplier = Suppliers.memoize(countingSupplier);
119    checkMemoize(countingSupplier, memoizedSupplier);
120    // Calls to the original memoized supplier shouldn't affect its copy.
121    memoizedSupplier.get();
122
123    Supplier<Integer> copy = reserialize(memoizedSupplier);
124    memoizedSupplier.get();
125
126    CountingSupplier countingCopy = (CountingSupplier)
127        ((Suppliers.MemoizingSupplier<Integer>) copy).delegate;
128    checkMemoize(countingCopy, copy);
129  }
130
131  private void checkMemoize(
132      CountingSupplier countingSupplier, Supplier<Integer> memoizedSupplier) {
133    // the underlying supplier hasn't executed yet
134    assertEquals(0, countingSupplier.calls);
135
136    assertEquals(10, (int) memoizedSupplier.get());
137
138    // now it has
139    assertEquals(1, countingSupplier.calls);
140
141    assertEquals(10, (int) memoizedSupplier.get());
142
143    // it still should only have executed once due to memoization
144    assertEquals(1, countingSupplier.calls);
145  }
146
147  public void testMemoizeExceptionThrown() {
148    Supplier<Integer> exceptingSupplier = new Supplier<Integer>() {
149      @Override
150      public Integer get() {
151        throw new NullPointerException();
152      }
153    };
154
155    Supplier<Integer> memoizedSupplier = Suppliers.memoize(exceptingSupplier);
156
157    // call get() twice to make sure that memoization doesn't interfere
158    // with throwing the exception
159    for (int i = 0; i < 2; i++) {
160      try {
161        memoizedSupplier.get();
162        fail("failed to throw NullPointerException");
163      } catch (NullPointerException e) {
164        // this is what should happen
165      }
166    }
167  }
168
169  @GwtIncompatible("Thread.sleep")
170  public void testMemoizeWithExpiration() throws InterruptedException {
171    CountingSupplier countingSupplier = new CountingSupplier();
172
173    Supplier<Integer> memoizedSupplier = Suppliers.memoizeWithExpiration(
174        countingSupplier, 75, TimeUnit.MILLISECONDS);
175
176    checkExpiration(countingSupplier, memoizedSupplier);
177  }
178
179  @GwtIncompatible("Thread.sleep, SerializationTester")
180  public void testMemoizeWithExpirationSerialized()
181      throws InterruptedException {
182    CountingSupplier countingSupplier = new CountingSupplier();
183
184    Supplier<Integer> memoizedSupplier = Suppliers.memoizeWithExpiration(
185        countingSupplier, 75, TimeUnit.MILLISECONDS);
186    // Calls to the original memoized supplier shouldn't affect its copy.
187    memoizedSupplier.get();
188
189    Supplier<Integer> copy = reserialize(memoizedSupplier);
190    memoizedSupplier.get();
191
192    CountingSupplier countingCopy = (CountingSupplier)
193        ((Suppliers.ExpiringMemoizingSupplier<Integer>) copy).delegate;
194    checkExpiration(countingCopy, copy);
195  }
196
197  @GwtIncompatible("Thread.sleep")
198  private void checkExpiration(
199      CountingSupplier countingSupplier, Supplier<Integer> memoizedSupplier)
200      throws InterruptedException {
201    // the underlying supplier hasn't executed yet
202    assertEquals(0, countingSupplier.calls);
203
204    assertEquals(10, (int) memoizedSupplier.get());
205    // now it has
206    assertEquals(1, countingSupplier.calls);
207
208    assertEquals(10, (int) memoizedSupplier.get());
209    // it still should only have executed once due to memoization
210    assertEquals(1, countingSupplier.calls);
211
212    Thread.sleep(150);
213
214    assertEquals(20, (int) memoizedSupplier.get());
215    // old value expired
216    assertEquals(2, countingSupplier.calls);
217
218    assertEquals(20, (int) memoizedSupplier.get());
219    // it still should only have executed twice due to memoization
220    assertEquals(2, countingSupplier.calls);
221  }
222
223  public void testOfInstanceSuppliesSameInstance() {
224    Object toBeSupplied = new Object();
225    Supplier<Object> objectSupplier = Suppliers.ofInstance(toBeSupplied);
226    assertSame(toBeSupplied,objectSupplier.get());
227    assertSame(toBeSupplied,objectSupplier.get()); // idempotent
228  }
229
230  public void testOfInstanceSuppliesNull() {
231    Supplier<Integer> nullSupplier = Suppliers.ofInstance(null);
232    assertNull(nullSupplier.get());
233  }
234
235  @GwtIncompatible("Thread")
236
237  public void testExpiringMemoizedSupplierThreadSafe() throws Throwable {
238    Function<Supplier<Boolean>, Supplier<Boolean>> memoizer =
239        new Function<Supplier<Boolean>, Supplier<Boolean>>() {
240      @Override public Supplier<Boolean> apply(Supplier<Boolean> supplier) {
241        return Suppliers.memoizeWithExpiration(
242            supplier, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
243      }
244    };
245    testSupplierThreadSafe(memoizer);
246  }
247
248  @GwtIncompatible("Thread")
249
250  public void testMemoizedSupplierThreadSafe() throws Throwable {
251    Function<Supplier<Boolean>, Supplier<Boolean>> memoizer =
252        new Function<Supplier<Boolean>, Supplier<Boolean>>() {
253      @Override public Supplier<Boolean> apply(Supplier<Boolean> supplier) {
254        return Suppliers.memoize(supplier);
255      }
256    };
257    testSupplierThreadSafe(memoizer);
258  }
259
260  @GwtIncompatible("Thread")
261  public void testSupplierThreadSafe(
262      Function<Supplier<Boolean>, Supplier<Boolean>> memoizer)
263      throws Throwable {
264    final AtomicInteger count = new AtomicInteger(0);
265    final AtomicReference<Throwable> thrown =
266        new AtomicReference<Throwable>(null);
267    final int numThreads = 3;
268    final Thread[] threads = new Thread[numThreads];
269    final long timeout = TimeUnit.SECONDS.toNanos(60);
270
271    final Supplier<Boolean> supplier = new Supplier<Boolean>() {
272      boolean isWaiting(Thread thread) {
273        switch (thread.getState()) {
274          case BLOCKED:
275          case WAITING:
276          case TIMED_WAITING:
277          return true;
278          default:
279          return false;
280        }
281      }
282
283      int waitingThreads() {
284        int waitingThreads = 0;
285        for (Thread thread : threads) {
286          if (isWaiting(thread)) {
287            waitingThreads++;
288          }
289        }
290        return waitingThreads;
291      }
292
293      @Override
294      public Boolean get() {
295        // Check that this method is called exactly once, by the first
296        // thread to synchronize.
297        long t0 = System.nanoTime();
298        while (waitingThreads() != numThreads - 1) {
299          if (System.nanoTime() - t0 > timeout) {
300            thrown.set(new TimeoutException(
301                "timed out waiting for other threads to block" +
302                " synchronizing on supplier"));
303            break;
304          }
305          Thread.yield();
306        }
307        count.getAndIncrement();
308        return Boolean.TRUE;
309      }
310    };
311
312    final Supplier<Boolean> memoizedSupplier = memoizer.apply(supplier);
313
314    for (int i = 0; i < numThreads; i++) {
315      threads[i] = new Thread() {
316        @Override public void run() {
317          assertSame(Boolean.TRUE, memoizedSupplier.get());
318        }
319      };
320    }
321    for (Thread t : threads) {
322      t.start();
323    }
324    for (Thread t : threads) {
325      t.join();
326    }
327
328    if (thrown.get() != null) {
329      throw thrown.get();
330    }
331    assertEquals(1, count.get());
332  }
333
334  @GwtIncompatible("Thread")
335
336  public void testSynchronizedSupplierThreadSafe()
337      throws InterruptedException {
338    final Supplier<Integer> nonThreadSafe = new Supplier<Integer>() {
339      int counter = 0;
340      @Override
341      public Integer get() {
342        int nextValue = counter + 1;
343        Thread.yield();
344        counter = nextValue;
345        return counter;
346      }
347    };
348
349    final int numThreads = 10;
350    final int iterations = 1000;
351    Thread[] threads = new Thread[numThreads];
352    for (int i = 0; i < numThreads; i++) {
353      threads[i] = new Thread() {
354        @Override public void run() {
355          for (int j = 0; j < iterations; j++) {
356            Suppliers.synchronizedSupplier(nonThreadSafe).get();
357          }
358        }
359      };
360    }
361    for (Thread t : threads) {
362      t.start();
363    }
364    for (Thread t : threads) {
365      t.join();
366    }
367
368    assertEquals(numThreads * iterations + 1, (int) nonThreadSafe.get());
369  }
370
371  public void testSupplierFunction() {
372    Supplier<Integer> supplier = Suppliers.ofInstance(14);
373    Function<Supplier<Integer>, Integer> supplierFunction =
374        Suppliers.supplierFunction();
375
376    assertEquals(14, (int) supplierFunction.apply(supplier));
377  }
378
379  @GwtIncompatible("SerializationTester")
380  public void testSerialization() {
381    assertEquals(
382        Integer.valueOf(5), reserialize(Suppliers.ofInstance(5)).get());
383    assertEquals(Integer.valueOf(5), reserialize(Suppliers.compose(
384        Functions.identity(), Suppliers.ofInstance(5))).get());
385    assertEquals(Integer.valueOf(5),
386        reserialize(Suppliers.memoize(Suppliers.ofInstance(5))).get());
387    assertEquals(Integer.valueOf(5),
388        reserialize(Suppliers.memoizeWithExpiration(
389            Suppliers.ofInstance(5), 30, TimeUnit.SECONDS)).get());
390    assertEquals(Integer.valueOf(5), reserialize(
391        Suppliers.synchronizedSupplier(Suppliers.ofInstance(5))).get());
392  }
393
394  @GwtIncompatible("NullPointerTest")
395  public void testNullPointers() throws Exception {
396    NullPointerTester tester = new NullPointerTester();
397    tester.testAllPublicStaticMethods(Suppliers.class);
398  }
399}
400