package org.mockito.internal.creation.bytebuddy;
import org.junit.Before;
import org.junit.Test;
import org.mockito.mock.SerializableMode;
import org.mockitoutil.VmArgAssumptions;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.WeakHashMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.internal.creation.bytebuddy.MockFeatures.withMockFeatures;
import static org.mockitoutil.ClassLoaders.inMemoryClassLoader;
import static org.mockitoutil.SimpleClassGenerator.makeMarkerInterface;
public class TypeCachingMockBytecodeGeneratorTest {
@Before
public void ensure_disable_gc_is_activated() throws Exception {
VmArgAssumptions.assumeVmArgNotPresent("-XX:+DisableExplicitGC");
}
@Test
public void ensure_cache_is_cleared_if_no_reference_to_classloader_and_classes() throws Exception {
// given
ClassLoader classloader_with_life_shorter_than_cache = inMemoryClassLoader()
.withClassDefinition("foo.Bar", makeMarkerInterface("foo.Bar"))
.build();
TypeCachingBytecodeGenerator cachingMockBytecodeGenerator = new TypeCachingBytecodeGenerator(new SubclassBytecodeGenerator(), true);
Class<?> the_mock_type = cachingMockBytecodeGenerator.mockClass(withMockFeatures(
classloader_with_life_shorter_than_cache.loadClass("foo.Bar"),
Collections.<Class<?>>emptySet(),
SerializableMode.NONE
));
assertThat(cachingMockBytecodeGenerator.avoidingClassLeakageCache).hasSize(1);
// when
classloader_with_life_shorter_than_cache = is_no_more_referenced();
the_mock_type = is_no_more_referenced();
System.gc();
ensure_gc_happened();
cachingMockBytecodeGenerator.cleanUpCachesForObsoleteClassLoaders();
// then
assertThat(cachingMockBytecodeGenerator.avoidingClassLeakageCache).isEmpty();
}
@Test
public void ensure_cache_returns_same_instance() throws Exception {
// given
ClassLoader classloader_with_life_shorter_than_cache = inMemoryClassLoader()
.withClassDefinition("foo.Bar", makeMarkerInterface("foo.Bar"))
.build();
TypeCachingBytecodeGenerator cachingMockBytecodeGenerator = new TypeCachingBytecodeGenerator(new SubclassBytecodeGenerator(), true);
Class<?> the_mock_type = cachingMockBytecodeGenerator.mockClass(withMockFeatures(
classloader_with_life_shorter_than_cache.loadClass("foo.Bar"),
Collections.<Class<?>>emptySet(),
SerializableMode.NONE
));
Class<?> other_mock_type = cachingMockBytecodeGenerator.mockClass(withMockFeatures(
classloader_with_life_shorter_than_cache.loadClass("foo.Bar"),
Collections.<Class<?>>emptySet(),
SerializableMode.NONE
));
assertThat(other_mock_type).isSameAs(the_mock_type);
assertThat(cachingMockBytecodeGenerator.avoidingClassLeakageCache).hasSize(1);
// when
classloader_with_life_shorter_than_cache = is_no_more_referenced();
the_mock_type = is_no_more_referenced();
System.gc();
ensure_gc_happened();
cachingMockBytecodeGenerator.cleanUpCachesForObsoleteClassLoaders();
// then
assertThat(cachingMockBytecodeGenerator.avoidingClassLeakageCache).hasSize(1);
}
@Test
public void validate_simple_code_idea_where_weakhashmap_with_classloader_as_key_get_GCed_when_no_more_references() throws Exception {
// given
WeakHashMap<ClassLoader, Object> cache = new WeakHashMap<ClassLoader, Object>();
ClassLoader short_lived_classloader = inMemoryClassLoader()
.withClassDefinition("foo.Bar", makeMarkerInterface("foo.Bar"))
.build();
cache.put(short_lived_classloader, new HoldingAReference(new WeakReference<Class<?>>(short_lived_classloader.loadClass("foo.Bar"))));
assertThat(cache).hasSize(1);
// when
short_lived_classloader = is_no_more_referenced();
System.gc();
ensure_gc_happened();
// then
assertThat(cache).isEmpty();
}
static class HoldingAReference {
final WeakReference<Class<?>> a;
HoldingAReference(WeakReference<Class<?>> a) {
this.a = a;
}
}
private static <T> T is_no_more_referenced() {
return null;
}
private static void ensure_gc_happened() throws InterruptedException {
// wait in order to make sure the GC happened
Thread.sleep(500);
}
}