Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public AdditionalLauncher(String name) {
setPersistenceHandler(null);
}

public String name() {
return name;
}

public AdditionalLauncher withVerifyActions(Action... actions) {
verifyActions.addAll(List.of(actions));
return this;
Expand Down
183 changes: 120 additions & 63 deletions test/jdk/tools/jpackage/share/AddLShortcutTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,21 @@
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import jdk.internal.util.OperatingSystem;
import jdk.jpackage.test.AdditionalLauncher;
import jdk.jpackage.test.Annotations.Parameter;
import jdk.jpackage.test.Annotations.ParameterSupplier;
import jdk.jpackage.test.Annotations.Test;
import jdk.jpackage.test.FileAssociations;
import jdk.jpackage.test.HelloApp;
import jdk.jpackage.test.JPackageCommand;
import jdk.jpackage.test.JavaAppDesc;
import jdk.jpackage.test.LauncherShortcut;
import jdk.jpackage.test.LauncherShortcut.InvokeShortcutSpec;
import jdk.jpackage.test.LauncherShortcut.StartupDirectory;
Expand All @@ -63,6 +68,7 @@
* @key jpackagePlatformPackage
* @library /test/jdk/tools/jpackage/helpers
* @build jdk.jpackage.test.*
* @requires (os.family != "mac")
* @requires (jpackage.test.SQETest != null)
* @compile -Xlint:all -Werror AddLShortcutTest.java
* @run main/othervm/timeout=540 -Xmx512m
Expand All @@ -76,6 +82,7 @@
* @key jpackagePlatformPackage
* @library /test/jdk/tools/jpackage/helpers
* @build jdk.jpackage.test.*
* @requires (os.family != "mac")
* @requires (jpackage.test.SQETest == null)
* @compile -Xlint:all -Werror AddLShortcutTest.java
* @run main/othervm/timeout=1080 -Xmx512m
Expand All @@ -85,14 +92,16 @@

public class AddLShortcutTest {

@Test
@Test(ifNotOS = OperatingSystem.MACOS)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need both (ifNotOS = OperatingSystem.MACOS) and (os.family != "mac")?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The one is for jtreg, and the other is for the jpackage test runner. The latter makes sense when the test is executed outside of jtreg.

public void test() {
// Configure several additional launchers with each combination of
// possible shortcut hints in add-launcher property file.
// default is true so Foo (no property), and Bar (properties set to "true")
// will have shortcuts while other launchers with some properties set
// to "false" will have none.

final var packageName = MethodHandles.lookup().lookupClass().getSimpleName();

PackageTest packageTest = new PackageTest().configureHelloApp();
packageTest.addInitializer(cmd -> {
cmd.addArguments("--arguments", "Duke", "--arguments", "is",
Expand All @@ -102,11 +111,14 @@ public void test() {
} else if (TKit.isLinux()) {
cmd.addArguments("--linux-shortcut");
}

cmd.setArgumentValue("--name", packageName);

var addLauncherApp = TKit.TEST_SRC_ROOT.resolve("apps/PrintEnv.java");
HelloApp.createBundle(JavaAppDesc.parse(addLauncherApp + "*another.jar:Welcome"), cmd.inputDir());
});

new FileAssociations(
MethodHandles.lookup().lookupClass().getSimpleName()).applyTo(
packageTest);
new FileAssociations(packageName).applyTo(packageTest);

new AdditionalLauncher("Foo")
.setDefaultArguments("yep!")
Expand All @@ -131,11 +143,16 @@ public void test() {
.setShortcuts(true, false)
.applyTo(packageTest);

new AdditionalLauncher("Launcher5")
.setDefaultArguments()
var launcher5 = new AdditionalLauncher("Launcher5")
.setDefaultArguments("--print-workdir")
.setIcon(GOLDEN_ICON)
.setShortcuts(false, true)
.applyTo(packageTest);
.setShortcut(LauncherShortcut.LINUX_SHORTCUT, StartupDirectory.APP_DIR)
.setShortcut(LauncherShortcut.WIN_DESKTOP_SHORTCUT, StartupDirectory.APP_DIR)
.setShortcut(LauncherShortcut.WIN_START_MENU_SHORTCUT, null)
.setProperty("main-jar", "another.jar")
.setProperty("main-class", "Welcome");

new ShortcutStartupDirectoryVerifier(packageName).add(launcher5).applyTo(packageTest);

packageTest.run();
}
Expand Down Expand Up @@ -260,82 +277,118 @@ public void testInvokeShortcuts(StartupDirectory startupDirectory) {
cmd.addArguments("--arguments", "--print-workdir");
}).addInitializer(JPackageCommand::ignoreFakeRuntime).addHelloAppInitializer(testApp + "*Hello");

var shortcutStartupDirectoryVerifier = new ShortcutStartupDirectoryVerifier(name, "a");

shortcutStartupDirectoryVerifier.applyTo(test, startupDirectory);

test.addInstallVerifier(cmd -> {
if (!cmd.isPackageUnpacked("Not invoking launcher shortcuts")) {
Collection<? extends InvokeShortcutSpec> invokeShortcutSpecs;
if (TKit.isLinux()) {
invokeShortcutSpecs = LinuxHelper.getInvokeShortcutSpecs(cmd);
} else if (TKit.isWindows()) {
invokeShortcutSpecs = WinShortcutVerifier.getInvokeShortcutSpecs(cmd);
} else {
throw new UnsupportedOperationException();
}
shortcutStartupDirectoryVerifier.verify(invokeShortcutSpecs);
}
});
new ShortcutStartupDirectoryVerifier(name).add("a", startupDirectory).applyTo(test);

test.run();
}


private record ShortcutStartupDirectoryVerifier(String packageName, String launcherName) {
ShortcutStartupDirectoryVerifier {
Objects.requireNonNull(packageName);
Objects.requireNonNull(launcherName);
private final static class ShortcutStartupDirectoryVerifier {

ShortcutStartupDirectoryVerifier(String packageName) {
this.packageName = Objects.requireNonNull(packageName);
}

void applyTo(PackageTest test, StartupDirectory startupDirectory) {
var al = new AdditionalLauncher(launcherName);
al.setShortcut(shortcut(), Objects.requireNonNull(startupDirectory));
al.addJavaOptions(String.format("-Djpackage.test.appOutput=${%s}/%s",
outputDirVarName(), expectedOutputFilename()));
al.withoutVerifyActions(Action.EXECUTE_LAUNCHER).applyTo(test);
void applyTo(PackageTest test) {
verifiers.values().forEach(verifier -> {
verifier.applyTo(test);
});
test.addInstallVerifier(cmd -> {
if (!cmd.isPackageUnpacked("Not invoking launcher shortcuts")) {
Collection<? extends InvokeShortcutSpec> invokeShortcutSpecs;
if (TKit.isLinux()) {
invokeShortcutSpecs = LinuxHelper.getInvokeShortcutSpecs(cmd);
} else if (TKit.isWindows()) {
invokeShortcutSpecs = WinShortcutVerifier.getInvokeShortcutSpecs(cmd);
} else {
throw new UnsupportedOperationException();
}

var invokeShortcutSpecsMap = invokeShortcutSpecs.stream().collect(Collectors.groupingBy(InvokeShortcutSpec::launcherName));

for (var e : verifiers.entrySet()) {
e.getValue().verify(invokeShortcutSpecsMap.get(e.getKey()));
}
}
});
}

void verify(Collection<? extends InvokeShortcutSpec> invokeShortcutSpecs) throws IOException {
ShortcutStartupDirectoryVerifier add(String launcherName, StartupDirectory startupDirectory) {
return add(new AdditionalLauncher(launcherName)
.setShortcut(shortcut(), Objects.requireNonNull(Objects.requireNonNull(startupDirectory))));
}

TKit.trace(String.format("Verify shortcut [%s]", launcherName));
ShortcutStartupDirectoryVerifier add(AdditionalLauncher addLauncher) {
var launcherVerifier = new LauncherVerifier(addLauncher);
verifiers.put(launcherVerifier.launcherName(), launcherVerifier);
return this;
}

var expectedOutputFile = Path.of(System.getenv(outputDirVarName())).resolve(expectedOutputFilename());

TKit.deleteIfExists(expectedOutputFile);
private final class LauncherVerifier {

var invokeShortcutSpec = invokeShortcutSpecs.stream().filter(v -> {
return launcherName.equals(v.launcherName());
}).findAny().orElseThrow();
private LauncherVerifier(AdditionalLauncher addLauncher) {
this.addLauncher = Objects.requireNonNull(addLauncher);
}

invokeShortcutSpec.execute();
private String launcherName() {
return addLauncher.name();
}

// On Linux, "gtk-launch" is used to launch a .desktop file. It is async and there is no
// way to make it wait for exit of a process it triggers.
TKit.waitForFileCreated(expectedOutputFile, Duration.ofSeconds(10), Duration.ofSeconds(3));
private void applyTo(PackageTest test) {
addLauncher.addJavaOptions(String.format("-Djpackage.test.appOutput=${%s}/%s",
outputDirVarName(), expectedOutputFilename()));
addLauncher.withoutVerifyActions(Action.EXECUTE_LAUNCHER).applyTo(test);
}

TKit.assertFileExists(expectedOutputFile);
var actualStr = Files.readAllLines(expectedOutputFile).getFirst();
private void verify(Collection<? extends InvokeShortcutSpec> invokeShortcutSpecs) throws IOException {
Objects.requireNonNull(invokeShortcutSpecs);
if (invokeShortcutSpecs.isEmpty()) {
throw new IllegalArgumentException();
}

var outputPrefix = "$CD=";
TKit.trace(String.format("Verify shortcut [%s]", launcherName()));

TKit.assertTrue(actualStr.startsWith(outputPrefix), "Check output starts with '" + outputPrefix+ "' string");
var expectedOutputFile = Path.of(System.getenv(outputDirVarName())).resolve(expectedOutputFilename());

invokeShortcutSpec.expectedWorkDirectory().ifPresent(expectedWorkDirectory -> {
TKit.assertEquals(
expectedWorkDirectory,
Path.of(actualStr.substring(outputPrefix.length())),
String.format("Check work directory of %s of launcher [%s]",
invokeShortcutSpec.shortcut().propertyName(),
invokeShortcutSpec.launcherName()));
});
}
TKit.deleteIfExists(expectedOutputFile);

var invokeShortcutSpec = invokeShortcutSpecs.stream().filter(v -> {
return launcherName().equals(v.launcherName());
}).findAny().orElseThrow();

invokeShortcutSpec.execute();

private String expectedOutputFilename() {
return String.format("%s-%s.out", packageName, launcherName);
// On Linux, "gtk-launch" is used to launch a .desktop file. It is async and there is no
// way to make it wait for exit of a process it triggers.
TKit.waitForFileCreated(expectedOutputFile, Duration.ofSeconds(10), Duration.ofSeconds(3));

TKit.assertFileExists(expectedOutputFile);
var actualStr = Files.readAllLines(expectedOutputFile).getFirst();

var outputPrefix = "$CD=";

TKit.assertTrue(actualStr.startsWith(outputPrefix), "Check output starts with '" + outputPrefix+ "' string");

invokeShortcutSpec.expectedWorkDirectory().ifPresent(expectedWorkDirectory -> {
TKit.assertEquals(
expectedWorkDirectory,
Path.of(actualStr.substring(outputPrefix.length())),
String.format("Check work directory of %s of launcher [%s]",
invokeShortcutSpec.shortcut().propertyName(),
invokeShortcutSpec.launcherName()));
});
}

private String expectedOutputFilename() {
return String.format("%s-%s.out", packageName, launcherName());
}

private final AdditionalLauncher addLauncher;
}

private String outputDirVarName() {

static private String outputDirVarName() {
if (TKit.isLinux()) {
return "HOME";
} else if (TKit.isWindows()) {
Expand All @@ -345,7 +398,7 @@ private String outputDirVarName() {
}
}

private LauncherShortcut shortcut() {
static private LauncherShortcut shortcut() {
if (TKit.isLinux()) {
return LauncherShortcut.LINUX_SHORTCUT;
} else if (TKit.isWindows()) {
Expand All @@ -354,6 +407,10 @@ private LauncherShortcut shortcut() {
throw new UnsupportedOperationException();
}
}

private final String packageName;
// Keep the order
private final Map<String, LauncherVerifier> verifiers = new LinkedHashMap<>();
}


Expand Down