package net.bytebuddy.build.maven;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.test.utility.MockitoRule;
import org.apache.maven.plugin.Mojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.testing.MojoRule;
import org.apache.maven.plugin.testing.SilentLog;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.collection.CollectResult;
import org.eclipse.aether.graph.DependencyNode;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.mockito.Mock;
import org.mockito.Mockito;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import static junit.framework.TestCase.fail;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
public class ByteBuddyMojoTest {
private static final String FOO = "foo", BAR = "bar", QUX = "qux", TEMP = "tmp";
@Rule
public MojoRule mojoRule = new MojoRule();
@Rule
public TestRule mockitoRule = new MockitoRule(this);
@Mock
private RepositorySystem repositorySystem;
@Mock
private DependencyNode root;
private File project;
@Before
public void setUp() throws Exception {
when(repositorySystem.collectDependencies(Mockito.<RepositorySystemSession>any(), Mockito.<CollectRequest>any()))
.thenReturn(new CollectResult(new CollectRequest()).setRoot(root));
project = File.createTempFile(FOO, TEMP);
assertThat(project.delete(), is(true));
assertThat(project.mkdir(), is(true));
}
@After
public void tearDown() throws Exception {
assertThat(project.delete(), is(true));
}
@Test
public void testEmptyTransformation() throws Exception {
execute("transform", "empty");
}
@Test
public void testSimpleTransformation() throws Exception {
Set<File> files = new HashSet<File>();
files.addAll(addClass("foo.Bar"));
files.addAll(addClass("foo.Qux"));
try {
execute("transform", "simple");
ClassLoader classLoader = new URLClassLoader(new URL[]{project.toURI().toURL()});
assertMethod(classLoader.loadClass("foo.Bar"), FOO, QUX);
assertMethod(classLoader.loadClass("foo.Bar"), BAR, BAR);
assertMethod(classLoader.loadClass("foo.Qux"), FOO, FOO);
assertMethod(classLoader.loadClass("foo.Qux"), BAR, BAR);
} finally {
for (File file : files) {
assertThat(file.delete(), is(true));
}
assertThat(new File(project, FOO).delete(), is(true));
}
}
@Test
public void testSimpleTransformationWithSuffix() throws Exception {
Set<File> files = new HashSet<File>();
files.addAll(addClass("foo.Bar"));
files.addAll(addClass("foo.Qux"));
try {
execute("transform", "suffix");
ClassLoader classLoader = new URLClassLoader(new URL[]{project.toURI().toURL()});
assertMethod(classLoader.loadClass("foo.Bar"), FOO, QUX);
assertMethod(classLoader.loadClass("foo.Bar"), BAR, BAR);
assertThat(classLoader.loadClass("foo.Bar").getDeclaredMethod(FOO + "$" + QUX), notNullValue(Method.class));
assertMethod(classLoader.loadClass("foo.Qux"), FOO, FOO);
assertMethod(classLoader.loadClass("foo.Qux"), BAR, BAR);
} finally {
for (File file : files) {
assertThat(file.delete(), is(true));
}
assertThat(new File(project, FOO).delete(), is(true));
}
}
@Test(expected = MojoExecutionException.class)
public void testLiveInitializer() throws Exception {
Set<File> files = new HashSet<File>();
files.addAll(addClass("foo.Bar"));
try {
execute("transform", "live");
ClassLoader classLoader = new URLClassLoader(new URL[]{project.toURI().toURL()});
assertMethod(classLoader.loadClass("foo.Bar"), FOO, QUX);
} finally {
for (File file : files) {
assertThat(file.delete(), is(true));
}
assertThat(new File(project, FOO).delete(), is(true));
}
}
@Test
public void testLiveInitializerAllowed() throws Exception {
Set<File> files = new HashSet<File>();
files.addAll(addClass("foo.Bar"));
try {
execute("transform", "live.allowed");
ClassLoader classLoader = new URLClassLoader(new URL[]{project.toURI().toURL()});
try {
assertMethod(classLoader.loadClass("foo.Bar"), FOO, QUX);
fail();
} catch (InvocationTargetException exception) {
assertThat(exception.getCause(), instanceOf(NullPointerException.class));
}
} finally {
for (File file : files) {
assertThat(file.delete(), is(true));
}
assertThat(new File(project, FOO).delete(), is(true));
}
}
@Test(expected = MojoExecutionException.class)
public void testIllegalTransformer() throws Exception {
Set<File> files = new HashSet<File>();
files.addAll(addClass("foo.Bar"));
try {
execute("transform", "illegal");
} finally {
for (File file : files) {
assertThat(file.delete(), is(true));
}
assertThat(new File(project, FOO).delete(), is(true));
}
}
@Test(expected = MojoExecutionException.class)
public void testIllegalTransformation() throws Exception {
Set<File> files = new HashSet<File>();
files.addAll(addClass("foo.Bar"));
try {
execute("transform", "illegal.apply");
} finally {
for (File file : files) {
assertThat(file.delete(), is(true));
}
assertThat(new File(project, FOO).delete(), is(true));
}
}
@Test
public void testTestTransformation() throws Exception {
Set<File> files = new HashSet<File>();
files.addAll(addClass("foo.Bar"));
files.addAll(addClass("foo.Qux"));
try {
execute("transform-test", "simple");
ClassLoader classLoader = new URLClassLoader(new URL[]{project.toURI().toURL()});
assertMethod(classLoader.loadClass("foo.Bar"), FOO, QUX);
assertMethod(classLoader.loadClass("foo.Bar"), BAR, BAR);
assertMethod(classLoader.loadClass("foo.Qux"), FOO, FOO);
assertMethod(classLoader.loadClass("foo.Qux"), BAR, BAR);
} finally {
for (File file : files) {
assertThat(file.delete(), is(true));
}
assertThat(new File(project, FOO).delete(), is(true));
}
}
@Test
public void testSimpleEntry() throws Exception {
Set<File> files = new HashSet<File>();
files.addAll(addClass("foo.Bar"));
files.addAll(addClass("foo.Qux"));
try {
execute("transform", "entry");
ClassLoader classLoader = new URLClassLoader(new URL[]{project.toURI().toURL()});
assertMethod(classLoader.loadClass("foo.Bar"), FOO, QUX);
assertMethod(classLoader.loadClass("foo.Bar"), BAR, BAR);
assertMethod(classLoader.loadClass("foo.Qux"), FOO, FOO);
assertMethod(classLoader.loadClass("foo.Qux"), BAR, BAR);
} finally {
for (File file : files) {
assertThat(file.delete(), is(true));
}
assertThat(new File(project, FOO).delete(), is(true));
}
}
@Test(expected = MojoExecutionException.class)
public void testIllegalByteBuddy() throws Exception {
Set<File> files = new HashSet<File>();
files.addAll(addClass("foo.Bar"));
files.addAll(addClass("foo.Qux"));
try {
execute("transform", "entry.illegal");
ClassLoader classLoader = new URLClassLoader(new URL[]{project.toURI().toURL()});
assertMethod(classLoader.loadClass("foo.Bar"), FOO, QUX);
assertMethod(classLoader.loadClass("foo.Bar"), BAR, BAR);
assertMethod(classLoader.loadClass("foo.Qux"), FOO, FOO);
assertMethod(classLoader.loadClass("foo.Qux"), BAR, BAR);
} finally {
for (File file : files) {
assertThat(file.delete(), is(true));
}
assertThat(new File(project, FOO).delete(), is(true));
}
}
@Test(expected = MojoExecutionException.class)
public void testIllegalTransform() throws Exception {
Set<File> files = new HashSet<File>();
files.addAll(addClass("foo.Bar"));
files.addAll(addClass("foo.Qux"));
try {
execute("transform", "entry.illegal.transform");
ClassLoader classLoader = new URLClassLoader(new URL[]{project.toURI().toURL()});
assertMethod(classLoader.loadClass("foo.Bar"), FOO, QUX);
assertMethod(classLoader.loadClass("foo.Bar"), BAR, BAR);
assertMethod(classLoader.loadClass("foo.Qux"), FOO, FOO);
assertMethod(classLoader.loadClass("foo.Qux"), BAR, BAR);
} finally {
for (File file : files) {
assertThat(file.delete(), is(true));
}
assertThat(new File(project, FOO).delete(), is(true));
}
}
private void execute(String goal, String target) throws Exception {
Mojo mojo = mojoRule.lookupMojo(goal, new File("src/test/resources/net/bytebuddy/test/" + target + ".pom.xml"));
if (goal.equals("transform")) {
mojoRule.setVariableValueToObject(mojo, "outputDirectory", project.getAbsolutePath());
mojoRule.setVariableValueToObject(mojo, "compileClasspathElements", Collections.emptyList());
} else if (goal.equals("transform-test")) {
mojoRule.setVariableValueToObject(mojo, "testOutputDirectory", project.getAbsolutePath());
mojoRule.setVariableValueToObject(mojo, "testClasspathElements", Collections.emptyList());
} else {
throw new AssertionError("Unknown goal: " + goal);
}
mojoRule.setVariableValueToObject(mojo, "repositorySystem", repositorySystem);
mojoRule.setVariableValueToObject(mojo, "groupId", FOO);
mojoRule.setVariableValueToObject(mojo, "artifactId", BAR);
mojoRule.setVariableValueToObject(mojo, "version", QUX);
mojo.setLog(new SilentLog());
mojo.execute();
}
private Collection<File> addClass(String name) throws IOException {
return new ByteBuddy()
.subclass(Object.class)
.name(name)
.defineMethod(FOO, String.class, Visibility.PUBLIC).intercept(FixedValue.value(FOO))
.defineMethod(BAR, String.class, Visibility.PUBLIC).intercept(FixedValue.value(BAR))
.make()
.saveIn(project)
.values();
}
private void assertMethod(Class<?> type, String name, Object expected) throws Exception {
assertThat(type.getDeclaredMethod(name).invoke(type.getDeclaredConstructor().newInstance()), is(expected));
}
}