package net.bytebuddy.implementation;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.test.utility.CallTraceable;
import org.hamcrest.CoreMatchers;
import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
@RunWith(Parameterized.class)
public class MethodDelegationConstructionTest<T extends CallTraceable> {
private static final String FOO = "foo", BAR = "bar";
private static final byte BYTE_MULTIPLICATOR = 3;
private static final short SHORT_MULTIPLICATOR = 3;
private static final char CHAR_MULTIPLICATOR = 3;
private static final int INT_MULTIPLICATOR = 3;
private static final long LONG_MULTIPLICATOR = 3L;
private static final float FLOAT_MULTIPLICATOR = 3f;
private static final double DOUBLE_MULTIPLICATOR = 3d;
private static final boolean DEFAULT_BOOLEAN = false;
private static final byte DEFAULT_BYTE = 1;
private static final short DEFAULT_SHORT = 1;
private static final char DEFAULT_CHAR = 1;
private static final int DEFAULT_INT = 1;
private static final long DEFAULT_LONG = 1L;
private static final float DEFAULT_FLOAT = 1f;
private static final double DEFAULT_DOUBLE = 1d;
private final Class<T> sourceType;
private final Class<?> targetType;
private final Class<?>[] parameterTypes;
private final Object[] arguments;
private final Matcher<?> matcher;
public MethodDelegationConstructionTest(Class<T> sourceType,
Class<?> targetType,
Class<?>[] parameterTypes,
Object[] arguments,
Matcher<?> matcher) {
this.sourceType = sourceType;
this.targetType = targetType;
this.parameterTypes = parameterTypes;
this.arguments = arguments;
this.matcher = matcher;
}
@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{BooleanSource.class, BooleanTarget.class, new Class<?>[]{boolean.class}, new Object[]{DEFAULT_BOOLEAN}, is(!DEFAULT_BOOLEAN)},
{ByteSource.class, ByteTarget.class, new Class<?>[]{byte.class}, new Object[]{DEFAULT_BYTE}, is((byte) (DEFAULT_BYTE * BYTE_MULTIPLICATOR))},
{ShortSource.class, ShortTarget.class, new Class<?>[]{short.class}, new Object[]{DEFAULT_SHORT}, is((short) (DEFAULT_SHORT * SHORT_MULTIPLICATOR))},
{CharSource.class, CharTarget.class, new Class<?>[]{char.class}, new Object[]{DEFAULT_CHAR}, is((char) (DEFAULT_CHAR * CHAR_MULTIPLICATOR))},
{IntSource.class, IntTarget.class, new Class<?>[]{int.class}, new Object[]{DEFAULT_INT}, is(DEFAULT_INT * INT_MULTIPLICATOR)},
{LongSource.class, LongTarget.class, new Class<?>[]{long.class}, new Object[]{DEFAULT_LONG}, is(DEFAULT_LONG * LONG_MULTIPLICATOR)},
{FloatSource.class, FloatTarget.class, new Class<?>[]{float.class}, new Object[]{DEFAULT_FLOAT}, is(DEFAULT_FLOAT * FLOAT_MULTIPLICATOR)},
{DoubleSource.class, DoubleTarget.class, new Class<?>[]{double.class}, new Object[]{DEFAULT_DOUBLE}, is(DEFAULT_DOUBLE * DOUBLE_MULTIPLICATOR)},
{VoidSource.class, VoidTarget.class, new Class<?>[0], new Object[0], nullValue()},
{StringSource.class, StringTarget.class, new Class<?>[]{String.class}, new Object[]{FOO}, is(FOO + BAR)},
});
}
@Test
@SuppressWarnings("unchecked")
public void testConstruction() throws Exception {
DynamicType.Loaded<T> loaded = new ByteBuddy()
.subclass(sourceType)
.method(isDeclaredBy(sourceType))
.intercept(MethodDelegation.toConstructor(targetType))
.make()
.load(sourceType.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER);
assertThat(loaded.getLoadedAuxiliaryTypes().size(), is(0));
assertThat(loaded.getLoaded().getDeclaredMethods().length, is(1));
assertThat(loaded.getLoaded().getDeclaredFields().length, is(0));
T instance = loaded.getLoaded().getDeclaredConstructor().newInstance();
assertThat(instance.getClass(), not(CoreMatchers.<Class<?>>is(sourceType)));
assertThat(instance, instanceOf(sourceType));
Object value = loaded.getLoaded().getDeclaredMethod(FOO, parameterTypes).invoke(instance, arguments);
assertThat(value, instanceOf(targetType));
Field field = targetType.getDeclaredField("value");
field.setAccessible(true);
assertThat(field.get(value), (Matcher) matcher);
instance.assertZeroCalls();
}
public static class BooleanSource extends CallTraceable {
@SuppressWarnings("unused")
public BooleanTarget foo(boolean b) {
register(FOO);
return null;
}
}
public static class BooleanTarget {
@SuppressWarnings("unused")
private final boolean value;
public BooleanTarget(boolean value) {
this.value = !value;
}
}
public static class ByteSource extends CallTraceable {
@SuppressWarnings("unused")
public ByteTarget foo(byte b) {
register(FOO);
return null;
}
}
public static class ByteTarget {
@SuppressWarnings("unused")
private final byte value;
public ByteTarget(byte b) {
value = bar(b);
}
private static byte bar(byte b) {
return (byte) (b * BYTE_MULTIPLICATOR);
}
}
public static class ShortSource extends CallTraceable {
@SuppressWarnings("unused")
public ShortTarget foo(short s) {
register(FOO);
return null;
}
}
public static class ShortTarget {
@SuppressWarnings("unused")
private final short value;
public ShortTarget(short s) {
this.value = bar(s);
}
private static short bar(short s) {
return (short) (s * SHORT_MULTIPLICATOR);
}
}
public static class CharSource extends CallTraceable {
@SuppressWarnings("unused")
public CharTarget foo(char s) {
register(FOO);
return null;
}
}
public static class CharTarget {
@SuppressWarnings("unused")
private final char value;
public CharTarget(char c) {
this.value = bar(c);
}
private static char bar(char c) {
return (char) (c * CHAR_MULTIPLICATOR);
}
}
public static class IntSource extends CallTraceable {
@SuppressWarnings("unused")
public IntTarget foo(int i) {
register(FOO);
return null;
}
}
public static class IntTarget {
@SuppressWarnings("unused")
private final int value;
public IntTarget(int i) {
this.value = bar(i);
}
private static int bar(int i) {
return i * INT_MULTIPLICATOR;
}
}
public static class LongSource extends CallTraceable {
@SuppressWarnings("unused")
public LongTarget foo(long l) {
register(FOO);
return null;
}
}
public static class LongTarget {
@SuppressWarnings("unused")
private final long value;
public LongTarget(long l) {
this.value = bar(l);
}
private static long bar(long l) {
return l * LONG_MULTIPLICATOR;
}
}
public static class FloatSource extends CallTraceable {
@SuppressWarnings("unused")
public FloatTarget foo(float f) {
register(FOO);
return null;
}
}
public static class FloatTarget {
@SuppressWarnings("unused")
private final float value;
public FloatTarget(float f) {
this.value = bar(f);
}
private static float bar(float f) {
return f * FLOAT_MULTIPLICATOR;
}
}
public static class DoubleSource extends CallTraceable {
@SuppressWarnings("unused")
public DoubleTarget foo(double d) {
register(FOO);
return null;
}
}
public static class DoubleTarget {
@SuppressWarnings("unused")
private final double value;
public DoubleTarget(double d) {
this.value = bar(d);
}
public static double bar(double d) {
return d * DOUBLE_MULTIPLICATOR;
}
}
public static class VoidSource extends CallTraceable {
public VoidTarget foo() {
register(FOO);
return null;
}
}
public static class VoidTarget {
@SuppressWarnings("unused")
private final Void value = null;
}
public static class StringSource extends CallTraceable {
public StringTarget foo(String s) {
register(FOO);
return null;
}
}
public static class StringTarget {
@SuppressWarnings("unused")
private final String value;
public StringTarget(String s) {
this.value = bar(s);
}
public static String bar(String s) {
return s + BAR;
}
}
}