1 package org.mockito.internal.creation.bytebuddy; 2 3 import org.junit.Before; 4 import org.junit.Test; 5 import org.mockito.mock.SerializableMode; 6 import org.mockitoutil.VmArgAssumptions; 7 8 import java.lang.ref.PhantomReference; 9 import java.lang.ref.Reference; 10 import java.lang.ref.ReferenceQueue; 11 import java.lang.ref.WeakReference; 12 import java.util.Collections; 13 import java.util.WeakHashMap; 14 15 import static org.assertj.core.api.Assertions.assertThat; 16 import static org.mockito.internal.creation.bytebuddy.MockFeatures.withMockFeatures; 17 import static org.mockitoutil.ClassLoaders.inMemoryClassLoader; 18 import static org.mockitoutil.SimpleClassGenerator.makeMarkerInterface; 19 20 public class TypeCachingMockBytecodeGeneratorTest { 21 22 @Before ensure_disable_gc_is_activated()23 public void ensure_disable_gc_is_activated() throws Exception { 24 VmArgAssumptions.assumeVmArgNotPresent("-XX:+DisableExplicitGC"); 25 } 26 27 @Test ensure_cache_is_cleared_if_no_reference_to_classloader_and_classes()28 public void ensure_cache_is_cleared_if_no_reference_to_classloader_and_classes() throws Exception { 29 // given 30 ClassLoader classloader_with_life_shorter_than_cache = inMemoryClassLoader() 31 .withClassDefinition("foo.Bar", makeMarkerInterface("foo.Bar")) 32 .build(); 33 34 TypeCachingBytecodeGenerator cachingMockBytecodeGenerator = new TypeCachingBytecodeGenerator(new SubclassBytecodeGenerator(), true); 35 36 Class<?> the_mock_type = cachingMockBytecodeGenerator.mockClass(withMockFeatures( 37 classloader_with_life_shorter_than_cache.loadClass("foo.Bar"), 38 Collections.<Class<?>>emptySet(), 39 SerializableMode.NONE 40 )); 41 42 ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>(); 43 Reference<Object> typeReference = new PhantomReference<Object>(the_mock_type, referenceQueue); 44 45 // when 46 classloader_with_life_shorter_than_cache = is_no_more_referenced(); 47 the_mock_type = is_no_more_referenced(); 48 49 System.gc(); 50 ensure_gc_happened(); 51 52 // then 53 assertThat(referenceQueue.poll()).isEqualTo(typeReference); 54 } 55 56 @Test ensure_cache_returns_same_instance()57 public void ensure_cache_returns_same_instance() throws Exception { 58 // given 59 ClassLoader classloader_with_life_shorter_than_cache = inMemoryClassLoader() 60 .withClassDefinition("foo.Bar", makeMarkerInterface("foo.Bar")) 61 .build(); 62 63 TypeCachingBytecodeGenerator cachingMockBytecodeGenerator = new TypeCachingBytecodeGenerator(new SubclassBytecodeGenerator(), true); 64 Class<?> the_mock_type = cachingMockBytecodeGenerator.mockClass(withMockFeatures( 65 classloader_with_life_shorter_than_cache.loadClass("foo.Bar"), 66 Collections.<Class<?>>emptySet(), 67 SerializableMode.NONE 68 )); 69 70 Class<?> other_mock_type = cachingMockBytecodeGenerator.mockClass(withMockFeatures( 71 classloader_with_life_shorter_than_cache.loadClass("foo.Bar"), 72 Collections.<Class<?>>emptySet(), 73 SerializableMode.NONE 74 )); 75 76 assertThat(other_mock_type).isSameAs(the_mock_type); 77 78 ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>(); 79 Reference<Object> typeReference = new PhantomReference<Object>(the_mock_type, referenceQueue); 80 81 // when 82 classloader_with_life_shorter_than_cache = is_no_more_referenced(); 83 the_mock_type = is_no_more_referenced(); 84 other_mock_type = is_no_more_referenced(); 85 86 System.gc(); 87 ensure_gc_happened(); 88 89 // then 90 assertThat(referenceQueue.poll()).isEqualTo(typeReference); 91 } 92 93 @Test ensure_cache_returns_different_instance_serializableMode()94 public void ensure_cache_returns_different_instance_serializableMode() throws Exception { 95 // given 96 ClassLoader classloader_with_life_shorter_than_cache = inMemoryClassLoader() 97 .withClassDefinition("foo.Bar", makeMarkerInterface("foo.Bar")) 98 .build(); 99 100 TypeCachingBytecodeGenerator cachingMockBytecodeGenerator = new TypeCachingBytecodeGenerator(new SubclassBytecodeGenerator(), true); 101 Class<?> the_mock_type = cachingMockBytecodeGenerator.mockClass(withMockFeatures( 102 classloader_with_life_shorter_than_cache.loadClass("foo.Bar"), 103 Collections.<Class<?>>emptySet(), 104 SerializableMode.NONE 105 )); 106 107 Class<?> other_mock_type = cachingMockBytecodeGenerator.mockClass(withMockFeatures( 108 classloader_with_life_shorter_than_cache.loadClass("foo.Bar"), 109 Collections.<Class<?>>emptySet(), 110 SerializableMode.BASIC 111 )); 112 113 assertThat(other_mock_type).isNotSameAs(the_mock_type); 114 } 115 116 @Test validate_simple_code_idea_where_weakhashmap_with_classloader_as_key_get_GCed_when_no_more_references()117 public void validate_simple_code_idea_where_weakhashmap_with_classloader_as_key_get_GCed_when_no_more_references() throws Exception { 118 // given 119 WeakHashMap<ClassLoader, Object> cache = new WeakHashMap<ClassLoader, Object>(); 120 ClassLoader short_lived_classloader = inMemoryClassLoader() 121 .withClassDefinition("foo.Bar", makeMarkerInterface("foo.Bar")) 122 .build(); 123 124 cache.put(short_lived_classloader, new HoldingAReference(new WeakReference<Class<?>>(short_lived_classloader.loadClass("foo.Bar")))); 125 126 assertThat(cache).hasSize(1); 127 128 // when 129 short_lived_classloader = is_no_more_referenced(); 130 131 System.gc(); 132 ensure_gc_happened(); 133 134 // then 135 assertThat(cache).isEmpty(); 136 } 137 138 static class HoldingAReference { 139 final WeakReference<Class<?>> a; 140 HoldingAReference(WeakReference<Class<?>> a)141 HoldingAReference(WeakReference<Class<?>> a) { 142 this.a = a; 143 } 144 } 145 146 is_no_more_referenced()147 private static <T> T is_no_more_referenced() { 148 return null; 149 } 150 ensure_gc_happened()151 private static void ensure_gc_happened() throws InterruptedException { 152 // wait in order to make sure the GC happened 153 Thread.sleep(500); 154 } 155 } 156