package net.bytebuddy.dynamic; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.LoadedTypeInitializer; import net.bytebuddy.test.utility.MockitoRule; import net.bytebuddy.test.utility.ObjectPropertyAssertion; import net.bytebuddy.utility.RandomString; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.mockito.Mock; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.*; import java.util.jar.*; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.*; public class DynamicTypeDefaultTest { private static final String CLASS_FILE_EXTENSION = ".class"; private static final String FOOBAR = "foo/bar", QUXBAZ = "qux/baz", BARBAZ = "bar/baz", FOO = "foo", BAR = "bar", TEMP = "tmp"; @Rule public TestRule mockitoRule = new MockitoRule(this); private static final byte[] BINARY_FIRST = new byte[]{1, 2, 3}, BINARY_SECOND = new byte[]{4, 5, 6}, BINARY_THIRD = new byte[]{7, 8, 9}; @Mock private LoadedTypeInitializer mainLoadedTypeInitializer, auxiliaryLoadedTypeInitializer; @Mock private DynamicType auxiliaryType; @Mock private TypeDescription typeDescription, auxiliaryTypeDescription; private DynamicType dynamicType; private static void assertFile(File file, byte[] binaryRepresentation) throws IOException { FileInputStream fileInputStream = new FileInputStream(file); try { byte[] buffer = new byte[binaryRepresentation.length + 1]; assertThat(fileInputStream.read(buffer), is(binaryRepresentation.length)); int index = 0; for (byte b : binaryRepresentation) { assertThat(buffer[index++], is(b)); } assertThat(buffer[index], is((byte) 0)); } finally { fileInputStream.close(); } assertThat(file.delete(), is(true)); } private static File makeTemporaryFolder() throws IOException { File file = File.createTempFile(TEMP, TEMP); try { File folder = new File(file.getParentFile(), TEMP + RandomString.make()); assertThat(folder.mkdir(), is(true)); return folder; } finally { assertThat(file.delete(), is(true)); } } private static void assertJarFile(File file, Manifest manifest, Map<String, byte[]> expectedEntries) throws IOException { JarInputStream jarInputStream = new JarInputStream(new FileInputStream(file)); try { assertThat(jarInputStream.getManifest(), is(manifest)); JarEntry jarEntry; while ((jarEntry = jarInputStream.getNextJarEntry()) != null) { byte[] binary = expectedEntries.remove(jarEntry.getName()); assertThat(binary, notNullValue()); byte[] buffer = new byte[binary.length]; assertThat(jarInputStream.read(buffer), is(buffer.length)); assertThat(Arrays.equals(buffer, binary), is(true)); assertThat(jarInputStream.read(buffer), is(-1)); jarInputStream.closeEntry(); } assertThat(expectedEntries.size(), is(0)); } finally { jarInputStream.close(); } } @Before public void setUp() throws Exception { dynamicType = new DynamicType.Default(typeDescription, BINARY_FIRST, mainLoadedTypeInitializer, Collections.singletonList(auxiliaryType)); when(typeDescription.getName()).thenReturn(FOOBAR.replace('/', '.')); when(typeDescription.getInternalName()).thenReturn(FOOBAR); when(auxiliaryType.saveIn(any(File.class))).thenReturn(Collections.<TypeDescription, File>emptyMap()); when(auxiliaryTypeDescription.getName()).thenReturn(QUXBAZ.replace('/', '.')); when(auxiliaryTypeDescription.getInternalName()).thenReturn(QUXBAZ); when(auxiliaryType.getTypeDescription()).thenReturn(auxiliaryTypeDescription); when(auxiliaryType.getBytes()).thenReturn(BINARY_SECOND); when(auxiliaryType.getLoadedTypeInitializers()).thenReturn(Collections.singletonMap(auxiliaryTypeDescription, auxiliaryLoadedTypeInitializer)); when(auxiliaryType.getAuxiliaryTypes()).thenReturn(Collections.<TypeDescription, byte[]>emptyMap()); when(auxiliaryType.getAllTypes()).thenReturn(Collections.singletonMap(auxiliaryTypeDescription, BINARY_SECOND)); } @Test public void testByteArray() throws Exception { assertThat(dynamicType.getBytes(), is(BINARY_FIRST)); } @Test public void testTypeDescription() throws Exception { assertThat(dynamicType.getTypeDescription(), is(typeDescription)); } @Test public void testRawAuxiliaryTypes() throws Exception { assertThat(dynamicType.getAuxiliaryTypes().size(), is(1)); assertThat(dynamicType.getAuxiliaryTypes().get(auxiliaryTypeDescription), is(BINARY_SECOND)); } @Test public void testTypeInitializersNotAlive() throws Exception { assertThat(dynamicType.hasAliveLoadedTypeInitializers(), is(false)); } @Test public void testTypeInitializersAliveMain() throws Exception { when(mainLoadedTypeInitializer.isAlive()).thenReturn(true); assertThat(dynamicType.hasAliveLoadedTypeInitializers(), is(true)); } @Test public void testTypeInitializersAliveAuxiliary() throws Exception { when(auxiliaryLoadedTypeInitializer.isAlive()).thenReturn(true); assertThat(dynamicType.hasAliveLoadedTypeInitializers(), is(true)); } @Test public void testTypeInitializers() throws Exception { assertThat(dynamicType.getLoadedTypeInitializers().size(), is(2)); assertThat(dynamicType.getLoadedTypeInitializers().get(typeDescription), is(mainLoadedTypeInitializer)); assertThat(dynamicType.getLoadedTypeInitializers().get(auxiliaryTypeDescription), is(auxiliaryLoadedTypeInitializer)); } @Test public void testFileSaving() throws Exception { File folder = makeTemporaryFolder(); boolean folderDeletion, fileDeletion; try { Map<TypeDescription, File> files = dynamicType.saveIn(folder); assertThat(files.size(), is(1)); assertFile(files.get(typeDescription), BINARY_FIRST); } finally { folderDeletion = new File(folder, FOO).delete(); fileDeletion = folder.delete(); } assertThat(folderDeletion, is(true)); assertThat(fileDeletion, is(true)); verify(auxiliaryType).saveIn(folder); } @Test public void testJarCreation() throws Exception { File file = File.createTempFile(FOO, TEMP); assertThat(file.delete(), is(true)); boolean fileDeletion; try { assertThat(dynamicType.toJar(file), is(file)); assertThat(file.exists(), is(true)); assertThat(file.isFile(), is(true)); assertThat(file.length() > 0L, is(true)); Map<String, byte[]> bytes = new HashMap<String, byte[]>(); bytes.put(FOOBAR + CLASS_FILE_EXTENSION, BINARY_FIRST); bytes.put(QUXBAZ + CLASS_FILE_EXTENSION, BINARY_SECOND); Manifest manifest = new Manifest(); manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); assertJarFile(file, manifest, bytes); } finally { fileDeletion = file.delete(); } assertThat(fileDeletion, is(true)); } @Test public void testJarWithExplicitManifestCreation() throws Exception { File file = File.createTempFile(FOO, TEMP); assertThat(file.delete(), is(true)); boolean fileDeletion; try { Manifest manifest = new Manifest(); manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, BAR); assertThat(dynamicType.toJar(file, manifest), is(file)); assertThat(file.exists(), is(true)); assertThat(file.isFile(), is(true)); assertThat(file.length() > 0L, is(true)); Map<String, byte[]> bytes = new HashMap<String, byte[]>(); bytes.put(FOOBAR + CLASS_FILE_EXTENSION, BINARY_FIRST); bytes.put(QUXBAZ + CLASS_FILE_EXTENSION, BINARY_SECOND); assertJarFile(file, manifest, bytes); } finally { fileDeletion = file.delete(); } assertThat(fileDeletion, is(true)); } @Test public void testJarTargetInjection() throws Exception { File sourceFile = File.createTempFile(BAR, TEMP); Manifest manifest = new Manifest(); manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, BAR); JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(sourceFile), manifest); try { jarOutputStream.putNextEntry(new JarEntry(BARBAZ + CLASS_FILE_EXTENSION)); jarOutputStream.write(BINARY_THIRD); jarOutputStream.closeEntry(); jarOutputStream.putNextEntry(new JarEntry(FOOBAR + CLASS_FILE_EXTENSION)); jarOutputStream.write(BINARY_THIRD); jarOutputStream.closeEntry(); } finally { jarOutputStream.close(); } File file = File.createTempFile(FOO, TEMP); assertThat(file.delete(), is(true)); boolean fileDeletion; try { assertThat(dynamicType.inject(sourceFile, file), is(file)); assertThat(file.exists(), is(true)); assertThat(file.isFile(), is(true)); assertThat(file.length() > 0L, is(true)); Map<String, byte[]> bytes = new HashMap<String, byte[]>(); bytes.put(FOOBAR + CLASS_FILE_EXTENSION, BINARY_FIRST); bytes.put(QUXBAZ + CLASS_FILE_EXTENSION, BINARY_SECOND); bytes.put(BARBAZ + CLASS_FILE_EXTENSION, BINARY_THIRD); assertJarFile(file, manifest, bytes); } finally { fileDeletion = file.delete() & sourceFile.delete(); } assertThat(fileDeletion, is(true)); } @Test public void testJarSelfInjection() throws Exception { File file = File.createTempFile(BAR, TEMP); Manifest manifest = new Manifest(); manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, BAR); JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(file), manifest); try { jarOutputStream.putNextEntry(new JarEntry(BARBAZ + CLASS_FILE_EXTENSION)); jarOutputStream.write(BINARY_THIRD); jarOutputStream.closeEntry(); jarOutputStream.putNextEntry(new JarEntry(FOOBAR + CLASS_FILE_EXTENSION)); jarOutputStream.write(BINARY_THIRD); jarOutputStream.closeEntry(); } finally { jarOutputStream.close(); } boolean fileDeletion; try { assertThat(dynamicType.inject(file), is(file)); assertThat(file.exists(), is(true)); assertThat(file.isFile(), is(true)); assertThat(file.length() > 0L, is(true)); Map<String, byte[]> bytes = new HashMap<String, byte[]>(); bytes.put(FOOBAR + CLASS_FILE_EXTENSION, BINARY_FIRST); bytes.put(QUXBAZ + CLASS_FILE_EXTENSION, BINARY_SECOND); bytes.put(BARBAZ + CLASS_FILE_EXTENSION, BINARY_THIRD); assertJarFile(file, manifest, bytes); } finally { fileDeletion = file.delete(); } assertThat(fileDeletion, is(true)); } @Test public void testIterationOrder() throws Exception { Iterator<TypeDescription> types = dynamicType.getAllTypes().keySet().iterator(); assertThat(types.hasNext(), is(true)); assertThat(types.next(), is(typeDescription)); assertThat(types.hasNext(), is(true)); assertThat(types.next(), is(auxiliaryTypeDescription)); assertThat(types.hasNext(), is(false)); } @Test public void testObjectProperties() throws Exception { ObjectPropertyAssertion.of(DynamicType.Default.class).apply(); } }