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