jdk-24/test/jdk/tools/jpackage/share/InOutPathTest.java
2024-11-22 23:33:49 +00:00

302 lines
12 KiB
Java

/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
import jdk.internal.util.OperatingSystem;
import jdk.jpackage.test.AppImageFile;
import jdk.jpackage.test.ApplicationLayout;
import jdk.jpackage.test.PackageFile;
import jdk.jpackage.test.Annotations.Parameters;
import jdk.jpackage.test.Annotations.Test;
import jdk.jpackage.internal.util.function.ThrowingConsumer;
import jdk.jpackage.test.JPackageCommand;
import jdk.jpackage.test.JPackageCommand.AppLayoutAssert;
import jdk.jpackage.test.PackageTest;
import jdk.jpackage.test.PackageType;
import static jdk.jpackage.test.RunnablePackageTest.Action.CREATE_AND_UNPACK;
import jdk.jpackage.test.TKit;
/*
* @test
* @summary Test jpackage command line with overlapping input and output paths
* @library /test/jdk/tools/jpackage/helpers
* @build jdk.jpackage.test.*
* @compile InOutPathTest.java
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
* --jpt-run=InOutPathTest
*/
public final class InOutPathTest {
@Parameters
public static Collection input() {
List<Object[]> data = new ArrayList<>();
for (var packageTypeAlias : PackageTypeAlias.values()) {
data.addAll(List.of(new Object[][]{
{packageTypeAlias, wrap(InOutPathTest::outputDirInInputDir, "--dest in --input")},
{packageTypeAlias, wrap(InOutPathTest::outputDirSameAsInputDir, "--dest same as --input")},
{packageTypeAlias, wrap(InOutPathTest::tempDirInInputDir, "--temp in --input")},
{packageTypeAlias, wrap(cmd -> {
outputDirInInputDir(cmd);
tempDirInInputDir(cmd);
}, "--dest and --temp in --input")},
}));
data.addAll(additionalContentInput(packageTypeAlias, "--app-content"));
}
return data;
}
@Parameters(ifNotOS = OperatingSystem.MACOS)
public static Collection<Object[]> appContentInputOther() {
return List.of(new Object[][]{
{PackageTypeAlias.IMAGE, wrap(cmd -> {
additionalContent(cmd, "--app-content", cmd.outputBundle());
}, "--app-content same as output bundle")},
});
}
@Parameters(ifOS = OperatingSystem.MACOS)
public static Collection<Object[]> appContentInputOSX() {
var contentsFolder = "Contents/MacOS";
return List.of(new Object[][]{
{PackageTypeAlias.IMAGE, wrap(cmd -> {
additionalContent(cmd, "--app-content", cmd.outputBundle().resolve(contentsFolder));
}, String.format("--app-content same as the \"%s\" folder in the output bundle", contentsFolder))},
});
}
@Parameters(ifOS = OperatingSystem.MACOS)
public static Collection<Object[]> inputOSX() {
return List.of(additionalContentInput(PackageType.MAC_DMG, "--mac-dmg-content").toArray(Object[][]::new));
}
private static List<Object[]> additionalContentInput(Object packageTypes, String argName) {
List<Object[]> data = new ArrayList<>();
data.addAll(List.of(new Object[][]{
{packageTypes, wrap(cmd -> {
additionalContent(cmd, argName, cmd.inputDir());
}, argName + " same as --input")},
}));
if (!TKit.isOSX()) {
data.addAll(List.of(new Object[][]{
{packageTypes, wrap(cmd -> {
additionalContent(cmd, argName, cmd.inputDir().resolve("foo"));
}, argName + " in --input")},
{packageTypes, wrap(cmd -> {
additionalContent(cmd, argName, cmd.outputDir().resolve("bar"));
}, argName + " in --dest")},
{packageTypes, wrap(cmd -> {
additionalContent(cmd, argName, cmd.outputDir());
}, argName + " same as --dest")},
{packageTypes, wrap(cmd -> {
tempDirInInputDir(cmd);
var tempDir = cmd.getArgumentValue("--temp");
Files.createDirectories(Path.of(tempDir));
cmd.addArguments(argName, tempDir);
}, argName + " as --temp; --temp in --input")},
}));
}
return data;
}
public InOutPathTest(PackageTypeAlias packageTypeAlias, Envelope configure) {
this(packageTypeAlias.packageTypes, configure);
}
public InOutPathTest(PackageType packageType, Envelope configure) {
this(Set.of(packageType), configure);
}
public InOutPathTest(Set<PackageType> packageTypes, Envelope configure) {
this.packageTypes = packageTypes;
this.configure = configure.value;
}
@Test
public void test() throws Throwable {
runTest(packageTypes, configure);
}
private static Envelope wrap(ThrowingConsumer<JPackageCommand> v, String label) {
return new Envelope(v, label);
}
private static boolean isAppImageValid(JPackageCommand cmd) {
return !cmd.hasArgument("--app-content") && !cmd.hasArgument("--mac-dmg-content");
}
private static void runTest(Set<PackageType> packageTypes,
ThrowingConsumer<JPackageCommand> configure) throws Throwable {
ThrowingConsumer<JPackageCommand> configureWrapper = cmd -> {
// Make sure the input directory is empty in every test run.
// This is needed because jpackage output directories in this test
// are subdirectories of the input directory.
cmd.setInputToEmptyDirectory();
configure.accept(cmd);
if (cmd.hasArgument("--temp") && cmd.isImagePackageType()) {
// Request to build app image wit user supplied temp directory,
// ignore external runtime if any to make use of the temp directory
// for runtime generation.
cmd.ignoreDefaultRuntime(true);
} else {
cmd.setFakeRuntime();
}
if (!isAppImageValid(cmd)) {
// Standard asserts for .jpackage.xml fail in messed up app image. Disable them.
// Other standard asserts for app image contents should pass.
cmd.excludeAppLayoutAsserts(AppLayoutAssert.APP_IMAGE_FILE);
}
};
if (packageTypes.contains(PackageType.IMAGE)) {
JPackageCommand cmd = JPackageCommand.helloAppImage(JAR_PATH.toString() + ":");
configureWrapper.accept(cmd);
cmd.executeAndAssertHelloAppImageCreated();
if (isAppImageValid(cmd)) {
verifyAppImage(cmd);
}
if (cmd.hasArgument("--app-content")) {
// `--app-content` can be set to the app image directory which
// should not exist before jpackage is executed:
// jpackage --name Foo --dest output --app-content output/Foo
// Verify the directory exists after jpackage execution.
// At least this will catch the case when the value of
// `--app-content` option refers to a path unrelated to jpackage I/O.
TKit.assertDirectoryExists(Path.of(cmd.getArgumentValue("--app-content")));
}
} else {
new PackageTest()
.forTypes(packageTypes)
.configureHelloApp(JAR_PATH.toString() + ":")
.addInitializer(configureWrapper)
.addInstallVerifier(InOutPathTest::verifyAppImage)
.run(CREATE_AND_UNPACK);
}
}
private static void outputDirInInputDir(JPackageCommand cmd) throws
IOException {
// Set output dir as a subdir of input dir
Path outputDir = cmd.inputDir().resolve("out");
TKit.createDirectories(outputDir);
cmd.setArgumentValue("--dest", outputDir);
}
private static void outputDirSameAsInputDir(JPackageCommand cmd) throws
IOException {
// Set output dir the same as the input dir
cmd.setArgumentValue("--dest", cmd.inputDir());
}
private static void tempDirInInputDir(JPackageCommand cmd) {
// Set temp dir as a subdir of input dir
Path tmpDir = cmd.inputDir().resolve("tmp");
cmd.setArgumentValue("--temp", tmpDir);
}
private static void additionalContent(JPackageCommand cmd,
String argName, Path base) throws IOException {
Path appContentFile = base.resolve(base.toString().replaceAll("[\\\\/]",
"-") + "-foo.txt");
TKit.createDirectories(appContentFile.getParent());
TKit.createTextFile(appContentFile, List.of("Hello Duke!"));
cmd.addArguments(argName, appContentFile.getParent());
}
private static void verifyAppImage(JPackageCommand cmd) throws IOException {
if (!isAppImageValid(cmd)) {
// Don't verify the contents of app image as it is invalid.
// jpackage exited without getting stuck in infinite spiral.
// No more expectations from the tool for the give arguments.
return;
}
final Path rootDir = cmd.isImagePackageType() ? cmd.outputBundle() : cmd.pathToUnpackedPackageFile(
cmd.appInstallationDirectory());
final Path appDir = ApplicationLayout.platformAppImage().resolveAt(
rootDir).appDirectory();
final var knownFiles = Set.of(
JAR_PATH.getName(0).toString(),
PackageFile.getPathInAppImage(Path.of("")).getFileName().toString(),
AppImageFile.getPathInAppImage(Path.of("")).getFileName().toString(),
cmd.name() + ".cfg"
);
TKit.assertFileExists(appDir.resolve(JAR_PATH));
try (Stream<Path> actualFilesStream = Files.list(appDir)) {
var unexpectedFiles = actualFilesStream.map(path -> {
return path.getFileName().toString();
}).filter(Predicate.not(knownFiles::contains)).toList();
TKit.assertStringListEquals(List.of(), unexpectedFiles,
"Check there are no unexpected files in `app` folder");
}
}
private static final record Envelope(ThrowingConsumer<JPackageCommand> value, String label) {
@Override
public String toString() {
// Will produce the same test description for the same label every
// time it's executed.
// The test runner will keep the same test output directory.
return label;
}
}
private enum PackageTypeAlias {
IMAGE(Set.of(PackageType.IMAGE)),
NATIVE(PackageType.NATIVE),
;
PackageTypeAlias(Set<PackageType> packageTypes) {
this.packageTypes = packageTypes;
}
private final Set<PackageType> packageTypes;
}
private final Set<PackageType> packageTypes;
private final ThrowingConsumer<JPackageCommand> configure;
// Placing jar file in the "Resources" subdir of the input directory would allow
// to use the input directory with `--app-content` on OSX.
// For other platforms it doesn't matter. Keep it the same across
// all platforms for simplicity.
private static final Path JAR_PATH = Path.of("Resources/duke.jar");
}