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