• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.google.common.base;
18 
19 import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows;
20 import static com.google.common.testing.SerializableTester.reserialize;
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import com.google.common.annotations.GwtCompatible;
24 import com.google.common.annotations.GwtIncompatible;
25 import com.google.common.annotations.J2ktIncompatible;
26 import com.google.common.collect.Lists;
27 import com.google.common.testing.ClassSanityTester;
28 import com.google.common.testing.EqualsTester;
29 import java.io.NotSerializableException;
30 import java.io.Serializable;
31 import java.time.Duration;
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.concurrent.TimeUnit;
35 import java.util.concurrent.TimeoutException;
36 import java.util.concurrent.atomic.AtomicInteger;
37 import java.util.concurrent.atomic.AtomicReference;
38 import junit.framework.TestCase;
39 import org.checkerframework.checker.nullness.qual.Nullable;
40 
41 /**
42  * Tests com.google.common.base.Suppliers.
43  *
44  * @author Laurence Gonsalves
45  * @author Harry Heymann
46  */
47 @ElementTypesAreNonnullByDefault
48 @GwtCompatible(emulated = true)
49 public class SuppliersTest extends TestCase {
50 
51   static class CountingSupplier implements Supplier<Integer> {
52     int calls = 0;
53 
54     @Override
get()55     public Integer get() {
56       calls++;
57       return calls * 10;
58     }
59 
60     @Override
toString()61     public String toString() {
62       return "CountingSupplier";
63     }
64   }
65 
66   static class ThrowingSupplier implements Supplier<Integer> {
67     @Override
get()68     public Integer get() {
69       throw new NullPointerException();
70     }
71   }
72 
73   static class SerializableCountingSupplier extends CountingSupplier implements Serializable {
74     private static final long serialVersionUID = 0L;
75   }
76 
77   static class SerializableThrowingSupplier extends ThrowingSupplier implements Serializable {
78     private static final long serialVersionUID = 0L;
79   }
80 
checkMemoize(CountingSupplier countingSupplier, Supplier<Integer> memoizedSupplier)81   static void checkMemoize(CountingSupplier countingSupplier, Supplier<Integer> memoizedSupplier) {
82     // the underlying supplier hasn't executed yet
83     assertEquals(0, countingSupplier.calls);
84 
85     assertEquals(10, (int) memoizedSupplier.get());
86 
87     // now it has
88     assertEquals(1, countingSupplier.calls);
89 
90     assertEquals(10, (int) memoizedSupplier.get());
91 
92     // it still should only have executed once due to memoization
93     assertEquals(1, countingSupplier.calls);
94   }
95 
testMemoize()96   public void testMemoize() {
97     memoizeTest(new CountingSupplier());
98     memoizeTest(new SerializableCountingSupplier());
99   }
100 
memoizeTest(CountingSupplier countingSupplier)101   private void memoizeTest(CountingSupplier countingSupplier) {
102     Supplier<Integer> memoizedSupplier = Suppliers.memoize(countingSupplier);
103     checkMemoize(countingSupplier, memoizedSupplier);
104   }
105 
testMemoize_redudantly()106   public void testMemoize_redudantly() {
107     memoize_redudantlyTest(new CountingSupplier());
108     memoize_redudantlyTest(new SerializableCountingSupplier());
109   }
110 
memoize_redudantlyTest(CountingSupplier countingSupplier)111   private void memoize_redudantlyTest(CountingSupplier countingSupplier) {
112     Supplier<Integer> memoizedSupplier = Suppliers.memoize(countingSupplier);
113     assertSame(memoizedSupplier, Suppliers.memoize(memoizedSupplier));
114   }
115 
testMemoizeExceptionThrown()116   public void testMemoizeExceptionThrown() {
117     memoizeExceptionThrownTest(new ThrowingSupplier());
118     memoizeExceptionThrownTest(new SerializableThrowingSupplier());
119   }
120 
memoizeExceptionThrownTest(ThrowingSupplier throwingSupplier)121   private void memoizeExceptionThrownTest(ThrowingSupplier throwingSupplier) {
122     Supplier<Integer> memoizedSupplier = Suppliers.memoize(throwingSupplier);
123     // call get() twice to make sure that memoization doesn't interfere
124     // with throwing the exception
125     for (int i = 0; i < 2; i++) {
126       try {
127         memoizedSupplier.get();
128         fail("failed to throw NullPointerException");
129       } catch (NullPointerException e) {
130         // this is what should happen
131       }
132     }
133   }
134 
135   @J2ktIncompatible
136   @GwtIncompatible // SerializableTester
testMemoizeNonSerializable()137   public void testMemoizeNonSerializable() throws Exception {
138     CountingSupplier countingSupplier = new CountingSupplier();
139     Supplier<Integer> memoizedSupplier = Suppliers.memoize(countingSupplier);
140     assertThat(memoizedSupplier.toString()).isEqualTo("Suppliers.memoize(CountingSupplier)");
141     checkMemoize(countingSupplier, memoizedSupplier);
142     // Calls to the original memoized supplier shouldn't affect its copy.
143     Object unused = memoizedSupplier.get();
144     assertThat(memoizedSupplier.toString())
145         .isEqualTo("Suppliers.memoize(<supplier that returned 10>)");
146 
147     // Should get an exception when we try to serialize.
148     RuntimeException ex = assertThrows(RuntimeException.class, () -> reserialize(memoizedSupplier));
149     assertThat(ex).hasCauseThat().isInstanceOf(NotSerializableException.class);
150   }
151 
152   @J2ktIncompatible
153   @GwtIncompatible // SerializableTester
testMemoizeSerializable()154   public void testMemoizeSerializable() throws Exception {
155     SerializableCountingSupplier countingSupplier = new SerializableCountingSupplier();
156     Supplier<Integer> memoizedSupplier = Suppliers.memoize(countingSupplier);
157     assertThat(memoizedSupplier.toString()).isEqualTo("Suppliers.memoize(CountingSupplier)");
158     checkMemoize(countingSupplier, memoizedSupplier);
159     // Calls to the original memoized supplier shouldn't affect its copy.
160     Object unused = memoizedSupplier.get();
161     assertThat(memoizedSupplier.toString())
162         .isEqualTo("Suppliers.memoize(<supplier that returned 10>)");
163 
164     Supplier<Integer> copy = reserialize(memoizedSupplier);
165     Object unused2 = memoizedSupplier.get();
166 
167     CountingSupplier countingCopy =
168         (CountingSupplier) ((Suppliers.MemoizingSupplier<Integer>) copy).delegate;
169     checkMemoize(countingCopy, copy);
170   }
171 
testCompose()172   public void testCompose() {
173     Supplier<Integer> fiveSupplier =
174         new Supplier<Integer>() {
175           @Override
176           public Integer get() {
177             return 5;
178           }
179         };
180 
181     Function<Number, Integer> intValueFunction =
182         new Function<Number, Integer>() {
183           @Override
184           public Integer apply(Number x) {
185             return x.intValue();
186           }
187         };
188 
189     Supplier<Integer> squareSupplier = Suppliers.compose(intValueFunction, fiveSupplier);
190 
191     assertEquals(Integer.valueOf(5), squareSupplier.get());
192   }
193 
testComposeWithLists()194   public void testComposeWithLists() {
195     Supplier<ArrayList<Integer>> listSupplier =
196         new Supplier<ArrayList<Integer>>() {
197           @Override
198           public ArrayList<Integer> get() {
199             return Lists.newArrayList(0);
200           }
201         };
202 
203     Function<List<Integer>, List<Integer>> addElementFunction =
204         new Function<List<Integer>, List<Integer>>() {
205           @Override
206           public List<Integer> apply(List<Integer> list) {
207             ArrayList<Integer> result = Lists.newArrayList(list);
208             result.add(1);
209             return result;
210           }
211         };
212 
213     Supplier<List<Integer>> addSupplier = Suppliers.compose(addElementFunction, listSupplier);
214 
215     List<Integer> result = addSupplier.get();
216     assertEquals(Integer.valueOf(0), result.get(0));
217     assertEquals(Integer.valueOf(1), result.get(1));
218   }
219 
220   @J2ktIncompatible
221   @GwtIncompatible // Thread.sleep
222   @SuppressWarnings("DoNotCall")
testMemoizeWithExpiration_longTimeUnit()223   public void testMemoizeWithExpiration_longTimeUnit() throws InterruptedException {
224     CountingSupplier countingSupplier = new CountingSupplier();
225 
226     Supplier<Integer> memoizedSupplier =
227         Suppliers.memoizeWithExpiration(countingSupplier, 75, TimeUnit.MILLISECONDS);
228 
229     checkExpiration(countingSupplier, memoizedSupplier);
230   }
231 
232   @J2ktIncompatible
233   @GwtIncompatible // Thread.sleep
234   @SuppressWarnings("Java7ApiChecker") // test of Java 8+ API
testMemoizeWithExpiration_duration()235   public void testMemoizeWithExpiration_duration() throws InterruptedException {
236     CountingSupplier countingSupplier = new CountingSupplier();
237 
238     Supplier<Integer> memoizedSupplier =
239         Suppliers.memoizeWithExpiration(countingSupplier, Duration.ofMillis(75));
240 
241     checkExpiration(countingSupplier, memoizedSupplier);
242   }
243 
244   @SuppressWarnings("DoNotCall")
testMemoizeWithExpiration_longTimeUnitNegative()245   public void testMemoizeWithExpiration_longTimeUnitNegative() throws InterruptedException {
246     assertThrows(
247         IllegalArgumentException.class,
248         () -> Suppliers.memoizeWithExpiration(() -> "", 0, TimeUnit.MILLISECONDS));
249 
250     assertThrows(
251         IllegalArgumentException.class,
252         () -> Suppliers.memoizeWithExpiration(() -> "", -1, TimeUnit.MILLISECONDS));
253   }
254 
255   @SuppressWarnings("Java7ApiChecker") // test of Java 8+ API
256   @J2ktIncompatible // Duration
257   @GwtIncompatible // Duration
testMemoizeWithExpiration_durationNegative()258   public void testMemoizeWithExpiration_durationNegative() throws InterruptedException {
259     assertThrows(
260         IllegalArgumentException.class,
261         () -> Suppliers.memoizeWithExpiration(() -> "", Duration.ZERO));
262 
263     assertThrows(
264         IllegalArgumentException.class,
265         () -> Suppliers.memoizeWithExpiration(() -> "", Duration.ofMillis(-1)));
266   }
267 
268   @J2ktIncompatible
269   @GwtIncompatible // Thread.sleep, SerializationTester
270   @SuppressWarnings("DoNotCall")
testMemoizeWithExpirationSerialized()271   public void testMemoizeWithExpirationSerialized() throws InterruptedException {
272     SerializableCountingSupplier countingSupplier = new SerializableCountingSupplier();
273 
274     Supplier<Integer> memoizedSupplier =
275         Suppliers.memoizeWithExpiration(countingSupplier, 75, TimeUnit.MILLISECONDS);
276     // Calls to the original memoized supplier shouldn't affect its copy.
277     Object unused = memoizedSupplier.get();
278 
279     Supplier<Integer> copy = reserialize(memoizedSupplier);
280     Object unused2 = memoizedSupplier.get();
281 
282     CountingSupplier countingCopy =
283         (CountingSupplier) ((Suppliers.ExpiringMemoizingSupplier<Integer>) copy).delegate;
284     checkExpiration(countingCopy, copy);
285   }
286 
287   @J2ktIncompatible
288   @GwtIncompatible // Thread.sleep
checkExpiration( CountingSupplier countingSupplier, Supplier<Integer> memoizedSupplier)289   private void checkExpiration(
290       CountingSupplier countingSupplier, Supplier<Integer> memoizedSupplier)
291       throws InterruptedException {
292     // the underlying supplier hasn't executed yet
293     assertEquals(0, countingSupplier.calls);
294 
295     assertEquals(10, (int) memoizedSupplier.get());
296     // now it has
297     assertEquals(1, countingSupplier.calls);
298 
299     assertEquals(10, (int) memoizedSupplier.get());
300     // it still should only have executed once due to memoization
301     assertEquals(1, countingSupplier.calls);
302 
303     Thread.sleep(150);
304 
305     assertEquals(20, (int) memoizedSupplier.get());
306     // old value expired
307     assertEquals(2, countingSupplier.calls);
308 
309     assertEquals(20, (int) memoizedSupplier.get());
310     // it still should only have executed twice due to memoization
311     assertEquals(2, countingSupplier.calls);
312   }
313 
testOfInstanceSuppliesSameInstance()314   public void testOfInstanceSuppliesSameInstance() {
315     Object toBeSupplied = new Object();
316     Supplier<Object> objectSupplier = Suppliers.ofInstance(toBeSupplied);
317     assertSame(toBeSupplied, objectSupplier.get());
318     assertSame(toBeSupplied, objectSupplier.get()); // idempotent
319   }
320 
testOfInstanceSuppliesNull()321   public void testOfInstanceSuppliesNull() {
322     Supplier<@Nullable Integer> nullSupplier = Suppliers.ofInstance(null);
323     assertNull(nullSupplier.get());
324   }
325 
326   @J2ktIncompatible
327   @GwtIncompatible // Thread
328   @SuppressWarnings("DoNotCall")
testExpiringMemoizedSupplierThreadSafe()329   public void testExpiringMemoizedSupplierThreadSafe() throws Throwable {
330     Function<Supplier<Boolean>, Supplier<Boolean>> memoizer =
331         new Function<Supplier<Boolean>, Supplier<Boolean>>() {
332           @Override
333           public Supplier<Boolean> apply(Supplier<Boolean> supplier) {
334             return Suppliers.memoizeWithExpiration(supplier, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
335           }
336         };
337     testSupplierThreadSafe(memoizer);
338   }
339 
340   @J2ktIncompatible
341   @GwtIncompatible // Thread
testMemoizedSupplierThreadSafe()342   public void testMemoizedSupplierThreadSafe() throws Throwable {
343     Function<Supplier<Boolean>, Supplier<Boolean>> memoizer =
344         new Function<Supplier<Boolean>, Supplier<Boolean>>() {
345           @Override
346           public Supplier<Boolean> apply(Supplier<Boolean> supplier) {
347             return Suppliers.memoize(supplier);
348           }
349         };
350     testSupplierThreadSafe(memoizer);
351   }
352 
353   @J2ktIncompatible
354   @GwtIncompatible // Thread
testSupplierThreadSafe(Function<Supplier<Boolean>, Supplier<Boolean>> memoizer)355   private void testSupplierThreadSafe(Function<Supplier<Boolean>, Supplier<Boolean>> memoizer)
356       throws Throwable {
357     final AtomicInteger count = new AtomicInteger(0);
358     final AtomicReference<Throwable> thrown = new AtomicReference<>(null);
359     final int numThreads = 3;
360     final Thread[] threads = new Thread[numThreads];
361     final long timeout = TimeUnit.SECONDS.toNanos(60);
362 
363     final Supplier<Boolean> supplier =
364         new Supplier<Boolean>() {
365           boolean isWaiting(Thread thread) {
366             switch (thread.getState()) {
367               case BLOCKED:
368               case WAITING:
369               case TIMED_WAITING:
370                 return true;
371               default:
372                 return false;
373             }
374           }
375 
376           int waitingThreads() {
377             int waitingThreads = 0;
378             for (Thread thread : threads) {
379               if (isWaiting(thread)) {
380                 waitingThreads++;
381               }
382             }
383             return waitingThreads;
384           }
385 
386           @Override
387           @SuppressWarnings("ThreadPriorityCheck") // doing our best to test for races
388           public Boolean get() {
389             // Check that this method is called exactly once, by the first
390             // thread to synchronize.
391             long t0 = System.nanoTime();
392             while (waitingThreads() != numThreads - 1) {
393               if (System.nanoTime() - t0 > timeout) {
394                 thrown.set(
395                     new TimeoutException(
396                         "timed out waiting for other threads to block"
397                             + " synchronizing on supplier"));
398                 break;
399               }
400               Thread.yield();
401             }
402             count.getAndIncrement();
403             return Boolean.TRUE;
404           }
405         };
406 
407     final Supplier<Boolean> memoizedSupplier = memoizer.apply(supplier);
408 
409     for (int i = 0; i < numThreads; i++) {
410       threads[i] =
411           new Thread() {
412             @Override
413             public void run() {
414               assertSame(Boolean.TRUE, memoizedSupplier.get());
415             }
416           };
417     }
418     for (Thread t : threads) {
419       t.start();
420     }
421     for (Thread t : threads) {
422       t.join();
423     }
424 
425     if (thrown.get() != null) {
426       throw thrown.get();
427     }
428     assertEquals(1, count.get());
429   }
430 
431   @J2ktIncompatible
432   @GwtIncompatible // Thread
433   @SuppressWarnings("ThreadPriorityCheck") // doing our best to test for races
testSynchronizedSupplierThreadSafe()434   public void testSynchronizedSupplierThreadSafe() throws InterruptedException {
435     final Supplier<Integer> nonThreadSafe =
436         new Supplier<Integer>() {
437           int counter = 0;
438 
439           @Override
440           public Integer get() {
441             int nextValue = counter + 1;
442             Thread.yield();
443             counter = nextValue;
444             return counter;
445           }
446         };
447 
448     final int numThreads = 10;
449     final int iterations = 1000;
450     Thread[] threads = new Thread[numThreads];
451     for (int i = 0; i < numThreads; i++) {
452       threads[i] =
453           new Thread() {
454             @Override
455             public void run() {
456               for (int j = 0; j < iterations; j++) {
457                 Object unused = Suppliers.synchronizedSupplier(nonThreadSafe).get();
458               }
459             }
460           };
461     }
462     for (Thread t : threads) {
463       t.start();
464     }
465     for (Thread t : threads) {
466       t.join();
467     }
468 
469     assertEquals(numThreads * iterations + 1, (int) nonThreadSafe.get());
470   }
471 
testSupplierFunction()472   public void testSupplierFunction() {
473     Supplier<Integer> supplier = Suppliers.ofInstance(14);
474     Function<Supplier<Integer>, Integer> supplierFunction = Suppliers.supplierFunction();
475 
476     assertEquals(14, (int) supplierFunction.apply(supplier));
477   }
478 
479   @J2ktIncompatible
480   @GwtIncompatible // SerializationTester
481   @SuppressWarnings("DoNotCall")
testSerialization()482   public void testSerialization() {
483     assertEquals(Integer.valueOf(5), reserialize(Suppliers.ofInstance(5)).get());
484     assertEquals(
485         Integer.valueOf(5),
486         reserialize(Suppliers.compose(Functions.identity(), Suppliers.ofInstance(5))).get());
487     assertEquals(Integer.valueOf(5), reserialize(Suppliers.memoize(Suppliers.ofInstance(5))).get());
488     assertEquals(
489         Integer.valueOf(5),
490         reserialize(Suppliers.memoizeWithExpiration(Suppliers.ofInstance(5), 30, TimeUnit.SECONDS))
491             .get());
492     assertEquals(
493         Integer.valueOf(5),
494         reserialize(Suppliers.synchronizedSupplier(Suppliers.ofInstance(5))).get());
495   }
496 
497   @J2ktIncompatible
498   @GwtIncompatible // reflection
499   @SuppressWarnings("Java7ApiChecker") // includes test of Java 8+ API
testSuppliersNullChecks()500   public void testSuppliersNullChecks() throws Exception {
501     new ClassSanityTester()
502         .setDefault(Duration.class, Duration.ofSeconds(1))
503         .forAllPublicStaticMethods(Suppliers.class)
504         .testNulls();
505   }
506 
507   @J2ktIncompatible
508   @GwtIncompatible // reflection
509   @AndroidIncompatible // TODO(cpovirk): ClassNotFoundException: com.google.common.base.Function
510   @SuppressWarnings("Java7ApiChecker") // includes test of Java 8+ API
testSuppliersSerializable()511   public void testSuppliersSerializable() throws Exception {
512     new ClassSanityTester()
513         .setDefault(Duration.class, Duration.ofSeconds(1))
514         .forAllPublicStaticMethods(Suppliers.class)
515         .testSerializable();
516   }
517 
testOfInstance_equals()518   public void testOfInstance_equals() {
519     new EqualsTester()
520         .addEqualityGroup(Suppliers.ofInstance("foo"), Suppliers.ofInstance("foo"))
521         .addEqualityGroup(Suppliers.ofInstance("bar"))
522         .testEquals();
523   }
524 
testCompose_equals()525   public void testCompose_equals() {
526     new EqualsTester()
527         .addEqualityGroup(
528             Suppliers.compose(Functions.constant(1), Suppliers.ofInstance("foo")),
529             Suppliers.compose(Functions.constant(1), Suppliers.ofInstance("foo")))
530         .addEqualityGroup(Suppliers.compose(Functions.constant(2), Suppliers.ofInstance("foo")))
531         .addEqualityGroup(Suppliers.compose(Functions.constant(1), Suppliers.ofInstance("bar")))
532         .testEquals();
533   }
534 }
535