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