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