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