package net.bytebuddy.dynamic;
import net.bytebuddy.dynamic.loading.ByteArrayClassLoader;
import net.bytebuddy.dynamic.loading.PackageDefinitionStrategy;
import net.bytebuddy.implementation.LoadedTypeInitializer;
import net.bytebuddy.test.utility.ClassFileExtraction;
import net.bytebuddy.test.utility.MockitoRule;
import net.bytebuddy.test.utility.ObjectPropertyAssertion;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.mockito.Mock;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class NexusTest {
private static final String FOO = "foo";
private static final int BAR = 42;
@Rule
public TestRule mockitoRule = new MockitoRule(this);
@Mock
private LoadedTypeInitializer loadedTypeInitializer;
@Mock
private ClassLoader classLoader;
@Test
public void testNexusIsPublic() throws Exception {
assertThat(Modifier.isPublic(Nexus.class.getModifiers()), is(true));
}
@Test
public void testNexusHasNoDeclaringType() throws Exception {
assertThat(Nexus.class.getDeclaringClass(), nullValue(Class.class));
}
@Test
public void testNexusHasNoDeclaredTypes() throws Exception {
assertThat(Nexus.class.getDeclaredClasses().length, is(0));
}
@Test
public void testNexusAccessorNonActive() throws Exception {
ClassLoader classLoader = new ByteArrayClassLoader.ChildFirst(getClass().getClassLoader(),
ClassFileExtraction.of(Nexus.class,
NexusAccessor.class,
NexusAccessor.Dispatcher.class,
NexusAccessor.Dispatcher.CreationAction.class,
NexusAccessor.Dispatcher.Available.class,
NexusAccessor.Dispatcher.Unavailable.class),
null,
ByteArrayClassLoader.PersistenceHandler.MANIFEST,
PackageDefinitionStrategy.NoOp.INSTANCE);
Field duplicateInitializers = classLoader.loadClass(Nexus.class.getName()).getDeclaredField("TYPE_INITIALIZERS");
duplicateInitializers.setAccessible(true);
assertThat(((Map<?, ?>) duplicateInitializers.get(null)).size(), is(0));
Field actualInitializers = Nexus.class.getDeclaredField("TYPE_INITIALIZERS");
actualInitializers.setAccessible(true);
assertThat(((Map<?, ?>) actualInitializers.get(null)).size(), is(0));
Class<?> accessor = classLoader.loadClass(NexusAccessor.class.getName());
ClassLoader qux = mock(ClassLoader.class);
when(loadedTypeInitializer.isAlive()).thenReturn(false);
assertThat(accessor
.getDeclaredMethod("register", String.class, ClassLoader.class, int.class, LoadedTypeInitializer.class)
.invoke(accessor.getDeclaredConstructor().newInstance(), FOO, qux, BAR, loadedTypeInitializer), nullValue(Object.class));
try {
assertThat(((Map<?, ?>) duplicateInitializers.get(null)).size(), is(0));
assertThat(((Map<?, ?>) actualInitializers.get(null)).size(), is(0));
} finally {
Constructor<Nexus> constructor = Nexus.class.getDeclaredConstructor(String.class, ClassLoader.class, ReferenceQueue.class, int.class);
constructor.setAccessible(true);
Object value = ((Map<?, ?>) actualInitializers.get(null)).remove(constructor.newInstance(FOO, qux, Nexus.NO_QUEUE, BAR));
assertThat(value, nullValue());
}
}
@Test
public void testNexusAccessorClassLoaderBoundary() throws Exception {
ClassLoader classLoader = new ByteArrayClassLoader.ChildFirst(getClass().getClassLoader(),
ClassFileExtraction.of(Nexus.class,
NexusAccessor.class,
NexusAccessor.Dispatcher.class,
NexusAccessor.Dispatcher.CreationAction.class,
NexusAccessor.Dispatcher.Available.class,
NexusAccessor.Dispatcher.Unavailable.class),
null,
ByteArrayClassLoader.PersistenceHandler.MANIFEST,
PackageDefinitionStrategy.NoOp.INSTANCE);
Field duplicateInitializers = classLoader.loadClass(Nexus.class.getName()).getDeclaredField("TYPE_INITIALIZERS");
duplicateInitializers.setAccessible(true);
assertThat(((Map<?, ?>) duplicateInitializers.get(null)).size(), is(0));
Field actualInitializers = Nexus.class.getDeclaredField("TYPE_INITIALIZERS");
actualInitializers.setAccessible(true);
assertThat(((Map<?, ?>) actualInitializers.get(null)).size(), is(0));
Class<?> accessor = classLoader.loadClass(NexusAccessor.class.getName());
ClassLoader qux = mock(ClassLoader.class);
when(loadedTypeInitializer.isAlive()).thenReturn(true);
assertThat(accessor
.getDeclaredMethod("register", String.class, ClassLoader.class, int.class, LoadedTypeInitializer.class)
.invoke(accessor.getDeclaredConstructor().newInstance(), FOO, qux, BAR, loadedTypeInitializer), nullValue(Object.class));
try {
assertThat(((Map<?, ?>) duplicateInitializers.get(null)).size(), is(0));
assertThat(((Map<?, ?>) actualInitializers.get(null)).size(), is(1));
} finally {
Constructor<Nexus> constructor = Nexus.class.getDeclaredConstructor(String.class, ClassLoader.class, ReferenceQueue.class, int.class);
constructor.setAccessible(true);
Object value = ((Map<?, ?>) actualInitializers.get(null)).remove(constructor.newInstance(FOO, qux, Nexus.NO_QUEUE, BAR));
assertThat(value, is((Object) loadedTypeInitializer));
}
}
@Test
public void testNexusAccessorClassLoaderNoResource() throws Exception {
ClassLoader classLoader = new ByteArrayClassLoader.ChildFirst(getClass().getClassLoader(),
ClassFileExtraction.of(Nexus.class,
NexusAccessor.class,
NexusAccessor.Dispatcher.class,
NexusAccessor.Dispatcher.CreationAction.class,
NexusAccessor.Dispatcher.Available.class,
NexusAccessor.Dispatcher.Unavailable.class),
null,
ByteArrayClassLoader.PersistenceHandler.LATENT,
PackageDefinitionStrategy.NoOp.INSTANCE);
Field duplicateInitializers = classLoader.loadClass(Nexus.class.getName()).getDeclaredField("TYPE_INITIALIZERS");
duplicateInitializers.setAccessible(true);
assertThat(((Map<?, ?>) duplicateInitializers.get(null)).size(), is(0));
Field actualInitializers = Nexus.class.getDeclaredField("TYPE_INITIALIZERS");
actualInitializers.setAccessible(true);
assertThat(((Map<?, ?>) actualInitializers.get(null)).size(), is(0));
Class<?> accessor = classLoader.loadClass(NexusAccessor.class.getName());
ClassLoader qux = mock(ClassLoader.class);
when(loadedTypeInitializer.isAlive()).thenReturn(true);
assertThat(accessor
.getDeclaredMethod("register", String.class, ClassLoader.class, int.class, LoadedTypeInitializer.class)
.invoke(accessor.getDeclaredConstructor().newInstance(), FOO, qux, BAR, loadedTypeInitializer), nullValue(Object.class));
try {
assertThat(((Map<?, ?>) duplicateInitializers.get(null)).size(), is(0));
assertThat(((Map<?, ?>) actualInitializers.get(null)).size(), is(1));
} finally {
Constructor<Nexus> constructor = Nexus.class.getDeclaredConstructor(String.class, ClassLoader.class, ReferenceQueue.class, int.class);
constructor.setAccessible(true);
Object value = ((Map<?, ?>) actualInitializers.get(null)).remove(constructor.newInstance(FOO, qux, Nexus.NO_QUEUE, BAR));
assertThat(value, is((Object) loadedTypeInitializer));
}
}
@Test
public void testNexusClean() throws Exception {
Field typeInitializers = ClassLoader.getSystemClassLoader().loadClass(Nexus.class.getName()).getDeclaredField("TYPE_INITIALIZERS");
typeInitializers.setAccessible(true);
ClassLoader classLoader = new URLClassLoader(new URL[0]);
when(loadedTypeInitializer.isAlive()).thenReturn(true);
assertThat(((Map<?, ?>) typeInitializers.get(null)).isEmpty(), is(true));
ReferenceQueue<ClassLoader> referenceQueue = new ReferenceQueue<ClassLoader>();
NexusAccessor nexusAccessor = new NexusAccessor(referenceQueue);
nexusAccessor.register(FOO, classLoader, BAR, loadedTypeInitializer);
assertThat(((Map<?, ?>) typeInitializers.get(null)).isEmpty(), is(false));
classLoader = null;
System.gc();
NexusAccessor.clean(referenceQueue.remove(100L));
assertThat(((Map<?, ?>) typeInitializers.get(null)).isEmpty(), is(true));
}
@Test
public void testNexusAccessorIsAvailable() throws Exception {
assertThat(NexusAccessor.isAlive(), is(true));
}
@Test
public void testUnavailableState() throws Exception {
assertThat(new NexusAccessor.Dispatcher.Unavailable(new Exception()).isAlive(), is(false));
}
@Test(expected = IllegalStateException.class)
public void testUnavailableDispatcherRegisterThrowsException() throws Exception {
new NexusAccessor.Dispatcher.Unavailable(new Exception()).register(FOO, classLoader, Nexus.NO_QUEUE, BAR, loadedTypeInitializer);
}
@Test(expected = IllegalStateException.class)
@SuppressWarnings("unchecked")
public void testUnavailableDispatcherCleanThrowsException() throws Exception {
new NexusAccessor.Dispatcher.Unavailable(new Exception()).clean(mock(Reference.class));
}
@Test
public void testNexusEquality() throws Exception {
Constructor<Nexus> constructor = Nexus.class.getDeclaredConstructor(String.class, ClassLoader.class, ReferenceQueue.class, int.class);
constructor.setAccessible(true);
assertThat(constructor.newInstance(FOO, classLoader, Nexus.NO_QUEUE, BAR),
is(constructor.newInstance(FOO, classLoader, Nexus.NO_QUEUE, BAR)));
assertThat(constructor.newInstance(FOO, classLoader, Nexus.NO_QUEUE, BAR).hashCode(),
is(constructor.newInstance(FOO, classLoader, Nexus.NO_QUEUE, BAR).hashCode()));
}
@Test
public void testObjectProperties() throws Exception {
ObjectPropertyAssertion.of(NexusAccessor.class).apply();
ObjectPropertyAssertion.of(NexusAccessor.Dispatcher.CreationAction.class).apply();
final Iterator<Method> methods = Arrays.asList(Object.class.getDeclaredMethods()).iterator();
ObjectPropertyAssertion.of(NexusAccessor.Dispatcher.Available.class)
.create(new ObjectPropertyAssertion.Creator<Method>() {
@Override
public Method create() {
return methods.next();
}
}).apply();
ObjectPropertyAssertion.of(NexusAccessor.Dispatcher.Unavailable.class).apply();
ObjectPropertyAssertion.of(NexusAccessor.InitializationAppender.class).apply();
}
}