diff --git a/src/test/java/rife/bld/extension/BootJarOperationTest.java b/src/test/java/rife/bld/extension/BootJarOperationTest.java index 5a167cb..cc48e56 100644 --- a/src/test/java/rife/bld/extension/BootJarOperationTest.java +++ b/src/test/java/rife/bld/extension/BootJarOperationTest.java @@ -16,5 +16,293 @@ package rife.bld.extension; +import org.junit.jupiter.api.Test; +import rife.bld.Project; +import rife.bld.dependencies.VersionNumber; +import rife.tools.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; + class BootJarOperationTest { + private static final String BLD = "bld-1.7.5.jar"; + private static final String EXAMPLES_LIB_COMPILE = "examples/lib/compile/"; + private static final String EXAMPLES_LIB_STANDALONE = "examples/lib/standalone/"; + private static final String LAUNCHER_JARS = """ + org/ + org/springframework/ + org/springframework/boot/ + org/springframework/boot/loader/ + org/springframework/boot/loader/ClassPathIndexFile.class + org/springframework/boot/loader/ExecutableArchiveLauncher.class + org/springframework/boot/loader/JarLauncher.class + org/springframework/boot/loader/LaunchedURLClassLoader$DefinePackageCallType.class + org/springframework/boot/loader/LaunchedURLClassLoader$UseFastConnectionExceptionsEnumeration.class + org/springframework/boot/loader/LaunchedURLClassLoader.class + org/springframework/boot/loader/Launcher.class + org/springframework/boot/loader/MainMethodRunner.class + org/springframework/boot/loader/PropertiesLauncher$ArchiveEntryFilter.class + org/springframework/boot/loader/PropertiesLauncher$ClassPathArchives.class + org/springframework/boot/loader/PropertiesLauncher$PrefixMatchingArchiveFilter.class + org/springframework/boot/loader/PropertiesLauncher.class + org/springframework/boot/loader/WarLauncher.class + org/springframework/boot/loader/archive/ + org/springframework/boot/loader/archive/Archive$Entry.class + org/springframework/boot/loader/archive/Archive$EntryFilter.class + org/springframework/boot/loader/archive/Archive.class + org/springframework/boot/loader/archive/ExplodedArchive$AbstractIterator.class + org/springframework/boot/loader/archive/ExplodedArchive$ArchiveIterator.class + org/springframework/boot/loader/archive/ExplodedArchive$EntryIterator.class + org/springframework/boot/loader/archive/ExplodedArchive$FileEntry.class + org/springframework/boot/loader/archive/ExplodedArchive$SimpleJarFileArchive.class + org/springframework/boot/loader/archive/ExplodedArchive.class + org/springframework/boot/loader/archive/JarFileArchive$AbstractIterator.class + org/springframework/boot/loader/archive/JarFileArchive$EntryIterator.class + org/springframework/boot/loader/archive/JarFileArchive$JarFileEntry.class + org/springframework/boot/loader/archive/JarFileArchive$NestedArchiveIterator.class + org/springframework/boot/loader/archive/JarFileArchive.class + org/springframework/boot/loader/data/ + org/springframework/boot/loader/data/RandomAccessData.class + org/springframework/boot/loader/data/RandomAccessDataFile$DataInputStream.class + org/springframework/boot/loader/data/RandomAccessDataFile$FileAccess.class + org/springframework/boot/loader/data/RandomAccessDataFile.class + org/springframework/boot/loader/jar/ + org/springframework/boot/loader/jar/AbstractJarFile$JarFileType.class + org/springframework/boot/loader/jar/AbstractJarFile.class + org/springframework/boot/loader/jar/AsciiBytes.class + org/springframework/boot/loader/jar/Bytes.class + org/springframework/boot/loader/jar/CentralDirectoryEndRecord$Zip64End.class + org/springframework/boot/loader/jar/CentralDirectoryEndRecord$Zip64Locator.class + org/springframework/boot/loader/jar/CentralDirectoryEndRecord.class + org/springframework/boot/loader/jar/CentralDirectoryFileHeader.class + org/springframework/boot/loader/jar/CentralDirectoryParser.class + org/springframework/boot/loader/jar/CentralDirectoryVisitor.class + org/springframework/boot/loader/jar/FileHeader.class + org/springframework/boot/loader/jar/Handler.class + org/springframework/boot/loader/jar/JarEntry.class + org/springframework/boot/loader/jar/JarEntryCertification.class + org/springframework/boot/loader/jar/JarEntryFilter.class + org/springframework/boot/loader/jar/JarFile$1.class + org/springframework/boot/loader/jar/JarFile$JarEntryEnumeration.class + org/springframework/boot/loader/jar/JarFile.class + org/springframework/boot/loader/jar/JarFileEntries$1.class + org/springframework/boot/loader/jar/JarFileEntries$EntryIterator.class + org/springframework/boot/loader/jar/JarFileEntries$Offsets.class + org/springframework/boot/loader/jar/JarFileEntries$Zip64Offsets.class + org/springframework/boot/loader/jar/JarFileEntries$ZipOffsets.class + org/springframework/boot/loader/jar/JarFileEntries.class + org/springframework/boot/loader/jar/JarFileWrapper.class + org/springframework/boot/loader/jar/JarURLConnection$1.class + org/springframework/boot/loader/jar/JarURLConnection$JarEntryName.class + org/springframework/boot/loader/jar/JarURLConnection.class + org/springframework/boot/loader/jar/StringSequence.class + org/springframework/boot/loader/jar/ZipInflaterInputStream.class + org/springframework/boot/loader/jarmode/ + org/springframework/boot/loader/jarmode/JarMode.class + org/springframework/boot/loader/jarmode/JarModeLauncher.class + org/springframework/boot/loader/jarmode/TestJarMode.class + org/springframework/boot/loader/util/ + org/springframework/boot/loader/util/SystemPropertyUtils.class + """; + private static final String MAIN_CLASS = "com.example.Foo"; + private static final String SPRING_BOOT = "spring-boot-3.1.5.jar"; + private static final String SPRING_BOOT_ACTUATOR = "spring-boot-actuator-3.1.5.jar"; + private static final String SPRING_BOOT_LOADER = "spring-boot-loader-3.1.5.jar"; + + private StringBuilder readJarEntries(File jar) throws IOException { + var jarEntries = new StringBuilder(); + try (var jarFile = new JarFile(jar)) { + for (Enumeration entries = jarFile.entries(); entries.hasMoreElements(); ) { + jarEntries.append(entries.nextElement().getName()).append('\n'); + } + } + return jarEntries; + } + + @Test + void testErrors() throws IOException { + var bootWar = new BootWarOperation(); + assertThatCode(bootWar::execute).isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("mainClass"); + + bootWar = bootWar.mainClass(MAIN_CLASS); + assertThatCode(bootWar::execute) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("spring-boot-loader"); + + assertThatCode(() -> new BootWarOperation().launcherJars(List.of(new File("foo")))) + .isInstanceOf(IOException.class) + .hasMessageContaining("found"); + + bootWar = bootWar.launcherJars(List.of(new File(EXAMPLES_LIB_STANDALONE + SPRING_BOOT_LOADER))); + assertThatCode(bootWar::execute) + .isInstanceOf((IllegalArgumentException.class)) + .hasMessageContaining("class required").hasMessageContaining("spring-boot-loader"); + + bootWar = bootWar.launcherClass("org.springframework.boot.loader.WarLauncher"); + assertThat(bootWar.verifyExecute()).isTrue(); + } + + @Test + void testJarExecute() throws Exception { + var tmp_dir = Files.createTempDirectory("bootjartmp").toFile(); + var jar = "foo-1.1.1.jar"; + new BootJarOperation() + .launcherClass("org.springframework.boot.loader.JarLauncher") + .launcherJars(List.of(new File(EXAMPLES_LIB_STANDALONE + SPRING_BOOT_LOADER))) + .destinationDirectory(tmp_dir) + .destinationArchiveFileName(jar) + .infLibs(new File(EXAMPLES_LIB_COMPILE + SPRING_BOOT), + new File(EXAMPLES_LIB_COMPILE + SPRING_BOOT_ACTUATOR)) + .mainClass(MAIN_CLASS) + .sourceDirectories(new File("build/main")) + .execute(); + + var jarFile = new File(tmp_dir, jar); + assertThat(jarFile).exists(); + + var jarEntries = readJarEntries(jarFile); + assertThat(jarEntries).isEqualToIgnoringNewLines( + "BOOT-INF/\n" + + "BOOT-INF/classes/\n" + + "BOOT-INF/classes/rife/\n" + + "BOOT-INF/classes/rife/bld/\n" + + "BOOT-INF/classes/rife/bld/extension/\n" + + "BOOT-INF/classes/rife/bld/extension/AbstractBootOperation$ManifestAttribute.class\n" + + "BOOT-INF/classes/rife/bld/extension/AbstractBootOperation.class\n" + + "BOOT-INF/classes/rife/bld/extension/BootJarOperation$ManifestAttribute.class\n" + + "BOOT-INF/classes/rife/bld/extension/BootJarOperation.class\n" + + "BOOT-INF/classes/rife/bld/extension/BootManifestAttribute.class\n" + + "BOOT-INF/classes/rife/bld/extension/BootWarOperation.class\n" + + "BOOT-INF/lib/\n" + + "BOOT-INF/lib/" + SPRING_BOOT + '\n' + + "BOOT-INF/lib/" + SPRING_BOOT_ACTUATOR + '\n' + + "META-INF/\n" + + "META-INF/MANIFEST.MF" + LAUNCHER_JARS); + + FileUtils.deleteDirectory(tmp_dir); + } + + @Test + void testJarProjectExecute() throws Exception { + var tmp_dir = Files.createTempDirectory("bootwartmp").toFile(); + new BootJarOperation() + .fromProject(new CustomProject(new File("."))) + .launcherJars(List.of(new File(EXAMPLES_LIB_STANDALONE + SPRING_BOOT_LOADER))) + .destinationDirectory(tmp_dir) + .infLibs(new File(EXAMPLES_LIB_COMPILE + SPRING_BOOT), + new File(EXAMPLES_LIB_COMPILE + SPRING_BOOT_ACTUATOR)) + .execute(); + + var jarFile = new File(tmp_dir, "test_project-0.0.1-boot.jar"); + assertThat(jarFile).exists(); + + var jarEntries = readJarEntries(jarFile); + assertThat(jarEntries).isEqualToIgnoringNewLines( + "BOOT-INF/\n" + + "BOOT-INF/classes/\n" + + "BOOT-INF/classes/rife/\n" + + "BOOT-INF/classes/rife/bld/\n" + + "BOOT-INF/classes/rife/bld/extension/\n" + + "BOOT-INF/classes/rife/bld/extension/AbstractBootOperation$ManifestAttribute.class\n" + + "BOOT-INF/classes/rife/bld/extension/AbstractBootOperation.class\n" + + "BOOT-INF/classes/rife/bld/extension/BootJarOperation$ManifestAttribute.class\n" + + "BOOT-INF/classes/rife/bld/extension/BootJarOperation.class\n" + + "BOOT-INF/classes/rife/bld/extension/BootManifestAttribute.class\n" + + "BOOT-INF/classes/rife/bld/extension/BootWarOperation.class\n" + + "BOOT-INF/lib/\n" + + "BOOT-INF/lib/" + BLD + '\n' + + "BOOT-INF/lib/" + SPRING_BOOT + '\n' + + "BOOT-INF/lib/" + SPRING_BOOT_ACTUATOR + '\n' + + "META-INF/\n" + + "META-INF/MANIFEST.MF" + LAUNCHER_JARS); + + FileUtils.deleteDirectory(tmp_dir); + } + + @Test + void testProject() throws IOException { + var tmp_dir = Files.createTempDirectory("bootjartmp").toFile(); + var project = new CustomProject(tmp_dir); + var bootJar = new BootJarOperation().fromProject(project); + + assertThat(bootJar.project()).as("project").isEqualTo(project); + assertThat(bootJar.mainClass()).as("mainClass").isEqualTo(MAIN_CLASS); + assertThat(bootJar.sourceDirectories()).as("sourceDirectories.size").hasSize(2); + assertThat(bootJar.manifestAttributes()).as("manifestAttributes.size").hasSize(3); + assertThat(bootJar.manifestAttributes().get(0)).as("Manifest-Version") + .isEqualTo(new BootManifestAttribute("Manifest-Version", "1.0")); + assertThat(bootJar.manifestAttributes().get(1).value()).as("Main-Class").endsWith("JarLauncher"); + assertThat(bootJar.manifestAttributes().get(2)).as("Start-Class") + .isEqualTo(new BootManifestAttribute("Start-Class", MAIN_CLASS)); + assertThat(bootJar.manifestAttribute("Manifest-Test", "tsst") + .manifestAttributes()).as("Manifest-Test").hasSize(4) + .element(3).extracting(BootManifestAttribute::name).isEqualTo("Manifest-Test"); + assertThat(bootJar.destinationDirectory()).as("destinationDirectory").isDirectory(); + assertThat(bootJar.destinationDirectory().getAbsolutePath()).as("destinationDirectory") + .isEqualTo(Path.of(tmp_dir.getPath(), "build", "dist").toString()); + assertThat(bootJar.infLibs()).as("infoLibs").isEmpty(); + assertThat(bootJar.launcherJars()).as("launcherJars").isEmpty(); + assertThat(bootJar.destinationArchiveFileName()).isEqualTo("test_project-0.0.1-boot.jar"); + + FileUtils.deleteDirectory(tmp_dir); + } + + @Test + void testWarProjectExecute() throws Exception { + var tmp_dir = Files.createTempDirectory("bootjartmp").toFile(); + new BootWarOperation() + .fromProject(new CustomProject(new File("."))) + .launcherJars(List.of(new File(EXAMPLES_LIB_STANDALONE + SPRING_BOOT_LOADER))) + .destinationDirectory(tmp_dir) + .infLibs(new File(EXAMPLES_LIB_COMPILE + SPRING_BOOT), + new File(EXAMPLES_LIB_COMPILE + SPRING_BOOT_ACTUATOR)) + .execute(); + + var warFile = new File(tmp_dir, "test_project-0.0.1-boot.war"); + assertThat(warFile).exists(); + + var jarEntries = readJarEntries(warFile); + assertThat(jarEntries).isEqualToIgnoringNewLines( + "META-INF/\n" + + "META-INF/MANIFEST.MF\n" + + "WEB-INF/\n" + + "WEB-INF/classes/\n" + + "WEB-INF/classes/rife/\n" + + "WEB-INF/classes/rife/bld/\n" + + "WEB-INF/classes/rife/bld/extension/\n" + + "WEB-INF/classes/rife/bld/extension/AbstractBootOperation$ManifestAttribute.class\n" + + "WEB-INF/classes/rife/bld/extension/AbstractBootOperation.class\n" + + "WEB-INF/classes/rife/bld/extension/BootJarOperation$ManifestAttribute.class\n" + + "WEB-INF/classes/rife/bld/extension/BootJarOperation.class\n" + + "WEB-INF/classes/rife/bld/extension/BootManifestAttribute.class\n" + + "WEB-INF/classes/rife/bld/extension/BootWarOperation.class\n" + + "WEB-INF/lib/\n" + + "WEB-INF/lib/" + BLD + '\n' + + "WEB-INF/lib/dist/\n" + + "WEB-INF/lib/" + SPRING_BOOT + '\n' + + "WEB-INF/lib/" + SPRING_BOOT_ACTUATOR + '\n' + LAUNCHER_JARS); + + FileUtils.deleteDirectory(tmp_dir); + } + + static class CustomProject extends Project { + CustomProject(File tmp) { + super(); + workDirectory = tmp; + pkg = "com.example"; + name = "test_project"; + version = new VersionNumber(0, 0, 1); + mainClass = MAIN_CLASS; + } + } } \ No newline at end of file