1 /* 2 * Copyright (C) 2014 Google Inc. 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.inject.internal; 18 19 import static com.google.inject.Asserts.awaitClear; 20 import static com.google.inject.Asserts.awaitFullGc; 21 import static com.google.inject.internal.WeakKeySetUtils.assertBlacklisted; 22 import static com.google.inject.internal.WeakKeySetUtils.assertInSet; 23 import static com.google.inject.internal.WeakKeySetUtils.assertNotBlacklisted; 24 import static com.google.inject.internal.WeakKeySetUtils.assertNotInSet; 25 import static com.google.inject.internal.WeakKeySetUtils.assertSourceNotInSet; 26 27 import com.google.common.collect.ImmutableList; 28 import com.google.common.collect.ImmutableMap; 29 import com.google.common.collect.ImmutableSet; 30 import com.google.inject.AbstractModule; 31 import com.google.inject.Binding; 32 import com.google.inject.Guice; 33 import com.google.inject.Injector; 34 import com.google.inject.Key; 35 import com.google.inject.Scope; 36 import com.google.inject.TypeLiteral; 37 import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding; 38 import com.google.inject.spi.ProvisionListenerBinding; 39 import com.google.inject.spi.ScopeBinding; 40 import com.google.inject.spi.TypeConverterBinding; 41 import com.google.inject.spi.TypeListenerBinding; 42 import java.lang.annotation.Annotation; 43 import java.lang.ref.WeakReference; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Set; 47 import junit.framework.TestCase; 48 49 /** 50 * Tests for {@link WeakKeySet}. 51 * 52 * <p>Multibinding specific tests can be found in MultibinderTest and MapBinderTest. 53 * 54 * @author dweis@google.com (Daniel Weis) 55 */ 56 public class WeakKeySetTest extends TestCase { 57 58 private WeakKeySet set; 59 60 @Override setUp()61 protected void setUp() throws Exception { 62 set = new WeakKeySet(new Object()); 63 } 64 testEviction()65 public void testEviction() { 66 TestState state = new TestState(); 67 Key<Integer> key = Key.get(Integer.class); 68 Object source = new Object(); 69 70 WeakReference<Key<Integer>> weakKeyRef = new WeakReference<>(key); 71 72 set.add(key, state, source); 73 assertInSet(set, key, 1, source); 74 75 state = null; 76 77 awaitFullGc(); 78 79 assertNotInSet(set, Key.get(Integer.class)); 80 81 // Ensure there are no hanging references. 82 key = null; 83 awaitClear(weakKeyRef); 84 } 85 testEviction_nullSource()86 public void testEviction_nullSource() { 87 TestState state = new TestState(); 88 Key<Integer> key = Key.get(Integer.class); 89 Object source = null; 90 91 WeakReference<Key<Integer>> weakKeyRef = new WeakReference<>(key); 92 93 set.add(key, state, source); 94 assertInSet(set, key, 1, source); 95 96 state = null; 97 98 awaitFullGc(); 99 100 assertNotInSet(set, Key.get(Integer.class)); 101 102 // Ensure there are no hanging references. 103 key = null; 104 awaitClear(weakKeyRef); 105 } 106 testEviction_keyOverlap_2x()107 public void testEviction_keyOverlap_2x() { 108 TestState state1 = new TestState(); 109 TestState state2 = new TestState(); 110 Key<Integer> key1 = Key.get(Integer.class); 111 Key<Integer> key2 = Key.get(Integer.class); 112 Object source1 = new Object(); 113 Object source2 = new Object(); 114 115 set.add(key1, state1, source1); 116 assertInSet(set, key1, 1, source1); 117 118 set.add(key2, state2, source2); 119 assertInSet(set, key2, 2, source1, source2); 120 121 WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<>(key1); 122 WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<>(key2); 123 WeakReference<Object> weakSource1Ref = new WeakReference<>(source1); 124 WeakReference<Object> weakSource2Ref = new WeakReference<>(source2); 125 126 Key<Integer> key = key1 = key2 = Key.get(Integer.class); 127 state1 = null; 128 129 awaitFullGc(); 130 131 assertSourceNotInSet(set, key, source1); 132 assertInSet(set, key, 1, source2); 133 134 source1 = source2 = null; 135 136 awaitClear(weakSource1Ref); 137 // Key1 will be referenced as the key in the sources backingSet and won't be 138 // GC'd. 139 140 // Should not be GC'd until state2 goes away. 141 assertNotNull(weakSource2Ref.get()); 142 143 state2 = null; 144 145 awaitFullGc(); 146 147 assertNotInSet(set, key); 148 149 awaitClear(weakKey2Ref); 150 awaitClear(weakSource2Ref); 151 // Now that the backing set is emptied, key1 is released. 152 awaitClear(weakKey1Ref); 153 } 154 testNoEviction_keyOverlap_2x()155 public void testNoEviction_keyOverlap_2x() { 156 TestState state1 = new TestState(); 157 TestState state2 = new TestState(); 158 Key<Integer> key1 = Key.get(Integer.class); 159 Key<Integer> key2 = Key.get(Integer.class); 160 Object source1 = new Object(); 161 Object source2 = new Object(); 162 163 set.add(key1, state1, source1); 164 assertInSet(set, key1, 1, source1); 165 166 set.add(key2, state2, source2); 167 assertInSet(set, key2, 2, source1, source2); 168 169 WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<>(key1); 170 WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<>(key2); 171 172 Key<Integer> key = key1 = key2 = Key.get(Integer.class); 173 174 awaitFullGc(); 175 assertInSet(set, key, 2, source1, source2); 176 177 // Ensure the keys don't get GC'd when states are still referenced. key1 will be present in the 178 // as the map key but key2 could be GC'd if the implementation does something wrong. 179 assertNotNull(weakKey1Ref.get()); 180 assertNotNull(weakKey2Ref.get()); 181 } 182 testEviction_keyAndSourceOverlap_null()183 public void testEviction_keyAndSourceOverlap_null() { 184 TestState state1 = new TestState(); 185 TestState state2 = new TestState(); 186 Key<Integer> key1 = Key.get(Integer.class); 187 Key<Integer> key2 = Key.get(Integer.class); 188 Object source = null; 189 190 set.add(key1, state1, source); 191 assertInSet(set, key1, 1, source); 192 193 set.add(key2, state2, source); 194 // Same source so still only one value. 195 assertInSet(set, key2, 1, source); 196 assertInSet(set, key1, 1, source); 197 198 WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<>(key1); 199 WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<>(key2); 200 WeakReference<Object> weakSourceRef = new WeakReference<>(source); 201 202 Key<Integer> key = key1 = key2 = Key.get(Integer.class); 203 state1 = null; 204 205 awaitFullGc(); 206 // Should still have a single source. 207 assertInSet(set, key, 1, source); 208 209 source = null; 210 211 awaitClear(weakSourceRef); 212 // Key1 will be referenced as the key in the sources backingSet and won't be 213 // GC'd. 214 215 state2 = null; 216 217 awaitFullGc(); 218 assertNotInSet(set, key); 219 220 awaitClear(weakKey2Ref); 221 awaitClear(weakSourceRef); 222 // Now that the backing set is emptied, key1 is released. 223 awaitClear(weakKey1Ref); 224 } 225 testEviction_keyAndSourceOverlap_nonNull()226 public void testEviction_keyAndSourceOverlap_nonNull() { 227 TestState state1 = new TestState(); 228 TestState state2 = new TestState(); 229 Key<Integer> key1 = Key.get(Integer.class); 230 Key<Integer> key2 = Key.get(Integer.class); 231 Object source = new Object(); 232 233 set.add(key1, state1, source); 234 assertInSet(set, key1, 1, source); 235 236 set.add(key2, state2, source); 237 // Same source so still only one value. 238 assertInSet(set, key2, 1, source); 239 240 WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<>(key1); 241 WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<>(key2); 242 WeakReference<Object> weakSourceRef = new WeakReference<>(source); 243 244 Key<Integer> key = key1 = key2 = Key.get(Integer.class); 245 state1 = null; 246 247 awaitFullGc(); 248 249 // Same source so still only one value. 250 assertInSet(set, key, 1, source); 251 assertInSet(set, key1, 1, source); 252 253 source = null; 254 255 awaitFullGc(); 256 assertNotNull(weakSourceRef.get()); 257 // Key1 will be referenced as the key in the sources backingSet and won't be 258 // GC'd. 259 260 state2 = null; 261 262 awaitFullGc(); 263 264 assertNotInSet(set, key); 265 266 awaitClear(weakKey2Ref); 267 awaitClear(weakSourceRef); 268 // Now that the backing set is emptied, key1 is released. 269 awaitClear(weakKey1Ref); 270 } 271 testEviction_keyOverlap_3x()272 public void testEviction_keyOverlap_3x() { 273 TestState state1 = new TestState(); 274 TestState state2 = new TestState(); 275 TestState state3 = new TestState(); 276 Key<Integer> key1 = Key.get(Integer.class); 277 Key<Integer> key2 = Key.get(Integer.class); 278 Key<Integer> key3 = Key.get(Integer.class); 279 Object source1 = new Object(); 280 Object source2 = new Object(); 281 Object source3 = new Object(); 282 283 set.add(key1, state1, source1); 284 assertInSet(set, key1, 1, source1); 285 286 set.add(key2, state2, source2); 287 assertInSet(set, key1, 2, source1, source2); 288 289 set.add(key3, state3, source3); 290 assertInSet(set, key1, 3, source1, source2, source3); 291 292 WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<>(key1); 293 WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<>(key2); 294 WeakReference<Key<Integer>> weakKey3Ref = new WeakReference<>(key3); 295 WeakReference<Object> weakSource1Ref = new WeakReference<>(source1); 296 WeakReference<Object> weakSource2Ref = new WeakReference<>(source2); 297 WeakReference<Object> weakSource3Ref = new WeakReference<>(source3); 298 299 Key<Integer> key = key1 = key2 = key3 = Key.get(Integer.class); 300 state1 = null; 301 302 awaitFullGc(); 303 assertSourceNotInSet(set, key, source1); 304 assertInSet(set, key, 2, source2, source3); 305 306 source1 = null; 307 // Key1 will be referenced as the key in the sources backingSet and won't be 308 // GC'd. 309 awaitClear(weakSource1Ref); 310 311 state2 = null; 312 awaitFullGc(); 313 assertSourceNotInSet(set, key, source2); 314 assertInSet(set, key, 1, source3); 315 316 awaitClear(weakKey2Ref); 317 318 source2 = null; 319 awaitClear(weakSource2Ref); 320 // Key1 will be referenced as the key in the sources backingSet and won't be 321 // GC'd. 322 323 state3 = null; 324 awaitFullGc(); 325 assertNotInSet(set, key); 326 327 awaitClear(weakKey3Ref); 328 source3 = null; 329 awaitClear(weakSource3Ref); 330 // Now that the backing set is emptied, key1 is released. 331 awaitClear(weakKey1Ref); 332 } 333 testWeakKeySet_integration()334 public void testWeakKeySet_integration() { 335 Injector parentInjector = 336 Guice.createInjector( 337 new AbstractModule() { 338 @Override 339 protected void configure() { 340 bind(Integer.class).toInstance(4); 341 } 342 }); 343 assertNotBlacklisted(parentInjector, Key.get(String.class)); 344 345 Injector childInjector = 346 parentInjector.createChildInjector( 347 new AbstractModule() { 348 @Override 349 protected void configure() { 350 bind(String.class).toInstance("bar"); 351 } 352 }); 353 WeakReference<Injector> weakRef = new WeakReference<>(childInjector); 354 assertBlacklisted(parentInjector, Key.get(String.class)); 355 356 // Clear the ref, GC, and ensure that we are no longer blacklisting. 357 childInjector = null; 358 awaitClear(weakRef); 359 assertNotBlacklisted(parentInjector, Key.get(String.class)); 360 } 361 testWeakKeySet_integration_multipleChildren()362 public void testWeakKeySet_integration_multipleChildren() { 363 Injector parentInjector = 364 Guice.createInjector( 365 new AbstractModule() { 366 @Override 367 protected void configure() { 368 bind(Integer.class).toInstance(4); 369 } 370 }); 371 assertNotBlacklisted(parentInjector, Key.get(String.class)); 372 assertNotBlacklisted(parentInjector, Key.get(Long.class)); 373 374 Injector childInjector1 = 375 parentInjector.createChildInjector( 376 new AbstractModule() { 377 @Override 378 protected void configure() { 379 bind(String.class).toInstance("foo"); 380 } 381 }); 382 WeakReference<Injector> weakRef1 = new WeakReference<>(childInjector1); 383 assertBlacklisted(parentInjector, Key.get(String.class)); 384 assertNotBlacklisted(parentInjector, Key.get(Long.class)); 385 386 Injector childInjector2 = 387 parentInjector.createChildInjector( 388 new AbstractModule() { 389 @Override 390 protected void configure() { 391 bind(Long.class).toInstance(6L); 392 } 393 }); 394 WeakReference<Injector> weakRef2 = new WeakReference<>(childInjector2); 395 assertBlacklisted(parentInjector, Key.get(String.class)); 396 assertBlacklisted(parentInjector, Key.get(Long.class)); 397 398 // Clear ref1, GC, and ensure that we still blacklist. 399 childInjector1 = null; 400 awaitClear(weakRef1); 401 assertNotBlacklisted(parentInjector, Key.get(String.class)); 402 assertBlacklisted(parentInjector, Key.get(Long.class)); 403 404 // Clear the ref, GC, and ensure that we are no longer blacklisting. 405 childInjector2 = null; 406 awaitClear(weakRef2); 407 assertNotBlacklisted(parentInjector, Key.get(String.class)); 408 assertNotBlacklisted(parentInjector, Key.get(Long.class)); 409 } 410 testWeakKeySet_integration_multipleChildren_overlappingKeys()411 public void testWeakKeySet_integration_multipleChildren_overlappingKeys() { 412 Injector parentInjector = 413 Guice.createInjector( 414 new AbstractModule() { 415 @Override 416 protected void configure() { 417 bind(Integer.class).toInstance(4); 418 } 419 }); 420 assertNotBlacklisted(parentInjector, Key.get(String.class)); 421 422 Injector childInjector1 = 423 parentInjector.createChildInjector( 424 new AbstractModule() { 425 @Override 426 protected void configure() { 427 bind(String.class).toInstance("foo"); 428 } 429 }); 430 WeakReference<Injector> weakRef1 = new WeakReference<>(childInjector1); 431 assertBlacklisted(parentInjector, Key.get(String.class)); 432 433 Injector childInjector2 = 434 parentInjector.createChildInjector( 435 new AbstractModule() { 436 @Override 437 protected void configure() { 438 bind(String.class).toInstance("bar"); 439 } 440 }); 441 WeakReference<Injector> weakRef2 = new WeakReference<>(childInjector2); 442 assertBlacklisted(parentInjector, Key.get(String.class)); 443 444 // Clear ref1, GC, and ensure that we still blacklist. 445 childInjector1 = null; 446 awaitClear(weakRef1); 447 assertBlacklisted(parentInjector, Key.get(String.class)); 448 449 // Clear the ref, GC, and ensure that we are no longer blacklisting. 450 childInjector2 = null; 451 awaitClear(weakRef2); 452 assertNotBlacklisted(parentInjector, Key.get(String.class)); 453 } 454 455 private static class TestState implements State { 456 @Override parent()457 public State parent() { 458 return new TestState(); 459 } 460 461 @Override getExplicitBinding(Key<T> key)462 public <T> BindingImpl<T> getExplicitBinding(Key<T> key) { 463 return null; 464 } 465 466 @Override getExplicitBindingsThisLevel()467 public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() { 468 throw new UnsupportedOperationException(); 469 } 470 471 @Override putBinding(Key<?> key, BindingImpl<?> binding)472 public void putBinding(Key<?> key, BindingImpl<?> binding) { 473 throw new UnsupportedOperationException(); 474 } 475 476 @Override getScopeBinding(Class<? extends Annotation> scopingAnnotation)477 public ScopeBinding getScopeBinding(Class<? extends Annotation> scopingAnnotation) { 478 return null; 479 } 480 481 @Override putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope)482 public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) { 483 throw new UnsupportedOperationException(); 484 } 485 486 @Override addConverter(TypeConverterBinding typeConverterBinding)487 public void addConverter(TypeConverterBinding typeConverterBinding) { 488 throw new UnsupportedOperationException(); 489 } 490 491 @Override getConverter( String stringValue, TypeLiteral<?> type, Errors errors, Object source)492 public TypeConverterBinding getConverter( 493 String stringValue, TypeLiteral<?> type, Errors errors, Object source) { 494 throw new UnsupportedOperationException(); 495 } 496 497 @Override getConvertersThisLevel()498 public Iterable<TypeConverterBinding> getConvertersThisLevel() { 499 return ImmutableSet.of(); 500 } 501 502 /*if[AOP]*/ 503 @Override addMethodAspect(MethodAspect methodAspect)504 public void addMethodAspect(MethodAspect methodAspect) { 505 throw new UnsupportedOperationException(); 506 } 507 508 @Override getMethodAspects()509 public ImmutableList<MethodAspect> getMethodAspects() { 510 return ImmutableList.of(); 511 } 512 /*end[AOP]*/ 513 514 @Override addTypeListener(TypeListenerBinding typeListenerBinding)515 public void addTypeListener(TypeListenerBinding typeListenerBinding) { 516 throw new UnsupportedOperationException(); 517 } 518 519 @Override getTypeListenerBindings()520 public List<TypeListenerBinding> getTypeListenerBindings() { 521 return ImmutableList.of(); 522 } 523 524 @Override addProvisionListener(ProvisionListenerBinding provisionListenerBinding)525 public void addProvisionListener(ProvisionListenerBinding provisionListenerBinding) { 526 throw new UnsupportedOperationException(); 527 } 528 529 @Override getProvisionListenerBindings()530 public List<ProvisionListenerBinding> getProvisionListenerBindings() { 531 return ImmutableList.of(); 532 } 533 534 @Override addScanner(ModuleAnnotatedMethodScannerBinding scanner)535 public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) { 536 throw new UnsupportedOperationException(); 537 } 538 539 @Override getScannerBindings()540 public List<ModuleAnnotatedMethodScannerBinding> getScannerBindings() { 541 return ImmutableList.of(); 542 } 543 544 @Override blacklist(Key<?> key, State state, Object source)545 public void blacklist(Key<?> key, State state, Object source) {} 546 547 @Override isBlacklisted(Key<?> key)548 public boolean isBlacklisted(Key<?> key) { 549 return true; 550 } 551 552 @Override getSourcesForBlacklistedKey(Key<?> key)553 public Set<Object> getSourcesForBlacklistedKey(Key<?> key) { 554 throw new UnsupportedOperationException(); 555 } 556 557 @Override lock()558 public Object lock() { 559 throw new UnsupportedOperationException(); 560 } 561 singletonCreationLock()562 public Object singletonCreationLock() { 563 throw new UnsupportedOperationException(); 564 } 565 566 @Override getScopes()567 public Map<Class<? extends Annotation>, Scope> getScopes() { 568 return ImmutableMap.of(); 569 } 570 } 571 } 572