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