8233270: Add support to jtreg helpers to unpack packages

8230933: Default icon is not set for additional launchers

Reviewed-by: herrick, prr, almatvee
This commit is contained in:
Alexey Semenyuk 2019-12-17 13:56:47 -05:00
parent 0743555685
commit 14459b2ad2
44 changed files with 2037 additions and 888 deletions

View File

@ -48,8 +48,9 @@ final class DesktopIntegration {
static final String DESKTOP_COMMANDS_UNINSTALL = "DESKTOP_COMMANDS_UNINSTALL"; static final String DESKTOP_COMMANDS_UNINSTALL = "DESKTOP_COMMANDS_UNINSTALL";
static final String UTILITY_SCRIPTS = "UTILITY_SCRIPTS"; static final String UTILITY_SCRIPTS = "UTILITY_SCRIPTS";
DesktopIntegration(PlatformPackage thePackage, private DesktopIntegration(PlatformPackage thePackage,
Map<String, ? super Object> params) { Map<String, ? super Object> params,
Map<String, ? super Object> mainParams) throws IOException {
associations = FileAssociation.fetchFrom(params).stream() associations = FileAssociation.fetchFrom(params).stream()
.filter(fa -> !fa.mimeTypes.isEmpty()) .filter(fa -> !fa.mimeTypes.isEmpty())
@ -60,11 +61,25 @@ final class DesktopIntegration {
this.thePackage = thePackage; this.thePackage = thePackage;
final File customIconFile = ICON_PNG.fetchFrom(params); // Need desktop and icon files if one of conditions is met:
// - there are file associations configured
// - user explicitely requested to create a shortcut
boolean withDesktopFile = !associations.isEmpty() || SHORTCUT_HINT.fetchFrom(params);
var curIconResource = LinuxAppImageBuilder.createIconResource(DEFAULT_ICON,
ICON_PNG, params, mainParams);
if (curIconResource == null) {
// This is additional launcher with explicit `no icon` configuration.
withDesktopFile = false;
} else {
final Path nullPath = null;
if (curIconResource.saveToFile(nullPath)
!= OverridableResource.Source.DefaultResource) {
// This launcher has custom icon configured.
withDesktopFile = true;
}
}
iconResource = createResource(DEFAULT_ICON, params)
.setCategory(I18N.getString("resource.menu-icon"))
.setExternal(customIconFile);
desktopFileResource = createResource("template.desktop", params) desktopFileResource = createResource("template.desktop", params)
.setCategory(I18N.getString("resource.menu-shortcut-descriptor")) .setCategory(I18N.getString("resource.menu-shortcut-descriptor"))
.setPublicName(APP_NAME.fetchFrom(params) + ".desktop"); .setPublicName(APP_NAME.fetchFrom(params) + ".desktop");
@ -79,27 +94,42 @@ final class DesktopIntegration {
mimeInfoFile = new DesktopFile(mimeInfoFileName); mimeInfoFile = new DesktopFile(mimeInfoFileName);
if (!associations.isEmpty() || SHORTCUT_HINT.fetchFrom(params) || customIconFile != null) { if (withDesktopFile) {
//
// Create primary .desktop file if one of conditions is met:
// - there are file associations configured
// - user explicitely requested to create a shortcut
// - custom icon specified
//
desktopFile = new DesktopFile(desktopFileName); desktopFile = new DesktopFile(desktopFileName);
iconFile = new DesktopFile(APP_NAME.fetchFrom(params) iconFile = new DesktopFile(APP_NAME.fetchFrom(params)
+ IOUtils.getSuffix(Path.of(DEFAULT_ICON))); + IOUtils.getSuffix(Path.of(DEFAULT_ICON)));
if (curIconResource == null) {
// Create default icon.
curIconResource = LinuxAppImageBuilder.createIconResource(
DEFAULT_ICON, ICON_PNG, mainParams, null);
}
} else { } else {
desktopFile = null; desktopFile = null;
iconFile = null; iconFile = null;
} }
iconResource = curIconResource;
desktopFileData = Collections.unmodifiableMap( desktopFileData = Collections.unmodifiableMap(
createDataForDesktopFile(params)); createDataForDesktopFile(params));
nestedIntegrations = launchers.stream().map( nestedIntegrations = new ArrayList<>();
launcherParams -> new DesktopIntegration(thePackage, for (var launcherParams : launchers) {
launcherParams)).collect(Collectors.toList()); launcherParams = AddLauncherArguments.merge(params, launcherParams,
ICON.getID(), ICON_PNG.getID(), ADD_LAUNCHERS.getID(),
FILE_ASSOCIATIONS.getID());
nestedIntegrations.add(new DesktopIntegration(thePackage,
launcherParams, params));
}
}
static DesktopIntegration create(PlatformPackage thePackage,
Map<String, ? super Object> params) throws IOException {
if (StandardBundlerParam.isRuntimeInstaller(params)) {
return null;
}
return new DesktopIntegration(thePackage, params, null);
} }
List<String> requiredPackages() { List<String> requiredPackages() {

View File

@ -30,10 +30,10 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static jdk.incubator.jpackage.internal.LinuxAppBundler.ICON_PNG;
import static jdk.incubator.jpackage.internal.OverridableResource.createResource; import static jdk.incubator.jpackage.internal.OverridableResource.createResource;
import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; import static jdk.incubator.jpackage.internal.StandardBundlerParam.*;
@ -45,21 +45,6 @@ public class LinuxAppImageBuilder extends AbstractAppImageBuilder {
private final ApplicationLayout appLayout; private final ApplicationLayout appLayout;
public static final BundlerParamInfo<File> ICON_PNG =
new StandardBundlerParam<>(
"icon.png",
File.class,
params -> {
File f = ICON.fetchFrom(params);
if (f != null && !f.getName().toLowerCase().endsWith(".png")) {
Log.error(MessageFormat.format(I18N.getString(
"message.icon-not-png"), f));
return null;
}
return f;
},
(s, p) -> new File(s));
private static ApplicationLayout createAppLayout(Map<String, Object> params, private static ApplicationLayout createAppLayout(Map<String, Object> params,
Path imageOutDir) { Path imageOutDir) {
return ApplicationLayout.linuxAppImage().resolveAt( return ApplicationLayout.linuxAppImage().resolveAt(
@ -113,8 +98,6 @@ public class LinuxAppImageBuilder extends AbstractAppImageBuilder {
@Override @Override
public void prepareApplicationFiles(Map<String, ? super Object> params) public void prepareApplicationFiles(Map<String, ? super Object> params)
throws IOException { throws IOException {
Map<String, ? super Object> originalParams = new HashMap<>(params);
appLayout.roots().stream().forEach(dir -> { appLayout.roots().stream().forEach(dir -> {
try { try {
IOUtils.writableOutputDir(dir); IOUtils.writableOutputDir(dir);
@ -124,7 +107,7 @@ public class LinuxAppImageBuilder extends AbstractAppImageBuilder {
}); });
// create the primary launcher // create the primary launcher
createLauncherForEntryPoint(params); createLauncherForEntryPoint(params, null);
// Copy library to the launcher folder // Copy library to the launcher folder
try (InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) { try (InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) {
@ -135,23 +118,20 @@ public class LinuxAppImageBuilder extends AbstractAppImageBuilder {
List<Map<String, ? super Object>> entryPoints List<Map<String, ? super Object>> entryPoints
= StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params); = StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params);
for (Map<String, ? super Object> entryPoint : entryPoints) { for (Map<String, ? super Object> entryPoint : entryPoints) {
createLauncherForEntryPoint( createLauncherForEntryPoint(AddLauncherArguments.merge(params,
AddLauncherArguments.merge(originalParams, entryPoint)); entryPoint, ICON.getID(), ICON_PNG.getID()), params);
} }
// Copy class path entries to Java folder // Copy class path entries to Java folder
copyApplication(params); copyApplication(params);
// Copy icon to Resources folder
copyIcon(params);
} }
@Override @Override
public void prepareJreFiles(Map<String, ? super Object> params) public void prepareJreFiles(Map<String, ? super Object> params)
throws IOException {} throws IOException {}
private void createLauncherForEntryPoint( private void createLauncherForEntryPoint(Map<String, ? super Object> params,
Map<String, ? super Object> params) throws IOException { Map<String, ? super Object> mainParams) throws IOException {
// Copy executable to launchers folder // Copy executable to launchers folder
Path executableFile = appLayout.launchersDirectory().resolve(getLauncherName(params)); Path executableFile = appLayout.launchersDirectory().resolve(getLauncherName(params));
try (InputStream is_launcher = try (InputStream is_launcher =
@ -163,19 +143,15 @@ public class LinuxAppImageBuilder extends AbstractAppImageBuilder {
executableFile.toFile().setWritable(true, true); executableFile.toFile().setWritable(true, true);
writeCfgFile(params, getLauncherCfgPath(params).toFile()); writeCfgFile(params, getLauncherCfgPath(params).toFile());
}
private void copyIcon(Map<String, ? super Object> params) var iconResource = createIconResource(DEFAULT_ICON, ICON_PNG, params,
throws IOException { mainParams);
if (iconResource != null) {
Path iconTarget = appLayout.destktopIntegrationDirectory().resolve( Path iconTarget = appLayout.destktopIntegrationDirectory().resolve(
APP_NAME.fetchFrom(params) + IOUtils.getSuffix(Path.of( APP_NAME.fetchFrom(params) + IOUtils.getSuffix(Path.of(
DEFAULT_ICON))); DEFAULT_ICON)));
iconResource.saveToFile(iconTarget);
createResource(DEFAULT_ICON, params) }
.setCategory("icon")
.setExternal(ICON_PNG.fetchFrom(params))
.saveToFile(iconTarget);
} }
private void copyApplication(Map<String, ? super Object> params) private void copyApplication(Map<String, ? super Object> params)

View File

@ -135,11 +135,7 @@ abstract class LinuxPackageBundler extends AbstractBundler {
} }
} }
if (!StandardBundlerParam.isRuntimeInstaller(params)) { desktopIntegration = DesktopIntegration.create(thePackage, params);
desktopIntegration = new DesktopIntegration(thePackage, params);
} else {
desktopIntegration = null;
}
Map<String, String> data = createDefaultReplacementData(params); Map<String, String> data = createDefaultReplacementData(params);
if (desktopIntegration != null) { if (desktopIntegration != null) {

View File

@ -102,21 +102,6 @@ public class MacAppBundler extends AbstractImageBundler {
params -> IDENTIFIER.fetchFrom(params) + ".", params -> IDENTIFIER.fetchFrom(params) + ".",
(s, p) -> s); (s, p) -> s);
public static final BundlerParamInfo<File> ICON_ICNS =
new StandardBundlerParam<>(
"icon.icns",
File.class,
params -> {
File f = ICON.fetchFrom(params);
if (f != null && !f.getName().toLowerCase().endsWith(".icns")) {
Log.error(MessageFormat.format(
I18N.getString("message.icon-not-icns"), f));
return null;
}
return f;
},
(s, p) -> new File(s));
public static boolean validCFBundleVersion(String v) { public static boolean validCFBundleVersion(String v) {
// CFBundleVersion (String - iOS, OS X) specifies the build version // CFBundleVersion (String - iOS, OS X) specifies the build version
// number of the bundle, which identifies an iteration (released or // number of the bundle, which identifies an iteration (released or

View File

@ -29,6 +29,7 @@ import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.*; import java.util.*;
import static jdk.incubator.jpackage.internal.MacAppImageBuilder.ICON_ICNS;
import static jdk.incubator.jpackage.internal.OverridableResource.createResource; import static jdk.incubator.jpackage.internal.OverridableResource.createResource;
import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; import static jdk.incubator.jpackage.internal.StandardBundlerParam.*;
@ -160,7 +161,7 @@ public class MacDmgBundler extends MacBaseInstallerBundler {
createResource(TEMPLATE_BUNDLE_ICON, params) createResource(TEMPLATE_BUNDLE_ICON, params)
.setCategory(I18N.getString("resource.volume-icon")) .setCategory(I18N.getString("resource.volume-icon"))
.setExternal(MacAppBundler.ICON_ICNS.fetchFrom(params)) .setExternal(ICON_ICNS.fetchFrom(params))
.saveToFile(getConfig_VolumeIcon(params)); .saveToFile(getConfig_VolumeIcon(params));
createResource(null, params) createResource(null, params)

View File

@ -25,23 +25,21 @@
package jdk.incubator.jpackage.internal; package jdk.incubator.jpackage.internal;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.text.MessageFormat; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.ArrayList; import static jdk.incubator.jpackage.internal.OverridableResource.createResource;
import static jdk.incubator.jpackage.internal.StandardBundlerParam.*;
import jdk.incubator.jpackage.internal.resources.ResourceLocator; import jdk.incubator.jpackage.internal.resources.ResourceLocator;
import static jdk.incubator.jpackage.internal.StandardBundlerParam.*;
/* /*
* AbstractAppImageBuilder * AbstractAppImageBuilder
@ -188,4 +186,57 @@ public abstract class AbstractAppImageBuilder {
} }
return sb.toString(); return sb.toString();
} }
public static OverridableResource createIconResource(String defaultIconName,
BundlerParamInfo<File> iconParam, Map<String, ? super Object> params,
Map<String, ? super Object> mainParams) throws IOException {
if (mainParams != null) {
params = AddLauncherArguments.merge(mainParams, params, ICON.getID(),
iconParam.getID());
}
final String resourcePublicName = APP_NAME.fetchFrom(params)
+ IOUtils.getSuffix(Path.of(defaultIconName));
IconType iconType = getLauncherIconType(params);
if (iconType == IconType.NoIcon) {
return null;
}
OverridableResource resource = createResource(defaultIconName, params)
.setCategory("icon")
.setExternal(iconParam.fetchFrom(params))
.setPublicName(resourcePublicName);
if (iconType == IconType.DefaultOrResourceDirIcon && mainParams != null) {
// No icon explicitly configured for this launcher.
// Dry-run resource creation to figure out its source.
final Path nullPath = null;
if (resource.saveToFile(nullPath)
!= OverridableResource.Source.ResourceDir) {
// No icon in resource dir for this launcher, inherit icon
// configured for the main launcher.
resource = createIconResource(defaultIconName, iconParam,
mainParams, null).setLogPublicName(resourcePublicName);
}
}
return resource;
}
private enum IconType { DefaultOrResourceDirIcon, CustomIcon, NoIcon };
private static IconType getLauncherIconType(Map<String, ? super Object> params) {
File launcherIcon = ICON.fetchFrom(params);
if (launcherIcon == null) {
return IconType.DefaultOrResourceDirIcon;
}
if (launcherIcon.getName().isEmpty()) {
return IconType.NoIcon;
}
return IconType.CustomIcon;
}
} }

View File

@ -29,6 +29,7 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.io.File; import java.io.File;
import java.util.List;
import jdk.incubator.jpackage.internal.Arguments.CLIOptions; import jdk.incubator.jpackage.internal.Arguments.CLIOptions;
/* /*
@ -160,8 +161,10 @@ class AddLauncherArguments {
static Map<String, ? super Object> merge( static Map<String, ? super Object> merge(
Map<String, ? super Object> original, Map<String, ? super Object> original,
Map<String, ? super Object> additional) { Map<String, ? super Object> additional, String... exclude) {
Map<String, ? super Object> tmp = new HashMap<>(original); Map<String, ? super Object> tmp = new HashMap<>(original);
List.of(exclude).forEach(tmp::remove);
if (additional.containsKey(CLIOptions.MODULE.getId())) { if (additional.containsKey(CLIOptions.MODULE.getId())) {
tmp.remove(CLIOptions.MAIN_JAR.getId()); tmp.remove(CLIOptions.MAIN_JAR.getId());
tmp.remove(CLIOptions.APPCLASS.getId()); tmp.remove(CLIOptions.APPCLASS.getId());

View File

@ -30,10 +30,7 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.HashMap; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static jdk.incubator.jpackage.internal.StandardBundlerParam.RESOURCE_DIR; import static jdk.incubator.jpackage.internal.StandardBundlerParam.RESOURCE_DIR;
@ -64,6 +61,7 @@ final class OverridableResource {
OverridableResource(String defaultName) { OverridableResource(String defaultName) {
this.defaultName = defaultName; this.defaultName = defaultName;
setSourceOrder(Source.values());
} }
OverridableResource setSubstitutionData(Map<String, String> v) { OverridableResource setSubstitutionData(Map<String, String> v) {
@ -90,6 +88,15 @@ final class OverridableResource {
return setResourceDir(toPath(v)); return setResourceDir(toPath(v));
} }
enum Source { External, ResourceDir, DefaultResource };
OverridableResource setSourceOrder(Source... v) {
sources = Stream.of(v)
.map(source -> Map.entry(source, getHandler(source)))
.collect(Collectors.toList());
return this;
}
/** /**
* Set name of file to look for in resource dir. * Set name of file to look for in resource dir.
* *
@ -104,6 +111,20 @@ final class OverridableResource {
return setPublicName(Path.of(v)); return setPublicName(Path.of(v));
} }
/**
* Set name of file to look for in resource dir to put in verbose log.
*
* @return this
*/
OverridableResource setLogPublicName(Path v) {
logPublicName = v;
return this;
}
OverridableResource setLogPublicName(String v) {
return setLogPublicName(Path.of(v));
}
OverridableResource setExternal(Path v) { OverridableResource setExternal(Path v) {
externalPath = v; externalPath = v;
return this; return this;
@ -113,57 +134,17 @@ final class OverridableResource {
return setExternal(toPath(v)); return setExternal(toPath(v));
} }
void saveToFile(Path dest) throws IOException { Source saveToFile(Path dest) throws IOException {
final String printableCategory; for (var source: sources) {
if (category != null) { if (source.getValue().apply(dest)) {
printableCategory = String.format("[%s]", category); return source.getKey();
} else {
printableCategory = "";
}
if (externalPath != null && externalPath.toFile().exists()) {
Log.verbose(MessageFormat.format(I18N.getString(
"message.using-custom-resource-from-file"),
printableCategory,
externalPath.toAbsolutePath().normalize()));
try (InputStream in = Files.newInputStream(externalPath)) {
processResourceStream(in, dest);
}
return;
}
final Path resourceName = Optional.ofNullable(publicName).orElse(
dest.getFileName());
if (resourceDir != null) {
final Path customResource = resourceDir.resolve(resourceName);
if (customResource.toFile().exists()) {
Log.verbose(MessageFormat.format(I18N.getString(
"message.using-custom-resource"), printableCategory,
resourceDir.normalize().toAbsolutePath().relativize(
customResource.normalize().toAbsolutePath())));
try (InputStream in = Files.newInputStream(customResource)) {
processResourceStream(in, dest);
}
return;
}
}
if (defaultName != null) {
Log.verbose(MessageFormat.format(
I18N.getString("message.using-default-resource"),
defaultName, printableCategory, resourceName));
try (InputStream in = readDefault(defaultName)) {
processResourceStream(in, dest);
} }
} }
return null;
} }
void saveToFile(File dest) throws IOException { Source saveToFile(File dest) throws IOException {
saveToFile(dest.toPath()); return saveToFile(toPath(dest));
} }
static InputStream readDefault(String resourceName) { static InputStream readDefault(String resourceName) {
@ -176,6 +157,81 @@ final class OverridableResource {
RESOURCE_DIR.fetchFrom(params)); RESOURCE_DIR.fetchFrom(params));
} }
private String getPrintableCategory() {
if (category != null) {
return String.format("[%s]", category);
}
return "";
}
private boolean useExternal(Path dest) throws IOException {
boolean used = externalPath != null && Files.exists(externalPath);
if (used && dest != null) {
Log.verbose(MessageFormat.format(I18N.getString(
"message.using-custom-resource-from-file"),
getPrintableCategory(),
externalPath.toAbsolutePath().normalize()));
try (InputStream in = Files.newInputStream(externalPath)) {
processResourceStream(in, dest);
}
}
return used;
}
private boolean useResourceDir(Path dest) throws IOException {
boolean used = false;
if (dest == null && publicName == null) {
throw new IllegalStateException();
}
final Path resourceName = Optional.ofNullable(publicName).orElseGet(
() -> dest.getFileName());
if (resourceDir != null) {
final Path customResource = resourceDir.resolve(resourceName);
used = Files.exists(customResource);
if (used && dest != null) {
final Path logResourceName;
if (logPublicName != null) {
logResourceName = logPublicName.normalize();
} else {
logResourceName = resourceName.normalize();
}
Log.verbose(MessageFormat.format(I18N.getString(
"message.using-custom-resource"), getPrintableCategory(),
logResourceName));
try (InputStream in = Files.newInputStream(customResource)) {
processResourceStream(in, dest);
}
}
}
return used;
}
private boolean useDefault(Path dest) throws IOException {
boolean used = defaultName != null;
if (used && dest != null) {
final Path resourceName = Optional
.ofNullable(logPublicName)
.orElse(Optional
.ofNullable(publicName)
.orElseGet(() -> dest.getFileName()));
Log.verbose(MessageFormat.format(
I18N.getString("message.using-default-resource"),
defaultName, getPrintableCategory(), resourceName));
try (InputStream in = readDefault(defaultName)) {
processResourceStream(in, dest);
}
}
return used;
}
private static List<String> substitute(Stream<String> lines, private static List<String> substitute(Stream<String> lines,
Map<String, String> substitutionData) { Map<String, String> substitutionData) {
return lines.map(line -> { return lines.map(line -> {
@ -210,10 +266,33 @@ final class OverridableResource {
} }
} }
private SourceHandler getHandler(Source sourceType) {
switch (sourceType) {
case DefaultResource:
return this::useDefault;
case External:
return this::useExternal;
case ResourceDir:
return this::useResourceDir;
default:
throw new IllegalArgumentException();
}
}
private Map<String, String> substitutionData; private Map<String, String> substitutionData;
private String category; private String category;
private Path resourceDir; private Path resourceDir;
private Path publicName; private Path publicName;
private Path logPublicName;
private Path externalPath; private Path externalPath;
private final String defaultName; private final String defaultName;
private List<Map.Entry<Source, SourceHandler>> sources;
@FunctionalInterface
static interface SourceHandler {
public boolean apply(Path dest) throws IOException;
}
} }

View File

@ -37,21 +37,6 @@ public class WinAppBundler extends AbstractImageBundler {
private static final ResourceBundle I18N = ResourceBundle.getBundle( private static final ResourceBundle I18N = ResourceBundle.getBundle(
"jdk.incubator.jpackage.internal.resources.WinResources"); "jdk.incubator.jpackage.internal.resources.WinResources");
static final BundlerParamInfo<File> ICON_ICO =
new StandardBundlerParam<>(
"icon.ico",
File.class,
params -> {
File f = ICON.fetchFrom(params);
if (f != null && !f.getName().toLowerCase().endsWith(".ico")) {
Log.error(MessageFormat.format(
I18N.getString("message.icon-not-ico"), f));
return null;
}
return f;
},
(s, p) -> new File(s));
@Override @Override
public boolean validate(Map<String, ? super Object> params) public boolean validate(Map<String, ? super Object> params)
throws ConfigException { throws ConfigException {

View File

@ -152,11 +152,6 @@ public class WindowsAppImageBuilder extends AbstractAppImageBuilder {
return "app/" + APP_NAME.fetchFrom(params) +".cfg"; return "app/" + APP_NAME.fetchFrom(params) +".cfg";
} }
private File getConfig_AppIcon(Map<String, ? super Object> params) {
return new File(getConfigRoot(params),
APP_NAME.fetchFrom(params) + ".ico");
}
private File getConfig_ExecutableProperties( private File getConfig_ExecutableProperties(
Map<String, ? super Object> params) { Map<String, ? super Object> params) {
return new File(getConfigRoot(params), return new File(getConfigRoot(params),
@ -180,8 +175,6 @@ public class WindowsAppImageBuilder extends AbstractAppImageBuilder {
@Override @Override
public void prepareApplicationFiles(Map<String, ? super Object> params) public void prepareApplicationFiles(Map<String, ? super Object> params)
throws IOException { throws IOException {
Map<String, ? super Object> originalParams = new HashMap<>(params);
try { try {
IOUtils.writableOutputDir(root); IOUtils.writableOutputDir(root);
IOUtils.writableOutputDir(binDir); IOUtils.writableOutputDir(binDir);
@ -191,7 +184,7 @@ public class WindowsAppImageBuilder extends AbstractAppImageBuilder {
AppImageFile.save(root, params); AppImageFile.save(root, params);
// create the .exe launchers // create the .exe launchers
createLauncherForEntryPoint(params); createLauncherForEntryPoint(params, null);
// copy the jars // copy the jars
copyApplication(params); copyApplication(params);
@ -207,8 +200,8 @@ public class WindowsAppImageBuilder extends AbstractAppImageBuilder {
List<Map<String, ? super Object>> entryPoints = List<Map<String, ? super Object>> entryPoints =
StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params); StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params);
for (Map<String, ? super Object> entryPoint : entryPoints) { for (Map<String, ? super Object> entryPoint : entryPoints) {
createLauncherForEntryPoint( createLauncherForEntryPoint(AddLauncherArguments.merge(params,
AddLauncherArguments.merge(originalParams, entryPoint)); entryPoint, ICON.getID(), ICON_ICO.getID()), params);
} }
} }
@ -272,15 +265,18 @@ public class WindowsAppImageBuilder extends AbstractAppImageBuilder {
.saveToFile(getConfig_ExecutableProperties(params)); .saveToFile(getConfig_ExecutableProperties(params));
} }
private void createLauncherForEntryPoint( private void createLauncherForEntryPoint(Map<String, ? super Object> params,
Map<String, ? super Object> params) throws IOException { Map<String, ? super Object> mainParams) throws IOException {
File iconTarget = getConfig_AppIcon(params); var iconResource = createIconResource(TEMPLATE_APP_ICON, ICON_ICO, params,
mainParams);
createResource(TEMPLATE_APP_ICON, params) Path iconTarget = null;
.setCategory("icon") if (iconResource != null) {
.setExternal(ICON_ICO.fetchFrom(params)) iconTarget = binDir.resolve(APP_NAME.fetchFrom(params) + ".ico");
.saveToFile(iconTarget); if (null == iconResource.saveToFile(iconTarget)) {
iconTarget = null;
}
}
writeCfgFile(params, root.resolve( writeCfgFile(params, root.resolve(
getLauncherCfgName(params)).toFile()); getLauncherCfgName(params)).toFile());
@ -315,8 +311,8 @@ public class WindowsAppImageBuilder extends AbstractAppImageBuilder {
launcher.setWritable(true); launcher.setWritable(true);
if (iconTarget.exists()) { if (iconTarget != null) {
iconSwap(iconTarget.getAbsolutePath(), iconSwap(iconTarget.toAbsolutePath().toString(),
launcher.getAbsolutePath()); launcher.getAbsolutePath());
} }
@ -336,9 +332,6 @@ public class WindowsAppImageBuilder extends AbstractAppImageBuilder {
executableFile.toFile().setReadOnly(); executableFile.toFile().setReadOnly();
} }
} }
Files.copy(iconTarget.toPath(),
binDir.resolve(APP_NAME.fetchFrom(params) + ".ico"));
} }
private void copyApplication(Map<String, ? super Object> params) private void copyApplication(Map<String, ? super Object> params)

View File

@ -398,10 +398,6 @@ public class JPackageHelper {
createModule("Hello.java", "input", "hello", moduleArgs, true); createModule("Hello.java", "input", "hello", moduleArgs, true);
} }
public static void createOtherModule() throws Exception {
createModule("Other.java", "input-other", "other", null, false);
}
private static void createModule(String javaFile, String inputDir, String aName, private static void createModule(String javaFile, String inputDir, String aName,
ModuleArgs moduleArgs, boolean createModularJar) throws Exception { ModuleArgs moduleArgs, boolean createModularJar) throws Exception {
int retVal; int retVal;

View File

@ -39,6 +39,11 @@ import jdk.jpackage.test.Functional.ThrowingSupplier;
public final class Executor extends CommandArguments<Executor> { public final class Executor extends CommandArguments<Executor> {
public static Executor of(String... cmdline) {
return new Executor().setExecutable(cmdline[0]).addArguments(
Arrays.copyOfRange(cmdline, 1, cmdline.length));
}
public Executor() { public Executor() {
saveOutputType = new HashSet<>(Set.of(SaveOutputType.NONE)); saveOutputType = new HashSet<>(Set.of(SaveOutputType.NONE));
} }
@ -170,7 +175,7 @@ public final class Executor extends CommandArguments<Executor> {
private List<String> output; private List<String> output;
} }
public Result execute() { public Result executeWithoutExitCodeCheck() {
if (toolProvider != null && directory != null) { if (toolProvider != null && directory != null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Can't change directory when using tool provider"); "Can't change directory when using tool provider");
@ -189,12 +194,20 @@ public final class Executor extends CommandArguments<Executor> {
}).get(); }).get();
} }
public Result execute(int expectedCode) {
return executeWithoutExitCodeCheck().assertExitCodeIs(expectedCode);
}
public Result execute() {
return execute(0);
}
public String executeAndGetFirstLineOfOutput() { public String executeAndGetFirstLineOfOutput() {
return saveFirstLineOfOutput().execute().assertExitCodeIsZero().getFirstLineOfOutput(); return saveFirstLineOfOutput().execute().getFirstLineOfOutput();
} }
public List<String> executeAndGetOutput() { public List<String> executeAndGetOutput() {
return saveOutput().execute().assertExitCodeIsZero().getOutput(); return saveOutput().execute().getOutput();
} }
private boolean withSavedOutput() { private boolean withSavedOutput() {
@ -203,7 +216,9 @@ public final class Executor extends CommandArguments<Executor> {
} }
private Path executablePath() { private Path executablePath() {
if (directory == null || executable.isAbsolute()) { if (directory == null
|| executable.isAbsolute()
|| !Set.of(".", "..").contains(executable.getName(0).toString())) {
return executable; return executable;
} }
@ -237,7 +252,7 @@ public final class Executor extends CommandArguments<Executor> {
sb.append(String.format("; in directory [%s]", directory)); sb.append(String.format("; in directory [%s]", directory));
} }
TKit.trace("Execute " + sb.toString() + "..."); trace("Execute " + sb.toString() + "...");
Process process = builder.start(); Process process = builder.start();
List<String> outputLines = null; List<String> outputLines = null;
@ -266,7 +281,7 @@ public final class Executor extends CommandArguments<Executor> {
} }
Result reply = new Result(process.waitFor()); Result reply = new Result(process.waitFor());
TKit.trace("Done. Exit code: " + reply.exitCode); trace("Done. Exit code: " + reply.exitCode);
if (outputLines != null) { if (outputLines != null) {
reply.output = Collections.unmodifiableList(outputLines); reply.output = Collections.unmodifiableList(outputLines);
@ -275,10 +290,10 @@ public final class Executor extends CommandArguments<Executor> {
} }
private Result runToolProvider(PrintStream out, PrintStream err) { private Result runToolProvider(PrintStream out, PrintStream err) {
TKit.trace("Execute " + getPrintableCommandLine() + "..."); trace("Execute " + getPrintableCommandLine() + "...");
Result reply = new Result(toolProvider.run(out, err, args.toArray( Result reply = new Result(toolProvider.run(out, err, args.toArray(
String[]::new))); String[]::new)));
TKit.trace("Done. Exit code: " + reply.exitCode); trace("Done. Exit code: " + reply.exitCode);
return reply; return reply;
} }
@ -353,6 +368,10 @@ public final class Executor extends CommandArguments<Executor> {
Collectors.joining(" ")); Collectors.joining(" "));
} }
private static void trace(String msg) {
TKit.trace(String.format("exec: %s", msg));
}
private ToolProvider toolProvider; private ToolProvider toolProvider;
private Path executable; private Path executable;
private Set<SaveOutputType> saveOutputType; private Set<SaveOutputType> saveOutputType;

View File

@ -23,10 +23,7 @@
package jdk.jpackage.test; package jdk.jpackage.test;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.function.Consumer; import java.util.function.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class Functional { public class Functional {
@ -45,6 +42,21 @@ public class Functional {
} }
} }
@FunctionalInterface
public interface ThrowingBiConsumer<T, U> {
void accept(T t, U u) throws Throwable;
public static <T, U> BiConsumer<T, U> toBiConsumer(ThrowingBiConsumer<T, U> v) {
return (t, u) -> {
try {
v.accept(t, u);
} catch (Throwable ex) {
rethrowUnchecked(ex);
}
};
}
}
@FunctionalInterface @FunctionalInterface
public interface ThrowingSupplier<T> { public interface ThrowingSupplier<T> {
T get() throws Throwable; T get() throws Throwable;
@ -102,6 +114,10 @@ public class Functional {
return v; return v;
} }
public static <T, U> BiConsumer<T, U> identity(BiConsumer<T, U> v) {
return v;
}
public static Runnable identity(Runnable v) { public static Runnable identity(Runnable v) {
return v; return v;
} }

View File

@ -26,16 +26,16 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.jpackage.test.Functional.ThrowingFunction; import jdk.jpackage.test.Functional.ThrowingFunction;
import jdk.jpackage.test.Functional.ThrowingSupplier; import jdk.jpackage.test.Functional.ThrowingSupplier;
public class HelloApp { public final class HelloApp {
HelloApp(JavaAppDesc appDesc) { HelloApp(JavaAppDesc appDesc) {
if (appDesc == null) { if (appDesc == null) {
@ -131,13 +131,13 @@ public class HelloApp {
if (moduleName == null && CLASS_NAME.equals(qualifiedClassName)) { if (moduleName == null && CLASS_NAME.equals(qualifiedClassName)) {
// Use Hello.java as is. // Use Hello.java as is.
cmd.addAction((self) -> { cmd.addPrerequisiteAction((self) -> {
Path jarFile = self.inputDir().resolve(jarFileName); Path jarFile = self.inputDir().resolve(jarFileName);
createJarBuilder().setOutputJar(jarFile).addSourceFile( createJarBuilder().setOutputJar(jarFile).addSourceFile(
HELLO_JAVA).create(); HELLO_JAVA).create();
}); });
} else { } else {
cmd.addAction((self) -> { cmd.addPrerequisiteAction((self) -> {
final Path jarFile; final Path jarFile;
if (moduleName == null) { if (moduleName == null) {
jarFile = self.inputDir().resolve(jarFileName); jarFile = self.inputDir().resolve(jarFileName);
@ -177,9 +177,11 @@ public class HelloApp {
"hello.jar"); "hello.jar");
} }
static void verifyOutputFile(Path outputFile, List<String> args) { static void verifyOutputFile(Path outputFile, List<String> args,
Map<String, String> params) {
if (!outputFile.isAbsolute()) { if (!outputFile.isAbsolute()) {
verifyOutputFile(outputFile.toAbsolutePath().normalize(), args); verifyOutputFile(outputFile.toAbsolutePath().normalize(), args,
params);
return; return;
} }
@ -193,38 +195,121 @@ public class HelloApp {
String.format("args.length: %d", args.size()) String.format("args.length: %d", args.size())
)); ));
expected.addAll(args); expected.addAll(args);
expected.addAll(params.entrySet().stream()
.sorted(Comparator.comparing(Map.Entry::getKey))
.map(entry -> String.format("-D%s=%s", entry.getKey(),
entry.getValue()))
.collect(Collectors.toList()));
TKit.assertStringListEquals(expected, contents, String.format( TKit.assertStringListEquals(expected, contents, String.format(
"Check contents of [%s] file", outputFile)); "Check contents of [%s] file", outputFile));
} }
public static void executeLauncherAndVerifyOutput(JPackageCommand cmd) { public static void executeLauncherAndVerifyOutput(JPackageCommand cmd,
String... args) {
final Path launcherPath = cmd.appLauncherPath(); final Path launcherPath = cmd.appLauncherPath();
if (!cmd.isFakeRuntime(String.format("Not running [%s] launcher", if (cmd.isFakeRuntime(String.format("Not running [%s] launcher",
launcherPath))) { launcherPath))) {
executeAndVerifyOutput(launcherPath, cmd.getAllArgumentValues( return;
"--arguments"));
} }
assertApp(launcherPath)
.addDefaultArguments(Optional
.ofNullable(cmd.getAllArgumentValues("--arguments"))
.orElseGet(() -> new String[0]))
.addJavaOptions(Optional
.ofNullable(cmd.getAllArgumentValues("--java-options"))
.orElseGet(() -> new String[0]))
.executeAndVerifyOutput(args);
} }
public static void executeAndVerifyOutput(Path helloAppLauncher, public final static class AppOutputVerifier {
String... defaultLauncherArgs) { AppOutputVerifier(Path helloAppLauncher) {
executeAndVerifyOutput(helloAppLauncher, List.of(defaultLauncherArgs)); this.launcherPath = helloAppLauncher;
this.params = new HashMap<>();
this.defaultLauncherArgs = new ArrayList<>();
}
public AppOutputVerifier addDefaultArguments(String... v) {
return addDefaultArguments(List.of(v));
}
public AppOutputVerifier addDefaultArguments(Collection<String> v) {
defaultLauncherArgs.addAll(v);
return this;
}
public AppOutputVerifier addParam(String name, String value) {
if (name.startsWith("param")) {
params.put(name, value);
}
return this;
}
public AppOutputVerifier addParams(Collection<Map.Entry<String, String>> v) {
v.forEach(entry -> addParam(entry.getKey(), entry.getValue()));
return this;
}
public AppOutputVerifier addParams(Map<String, String> v) {
return addParams(v.entrySet());
}
public AppOutputVerifier addParams(Map.Entry<String, String>... v) {
return addParams(List.of(v));
}
public AppOutputVerifier addJavaOptions(String... v) {
return addJavaOptions(List.of(v));
}
public AppOutputVerifier addJavaOptions(Collection<String> v) {
return addParams(v.stream()
.filter(javaOpt -> javaOpt.startsWith("-D"))
.map(javaOpt -> {
var components = javaOpt.split("=", 2);
return Map.entry(components[0].substring(2), components[1]);
})
.collect(Collectors.toList()));
}
public void executeAndVerifyOutput(String... args) {
// Output file will be created in the current directory.
Path outputFile = TKit.workDir().resolve(OUTPUT_FILENAME);
ThrowingFunction.toFunction(Files::deleteIfExists).apply(outputFile);
final Path executablePath;
if (launcherPath.isAbsolute()) {
executablePath = launcherPath;
} else {
// Make sure path to executable is relative to the current directory.
executablePath = Path.of(".").resolve(launcherPath.normalize());
}
final List<String> launcherArgs = List.of(args);
new Executor()
.setDirectory(outputFile.getParent())
.setExecutable(executablePath)
.addArguments(launcherArgs)
.dumpOutput()
.execute();
final List<String> appArgs;
if (launcherArgs.isEmpty()) {
appArgs = defaultLauncherArgs;
} else {
appArgs = launcherArgs;
}
verifyOutputFile(outputFile, appArgs, params);
}
private final Path launcherPath;
private final List<String> defaultLauncherArgs;
private final Map<String, String> params;
} }
public static void executeAndVerifyOutput(Path helloAppLauncher, public static AppOutputVerifier assertApp(Path helloAppLauncher) {
List<String> defaultLauncherArgs) { return new AppOutputVerifier(helloAppLauncher);
// Output file will be created in the current directory.
Path outputFile = TKit.workDir().resolve(OUTPUT_FILENAME);
ThrowingFunction.toFunction(Files::deleteIfExists).apply(outputFile);
new Executor()
.setDirectory(outputFile.getParent())
.setExecutable(helloAppLauncher)
.dumpOutput()
.execute()
.assertExitCodeIsZero();
verifyOutputFile(outputFile, defaultLauncherArgs);
} }
final static String OUTPUT_FILENAME = "appOutput.txt"; final static String OUTPUT_FILENAME = "appOutput.txt";

View File

@ -30,14 +30,15 @@ import java.security.SecureRandom;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import jdk.incubator.jpackage.internal.ApplicationLayout; import jdk.incubator.jpackage.internal.ApplicationLayout;
import jdk.jpackage.test.Functional.ThrowingConsumer; import jdk.jpackage.test.Functional.ThrowingConsumer;
import jdk.jpackage.test.Functional.ThrowingFunction; import jdk.jpackage.test.Functional.ThrowingFunction;
import jdk.jpackage.test.Functional.ThrowingSupplier;
/** /**
* jpackage command line with prerequisite actions. Prerequisite actions can be * jpackage command line with prerequisite actions. Prerequisite actions can be
@ -47,18 +48,20 @@ import jdk.jpackage.test.Functional.ThrowingFunction;
public final class JPackageCommand extends CommandArguments<JPackageCommand> { public final class JPackageCommand extends CommandArguments<JPackageCommand> {
public JPackageCommand() { public JPackageCommand() {
actions = new ArrayList<>(); prerequisiteActions = new Actions();
verifyActions = new Actions();
} }
public JPackageCommand(JPackageCommand cmd) { public JPackageCommand(JPackageCommand cmd) {
this();
args.addAll(cmd.args); args.addAll(cmd.args);
withToolProvider = cmd.withToolProvider; withToolProvider = cmd.withToolProvider;
saveConsoleOutput = cmd.saveConsoleOutput; saveConsoleOutput = cmd.saveConsoleOutput;
suppressOutput = cmd.suppressOutput; suppressOutput = cmd.suppressOutput;
ignoreDefaultRuntime = cmd.ignoreDefaultRuntime; ignoreDefaultRuntime = cmd.ignoreDefaultRuntime;
ignoreDefaultVerbose = cmd.ignoreDefaultVerbose;
immutable = cmd.immutable; immutable = cmd.immutable;
actionsExecuted = cmd.actionsExecuted; prerequisiteActions = new Actions(cmd.prerequisiteActions);
verifyActions = new Actions(cmd.verifyActions);
} }
JPackageCommand createImmutableCopy() { JPackageCommand createImmutableCopy() {
@ -204,8 +207,8 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
} }
public JPackageCommand setDefaultInputOutput() { public JPackageCommand setDefaultInputOutput() {
addArguments("--input", TKit.defaultInputDir()); setArgumentValue("--input", TKit.workDir().resolve("input"));
addArguments("--dest", TKit.defaultOutputDir()); setArgumentValue("--dest", TKit.workDir().resolve("output"));
return this; return this;
} }
@ -221,7 +224,7 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
} }
}; };
addAction(cmd -> { addPrerequisiteAction(cmd -> {
Path fakeRuntimeDir = TKit.workDir().resolve("fake_runtime"); Path fakeRuntimeDir = TKit.workDir().resolve("fake_runtime");
TKit.trace(String.format("Init fake runtime in [%s] directory", TKit.trace(String.format("Init fake runtime in [%s] directory",
@ -254,9 +257,15 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
return this; return this;
} }
JPackageCommand addAction(ThrowingConsumer<JPackageCommand> action) { JPackageCommand addPrerequisiteAction(ThrowingConsumer<JPackageCommand> action) {
verifyMutable(); verifyMutable();
actions.add(ThrowingConsumer.toConsumer(action)); prerequisiteActions.add(action);
return this;
}
JPackageCommand addVerifyAction(ThrowingConsumer<JPackageCommand> action) {
verifyMutable();
verifyActions.add(action);
return this; return this;
} }
@ -363,6 +372,12 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
* `/opt/foo` * `/opt/foo`
*/ */
public Path appInstallationDirectory() { public Path appInstallationDirectory() {
Path unpackedDir = getArgumentValue(UNPACKED_PATH_ARGNAME, () -> null,
Path::of);
if (unpackedDir != null) {
return unpackedDir;
}
if (isImagePackageType()) { if (isImagePackageType()) {
return null; return null;
} }
@ -426,7 +441,7 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
launcherName = launcherName + ".exe"; launcherName = launcherName + ".exe";
} }
if (isImagePackageType()) { if (isImagePackageType() || isPackageUnpacked()) {
return appLayout().launchersDirectory().resolve(launcherName); return appLayout().launchersDirectory().resolve(launcherName);
} }
@ -496,6 +511,19 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
return false; return false;
} }
public boolean isPackageUnpacked(String msg) {
if (isPackageUnpacked()) {
TKit.trace(String.format(
"%s because package was unpacked, not installed", msg));
return true;
}
return false;
}
boolean isPackageUnpacked() {
return hasArgument(UNPACKED_PATH_ARGNAME);
}
public static void useToolProviderByDefault() { public static void useToolProviderByDefault() {
defaultWithToolProvider = true; defaultWithToolProvider = true;
} }
@ -528,24 +556,28 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
return this; return this;
} }
public JPackageCommand ignoreDefaultVerbose(boolean v) {
verifyMutable();
ignoreDefaultVerbose = v;
return this;
}
public boolean isWithToolProvider() { public boolean isWithToolProvider() {
return Optional.ofNullable(withToolProvider).orElse( return Optional.ofNullable(withToolProvider).orElse(
defaultWithToolProvider); defaultWithToolProvider);
} }
public JPackageCommand executePrerequisiteActions() { public JPackageCommand executePrerequisiteActions() {
verifyMutable(); prerequisiteActions.run();
if (!actionsExecuted) {
actionsExecuted = true;
if (actions != null) {
actions.stream().forEach(r -> r.accept(this));
}
}
return this; return this;
} }
public Executor createExecutor() { public JPackageCommand executeVerifyActions() {
verifyMutable(); verifyActions.run();
return this;
}
private Executor createExecutor() {
Executor exec = new Executor() Executor exec = new Executor()
.saveOutput(saveConsoleOutput).dumpOutput(!suppressOutput) .saveOutput(saveConsoleOutput).dumpOutput(!suppressOutput)
.addArguments(args); .addArguments(args);
@ -560,27 +592,60 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
} }
public Executor.Result execute() { public Executor.Result execute() {
return execute(0);
}
public Executor.Result execute(int expectedExitCode) {
executePrerequisiteActions(); executePrerequisiteActions();
if (isImagePackageType()) { if (isImagePackageType()) {
TKit.deleteDirectoryContentsRecursive(outputDir()); TKit.deleteDirectoryContentsRecursive(outputDir());
} else if (ThrowingSupplier.toSupplier(() -> Files.deleteIfExists(
outputBundle())).get()) {
TKit.trace(
String.format("Deleted [%s] file before running jpackage",
outputBundle()));
} }
return new JPackageCommand(this) Path resourceDir = getArgumentValue("--resource-dir", () -> null, Path::of);
if (resourceDir != null && Files.isDirectory(resourceDir)) {
TKit.trace(String.format("Files in [%s] resource dir:",
resourceDir));
try (var files = Files.walk(resourceDir, 1)) {
files.sequential()
.filter(Predicate.not(resourceDir::equals))
.map(path -> String.format("[%s]", path.getFileName()))
.forEachOrdered(TKit::trace);
TKit.trace("Done");
} catch (IOException ex) {
TKit.trace(String.format(
"Failed to list files in [%s] resource directory: %s",
resourceDir, ex));
}
}
Executor.Result result = new JPackageCommand(this)
.adjustArgumentsBeforeExecution() .adjustArgumentsBeforeExecution()
.createExecutor() .createExecutor()
.execute(); .execute(expectedExitCode);
if (result.exitCode == 0) {
executeVerifyActions();
}
return result;
} }
public JPackageCommand executeAndAssertHelloAppImageCreated() { public Executor.Result executeAndAssertHelloAppImageCreated() {
executeAndAssertImageCreated(); Executor.Result result = executeAndAssertImageCreated();
HelloApp.executeLauncherAndVerifyOutput(this); HelloApp.executeLauncherAndVerifyOutput(this);
return this; return result;
} }
public JPackageCommand executeAndAssertImageCreated() { public Executor.Result executeAndAssertImageCreated() {
execute().assertExitCodeIsZero(); Executor.Result result = execute();
return assertImageCreated(); assertImageCreated();
return result;
} }
public JPackageCommand assertImageCreated() { public JPackageCommand assertImageCreated() {
@ -595,23 +660,26 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
return this; return this;
} }
JPackageCommand setUnpackedPackageLocation(Path path) {
verifyIsOfType(PackageType.NATIVE);
setArgumentValue(UNPACKED_PATH_ARGNAME, path);
return this;
}
private JPackageCommand adjustArgumentsBeforeExecution() { private JPackageCommand adjustArgumentsBeforeExecution() {
if (!hasArgument("--runtime-image") && !hasArgument("--app-image") && DEFAULT_RUNTIME_IMAGE != null && !ignoreDefaultRuntime) { if (!hasArgument("--runtime-image") && !hasArgument("--app-image") && DEFAULT_RUNTIME_IMAGE != null && !ignoreDefaultRuntime) {
addArguments("--runtime-image", DEFAULT_RUNTIME_IMAGE); addArguments("--runtime-image", DEFAULT_RUNTIME_IMAGE);
} }
if (!hasArgument("--verbose") && TKit.VERBOSE_JPACKAGE) { if (!hasArgument("--verbose") && TKit.VERBOSE_JPACKAGE && !ignoreDefaultVerbose) {
addArgument("--verbose"); addArgument("--verbose");
} }
return this; return this;
} }
String getPrintableCommandLine() { public String getPrintableCommandLine() {
return new Executor() return createExecutor().getPrintableCommandLine();
.setExecutable(JavaTool.JPACKAGE)
.addArguments(args)
.getPrintableCommandLine();
} }
public void verifyIsOfType(Collection<PackageType> types) { public void verifyIsOfType(Collection<PackageType> types) {
@ -699,13 +767,48 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
return !immutable; return !immutable;
} }
private final class Actions implements Runnable {
Actions() {
actions = new ArrayList<>();
}
Actions(Actions other) {
this();
actions.addAll(other.actions);
}
void add(ThrowingConsumer<JPackageCommand> action) {
Objects.requireNonNull(action);
verifyMutable();
actions.add(new Consumer<JPackageCommand>() {
@Override
public void accept(JPackageCommand t) {
if (!executed) {
executed = true;
ThrowingConsumer.toConsumer(action).accept(t);
}
}
private boolean executed;
});
}
@Override
public void run() {
verifyMutable();
actions.forEach(action -> action.accept(JPackageCommand.this));
}
private final List<Consumer<JPackageCommand>> actions;
}
private Boolean withToolProvider; private Boolean withToolProvider;
private boolean saveConsoleOutput; private boolean saveConsoleOutput;
private boolean suppressOutput; private boolean suppressOutput;
private boolean ignoreDefaultRuntime; private boolean ignoreDefaultRuntime;
private boolean ignoreDefaultVerbose;
private boolean immutable; private boolean immutable;
private boolean actionsExecuted; private final Actions prerequisiteActions;
private final List<Consumer<JPackageCommand>> actions; private final Actions verifyActions;
private static boolean defaultWithToolProvider; private static boolean defaultWithToolProvider;
private final static Map<String, PackageType> PACKAGE_TYPES = Functional.identity( private final static Map<String, PackageType> PACKAGE_TYPES = Functional.identity(
@ -729,4 +832,6 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
} }
return null; return null;
}).get(); }).get();
private final static String UNPACKED_PATH_ARGNAME = "jpt-unpacked-folder";
} }

View File

@ -67,7 +67,7 @@ public final class JarBuilder {
.setToolProvider(JavaTool.JAVAC) .setToolProvider(JavaTool.JAVAC)
.addArguments("-d", workDir.toString()) .addArguments("-d", workDir.toString())
.addPathArguments(sourceFiles) .addPathArguments(sourceFiles)
.execute().assertExitCodeIsZero(); .execute();
} }
Files.createDirectories(outputJar.getParent()); Files.createDirectories(outputJar.getParent());
@ -87,7 +87,7 @@ public final class JarBuilder {
jarExe.addArguments("-e", mainClass); jarExe.addArguments("-e", mainClass);
} }
jarExe.addArguments("-C", workDir.toString(), "."); jarExe.addArguments("-C", workDir.toString(), ".");
jarExe.execute().assertExitCodeIsZero(); jarExe.execute();
}); });
} }
private List<Path> sourceFiles; private List<Path> sourceFiles;

View File

@ -25,14 +25,11 @@ package jdk.jpackage.test;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import jdk.jpackage.test.PackageTest.PackageHandlers;
public class LinuxHelper { public class LinuxHelper {
private static String getRelease(JPackageCommand cmd) { private static String getRelease(JPackageCommand cmd) {
@ -45,6 +42,19 @@ public class LinuxHelper {
() -> cmd.name().toLowerCase()); () -> cmd.name().toLowerCase());
} }
public static Path getDesktopFile(JPackageCommand cmd) {
return getDesktopFile(cmd, null);
}
public static Path getDesktopFile(JPackageCommand cmd, String launcherName) {
cmd.verifyIsOfType(PackageType.LINUX);
String desktopFileName = String.format("%s-%s.desktop", getPackageName(
cmd), Optional.ofNullable(launcherName).orElseGet(
() -> cmd.name()));
return cmd.appLayout().destktopIntegrationDirectory().resolve(
desktopFileName);
}
static String getBundleName(JPackageCommand cmd) { static String getBundleName(JPackageCommand cmd) {
cmd.verifyIsOfType(PackageType.LINUX); cmd.verifyIsOfType(PackageType.LINUX);
@ -73,18 +83,14 @@ public class LinuxHelper {
final PackageType packageType = cmd.packageType(); final PackageType packageType = cmd.packageType();
final Path packageFile = cmd.outputBundle(); final Path packageFile = cmd.outputBundle();
Executor exec = new Executor(); Executor exec = null;
switch (packageType) { switch (packageType) {
case LINUX_DEB: case LINUX_DEB:
exec.setExecutable("dpkg") exec = Executor.of("dpkg", "--contents").addArgument(packageFile);
.addArgument("--contents")
.addArgument(packageFile);
break; break;
case LINUX_RPM: case LINUX_RPM:
exec.setExecutable("rpm") exec = Executor.of("rpm", "-qpl").addArgument(packageFile);
.addArgument("-qpl")
.addArgument(packageFile);
break; break;
} }
@ -109,8 +115,8 @@ public class LinuxHelper {
Collectors.toList()); Collectors.toList());
case LINUX_RPM: case LINUX_RPM:
return new Executor().setExecutable("rpm") return Executor.of("rpm", "-qp", "-R")
.addArguments("-qp", "-R", cmd.outputBundle().toString()) .addArgument(cmd.outputBundle())
.executeAndGetOutput(); .executeAndGetOutput();
} }
// Unreachable // Unreachable
@ -141,6 +147,57 @@ public class LinuxHelper {
return null; return null;
} }
static PackageHandlers createDebPackageHandlers() {
PackageHandlers deb = new PackageHandlers();
deb.installHandler = cmd -> {
cmd.verifyIsOfType(PackageType.LINUX_DEB);
Executor.of("sudo", "dpkg", "-i")
.addArgument(cmd.outputBundle())
.execute();
};
deb.uninstallHandler = cmd -> {
cmd.verifyIsOfType(PackageType.LINUX_DEB);
Executor.of("sudo", "dpkg", "-r", getPackageName(cmd)).execute();
};
deb.unpackHandler = (cmd, destinationDir) -> {
cmd.verifyIsOfType(PackageType.LINUX_DEB);
Executor.of("dpkg", "-x")
.addArgument(cmd.outputBundle())
.addArgument(destinationDir)
.execute();
return destinationDir.resolve(String.format(".%s",
cmd.appInstallationDirectory())).normalize();
};
return deb;
}
static PackageHandlers createRpmPackageHandlers() {
PackageHandlers rpm = new PackageHandlers();
rpm.installHandler = cmd -> {
cmd.verifyIsOfType(PackageType.LINUX_RPM);
Executor.of("sudo", "rpm", "-i")
.addArgument(cmd.outputBundle())
.execute();
};
rpm.uninstallHandler = cmd -> {
cmd.verifyIsOfType(PackageType.LINUX_RPM);
Executor.of("sudo", "rpm", "-e", getPackageName(cmd)).execute();
};
rpm.unpackHandler = (cmd, destinationDir) -> {
cmd.verifyIsOfType(PackageType.LINUX_RPM);
Executor.of("sh", "-c", String.format(
"rpm2cpio '%s' | cpio -idm --quiet",
JPackageCommand.escapeAndJoin(
cmd.outputBundle().toAbsolutePath().toString())))
.setDirectory(destinationDir)
.execute();
return destinationDir.resolve(String.format(".%s",
cmd.appInstallationDirectory())).normalize();
};
return rpm;
}
static Path getLauncherPath(JPackageCommand cmd) { static Path getLauncherPath(JPackageCommand cmd) {
cmd.verifyIsOfType(PackageType.LINUX); cmd.verifyIsOfType(PackageType.LINUX);
@ -173,20 +230,15 @@ public class LinuxHelper {
} }
static String getDebBundleProperty(Path bundle, String fieldName) { static String getDebBundleProperty(Path bundle, String fieldName) {
return new Executor() return Executor.of("dpkg-deb", "-f")
.setExecutable("dpkg-deb") .addArgument(bundle)
.addArguments("-f", bundle.toString(), fieldName) .addArgument(fieldName)
.executeAndGetFirstLineOfOutput(); .executeAndGetFirstLineOfOutput();
} }
static String getRpmBundleProperty(Path bundle, String fieldName) { static String getRpmBundleProperty(Path bundle, String fieldName) {
return new Executor() return Executor.of("rpm", "-qp", "--queryformat", String.format("%%{%s}", fieldName))
.setExecutable("rpm") .addArgument(bundle)
.addArguments(
"-qp",
"--queryformat",
String.format("%%{%s}", fieldName),
bundle.toString())
.executeAndGetFirstLineOfOutput(); .executeAndGetFirstLineOfOutput();
} }
@ -264,13 +316,10 @@ public class LinuxHelper {
test.addBundleVerifier(cmd -> { test.addBundleVerifier(cmd -> {
TKit.withTempDirectory("dpkg-control-files", tempDir -> { TKit.withTempDirectory("dpkg-control-files", tempDir -> {
// Extract control Debian package files into temporary directory // Extract control Debian package files into temporary directory
new Executor() Executor.of("dpkg", "-e")
.setExecutable("dpkg") .addArgument(cmd.outputBundle())
.addArguments( .addArgument(tempDir)
"-e", .execute();
cmd.outputBundle().toString(),
tempDir.toString()
).execute().assertExitCodeIsZero();
Path controlFile = Path.of("postinst"); Path controlFile = Path.of("postinst");
@ -318,6 +367,10 @@ public class LinuxHelper {
static void addFileAssociationsVerifier(PackageTest test, FileAssociations fa) { static void addFileAssociationsVerifier(PackageTest test, FileAssociations fa) {
test.addInstallVerifier(cmd -> { test.addInstallVerifier(cmd -> {
if (cmd.isPackageUnpacked("Not running file associations checks")) {
return;
}
PackageTest.withTestFileAssociationsFile(fa, testFile -> { PackageTest.withTestFileAssociationsFile(fa, testFile -> {
String mimeType = queryFileMimeType(testFile); String mimeType = queryFileMimeType(testFile);
@ -363,16 +416,12 @@ public class LinuxHelper {
} }
private static String queryFileMimeType(Path file) { private static String queryFileMimeType(Path file) {
return new Executor() return Executor.of("xdg-mime", "query", "filetype").addArgument(file)
.setExecutable("xdg-mime")
.addArguments("query", "filetype", file.toString())
.executeAndGetFirstLineOfOutput(); .executeAndGetFirstLineOfOutput();
} }
private static String queryMimeTypeDefaultHandler(String mimeType) { private static String queryMimeTypeDefaultHandler(String mimeType) {
return new Executor() return Executor.of("xdg-mime", "query", "default", mimeType)
.setExecutable("xdg-mime")
.addArguments("query", "default", mimeType)
.executeAndGetFirstLineOfOutput(); .executeAndGetFirstLineOfOutput();
} }
@ -383,16 +432,14 @@ public class LinuxHelper {
String arch = archs.get(type); String arch = archs.get(type);
if (arch == null) { if (arch == null) {
Executor exec = new Executor(); Executor exec = null;
switch (type) { switch (type) {
case LINUX_DEB: case LINUX_DEB:
exec.setExecutable("dpkg").addArgument( exec = Executor.of("dpkg", "--print-architecture");
"--print-architecture");
break; break;
case LINUX_RPM: case LINUX_RPM:
exec.setExecutable("rpmbuild").addArgument( exec = Executor.of("rpmbuild", "--eval=%{_target_cpu}");
"--eval=%{_target_cpu}");
break; break;
} }
arch = exec.executeAndGetFirstLineOfOutput(); arch = exec.executeAndGetFirstLineOfOutput();

View File

@ -29,6 +29,7 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
@ -39,6 +40,7 @@ import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFactory;
import jdk.jpackage.test.Functional.ThrowingConsumer; import jdk.jpackage.test.Functional.ThrowingConsumer;
import jdk.jpackage.test.Functional.ThrowingSupplier; import jdk.jpackage.test.Functional.ThrowingSupplier;
import jdk.jpackage.test.PackageTest.PackageHandlers;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
public class MacHelper { public class MacHelper {
@ -47,10 +49,12 @@ public class MacHelper {
ThrowingConsumer<Path> consumer) { ThrowingConsumer<Path> consumer) {
cmd.verifyIsOfType(PackageType.MAC_DMG); cmd.verifyIsOfType(PackageType.MAC_DMG);
var plist = readPList(new Executor() // Explode DMG assuming this can require interaction, thus use `yes`.
.setExecutable("/usr/bin/hdiutil") var plist = readPList(Executor.of("sh", "-c",
String.join(" ", "yes", "|", "/usr/bin/hdiutil", "attach",
JPackageCommand.escapeAndJoin(
cmd.outputBundle().toString()), "-plist"))
.dumpOutput() .dumpOutput()
.addArguments("attach", cmd.outputBundle().toString(), "-plist")
.executeAndGetOutput()); .executeAndGetOutput());
final Path mountPoint = Path.of(plist.queryValue("mount-point")); final Path mountPoint = Path.of(plist.queryValue("mount-point"));
@ -60,10 +64,7 @@ public class MacHelper {
cmd.outputBundle(), dmgImage)); cmd.outputBundle(), dmgImage));
ThrowingConsumer.toConsumer(consumer).accept(dmgImage); ThrowingConsumer.toConsumer(consumer).accept(dmgImage);
} finally { } finally {
new Executor() Executor.of("/usr/bin/hdiutil", "detach").addArgument(mountPoint).execute();
.setExecutable("/usr/bin/hdiutil")
.addArgument("detach").addArgument(mountPoint)
.execute().assertExitCodeIsZero();
} }
} }
@ -82,8 +83,62 @@ public class MacHelper {
} }
public static PListWrapper readPList(Stream<String> lines) { public static PListWrapper readPList(Stream<String> lines) {
return ThrowingSupplier.toSupplier(() -> new PListWrapper(lines.collect( return ThrowingSupplier.toSupplier(() -> new PListWrapper(lines
Collectors.joining()))).get(); // Skip leading lines before xml declaration
.dropWhile(Pattern.compile("\\s?<\\?xml\\b.+\\?>").asPredicate().negate())
.collect(Collectors.joining()))).get();
}
static PackageHandlers createDmgPackageHandlers() {
PackageHandlers dmg = new PackageHandlers();
dmg.installHandler = cmd -> {
withExplodedDmg(cmd, dmgImage -> {
Executor.of("sudo", "cp", "-r")
.addArgument(dmgImage)
.addArgument("/Applications")
.execute();
});
};
dmg.unpackHandler = (cmd, destinationDir) -> {
Path[] unpackedFolder = new Path[1];
withExplodedDmg(cmd, dmgImage -> {
Executor.of("cp", "-r")
.addArgument(dmgImage)
.addArgument(destinationDir)
.execute();
unpackedFolder[0] = destinationDir.resolve(dmgImage.getFileName());
});
return unpackedFolder[0];
};
dmg.uninstallHandler = cmd -> {
cmd.verifyIsOfType(PackageType.MAC_DMG);
Executor.of("sudo", "rm", "-rf")
.addArgument(cmd.appInstallationDirectory())
.execute();
};
return dmg;
}
static PackageHandlers createPkgPackageHandlers() {
PackageHandlers pkg = new PackageHandlers();
pkg.installHandler = cmd -> {
cmd.verifyIsOfType(PackageType.MAC_PKG);
Executor.of("sudo", "/usr/sbin/installer", "-allowUntrusted", "-pkg")
.addArgument(cmd.outputBundle())
.addArguments("-target", "/")
.execute();
};
pkg.uninstallHandler = cmd -> {
cmd.verifyIsOfType(PackageType.MAC_PKG);
Executor.of("sudo", "rm", "-rf")
.addArgument(cmd.appInstallationDirectory())
.execute();
};
return pkg;
} }
static String getBundleName(JPackageCommand cmd) { static String getBundleName(JPackageCommand cmd) {

View File

@ -28,14 +28,13 @@ import java.io.File;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.function.BiConsumer; import java.util.function.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import jdk.jpackage.test.Functional.ThrowingConsumer; import jdk.jpackage.test.Functional.ThrowingConsumer;
import jdk.incubator.jpackage.internal.AppImageFile; import jdk.incubator.jpackage.internal.AppImageFile;
import jdk.jpackage.test.Functional.ThrowingBiConsumer;
import jdk.jpackage.test.Functional.ThrowingRunnable;
import static jdk.jpackage.test.PackageType.*; import static jdk.jpackage.test.PackageType.*;
/** /**
@ -45,25 +44,16 @@ import static jdk.jpackage.test.PackageType.*;
* Provides methods to hook up custom configuration of jpackage command and * Provides methods to hook up custom configuration of jpackage command and
* verification of the output bundle. * verification of the output bundle.
*/ */
public final class PackageTest { public final class PackageTest extends RunnablePackageTest {
/**
* Default test configuration for jpackage command. Default jpackage command
* initialization includes:
* <li>Set --input and --dest parameters.
* <li>Set --name parameter. Value of the parameter is the name of the first
* class with main function found in the callers stack. Defaults can be
* overridden with custom initializers set with subsequent addInitializer()
* function calls.
*/
public PackageTest() { public PackageTest() {
action = DEFAULT_ACTION;
excludeTypes = new HashSet<>(); excludeTypes = new HashSet<>();
forTypes(); forTypes();
setExpectedExitCode(0); setExpectedExitCode(0);
handlers = new HashMap<>();
namedInitializers = new HashSet<>(); namedInitializers = new HashSet<>();
currentTypes.forEach(v -> handlers.put(v, new Handler(v))); handlers = currentTypes.stream()
.collect(Collectors.toMap(v -> v, v -> new Handler()));
packageHandlers = createDefaultPackageHandlers();
} }
public PackageTest excludeTypes(PackageType... types) { public PackageTest excludeTypes(PackageType... types) {
@ -117,19 +107,37 @@ public final class PackageTest {
namedInitializers.add(id); namedInitializers.add(id);
} }
currentTypes.stream().forEach(type -> handlers.get(type).addInitializer( currentTypes.forEach(type -> handlers.get(type).addInitializer(
ThrowingConsumer.toConsumer(v))); ThrowingConsumer.toConsumer(v)));
return this; return this;
} }
private PackageTest addRunOnceInitializer(ThrowingRunnable v, String id) {
return addInitializer(new ThrowingConsumer<JPackageCommand>() {
@Override
public void accept(JPackageCommand unused) throws Throwable {
if (!executed) {
executed = true;
v.run();
}
}
private boolean executed;
}, id);
}
public PackageTest addInitializer(ThrowingConsumer<JPackageCommand> v) { public PackageTest addInitializer(ThrowingConsumer<JPackageCommand> v) {
return addInitializer(v, null); return addInitializer(v, null);
} }
public PackageTest addRunOnceInitializer(ThrowingRunnable v) {
return addRunOnceInitializer(v, null);
}
public PackageTest addBundleVerifier( public PackageTest addBundleVerifier(
BiConsumer<JPackageCommand, Executor.Result> v) { ThrowingBiConsumer<JPackageCommand, Executor.Result> v) {
currentTypes.stream().forEach( currentTypes.forEach(type -> handlers.get(type).addBundleVerifier(
type -> handlers.get(type).addBundleVerifier(v)); ThrowingBiConsumer.toBiConsumer(v)));
return this; return this;
} }
@ -139,19 +147,26 @@ public final class PackageTest {
} }
public PackageTest addBundlePropertyVerifier(String propertyName, public PackageTest addBundlePropertyVerifier(String propertyName,
BiConsumer<String, String> pred) { Predicate<String> pred, String predLabel) {
return addBundleVerifier(cmd -> { return addBundleVerifier(cmd -> {
pred.accept(propertyName, final String value;
LinuxHelper.getBundleProperty(cmd, propertyName)); if (TKit.isLinux()) {
value = LinuxHelper.getBundleProperty(cmd, propertyName);
} else if (TKit.isWindows()) {
value = WindowsHelper.getMsiProperty(cmd, propertyName);
} else {
throw new IllegalStateException();
}
TKit.assertTrue(pred.test(value), String.format(
"Check value of %s property %s [%s]", propertyName,
predLabel, value));
}); });
} }
public PackageTest addBundlePropertyVerifier(String propertyName, public PackageTest addBundlePropertyVerifier(String propertyName,
String expectedPropertyValue) { String expectedPropertyValue) {
return addBundlePropertyVerifier(propertyName, (unused, v) -> { return addBundlePropertyVerifier(propertyName,
TKit.assertEquals(expectedPropertyValue, v, String.format( expectedPropertyValue::equals, "is");
"Check value of %s property is [%s]", propertyName, v));
});
} }
public PackageTest addBundleDesktopIntegrationVerifier(boolean integrated) { public PackageTest addBundleDesktopIntegrationVerifier(boolean integrated) {
@ -162,24 +177,39 @@ public final class PackageTest {
} }
public PackageTest addInstallVerifier(ThrowingConsumer<JPackageCommand> v) { public PackageTest addInstallVerifier(ThrowingConsumer<JPackageCommand> v) {
currentTypes.stream().forEach( currentTypes.forEach(type -> handlers.get(type).addInstallVerifier(
type -> handlers.get(type).addInstallVerifier( ThrowingConsumer.toConsumer(v)));
ThrowingConsumer.toConsumer(v)));
return this; return this;
} }
public PackageTest addUninstallVerifier(ThrowingConsumer<JPackageCommand> v) { public PackageTest addUninstallVerifier(ThrowingConsumer<JPackageCommand> v) {
currentTypes.stream().forEach( currentTypes.forEach(type -> handlers.get(type).addUninstallVerifier(
type -> handlers.get(type).addUninstallVerifier( ThrowingConsumer.toConsumer(v)));
ThrowingConsumer.toConsumer(v))); return this;
}
public PackageTest setPackageInstaller(Consumer<JPackageCommand> v) {
currentTypes.forEach(
type -> packageHandlers.get(type).installHandler = v);
return this;
}
public PackageTest setPackageUnpacker(
BiFunction<JPackageCommand, Path, Path> v) {
currentTypes.forEach(type -> packageHandlers.get(type).unpackHandler = v);
return this;
}
public PackageTest setPackageUninstaller(Consumer<JPackageCommand> v) {
currentTypes.forEach(
type -> packageHandlers.get(type).uninstallHandler = v);
return this; return this;
} }
static void withTestFileAssociationsFile(FileAssociations fa, static void withTestFileAssociationsFile(FileAssociations fa,
ThrowingConsumer<Path> consumer) { ThrowingConsumer<Path> consumer) {
final String testFileDefaultName = String.join(".", "test", final Path testFileDefaultName = Path.of("test" + fa.getSuffix());
fa.getSuffix()); TKit.withTempFile(testFileDefaultName, testFile -> {
TKit.withTempFile(testFileDefaultName, fa.getSuffix(), testFile -> {
if (TKit.isLinux()) { if (TKit.isLinux()) {
LinuxHelper.initFileAssociationsTestFile(testFile); LinuxHelper.initFileAssociationsTestFile(testFile);
} }
@ -192,7 +222,7 @@ public final class PackageTest {
// Setup test app to have valid jpackage command line before // Setup test app to have valid jpackage command line before
// running check of type of environment. // running check of type of environment.
addInitializer(cmd -> new HelloApp(null).addTo(cmd), "HelloApp"); addHelloAppInitializer(null);
String noActionMsg = "Not running file associations test"; String noActionMsg = "Not running file associations test";
if (GraphicsEnvironment.isHeadless()) { if (GraphicsEnvironment.isHeadless()) {
@ -202,7 +232,7 @@ public final class PackageTest {
} }
addInstallVerifier(cmd -> { addInstallVerifier(cmd -> {
if (cmd.isFakeRuntime(noActionMsg)) { if (cmd.isFakeRuntime(noActionMsg) || cmd.isPackageUnpacked(noActionMsg)) {
return; return;
} }
@ -225,7 +255,8 @@ public final class PackageTest {
// Wait a little bit after file has been created to // Wait a little bit after file has been created to
// make sure there are no pending writes into it. // make sure there are no pending writes into it.
Thread.sleep(3000); Thread.sleep(3000);
HelloApp.verifyOutputFile(appOutput, expectedArgs); HelloApp.verifyOutputFile(appOutput, expectedArgs,
Collections.emptyMap());
}); });
}); });
@ -236,7 +267,7 @@ public final class PackageTest {
return this; return this;
} }
PackageTest forTypes(Collection<PackageType> types, Runnable action) { public PackageTest forTypes(Collection<PackageType> types, Runnable action) {
Set<PackageType> oldTypes = Set.of(currentTypes.toArray( Set<PackageType> oldTypes = Set.of(currentTypes.toArray(
PackageType[]::new)); PackageType[]::new));
try { try {
@ -248,17 +279,17 @@ public final class PackageTest {
return this; return this;
} }
PackageTest forTypes(PackageType type, Runnable action) { public PackageTest forTypes(PackageType type, Runnable action) {
return forTypes(List.of(type), action); return forTypes(List.of(type), action);
} }
PackageTest notForTypes(Collection<PackageType> types, Runnable action) { public PackageTest notForTypes(Collection<PackageType> types, Runnable action) {
Set<PackageType> workset = new HashSet<>(currentTypes); Set<PackageType> workset = new HashSet<>(currentTypes);
workset.removeAll(types); workset.removeAll(types);
return forTypes(workset, action); return forTypes(workset, action);
} }
PackageTest notForTypes(PackageType type, Runnable action) { public PackageTest notForTypes(PackageType type, Runnable action) {
return notForTypes(List.of(type), action); return notForTypes(List.of(type), action);
} }
@ -266,55 +297,167 @@ public final class PackageTest {
return configureHelloApp(null); return configureHelloApp(null);
} }
public PackageTest configureHelloApp(String encodedName) { public PackageTest configureHelloApp(String javaAppDesc) {
addInitializer( addHelloAppInitializer(javaAppDesc);
cmd -> new HelloApp(JavaAppDesc.parse(encodedName)).addTo(cmd));
addInstallVerifier(HelloApp::executeLauncherAndVerifyOutput); addInstallVerifier(HelloApp::executeLauncherAndVerifyOutput);
return this; return this;
} }
public void run() { public final static class Group extends RunnablePackageTest {
List<Handler> supportedHandlers = handlers.values().stream() public Group(PackageTest... tests) {
.filter(entry -> !entry.isVoid()) handlers = Stream.of(tests)
.collect(Collectors.toList()); .map(PackageTest::createPackageTypeHandlers)
.flatMap(List<Consumer<Action>>::stream)
if (supportedHandlers.isEmpty()) { .collect(Collectors.toUnmodifiableList());
// No handlers with initializers found. Nothing to do.
return;
} }
Supplier<JPackageCommand> initializer = new Supplier<>() { @Override
@Override protected void runAction(Action... action) {
public JPackageCommand get() { if (Set.of(action).contains(Action.UNINSTALL)) {
JPackageCommand cmd = new JPackageCommand().setDefaultInputOutput(); ListIterator<Consumer<Action>> listIterator = handlers.listIterator(
if (bundleOutputDir != null) { handlers.size());
cmd.setArgumentValue("--dest", bundleOutputDir.toString()); while (listIterator.hasPrevious()) {
var handler = listIterator.previous();
List.of(action).forEach(handler::accept);
} }
cmd.setDefaultAppName(); } else {
return cmd; handlers.forEach(handler -> List.of(action).forEach(handler::accept));
} }
}; }
supportedHandlers.forEach(handler -> handler.accept(initializer.get())); private final List<Consumer<Action>> handlers;
} }
public PackageTest setAction(Action value) { final static class PackageHandlers {
action = value; Consumer<JPackageCommand> installHandler;
return this; Consumer<JPackageCommand> uninstallHandler;
BiFunction<JPackageCommand, Path, Path> unpackHandler;
} }
public Action getAction() { @Override
return action; protected void runActions(List<Action[]> actions) {
createPackageTypeHandlers().forEach(
handler -> actions.forEach(
action -> List.of(action).forEach(handler::accept)));
} }
private class Handler implements Consumer<JPackageCommand> { @Override
protected void runAction(Action... action) {
throw new UnsupportedOperationException();
}
Handler(PackageType type) { private void addHelloAppInitializer(String javaAppDesc) {
if (!PackageType.NATIVE.contains(type)) { addInitializer(
throw new IllegalArgumentException( cmd -> new HelloApp(JavaAppDesc.parse(javaAppDesc)).addTo(cmd),
"Attempt to configure a test for image packaging"); "HelloApp");
}
private List<Consumer<Action>> createPackageTypeHandlers() {
return PackageType.NATIVE.stream()
.map(type -> {
Handler handler = handlers.entrySet().stream()
.filter(entry -> !entry.getValue().isVoid())
.filter(entry -> entry.getKey() == type)
.map(entry -> entry.getValue())
.findAny().orElse(null);
Map.Entry<PackageType, Handler> result = null;
if (handler != null) {
result = Map.entry(type, handler);
}
return result;
})
.filter(Objects::nonNull)
.map(entry -> createPackageTypeHandler(
entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
}
private Consumer<Action> createPackageTypeHandler(
PackageType type, Handler handler) {
return ThrowingConsumer.toConsumer(new ThrowingConsumer<Action>() {
@Override
public void accept(Action action) throws Throwable {
if (action == Action.FINALIZE) {
if (unpackDir != null && Files.isDirectory(unpackDir)
&& !unpackDir.startsWith(TKit.workDir())) {
TKit.deleteDirectoryRecursive(unpackDir);
}
}
if (aborted) {
return;
}
final JPackageCommand curCmd;
if (Set.of(Action.INITIALIZE, Action.CREATE).contains(action)) {
curCmd = cmd;
} else {
curCmd = cmd.createImmutableCopy();
}
switch (action) {
case UNPACK: {
var handler = packageHandlers.get(type).unpackHandler;
if (!(aborted = (handler == null))) {
unpackDir = TKit.createTempDirectory(
String.format("unpacked-%s",
type.getName()));
unpackDir = handler.apply(cmd, unpackDir);
cmd.setUnpackedPackageLocation(unpackDir);
}
break;
}
case INSTALL: {
var handler = packageHandlers.get(type).installHandler;
if (!(aborted = (handler == null))) {
handler.accept(curCmd);
}
break;
}
case UNINSTALL: {
var handler = packageHandlers.get(type).uninstallHandler;
if (!(aborted = (handler == null))) {
handler.accept(curCmd);
}
break;
}
case CREATE:
handler.accept(action, curCmd);
aborted = (expectedJPackageExitCode != 0);
return;
default:
handler.accept(action, curCmd);
break;
}
if (aborted) {
TKit.trace(
String.format("Aborted [%s] action of %s command",
action, cmd.getPrintableCommandLine()));
}
} }
this.type = type;
private Path unpackDir;
private boolean aborted;
private final JPackageCommand cmd = Functional.identity(() -> {
JPackageCommand result = new JPackageCommand();
result.setDefaultInputOutput().setDefaultAppName();
if (BUNDLE_OUTPUT_DIR != null) {
result.setArgumentValue("--dest", BUNDLE_OUTPUT_DIR.toString());
}
type.applyTo(result);
return result;
}).get();
});
}
private class Handler implements BiConsumer<Action, JPackageCommand> {
Handler() {
initializers = new ArrayList<>(); initializers = new ArrayList<>();
bundleVerifiers = new ArrayList<>(); bundleVerifiers = new ArrayList<>();
installVerifiers = new ArrayList<>(); installVerifiers = new ArrayList<>();
@ -342,33 +485,35 @@ public final class PackageTest {
} }
@Override @Override
public void accept(JPackageCommand cmd) { public void accept(Action action, JPackageCommand cmd) {
type.applyTo(cmd);
initializers.stream().forEach(v -> v.accept(cmd));
cmd.executePrerequisiteActions();
switch (action) { switch (action) {
case INITIALIZE:
initializers.forEach(v -> v.accept(cmd));
if (cmd.isImagePackageType()) {
throw new UnsupportedOperationException();
}
cmd.executePrerequisiteActions();
break;
case CREATE: case CREATE:
Executor.Result result = cmd.execute(); Executor.Result result = cmd.execute(expectedJPackageExitCode);
result.assertExitCodeIs(expectedJPackageExitCode);
if (expectedJPackageExitCode == 0) { if (expectedJPackageExitCode == 0) {
TKit.assertFileExists(cmd.outputBundle()); TKit.assertFileExists(cmd.outputBundle());
} else { } else {
TKit.assertPathExists(cmd.outputBundle(), false); TKit.assertPathExists(cmd.outputBundle(), false);
} }
verifyPackageBundle(cmd.createImmutableCopy(), result); verifyPackageBundle(cmd, result);
break; break;
case VERIFY_INSTALL: case VERIFY_INSTALL:
if (expectedJPackageExitCode == 0) { if (expectedJPackageExitCode == 0) {
verifyPackageInstalled(cmd.createImmutableCopy()); verifyPackageInstalled(cmd);
} }
break; break;
case VERIFY_UNINSTALL: case VERIFY_UNINSTALL:
if (expectedJPackageExitCode == 0) { if (expectedJPackageExitCode == 0) {
verifyPackageUninstalled(cmd.createImmutableCopy()); verifyPackageUninstalled(cmd);
} }
break; break;
} }
@ -381,25 +526,33 @@ public final class PackageTest {
LinuxHelper.verifyPackageBundleEssential(cmd); LinuxHelper.verifyPackageBundleEssential(cmd);
} }
} }
bundleVerifiers.stream().forEach(v -> v.accept(cmd, result)); bundleVerifiers.forEach(v -> v.accept(cmd, result));
} }
private void verifyPackageInstalled(JPackageCommand cmd) { private void verifyPackageInstalled(JPackageCommand cmd) {
TKit.trace(String.format("Verify installed: %s", final String formatString;
cmd.getPrintableCommandLine())); if (cmd.isPackageUnpacked()) {
formatString = "Verify unpacked: %s";
} else {
formatString = "Verify installed: %s";
}
TKit.trace(String.format(formatString, cmd.getPrintableCommandLine()));
TKit.assertDirectoryExists(cmd.appRuntimeDirectory()); TKit.assertDirectoryExists(cmd.appRuntimeDirectory());
if (!cmd.isRuntime()) { if (!cmd.isRuntime()) {
TKit.assertExecutableFileExists(cmd.appLauncherPath()); TKit.assertExecutableFileExists(cmd.appLauncherPath());
if (PackageType.WINDOWS.contains(cmd.packageType())) { if (PackageType.WINDOWS.contains(cmd.packageType())
new WindowsHelper.AppVerifier(cmd); && !cmd.isPackageUnpacked(
"Not verifying desktop integration")) {
new WindowsHelper.DesktopIntegrationVerifier(cmd);
} }
} }
TKit.assertPathExists(AppImageFile.getPathInAppImage( TKit.assertPathExists(AppImageFile.getPathInAppImage(
cmd.appInstallationDirectory()), false); cmd.appInstallationDirectory()), false);
installVerifiers.stream().forEach(v -> v.accept(cmd)); installVerifiers.forEach(v -> v.accept(cmd));
} }
private void verifyPackageUninstalled(JPackageCommand cmd) { private void verifyPackageUninstalled(JPackageCommand cmd) {
@ -409,79 +562,63 @@ public final class PackageTest {
TKit.assertPathExists(cmd.appLauncherPath(), false); TKit.assertPathExists(cmd.appLauncherPath(), false);
if (PackageType.WINDOWS.contains(cmd.packageType())) { if (PackageType.WINDOWS.contains(cmd.packageType())) {
new WindowsHelper.AppVerifier(cmd); new WindowsHelper.DesktopIntegrationVerifier(cmd);
} }
} }
TKit.assertPathExists(cmd.appInstallationDirectory(), false); TKit.assertPathExists(cmd.appInstallationDirectory(), false);
uninstallVerifiers.stream().forEach(v -> v.accept(cmd)); uninstallVerifiers.forEach(v -> v.accept(cmd));
} }
private final PackageType type;
private final List<Consumer<JPackageCommand>> initializers; private final List<Consumer<JPackageCommand>> initializers;
private final List<BiConsumer<JPackageCommand, Executor.Result>> bundleVerifiers; private final List<BiConsumer<JPackageCommand, Executor.Result>> bundleVerifiers;
private final List<Consumer<JPackageCommand>> installVerifiers; private final List<Consumer<JPackageCommand>> installVerifiers;
private final List<Consumer<JPackageCommand>> uninstallVerifiers; private final List<Consumer<JPackageCommand>> uninstallVerifiers;
} }
private static Map<PackageType, PackageHandlers> createDefaultPackageHandlers() {
HashMap<PackageType, PackageHandlers> handlers = new HashMap<>();
if (TKit.isLinux()) {
handlers.put(PackageType.LINUX_DEB, LinuxHelper.createDebPackageHandlers());
handlers.put(PackageType.LINUX_RPM, LinuxHelper.createRpmPackageHandlers());
}
if (TKit.isWindows()) {
handlers.put(PackageType.WIN_MSI, WindowsHelper.createMsiPackageHandlers());
handlers.put(PackageType.WIN_EXE, WindowsHelper.createExePackageHandlers());
}
if (TKit.isOSX()) {
handlers.put(PackageType.MAC_DMG, MacHelper.createDmgPackageHandlers());
handlers.put(PackageType.MAC_PKG, MacHelper.createPkgPackageHandlers());
}
return handlers;
}
private Collection<PackageType> currentTypes; private Collection<PackageType> currentTypes;
private Set<PackageType> excludeTypes; private Set<PackageType> excludeTypes;
private int expectedJPackageExitCode; private int expectedJPackageExitCode;
private Map<PackageType, Handler> handlers; private Map<PackageType, Handler> handlers;
private Set<String> namedInitializers; private Set<String> namedInitializers;
private Action action; private Map<PackageType, PackageHandlers> packageHandlers;
/** private final static File BUNDLE_OUTPUT_DIR;
* Test action.
*/
static public enum Action {
/**
* Create bundle.
*/
CREATE,
/**
* Verify bundle installed.
*/
VERIFY_INSTALL,
/**
* Verify bundle uninstalled.
*/
VERIFY_UNINSTALL;
@Override
public String toString() {
return name().toLowerCase().replace('_', '-');
}
};
private final static Action DEFAULT_ACTION;
private final static File bundleOutputDir;
static { static {
final String propertyName = "output"; final String propertyName = "output";
String val = TKit.getConfigProperty(propertyName); String val = TKit.getConfigProperty(propertyName);
if (val == null) { if (val == null) {
bundleOutputDir = null; BUNDLE_OUTPUT_DIR = null;
} else { } else {
bundleOutputDir = new File(val).getAbsoluteFile(); BUNDLE_OUTPUT_DIR = new File(val).getAbsoluteFile();
if (!bundleOutputDir.isDirectory()) { if (!BUNDLE_OUTPUT_DIR.isDirectory()) {
throw new IllegalArgumentException(String.format( throw new IllegalArgumentException(String.format("Invalid value of %s sytem property: [%s]. Should be existing directory",
"Invalid value of %s sytem property: [%s]. Should be existing directory",
TKit.getConfigPropertyName(propertyName), TKit.getConfigPropertyName(propertyName),
bundleOutputDir)); BUNDLE_OUTPUT_DIR));
} }
} }
} }
static {
final String propertyName = "action";
String action = Optional.ofNullable(TKit.getConfigProperty(propertyName)).orElse(
Action.CREATE.toString()).toLowerCase();
DEFAULT_ACTION = Stream.of(Action.values()).filter(
a -> a.toString().equals(action)).findFirst().orElseThrow(
() -> new IllegalArgumentException(String.format(
"Unrecognized value of %s property: [%s]",
TKit.getConfigPropertyName(propertyName), action)));
}
} }

View File

@ -65,7 +65,7 @@ public enum PackageType {
} }
void applyTo(JPackageCommand cmd) { void applyTo(JPackageCommand cmd) {
cmd.addArguments("--type", getName()); cmd.setArgumentValue("--type", getName());
} }
String getSuffix() { String getSuffix() {

View File

@ -52,7 +52,7 @@ final public class TKit {
for (int i = 0; i != 10; ++i) { for (int i = 0; i != 10; ++i) {
if (root.resolve("apps").toFile().isDirectory()) { if (root.resolve("apps").toFile().isDirectory()) {
return root.toAbsolutePath(); return root.normalize().toAbsolutePath();
} }
root = root.resolve(".."); root = root.resolve("..");
} }
@ -60,6 +60,10 @@ final public class TKit {
throw new RuntimeException("Failed to locate apps directory"); throw new RuntimeException("Failed to locate apps directory");
}).get(); }).get();
public static final Path SRC_ROOT = Functional.identity(() -> {
return TEST_SRC_ROOT.resolve("../../../../src/jdk.incubator.jpackage").normalize().toAbsolutePath();
}).get();
public final static String ICON_SUFFIX = Functional.identity(() -> { public final static String ICON_SUFFIX = Functional.identity(() -> {
if (isOSX()) { if (isOSX()) {
return ".icns"; return ".icns";
@ -150,14 +154,6 @@ final public class TKit {
return currentTest.workDir(); return currentTest.workDir();
} }
static Path defaultInputDir() {
return workDir().resolve("input");
}
static Path defaultOutputDir() {
return workDir().resolve("output");
}
static String getCurrentDefaultAppName() { static String getCurrentDefaultAppName() {
// Construct app name from swapping and joining test base name // Construct app name from swapping and joining test base name
// and test function name. // and test function name.
@ -275,18 +271,16 @@ final public class TKit {
return Files.createDirectory(createUniqueFileName(role)); return Files.createDirectory(createUniqueFileName(role));
} }
public static Path createTempFile(String role, String suffix) throws public static Path createTempFile(Path templateFile) throws
IOException { IOException {
if (role == null) { return Files.createFile(createUniqueFileName(
return Files.createTempFile(workDir(), TEMP_FILE_PREFIX, suffix); templateFile.getFileName().toString()));
}
return Files.createFile(createUniqueFileName(role));
} }
public static Path withTempFile(String role, String suffix, public static Path withTempFile(Path templateFile,
ThrowingConsumer<Path> action) { ThrowingConsumer<Path> action) {
final Path tempFile = ThrowingSupplier.toSupplier(() -> createTempFile( final Path tempFile = ThrowingSupplier.toSupplier(() -> createTempFile(
role, suffix)).get(); templateFile)).get();
boolean keepIt = true; boolean keepIt = true;
try { try {
ThrowingConsumer.toConsumer(action).accept(tempFile); ThrowingConsumer.toConsumer(action).accept(tempFile);
@ -449,10 +443,11 @@ final public class TKit {
} }
public static Path createRelativePathCopy(final Path file) { public static Path createRelativePathCopy(final Path file) {
Path fileCopy = workDir().resolve(file.getFileName()).toAbsolutePath().normalize(); Path fileCopy = ThrowingSupplier.toSupplier(() -> {
Path localPath = createTempFile(file);
ThrowingRunnable.toRunnable(() -> Files.copy(file, fileCopy, Files.copy(file, localPath, StandardCopyOption.REPLACE_EXISTING);
StandardCopyOption.REPLACE_EXISTING)).run(); return localPath;
}).get().toAbsolutePath().normalize();
final Path basePath = Path.of(".").toAbsolutePath().normalize(); final Path basePath = Path.of(".").toAbsolutePath().normalize();
try { try {
@ -713,32 +708,32 @@ final public class TKit {
} }
} }
public final static class TextStreamAsserter { public final static class TextStreamVerifier {
TextStreamAsserter(String value) { TextStreamVerifier(String value) {
this.value = value; this.value = value;
predicate(String::contains); predicate(String::contains);
} }
public TextStreamAsserter label(String v) { public TextStreamVerifier label(String v) {
label = v; label = v;
return this; return this;
} }
public TextStreamAsserter predicate(BiPredicate<String, String> v) { public TextStreamVerifier predicate(BiPredicate<String, String> v) {
predicate = v; predicate = v;
return this; return this;
} }
public TextStreamAsserter negate() { public TextStreamVerifier negate() {
negate = true; negate = true;
return this; return this;
} }
public TextStreamAsserter orElseThrow(RuntimeException v) { public TextStreamVerifier orElseThrow(RuntimeException v) {
return orElseThrow(() -> v); return orElseThrow(() -> v);
} }
public TextStreamAsserter orElseThrow(Supplier<RuntimeException> v) { public TextStreamVerifier orElseThrow(Supplier<RuntimeException> v) {
createException = v; createException = v;
return this; return this;
} }
@ -779,8 +774,8 @@ final public class TKit {
final private String value; final private String value;
} }
public static TextStreamAsserter assertTextStream(String what) { public static TextStreamVerifier assertTextStream(String what) {
return new TextStreamAsserter(what); return new TextStreamVerifier(what);
} }
private static PrintStream openLogStream() { private static PrintStream openLogStream() {
@ -809,13 +804,23 @@ final public class TKit {
return "jpackage.test." + propertyName; return "jpackage.test." + propertyName;
} }
static Set<String> tokenizeConfigProperty(String propertyName) { static List<String> tokenizeConfigPropertyAsList(String propertyName) {
final String val = TKit.getConfigProperty(propertyName); final String val = TKit.getConfigProperty(propertyName);
if (val == null) { if (val == null) {
return null; return null;
} }
return Stream.of(val.toLowerCase().split(",")).map(String::strip).filter( return Stream.of(val.toLowerCase().split(","))
Predicate.not(String::isEmpty)).collect(Collectors.toSet()); .map(String::strip)
.filter(Predicate.not(String::isEmpty))
.collect(Collectors.toList());
}
static Set<String> tokenizeConfigProperty(String propertyName) {
List<String> tokens = tokenizeConfigPropertyAsList(propertyName);
if (tokens == null) {
return null;
}
return tokens.stream().collect(Collectors.toSet());
} }
static final Path LOG_FILE = Functional.identity(() -> { static final Path LOG_FILE = Functional.identity(() -> {

View File

@ -68,11 +68,11 @@ final class TestBuilder implements AutoCloseable {
CMDLINE_ARG_PREFIX + "exclude", CMDLINE_ARG_PREFIX + "exclude",
arg -> (excludedTests = Optional.ofNullable( arg -> (excludedTests = Optional.ofNullable(
excludedTests).orElse(new HashSet<String>())).add(arg), excludedTests).orElseGet(() -> new HashSet<String>())).add(arg),
CMDLINE_ARG_PREFIX + "include", CMDLINE_ARG_PREFIX + "include",
arg -> (includedTests = Optional.ofNullable( arg -> (includedTests = Optional.ofNullable(
includedTests).orElse(new HashSet<String>())).add(arg), includedTests).orElseGet(() -> new HashSet<String>())).add(arg),
CMDLINE_ARG_PREFIX + "space-subst", CMDLINE_ARG_PREFIX + "space-subst",
arg -> spaceSubstitute = arg, arg -> spaceSubstitute = arg,
@ -127,8 +127,7 @@ final class TestBuilder implements AutoCloseable {
// Log all matches before returning from the function // Log all matches before returning from the function
return tests.filter(test -> { return tests.filter(test -> {
String testDescription = test.createDescription().testFullName(); String testDescription = test.createDescription().testFullName();
boolean match = filters.stream().anyMatch( boolean match = filters.stream().anyMatch(testDescription::contains);
v -> testDescription.contains(v));
if (match) { if (match) {
trace(String.format(logMsg + ": %s", testDescription)); trace(String.format(logMsg + ": %s", testDescription));
} }
@ -159,7 +158,7 @@ final class TestBuilder implements AutoCloseable {
private void flushTestGroup() { private void flushTestGroup() {
if (testGroup != null) { if (testGroup != null) {
filterTestGroup().forEach(testBody -> createTestInstance(testBody)); filterTestGroup().forEach(this::createTestInstance);
clear(); clear();
} }
} }
@ -170,7 +169,7 @@ final class TestBuilder implements AutoCloseable {
Method testMethod = testBody.getMethod(); Method testMethod = testBody.getMethod();
if (Stream.of(BeforeEach.class, AfterEach.class).anyMatch( if (Stream.of(BeforeEach.class, AfterEach.class).anyMatch(
type -> testMethod.isAnnotationPresent(type))) { testMethod::isAnnotationPresent)) {
curBeforeActions = beforeActions; curBeforeActions = beforeActions;
curAfterActions = afterActions; curAfterActions = afterActions;
} else { } else {
@ -286,7 +285,7 @@ final class TestBuilder implements AutoCloseable {
List<Method> methods = Stream.of(methodClass.getMethods()).filter( List<Method> methods = Stream.of(methodClass.getMethods()).filter(
(m) -> filterMethod(methodName, m)).collect(Collectors.toList()); (m) -> filterMethod(methodName, m)).collect(Collectors.toList());
if (methods.isEmpty()) { if (methods.isEmpty()) {
new ParseException(String.format( throw new ParseException(String.format(
"Method [%s] not found in [%s] class;", "Method [%s] not found in [%s] class;",
methodName, className)); methodName, className));
} }

View File

@ -236,7 +236,13 @@ final class TestInstance implements ThrowingRunnable {
} }
if (!KEEP_WORK_DIR.contains(status)) { if (!KEEP_WORK_DIR.contains(status)) {
TKit.deleteDirectoryRecursive(workDir); if (Files.isSameFile(workDir, Path.of("."))) {
// 1. If the work directory is the current directory, don't
// delete it, just clean as deleting it would be confusing.
TKit.deleteDirectoryContentsRecursive(workDir);
} else {
TKit.deleteDirectoryRecursive(workDir);
}
} }
TKit.log(String.format("%s %s; checks=%d", status, fullName, TKit.log(String.format("%s %s; checks=%d", status, fullName,

View File

@ -26,8 +26,11 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import jdk.jpackage.test.Functional.ThrowingRunnable;
import jdk.jpackage.test.PackageTest.PackageHandlers;
public class WindowsHelper { public class WindowsHelper {
@ -38,22 +41,82 @@ public class WindowsHelper {
} }
static Path getInstallationDirectory(JPackageCommand cmd) { static Path getInstallationDirectory(JPackageCommand cmd) {
cmd.verifyIsOfType(PackageType.WINDOWS); Path installSubDir = getInstallationSubDirectory(cmd);
Path installDir = Path.of(
cmd.getArgumentValue("--install-dir", () -> cmd.name()));
if (isUserLocalInstall(cmd)) { if (isUserLocalInstall(cmd)) {
return USER_LOCAL.resolve(installDir); return USER_LOCAL.resolve(installSubDir);
} }
return PROGRAM_FILES.resolve(installDir); return PROGRAM_FILES.resolve(installSubDir);
}
static Path getInstallationSubDirectory(JPackageCommand cmd) {
cmd.verifyIsOfType(PackageType.WINDOWS);
return Path.of(cmd.getArgumentValue("--install-dir", () -> cmd.name()));
}
private static void runMsiexecWithRetries(Executor misexec) {
Executor.Result result = null;
for (int attempt = 0; attempt != 3; ++attempt) {
result = misexec.executeWithoutExitCodeCheck();
if (result.exitCode == 1618) {
// Another installation is already in progress.
// Wait a little and try again.
ThrowingRunnable.toRunnable(() -> Thread.sleep(3000)).run();
continue;
}
break;
}
result.assertExitCodeIsZero();
}
static PackageHandlers createMsiPackageHandlers() {
BiConsumer<JPackageCommand, Boolean> installMsi = (cmd, install) -> {
cmd.verifyIsOfType(PackageType.WIN_MSI);
runMsiexecWithRetries(Executor.of("msiexec", "/qn", "/norestart",
install ? "/i" : "/x").addArgument(cmd.outputBundle()));
};
PackageHandlers msi = new PackageHandlers();
msi.installHandler = cmd -> installMsi.accept(cmd, true);
msi.uninstallHandler = cmd -> installMsi.accept(cmd, false);
msi.unpackHandler = (cmd, destinationDir) -> {
cmd.verifyIsOfType(PackageType.WIN_MSI);
runMsiexecWithRetries(Executor.of("msiexec", "/a")
.addArgument(cmd.outputBundle().normalize())
.addArguments("/qn", String.format("TARGETDIR=%s",
destinationDir.toAbsolutePath().normalize())));
return destinationDir.resolve(getInstallationSubDirectory(cmd));
};
return msi;
}
static PackageHandlers createExePackageHandlers() {
PackageHandlers exe = new PackageHandlers();
exe.installHandler = cmd -> {
cmd.verifyIsOfType(PackageType.WIN_EXE);
new Executor().setExecutable(cmd.outputBundle()).execute();
};
return exe;
}
public static String getMsiProperty(JPackageCommand cmd, String propertyName) {
cmd.verifyIsOfType(PackageType.WIN_MSI);
return Executor.of("cscript.exe", "//Nologo")
.addArgument(TKit.TEST_SRC_ROOT.resolve("resources/query-msi-property.js"))
.addArgument(cmd.outputBundle())
.addArgument(propertyName)
.dumpOutput()
.executeAndGetOutput().stream().collect(Collectors.joining("\n"));
} }
private static boolean isUserLocalInstall(JPackageCommand cmd) { private static boolean isUserLocalInstall(JPackageCommand cmd) {
return cmd.hasArgument("--win-per-user-install"); return cmd.hasArgument("--win-per-user-install");
} }
static class AppVerifier { static class DesktopIntegrationVerifier {
AppVerifier(JPackageCommand cmd) { DesktopIntegrationVerifier(JPackageCommand cmd) {
cmd.verifyIsOfType(PackageType.WINDOWS); cmd.verifyIsOfType(PackageType.WINDOWS);
this.cmd = cmd; this.cmd = cmd;
verifyStartMenuShortcut(); verifyStartMenuShortcut();
@ -201,16 +264,15 @@ public class WindowsHelper {
} }
private static String queryRegistryValue(String keyPath, String valueName) { private static String queryRegistryValue(String keyPath, String valueName) {
Executor.Result status = new Executor() var status = Executor.of("reg", "query", keyPath, "/v", valueName)
.setExecutable("reg")
.saveOutput() .saveOutput()
.addArguments("query", keyPath, "/v", valueName) .executeWithoutExitCodeCheck();
.execute();
if (status.exitCode == 1) { if (status.exitCode == 1) {
// Should be the case of no such registry value or key // Should be the case of no such registry value or key
String lookupString = "ERROR: The system was unable to find the specified registry key or value."; String lookupString = "ERROR: The system was unable to find the specified registry key or value.";
status.getOutput().stream().filter(line -> line.equals(lookupString)).findFirst().orElseThrow( TKit.assertTextStream(lookupString)
() -> new RuntimeException(String.format( .predicate(String::equals)
.orElseThrow(() -> new RuntimeException(String.format(
"Failed to find [%s] string in the output", "Failed to find [%s] string in the output",
lookupString))); lookupString)));
TKit.trace(String.format( TKit.trace(String.format(

View File

@ -56,12 +56,10 @@ public class MaintainerTest {
.addInitializer(cmd -> { .addInitializer(cmd -> {
cmd.addArguments("--linux-deb-maintainer", MAINTAINER); cmd.addArguments("--linux-deb-maintainer", MAINTAINER);
}) })
.addBundlePropertyVerifier("Maintainer", (propName, propValue) -> { .addBundlePropertyVerifier("Maintainer", value -> {
String lookupValue = "<" + MAINTAINER + ">"; String lookupValue = "<" + MAINTAINER + ">";
TKit.assertTrue(propValue.endsWith(lookupValue), return value.endsWith(lookupValue);
String.format("Check value of %s property [%s] ends with %s", }, "ends with")
propName, propValue, lookupValue));
})
.run(); .run();
}); });
} }

View File

@ -25,6 +25,7 @@ import jdk.jpackage.test.TKit;
import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageTest;
import jdk.jpackage.test.PackageType; import jdk.jpackage.test.PackageType;
import jdk.jpackage.test.LinuxHelper; import jdk.jpackage.test.LinuxHelper;
import jdk.jpackage.test.Annotations.Test;
/** /**
@ -50,41 +51,38 @@ import jdk.jpackage.test.LinuxHelper;
* @build jdk.jpackage.test.* * @build jdk.jpackage.test.*
* @requires (os.family == "linux") * @requires (os.family == "linux")
* @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal
* @run main/othervm/timeout=360 -Xmx512m PackageDepsTest * @compile PackageDepsTest.java
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
* --jpt-run=PackageDepsTest
*/ */
public class PackageDepsTest { public class PackageDepsTest {
public static void main(String[] args) { @Test
// Pick the name of prerequisite package to be alphabetically public static void test() {
// preceeding the main package name.
// This is needed to make Bash script batch installing/uninstalling packages
// produced by jtreg tests install/uninstall packages in the right order.
final String PREREQ_PACKAGE_NAME = "apackagedepstestprereq"; final String PREREQ_PACKAGE_NAME = "apackagedepstestprereq";
TKit.run(args, () -> { PackageTest test1 = new PackageTest()
new PackageTest() .forTypes(PackageType.LINUX)
.forTypes(PackageType.LINUX) .configureHelloApp()
.configureHelloApp() .addInitializer(cmd -> {
.addInitializer(cmd -> { cmd.setArgumentValue("--name", PREREQ_PACKAGE_NAME);
cmd.setArgumentValue("--name", PREREQ_PACKAGE_NAME);
})
.run();
new PackageTest()
.forTypes(PackageType.LINUX)
.configureHelloApp()
.addInitializer(cmd -> {
cmd.addArguments("--linux-package-deps", PREREQ_PACKAGE_NAME);
})
.forTypes(PackageType.LINUX)
.addBundleVerifier(cmd -> {
TKit.assertTrue(
LinuxHelper.getPrerequisitePackages(cmd).contains(
PREREQ_PACKAGE_NAME), String.format(
"Check package depends on [%s] package",
PREREQ_PACKAGE_NAME));
})
.run();
}); });
PackageTest test2 = new PackageTest()
.forTypes(PackageType.LINUX)
.configureHelloApp()
.addInitializer(cmd -> {
cmd.addArguments("--linux-package-deps", PREREQ_PACKAGE_NAME);
})
.forTypes(PackageType.LINUX)
.addBundleVerifier(cmd -> {
TKit.assertTrue(
LinuxHelper.getPrerequisitePackages(cmd).contains(
PREREQ_PACKAGE_NAME), String.format(
"Check package depends on [%s] package",
PREREQ_PACKAGE_NAME));
});
new PackageTest.Group(test1, test2).run();
} }
} }

View File

@ -64,11 +64,9 @@ public class ReleaseTest {
.forTypes(PackageType.LINUX_RPM) .forTypes(PackageType.LINUX_RPM)
.addBundlePropertyVerifier("Release", RELEASE) .addBundlePropertyVerifier("Release", RELEASE)
.forTypes(PackageType.LINUX_DEB) .forTypes(PackageType.LINUX_DEB)
.addBundlePropertyVerifier("Version", (propName, propValue) -> { .addBundlePropertyVerifier("Version", propValue -> {
TKit.assertTrue(propValue.endsWith("-" + RELEASE), return propValue.endsWith("-" + RELEASE);
String.format("Check value of %s property [%s] ends with %s", }, "ends with")
propName, propValue, RELEASE));
})
.run(); .run();
}); });
} }

View File

@ -21,12 +21,12 @@
* questions. * questions.
*/ */
import java.nio.charset.StandardCharsets; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.Map; import java.util.Map;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import jdk.jpackage.test.AdditionalLauncher;
import jdk.jpackage.test.FileAssociations; import jdk.jpackage.test.FileAssociations;
import jdk.jpackage.test.PackageType; import jdk.jpackage.test.PackageType;
import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageTest;
@ -127,56 +127,50 @@ public class ShortcutHintTest {
*/ */
@Test @Test
public static void testAdditionaltLaunchers() { public static void testAdditionaltLaunchers() {
createTest().addInitializer(cmd -> { PackageTest test = createTest();
cmd.setFakeRuntime();
final String launcherName = "Foo"; new AdditionalLauncher("Foo").setIcon(TKit.TEST_SRC_ROOT.resolve(
final Path propsFile = TKit.workDir().resolve( "apps/dukeplug.png")).applyTo(test);
launcherName + ".properties");
cmd.addArguments("--add-launcher", String.format("%s=%s", test.addInitializer(JPackageCommand::setFakeRuntime).run();
launcherName, propsFile));
TKit.createPropertiesFile(propsFile, Map.entry("icon",
TKit.TEST_SRC_ROOT.resolve("apps/dukeplug.png").toString()));
}).run();
} }
/** /**
* .desktop file from resource dir. * .desktop file from resource dir.
*/ */
@Test @Test
public static void testDesktopFileFromResourceDir() { public static void testDesktopFileFromResourceDir() throws IOException {
final String expectedVersionString = "Version=12345678"; final String expectedVersionString = "Version=12345678";
TKit.withTempDirectory("resources", tempDir -> {
createTest().addInitializer(cmd -> {
cmd.setFakeRuntime();
cmd.addArgument("--linux-shortcut"); final Path tempDir = TKit.createTempDirectory("resources");
cmd.addArguments("--resource-dir", tempDir);
// Create custom .desktop file in resource directory createTest().addInitializer(cmd -> {
TKit.createTextFile(tempDir.resolve(cmd.name() + ".desktop"), cmd.setFakeRuntime();
List.of(
"[Desktop Entry]", cmd.addArgument("--linux-shortcut");
"Name=APPLICATION_NAME", cmd.addArguments("--resource-dir", tempDir);
"Exec=APPLICATION_LAUNCHER",
"Terminal=false", // Create custom .desktop file in resource directory
"Type=Application", TKit.createTextFile(tempDir.resolve(cmd.name() + ".desktop"),
"Categories=DEPLOY_BUNDLE_CATEGORY", List.of(
expectedVersionString "[Desktop Entry]",
)); "Name=APPLICATION_NAME",
}) "Exec=APPLICATION_LAUNCHER",
.addInstallVerifier(cmd -> { "Terminal=false",
Path desktopFile = cmd.appLayout().destktopIntegrationDirectory().resolve( "Type=Application",
String.format("%s-%s.desktop", "Categories=DEPLOY_BUNDLE_CATEGORY",
LinuxHelper.getPackageName(cmd), cmd.name())); expectedVersionString
TKit.assertFileExists(desktopFile); ));
TKit.assertTextStream(expectedVersionString) })
.label(String.format("[%s] file", desktopFile)) .addInstallVerifier(cmd -> {
.predicate(String::equals) Path desktopFile = cmd.appLayout().destktopIntegrationDirectory().resolve(
.apply(Files.readAllLines(desktopFile).stream()); String.format("%s-%s.desktop",
}).run(); LinuxHelper.getPackageName(cmd), cmd.name()));
}); TKit.assertFileExists(desktopFile);
TKit.assertTextStream(expectedVersionString)
.label(String.format("[%s] file", desktopFile))
.predicate(String::equals)
.apply(Files.readAllLines(desktopFile).stream());
}).run();
} }
} }

View File

@ -48,8 +48,7 @@ public class SigningBase {
.addArguments("--verify", "--deep", "--strict", "--verbose=2", .addArguments("--verify", "--deep", "--strict", "--verbose=2",
target.toString()) target.toString())
.saveOutput() .saveOutput()
.execute() .execute(exitCode).getOutput();
.assertExitCodeIs(exitCode).getOutput();
return result; return result;
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -41,7 +41,7 @@ find_all_packaging_tests ()
help_usage () help_usage ()
{ {
echo "Usage: `basename $0` [options] [test_names]" echo "Usage: `basename $0` [options] [--] [jtreg_options|test_names]"
echo "Options:" echo "Options:"
echo " -h - print this message" echo " -h - print this message"
echo " -v - verbose output" echo " -v - verbose output"
@ -58,16 +58,12 @@ help_usage ()
echo ' -l <logfile> - value for `jpackage.test.logfile` property.' echo ' -l <logfile> - value for `jpackage.test.logfile` property.'
echo " Optional, for jtreg tests debug purposes only." echo " Optional, for jtreg tests debug purposes only."
echo " -m <mode> - mode to run jtreg tests." echo " -m <mode> - mode to run jtreg tests."
echo ' Should be one of `create`, `update`, `verify-install` or `verify-uninstall`.' echo ' Should be one of `create`, `update` or `print-default-tests`.'
echo ' Optional, default mode is `update`.' echo ' Optional, default mode is `update`.'
echo ' - `create`' echo ' - `create`'
echo ' Remove all package bundles from the output directory before running jtreg tests.' echo ' Remove all package bundles from the output directory before running jtreg tests.'
echo ' - `update`' echo ' - `update`'
echo ' Run jtreg tests and overrite existing package bundles in the output directory.' echo ' Run jtreg tests and overrite existing package bundles in the output directory.'
echo ' - `verify-install`'
echo ' Verify installed packages created with the previous run of the script.'
echo ' - `verify-uninstall`'
echo ' Verify packages created with the previous run of the script were uninstalled cleanly.'
echo ' - `print-default-tests`' echo ' - `print-default-tests`'
echo ' Print default list of packaging tests and exit.' echo ' Print default list of packaging tests and exit.'
} }
@ -135,7 +131,10 @@ mode=update
# jtreg extra arguments # jtreg extra arguments
declare -a jtreg_args declare -a jtreg_args
# Run all tests # Create packages only
jtreg_args+=("-Djpackage.test.action=create")
# run all tests
run_all_tests= run_all_tests=
mapfile -t tests < <(find_all_packaging_tests) mapfile -t tests < <(find_all_packaging_tests)
@ -206,10 +205,6 @@ if [ "$mode" = create ]; then
true true
elif [ "$mode" = update ]; then elif [ "$mode" = update ]; then
true true
elif [ "$mode" = verify-install ]; then
jtreg_args+=("-Djpackage.test.action=$mode")
elif [ "$mode" = verify-uninstall ]; then
jtreg_args+=("-Djpackage.test.action=$mode")
else else
fatal_with_help_usage 'Invalid value of -m option:' [$mode] fatal_with_help_usage 'Invalid value of -m option:' [$mode]
fi fi
@ -218,7 +213,11 @@ if [ -z "$run_all_tests" ]; then
jtreg_args+=(-Djpackage.test.SQETest=yes) jtreg_args+=(-Djpackage.test.SQETest=yes)
fi fi
# All remaining command line arguments are tests to run that should override the defaults # Drop arguments separator
[ "$1" != "--" ] || shift
# All remaining command line arguments are tests to run
# that should override the defaults and explicit jtreg arguments
[ $# -eq 0 ] || tests=($@) [ $# -eq 0 ] || tests=($@)

View File

@ -27,12 +27,8 @@ import java.util.Map;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.*;
import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.Annotations.*;
import jdk.jpackage.test.PackageType;
import jdk.jpackage.test.FileAssociations;
import jdk.jpackage.test.Annotations.Test;
import jdk.jpackage.test.TKit;
/** /**
* Test --add-launcher parameter. Output of the test should be * Test --add-launcher parameter. Output of the test should be
@ -46,11 +42,25 @@ import jdk.jpackage.test.TKit;
* @test * @test
* @summary jpackage with --add-launcher * @summary jpackage with --add-launcher
* @key jpackagePlatformPackage * @key jpackagePlatformPackage
* @requires (jpackage.test.SQETest != null)
* @library ../helpers * @library ../helpers
* @build jdk.jpackage.test.* * @build jdk.jpackage.test.*
* @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal
* @compile AdditionalLaunchersTest.java * @compile AdditionalLaunchersTest.java
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
* --jpt-run=AdditionalLaunchersTest.test
*/
/*
* @test
* @summary jpackage with --add-launcher
* @key jpackagePlatformPackage
* @requires (jpackage.test.SQETest == null)
* @library ../helpers
* @build jdk.jpackage.test.*
* @modules jdk.jpackage/jdk.jpackage.internal
* @compile AdditionalLaunchersTest.java
* @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main
* --jpt-run=AdditionalLaunchersTest * --jpt-run=AdditionalLaunchersTest
*/ */
@ -72,87 +82,122 @@ public class AdditionalLaunchersTest {
MethodHandles.lookup().lookupClass().getSimpleName()).applyTo( MethodHandles.lookup().lookupClass().getSimpleName()).applyTo(
packageTest); packageTest);
new AdditionalLauncher("Baz2").setArguments().applyTo(packageTest); new AdditionalLauncher("Baz2")
new AdditionalLauncher("foo").setArguments("yep!").applyTo(packageTest); .setDefaultArguments()
.applyTo(packageTest);
AdditionalLauncher barLauncher = new AdditionalLauncher("Bar").setArguments( new AdditionalLauncher("foo")
"one", "two", "three"); .setDefaultArguments("yep!")
if (TKit.isLinux()) { .applyTo(packageTest);
barLauncher.setIcon(TKit.TEST_SRC_ROOT.resolve("apps/dukeplug.png"));
} new AdditionalLauncher("Bar")
barLauncher.applyTo(packageTest); .setDefaultArguments("one", "two", "three")
.setIcon(GOLDEN_ICON)
.applyTo(packageTest);
packageTest.run(); packageTest.run();
} }
private static Path replaceFileName(Path path, String newFileName) { @Test
String fname = path.getFileName().toString(); public void bug8230933() {
int lastDotIndex = fname.lastIndexOf("."); PackageTest packageTest = new PackageTest().configureHelloApp();
if (lastDotIndex != -1) {
fname = newFileName + fname.substring(lastDotIndex); new AdditionalLauncher("default_icon")
.applyTo(packageTest);
new AdditionalLauncher("no_icon")
.setNoIcon().applyTo(packageTest);
new AdditionalLauncher("custom_icon")
.setIcon(GOLDEN_ICON)
.applyTo(packageTest);
packageTest.run();
}
@Test
// Regular app
@Parameter("Hello")
// Modular app
@Parameter("com.other/com.other.CiaoBella")
public void testJavaOptions(String javaAppDesc) {
JPackageCommand cmd = JPackageCommand.helloAppImage(javaAppDesc)
.addArguments("--arguments", "courageous")
.addArguments("--java-options", "-Dparam1=xxx")
.addArguments("--java-options", "-Dparam2=yyy")
.addArguments("--java-options", "-Dparam3=zzz");
new AdditionalLauncher("Jack")
.addDefaultArguments("Jack of All Trades", "Master of None")
.setJavaOptions("-Dparam1=Contractor")
.applyTo(cmd);
new AdditionalLauncher("Monday")
.addDefaultArguments("Knock Your", "Socks Off")
.setJavaOptions("-Dparam2=Surprise workers!")
.applyTo(cmd);
// Should inherit default arguments and java options from the main launcher
new AdditionalLauncher("void").applyTo(cmd);
cmd.executeAndAssertHelloAppImageCreated();
}
/**
* Test usage of modular and non modular apps in additional launchers.
*/
@Test
@Parameter("true")
@Parameter("fase")
public void testMainLauncherIsModular(boolean mainLauncherIsModular) {
final var nonModularAppDesc = JavaAppDesc.parse("a.b.c.Hello");
final var modularAppDesc = JavaAppDesc.parse(
"module.jar:com.that/com.that.main.Florence");
final var nonModularJarCmd = JPackageCommand.helloAppImage(nonModularAppDesc);
final var modularJarCmd = JPackageCommand.helloAppImage(modularAppDesc);
final JPackageCommand cmd;
if (mainLauncherIsModular) {
// Create non modular jar.
nonModularJarCmd.executePrerequisiteActions();
cmd = modularJarCmd;
cmd.addArguments("--description",
"Test modular app with multiple add-launchers where one is modular app and other is non modular app");
cmd.addArguments("--input", nonModularJarCmd.getArgumentValue(
"--input"));
} else { } else {
fname = newFileName; // Create modular jar.
modularJarCmd.executePrerequisiteActions();
cmd = nonModularJarCmd;
cmd.addArguments("--description",
"Test non modular app with multiple add-launchers where one is modular app and other is non modular app");
cmd.addArguments("--module-path", modularJarCmd.getArgumentValue(
"--module-path"));
cmd.addArguments("--add-modules", modularAppDesc.moduleName());
} }
return path.getParent().resolve(fname);
new AdditionalLauncher("ModularAppLauncher")
.addRawProperties(Map.entry("module", JavaAppDesc.parse(
modularAppDesc.toString()).setJarFileName(null).toString()))
.addRawProperties(Map.entry("main-jar", ""))
.applyTo(cmd);
new AdditionalLauncher("NonModularAppLauncher")
// Use space ( ) character instead of equality sign (=) as
// a key/value separator
.setPersistenceHandler((path, properties) -> TKit.createTextFile(path,
properties.stream().map(entry -> String.join(" ", entry.getKey(),
entry.getValue()))))
.addRawProperties(Map.entry("main-class", nonModularAppDesc.className()))
.addRawProperties(Map.entry("main-jar", nonModularAppDesc.jarFileName()))
.applyTo(cmd);
cmd.executeAndAssertHelloAppImageCreated();
} }
static class AdditionalLauncher { private final static Path GOLDEN_ICON = TKit.TEST_SRC_ROOT.resolve(Path.of(
"resources", "icon" + TKit.ICON_SUFFIX));
AdditionalLauncher(String name) {
this.name = name;
}
AdditionalLauncher setArguments(String... args) {
arguments = List.of(args);
return this;
}
AdditionalLauncher setIcon(Path iconPath) {
icon = iconPath;
return this;
}
void applyTo(PackageTest test) {
final Path propsFile = TKit.workDir().resolve(name + ".properties");
test.addInitializer(cmd -> {
cmd.addArguments("--add-launcher", String.format("%s=%s", name,
propsFile));
Map<String, String> properties = new HashMap<>();
if (arguments != null) {
properties.put("arguments", String.join(" ",
arguments.toArray(String[]::new)));
}
if (icon != null) {
properties.put("icon", icon.toAbsolutePath().toString());
}
TKit.createPropertiesFile(propsFile, properties);
});
test.addInstallVerifier(cmd -> {
Path launcherPath = replaceFileName(cmd.appLauncherPath(), name);
TKit.assertExecutableFileExists(launcherPath);
if (cmd.isFakeRuntime(String.format(
"Not running %s launcher", launcherPath))) {
return;
}
HelloApp.executeAndVerifyOutput(launcherPath,
Optional.ofNullable(arguments).orElse(List.of()).toArray(
String[]::new));
});
test.addUninstallVerifier(cmd -> {
Path launcherPath = replaceFileName(cmd.appLauncherPath(), name);
TKit.assertPathExists(launcherPath, false);
});
}
private List<String> arguments;
private Path icon;
private final String name;
}
} }

View File

@ -23,9 +23,9 @@
import java.nio.file.Path; import java.nio.file.Path;
import jdk.jpackage.test.TKit; import jdk.jpackage.test.TKit;
import jdk.jpackage.test.PackageTest;
import jdk.jpackage.test.PackageType;
import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageCommand;
import jdk.jpackage.test.PackageTest;
import jdk.jpackage.test.Annotations.Test;
/** /**
* Test --app-image parameter. The output installer should provide the same * Test --app-image parameter. The output installer should provide the same
@ -41,34 +41,24 @@ import jdk.jpackage.test.JPackageCommand;
* @requires (jpackage.test.SQETest == null) * @requires (jpackage.test.SQETest == null)
* @build jdk.jpackage.test.* * @build jdk.jpackage.test.*
* @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal
* @run main/othervm/timeout=540 -Xmx512m AppImagePackageTest * @compile AppImagePackageTest.java
* @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main
* --jpt-run=AppImagePackageTest
*/ */
public class AppImagePackageTest { public class AppImagePackageTest {
public static void main(String[] args) { @Test
TKit.run(args, () -> { public static void test() {
Path appimageOutput = Path.of("appimage"); Path appimageOutput = TKit.workDir().resolve("appimage");
JPackageCommand appImageCmd = JPackageCommand.helloAppImage() JPackageCommand appImageCmd = JPackageCommand.helloAppImage()
.setArgumentValue("--dest", appimageOutput) .setArgumentValue("--dest", appimageOutput);
.addArguments("--type", "app-image");
PackageTest packageTest = new PackageTest(); new PackageTest()
if (packageTest.getAction() == PackageTest.Action.CREATE) { .addRunOnceInitializer(() -> appImageCmd.execute())
appImageCmd.execute(); .addInitializer(cmd -> {
} cmd.addArguments("--app-image", appImageCmd.outputBundle());
cmd.removeArgumentWithValue("--input");
packageTest.addInitializer(cmd -> { }).addBundleDesktopIntegrationVerifier(false).run();
Path appimageInput = appimageOutput.resolve(appImageCmd.name());
if (PackageType.MAC.contains(cmd.packageType())) {
// Why so complicated on macOS?
appimageInput = Path.of(appimageInput.toString() + ".app");
}
cmd.addArguments("--app-image", appimageInput);
cmd.removeArgumentWithValue("--input");
}).addBundleDesktopIntegrationVerifier(false).run();
});
} }
} }

View File

@ -81,7 +81,9 @@ public class ArgumentsTest {
Path launcherPath = cmd.appLauncherPath(); Path launcherPath = cmd.appLauncherPath();
if (!cmd.isFakeRuntime(String.format( if (!cmd.isFakeRuntime(String.format(
"Not running [%s] launcher", launcherPath))) { "Not running [%s] launcher", launcherPath))) {
HelloApp.executeAndVerifyOutput(launcherPath, TRICKY_ARGUMENTS); HelloApp.assertApp(launcherPath)
.addDefaultArguments(TRICKY_ARGUMENTS)
.executeAndVerifyOutput();
} }
} }

View File

@ -22,72 +22,414 @@
*/ */
import java.io.IOException; import java.io.IOException;
import java.util.*;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.util.function.Consumer;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import jdk.incubator.jpackage.internal.IOUtils; import jdk.jpackage.test.*;
import jdk.jpackage.test.TKit; import jdk.jpackage.test.Functional.ThrowingConsumer;
import jdk.jpackage.test.Functional; import jdk.jpackage.test.Functional.ThrowingBiConsumer;
import jdk.jpackage.test.Annotations.*; import jdk.jpackage.test.Annotations.*;
import jdk.jpackage.test.JPackageCommand;
/* /*
* @test * @test
* @summary jpackage create image with custom icon * @summary jpackage create image and package with custom icons for the main and additional launcher
* @library ../helpers * @library ../helpers
* @build jdk.jpackage.test.* * @build jdk.jpackage.test.*
* @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal
* @compile IconTest.java * @compile IconTest.java
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main
* --jpt-run=IconTest * --jpt-run=IconTest
*/ */
public class IconTest { public class IconTest {
@Test
public static void testResourceDir() throws IOException {
TKit.withTempDirectory("resources", tempDir -> {
JPackageCommand cmd = JPackageCommand.helloAppImage()
.addArguments("--resource-dir", tempDir);
Files.copy(GOLDEN_ICON, tempDir.resolve(appIconFileName(cmd)), enum IconType {
StandardCopyOption.REPLACE_EXISTING); /**
* Icon not specified.
*/
DefaultIcon,
testIt(cmd); /**
}); * Explicit no icon.
*/
NoIcon,
/**
* Custom icon on command line.
*/
CustomIcon,
/**
* Custom icon in resource dir.
*/
ResourceDirIcon,
/**
* Custom icon on command line and in resource dir.
*/
CustomWithResourceDirIcon
}
enum BundleType { AppImage, Package }
public IconTest(BundleType bundleType, IconType mainLauncherIconType,
IconType additionalLauncherIconType, String[] extraJPackageArgs) {
this.appImage = (bundleType == BundleType.AppImage);
this.extraJPackageArgs = extraJPackageArgs;
config = Map.of(
Launcher.Main, mainLauncherIconType,
Launcher.Additional, additionalLauncherIconType);
}
public IconTest(BundleType bundleType, IconType mainLauncherIconType,
IconType additionalLauncherIconType) {
this.appImage = (bundleType == BundleType.AppImage);
this.extraJPackageArgs = new String[0];
config = Map.of(
Launcher.Main, mainLauncherIconType,
Launcher.Additional, additionalLauncherIconType);
}
public IconTest(BundleType bundleType, IconType mainLauncherIconType) {
this.appImage = (bundleType == BundleType.AppImage);
this.extraJPackageArgs = new String[0];
config = Map.of(Launcher.Main, mainLauncherIconType);
}
@Parameters
public static Collection data() {
List<Object[]> data = new ArrayList<>();
var withLinuxShortcut = Set.of(IconType.DefaultIcon, IconType.NoIcon);
for (var bundleType : BundleType.values()) {
if (TKit.isWindows() && bundleType == BundleType.Package) {
// On Windows icons are embedded in launcher executables in
// application image. Nothing is changed when app image is
// packed in msi/exe package bundle, so skip testing of package
// bundle.
continue;
}
for (var mainLauncherIconType : IconType.values()) {
if (mainLauncherIconType == IconType.NoIcon) {
// `No icon` setting is not applicable for the main launcher.
continue;
}
if (TKit.isOSX()) {
// Custom icons not supported for additional launchers on Mac.
data.add(new Object[]{bundleType, mainLauncherIconType});
continue;
}
for (var additionalLauncherIconType : IconType.values()) {
data.add(new Object[]{bundleType, mainLauncherIconType,
additionalLauncherIconType});
if (TKit.isLinux() && bundleType == BundleType.Package
&& withLinuxShortcut.contains(mainLauncherIconType)
&& withLinuxShortcut.contains(
additionalLauncherIconType)) {
data.add(new Object[]{bundleType, mainLauncherIconType,
additionalLauncherIconType, new String[]{
"--linux-shortcut"}});
}
}
}
}
return data;
} }
@Test @Test
@Parameter("true") public void test() throws IOException {
@Parameter("false") if (appImage) {
public static void testParameter(boolean relativePath) throws IOException { JPackageCommand cmd = initAppImageTest();
final Path iconPath; var result = cmd.executeAndAssertImageCreated();
if (relativePath) { ThrowingConsumer.toConsumer(createInstallVerifier()).accept(cmd);
iconPath = TKit.createRelativePathCopy(GOLDEN_ICON); ThrowingBiConsumer.toBiConsumer(createBundleVerifier()).accept(cmd, result);
} else { } else {
iconPath = GOLDEN_ICON; PackageTest test = initPackageTest();
test.addInstallVerifier(createInstallVerifier());
test.addBundleVerifier(createBundleVerifier());
test.addBundleDesktopIntegrationVerifier(config.values().stream()
.anyMatch(this::isWithDesktopIntegration));
test.run(PackageTest.Action.CREATE_AND_UNPACK);
}
}
boolean isWithDesktopIntegration(IconType iconType) {
if (appImage) {
return false;
}
boolean withDesktopFile = !Set.of(
IconType.NoIcon,
IconType.DefaultIcon).contains(iconType);
withDesktopFile |= List.of(extraJPackageArgs).contains("--linux-shortcut");
return withDesktopFile;
}
private ThrowingBiConsumer<JPackageCommand, Executor.Result> createBundleVerifier() {
return (cmd, result) -> {
var verifier = createConsoleOutputVerifier(cmd.name(), config.get(
Launcher.Main), null);
if (verifier != null) {
verifier.apply(result.getOutput().stream());
}
if (config.containsKey(Launcher.Additional)) {
verifier = createConsoleOutputVerifier(
Launcher.Additional.launcherName, config.get(
Launcher.Additional), config.get(Launcher.Main));
if (verifier != null) {
verifier.apply(result.getOutput().stream());
}
}
};
}
private TKit.TextStreamVerifier createConsoleOutputVerifier(
String launcherName, IconType iconType, IconType mainIconType) {
if (iconType == IconType.DefaultIcon && mainIconType != null) {
iconType = mainIconType;
}
return createConsoleOutputVerifier(launcherName, iconType);
}
private static TKit.TextStreamVerifier createConsoleOutputVerifier(
String launcherName, IconType iconType) {
String lookupString = null;
switch (iconType) {
case DefaultIcon:
lookupString = String.format(
"Using default package resource %s [icon] (add %s%s to the resource-dir to customize)",
LauncherIconVerifier.getDefaultIcon().getFileName(),
launcherName, TKit.ICON_SUFFIX);
break;
case ResourceDirIcon:
lookupString = String.format(
"Using custom package resource [icon] (loaded from %s%s)",
launcherName, TKit.ICON_SUFFIX);
break;
case CustomIcon:
case CustomWithResourceDirIcon:
lookupString = "Using custom package resource [icon] (loaded from file";
break;
default:
return null;
} }
testIt(JPackageCommand.helloAppImage().addArguments("--icon", iconPath)); return TKit.assertTextStream(lookupString);
} }
private static String appIconFileName(JPackageCommand cmd) { private ThrowingConsumer<JPackageCommand> createInstallVerifier() {
return IOUtils.replaceSuffix(cmd.appLauncherPath().getFileName(), LauncherIconVerifier verifier = new LauncherIconVerifier();
TKit.ICON_SUFFIX).toString(); switch (config.get(Launcher.Main)) {
case NoIcon:
verifier.setExpectedIcon(null);
break;
case DefaultIcon:
verifier.setExpectedDefaultIcon();
break;
case CustomIcon:
verifier.setExpectedIcon(Launcher.Main.cmdlineIcon);
break;
case ResourceDirIcon:
verifier.setExpectedIcon(Launcher.Main.resourceDirIcon);
break;
case CustomWithResourceDirIcon:
verifier.setExpectedIcon(Launcher.Main2.cmdlineIcon);
break;
}
return cmd -> {
verifier.applyTo(cmd);
if (TKit.isLinux() && !cmd.isImagePackageType()) {
Path desktopFile = LinuxHelper.getDesktopFile(cmd);
if (isWithDesktopIntegration(config.get(Launcher.Main))) {
TKit.assertFileExists(desktopFile);
} else {
TKit.assertPathExists(desktopFile, false);
}
}
};
} }
private static void testIt(JPackageCommand cmd) throws IOException { private void initTest(JPackageCommand cmd, PackageTest test) {
cmd.executeAndAssertHelloAppImageCreated(); config.entrySet().forEach(ThrowingConsumer.toConsumer(entry -> {
initTest(entry.getKey(), entry.getValue(), cmd, test);
}));
Path iconPath = cmd.appLayout().destktopIntegrationDirectory().resolve( ThrowingConsumer<JPackageCommand> initializer = testCmd -> {
appIconFileName(cmd)); testCmd.saveConsoleOutput(true);
testCmd.setFakeRuntime();
testCmd.addArguments(extraJPackageArgs);
};
TKit.assertFileExists(iconPath); if (test != null) {
TKit.assertTrue(-1 == Files.mismatch(GOLDEN_ICON, iconPath), test.addInitializer(initializer);
String.format( } else {
"Check application icon file [%s] is a copy of source icon file [%s]", ThrowingConsumer.toConsumer(initializer).accept(cmd);
iconPath, GOLDEN_ICON)); }
} }
private final static Path GOLDEN_ICON = TKit.TEST_SRC_ROOT.resolve(Path.of( private static void initTest(Launcher cfg, IconType iconType,
"resources", "icon" + TKit.ICON_SUFFIX)); JPackageCommand cmd, PackageTest test) throws IOException {
Consumer<AdditionalLauncher> addLauncher = v -> {
if (test != null) {
v.applyTo(test);
} else {
v.applyTo(cmd);
}
};
switch (iconType) {
case DefaultIcon:
if (cfg.launcherName != null) {
addLauncher.accept(new AdditionalLauncher(cfg.launcherName));
}
break;
case NoIcon:
if (cfg.launcherName != null) {
addLauncher.accept(
new AdditionalLauncher(cfg.launcherName).setNoIcon());
}
break;
case CustomIcon:
if (test != null) {
addCustomIcon(null, test, cfg.launcherName, cfg.cmdlineIcon);
} else {
addCustomIcon(cmd, null, cfg.launcherName, cfg.cmdlineIcon);
}
break;
case ResourceDirIcon:
if (Launcher.PRIMARY.contains(cfg) && cfg.launcherName != null) {
addLauncher.accept(new AdditionalLauncher(cfg.launcherName));
}
if (test != null) {
test.addInitializer(testCmd -> {
addResourceDirIcon(testCmd, cfg.launcherName,
cfg.resourceDirIcon);
});
} else {
addResourceDirIcon(cmd, cfg.launcherName, cfg.resourceDirIcon);
}
break;
case CustomWithResourceDirIcon:
switch (cfg) {
case Main:
initTest(Launcher.Main2, IconType.CustomIcon, cmd, test);
initTest(Launcher.Main2, IconType.ResourceDirIcon, cmd, test);
break;
case Additional:
initTest(Launcher.Additional2, IconType.CustomIcon, cmd, test);
initTest(Launcher.Additional2, IconType.ResourceDirIcon, cmd, test);
break;
default:
throw new IllegalArgumentException();
}
break;
}
}
private JPackageCommand initAppImageTest() {
JPackageCommand cmd = JPackageCommand.helloAppImage();
initTest(cmd, null);
return cmd;
}
private PackageTest initPackageTest() {
PackageTest test = new PackageTest().configureHelloApp();
initTest(null, test);
return test;
}
private static void addResourceDirIcon(JPackageCommand cmd,
String launcherName, Path iconPath) throws IOException {
Path resourceDir = cmd.getArgumentValue("--resource-dir", () -> null,
Path::of);
if (resourceDir == null) {
resourceDir = TKit.createTempDirectory("resources");
cmd.addArguments("--resource-dir", resourceDir);
}
String dstIconFileName = Optional.ofNullable(launcherName).orElseGet(
() -> cmd.name()) + TKit.ICON_SUFFIX;
TKit.trace(String.format("Resource file: [%s] <- [%s]",
resourceDir.resolve(dstIconFileName), iconPath));
Files.copy(iconPath, resourceDir.resolve(dstIconFileName),
StandardCopyOption.REPLACE_EXISTING);
}
private static void addCustomIcon(JPackageCommand cmd, PackageTest test,
String launcherName, Path iconPath) throws IOException {
if (launcherName != null) {
AdditionalLauncher al = new AdditionalLauncher(launcherName).setIcon(
iconPath);
if (test != null) {
al.applyTo(test);
} else {
al.applyTo(cmd);
}
} else if (test != null) {
test.addInitializer(testCmd -> {
testCmd.addArguments("--icon", iconPath);
});
} else {
cmd.addArguments("--icon", iconPath);
}
}
private enum Launcher {
Main(null, ICONS[0], ICONS[1]),
Main2(null, ICONS[1], ICONS[0]),
Additional("x", ICONS[2], ICONS[3]),
Additional2("x", ICONS[3], ICONS[2]);
Launcher(String name, Path cmdlineIcon, Path resourceDirIcon) {
this.launcherName = name;
this.cmdlineIcon = cmdlineIcon;
this.resourceDirIcon = resourceDirIcon;
}
private final String launcherName;
private final Path cmdlineIcon;
private final Path resourceDirIcon;
private final static Set<Launcher> PRIMARY = Set.of(Main, Additional);
}
private final boolean appImage;
private final Map<Launcher, IconType> config;
private final String[] extraJPackageArgs;
private static Path iconPath(String name) {
return TKit.TEST_SRC_ROOT.resolve(Path.of("resources", name
+ TKit.ICON_SUFFIX));
}
private final static Path[] ICONS = Stream.of("icon", "icon2", "icon3",
"icon4")
.map(IconTest::iconPath)
.collect(Collectors.toList()).toArray(Path[]::new);
} }

View File

@ -126,12 +126,8 @@ public class InstallDirTest {
cmd.saveConsoleOutput(true); cmd.saveConsoleOutput(true);
}) })
.addBundleVerifier((cmd, result) -> { .addBundleVerifier((cmd, result) -> {
String errorMessage = JPackageCommand.filterOutput( TKit.assertTextStream(errorMessageSubstring).apply(
result.getOutput().stream()).filter(line -> line.contains( result.getOutput().stream());
errorMessageSubstring)).findFirst().orElse(null);
TKit.assertNotNull(errorMessage, String.format(
"Check output contains [%s] substring",
errorMessageSubstring));
}) })
.run(); .run();
} }

View File

@ -99,7 +99,10 @@ public class LicenseTest {
verifyLicenseFileInLinuxPackage(cmd, linuxLicenseFile(cmd)); verifyLicenseFileInLinuxPackage(cmd, linuxLicenseFile(cmd));
}) })
.addInstallVerifier(cmd -> { .addInstallVerifier(cmd -> {
TKit.assertReadableFileExists(linuxLicenseFile(cmd)); Path path = linuxLicenseFile(cmd);
if (path != null) {
TKit.assertReadableFileExists(path);
}
}) })
.addUninstallVerifier(cmd -> { .addUninstallVerifier(cmd -> {
verifyLicenseFileNotInstalledLinux(linuxLicenseFile(cmd)); verifyLicenseFileNotInstalledLinux(linuxLicenseFile(cmd));
@ -110,7 +113,10 @@ public class LicenseTest {
}) })
.forTypes(PackageType.LINUX_RPM) .forTypes(PackageType.LINUX_RPM)
.addInstallVerifier(cmd -> { .addInstallVerifier(cmd -> {
verifyLicenseFileInstalledRpm(rpmLicenseFile(cmd)); Path path = rpmLicenseFile(cmd);
if (path != null) {
verifyLicenseFileInstalledRpm(path);
}
}) })
.run(); .run();
} }
@ -124,6 +130,10 @@ public class LicenseTest {
} }
private static Path rpmLicenseFile(JPackageCommand cmd) { private static Path rpmLicenseFile(JPackageCommand cmd) {
if (cmd.isPackageUnpacked("Not checking for rpm license file")) {
return null;
}
final Path licenseRoot = Path.of( final Path licenseRoot = Path.of(
new Executor() new Executor()
.setExecutable("rpm") .setExecutable("rpm")
@ -236,7 +246,7 @@ public class LicenseTest {
void run() { void run() {
final Path srcLicenseFile = TKit.workDir().resolve("license"); final Path srcLicenseFile = TKit.workDir().resolve("license");
new PackageTest().configureHelloApp().forTypes(PackageType.LINUX_DEB) new PackageTest().forTypes(PackageType.LINUX_DEB).configureHelloApp()
.addInitializer(cmd -> { .addInitializer(cmd -> {
// Create source license file. // Create source license file.
Files.write(srcLicenseFile, List.of( Files.write(srcLicenseFile, List.of(

View File

@ -30,6 +30,7 @@ import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -119,6 +120,10 @@ public final class BasicTest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void testVerbose() { public void testVerbose() {
JPackageCommand cmd = JPackageCommand.helloAppImage() JPackageCommand cmd = JPackageCommand.helloAppImage()
// Disable default logic adding `--verbose` option
// to jpackage command line.
.ignoreDefaultVerbose(true)
.saveConsoleOutput(true)
.setFakeRuntime().executePrerequisiteActions(); .setFakeRuntime().executePrerequisiteActions();
List<String> expectedVerboseOutputStrings = new ArrayList<>(); List<String> expectedVerboseOutputStrings = new ArrayList<>();
@ -139,17 +144,17 @@ public final class BasicTest {
} }
TKit.deleteDirectoryContentsRecursive(cmd.outputDir()); TKit.deleteDirectoryContentsRecursive(cmd.outputDir());
List<String> nonVerboseOutput = cmd.createExecutor().executeAndGetOutput(); List<String> nonVerboseOutput = cmd.execute().getOutput();
List<String>[] verboseOutput = (List<String>[])new List<?>[1]; List<String>[] verboseOutput = (List<String>[])new List<?>[1];
// Directory clean up is not 100% reliable on Windows because of // Directory clean up is not 100% reliable on Windows because of
// antivirus software that can lock .exe files. Setup // antivirus software that can lock .exe files. Setup
// diffreent output directory instead of cleaning the default one for // different output directory instead of cleaning the default one for
// verbose jpackage run. // verbose jpackage run.
TKit.withTempDirectory("verbose-output", tempDir -> { TKit.withTempDirectory("verbose-output", tempDir -> {
cmd.setArgumentValue("--dest", tempDir); cmd.setArgumentValue("--dest", tempDir);
verboseOutput[0] = cmd.createExecutor().addArgument( cmd.addArgument("--verbose");
"--verbose").executeAndGetOutput(); verboseOutput[0] = cmd.execute().getOutput();
}); });
TKit.assertTrue(nonVerboseOutput.size() < verboseOutput[0].size(), TKit.assertTrue(nonVerboseOutput.size() < verboseOutput[0].size(),
@ -189,7 +194,7 @@ public final class BasicTest {
} }
@Test @Test
// Regular app // Regular app
@Parameter("Hello") @Parameter("Hello")
// Modular app // Modular app
@Parameter("com.other/com.other.Hello") @Parameter("com.other/com.other.Hello")
@ -227,62 +232,66 @@ public final class BasicTest {
*/ */
@Test @Test
public void testTemp() throws IOException { public void testTemp() throws IOException {
TKit.withTempDirectory("temp-root", tempRoot -> { final Path tempRoot = TKit.createTempDirectory("temp-root");
Function<JPackageCommand, Path> getTempDir = cmd -> {
return tempRoot.resolve(cmd.outputBundle().getFileName());
};
ThrowingConsumer<JPackageCommand> addTempDir = cmd -> { Function<JPackageCommand, Path> getTempDir = cmd -> {
return tempRoot.resolve(cmd.outputBundle().getFileName());
};
Supplier<PackageTest> createTest = () -> {
return new PackageTest()
.configureHelloApp()
// Force save of package bundle in test work directory.
.addInitializer(JPackageCommand::setDefaultInputOutput)
.addInitializer(cmd -> {
Path tempDir = getTempDir.apply(cmd); Path tempDir = getTempDir.apply(cmd);
Files.createDirectories(tempDir); Files.createDirectories(tempDir);
cmd.addArguments("--temp", tempDir); cmd.addArguments("--temp", tempDir);
}; });
};
new PackageTest().configureHelloApp().addInitializer(addTempDir) createTest.get()
.addBundleVerifier(cmd -> { .addBundleVerifier(cmd -> {
// Check jpackage actually used the supplied directory. // Check jpackage actually used the supplied directory.
Path tempDir = getTempDir.apply(cmd); Path tempDir = getTempDir.apply(cmd);
TKit.assertNotEquals(0, tempDir.toFile().list().length, TKit.assertNotEquals(0, tempDir.toFile().list().length,
String.format( String.format(
"Check jpackage wrote some data in the supplied temporary directory [%s]", "Check jpackage wrote some data in the supplied temporary directory [%s]",
tempDir)); tempDir));
}) })
.run(); .run(PackageTest.Action.CREATE);
new PackageTest().configureHelloApp().addInitializer(addTempDir) createTest.get()
.addInitializer(cmd -> { .addInitializer(cmd -> {
// Clean output from the previus jpackage run. // Clean output from the previus jpackage run.
Files.delete(cmd.outputBundle()); Files.delete(cmd.outputBundle());
}) })
// Temporary directory should not be empty, // Temporary directory should not be empty,
// jpackage should exit with error. // jpackage should exit with error.
.setExpectedExitCode(1) .setExpectedExitCode(1)
.run(); .run(PackageTest.Action.CREATE);
});
} }
@Test @Test
public void testAtFile() throws IOException { public void testAtFile() throws IOException {
JPackageCommand cmd = JPackageCommand.helloAppImage(); JPackageCommand cmd = JPackageCommand
.helloAppImage()
.setArgumentValue("--dest", TKit.createTempDirectory("output"));
// Init options file with the list of options configured // Init options file with the list of options configured
// for JPackageCommand instance. // for JPackageCommand instance.
final Path optionsFile = TKit.workDir().resolve("options"); final Path optionsFile = TKit.createTempFile(Path.of("options"));
Files.write(optionsFile, Files.write(optionsFile,
List.of(String.join(" ", cmd.getAllArguments()))); List.of(String.join(" ", cmd.getAllArguments())));
// Build app jar file. // Build app jar file.
cmd.executePrerequisiteActions(); cmd.executePrerequisiteActions();
// Make sure output directory is empty. Normally JPackageCommand would
// do this automatically.
TKit.deleteDirectoryContentsRecursive(cmd.outputDir());
// Instead of running jpackage command through configured // Instead of running jpackage command through configured
// JPackageCommand instance, run vanilla jpackage command with @ file. // JPackageCommand instance, run vanilla jpackage command with @ file.
getJPackageToolProvider() getJPackageToolProvider()
.addArgument(String.format("@%s", optionsFile)) .addArgument(String.format("@%s", optionsFile))
.execute().assertExitCodeIsZero(); .execute();
// Verify output of jpackage command. // Verify output of jpackage command.
cmd.assertImageCreated(); cmd.assertImageCreated();
@ -292,50 +301,45 @@ public final class BasicTest {
@Parameter("Hello") @Parameter("Hello")
@Parameter("com.foo/com.foo.main.Aloha") @Parameter("com.foo/com.foo.main.Aloha")
@Test @Test
public void testJLinkRuntime(String javaAppDesc) { public void testJLinkRuntime(String javaAppDesc) throws IOException {
JPackageCommand cmd = JPackageCommand.helloAppImage(javaAppDesc); JavaAppDesc appDesc = JavaAppDesc.parse(javaAppDesc);
// If `--module` parameter was set on jpackage command line, get its JPackageCommand cmd = JPackageCommand.helloAppImage(appDesc);
// value and extract module name.
// E.g.: foo.bar2/foo.bar.Buz -> foo.bar2 final String moduleName = appDesc.moduleName();
// Note: HelloApp class manages `--module` parameter on jpackage command line
final String moduleName = cmd.getArgumentValue("--module", () -> null,
(v) -> v.split("/", 2)[0]);
if (moduleName != null) { if (moduleName != null) {
// Build module jar. // Build module jar.
cmd.executePrerequisiteActions(); cmd.executePrerequisiteActions();
} }
TKit.withTempDirectory("runtime", tempDir -> { final Path runtimeDir = TKit.createTempDirectory("runtime").resolve("data");
final Path runtimeDir = tempDir.resolve("data");
// List of modules required for test app. // List of modules required for test app.
final var modules = new String[] { final var modules = new String[] {
"java.base", "java.base",
"java.desktop" "java.desktop"
}; };
Executor jlink = getToolProvider(JavaTool.JLINK) Executor jlink = getToolProvider(JavaTool.JLINK)
.saveOutput(false) .saveOutput(false)
.addArguments( .addArguments(
"--add-modules", String.join(",", modules), "--add-modules", String.join(",", modules),
"--output", runtimeDir.toString(), "--output", runtimeDir.toString(),
"--strip-debug", "--strip-debug",
"--no-header-files", "--no-header-files",
"--no-man-pages"); "--no-man-pages");
if (moduleName != null) { if (moduleName != null) {
jlink.addArguments("--add-modules", moduleName, "--module-path", jlink.addArguments("--add-modules", moduleName, "--module-path",
Path.of(cmd.getArgumentValue("--module-path")).resolve( Path.of(cmd.getArgumentValue("--module-path")).resolve(
"hello.jar").toString()); "hello.jar").toString());
} }
jlink.execute().assertExitCodeIsZero(); jlink.execute();
cmd.addArguments("--runtime-image", runtimeDir); cmd.addArguments("--runtime-image", runtimeDir);
cmd.executeAndAssertHelloAppImageCreated(); cmd.executeAndAssertHelloAppImageCreated();
});
} }
private static Executor getJPackageToolProvider() { private static Executor getJPackageToolProvider() {

View File

@ -209,8 +209,7 @@ public final class MainClassTest {
// file nor on command line. // file nor on command line.
List<String> output = cmd List<String> output = cmd
.saveConsoleOutput(true) .saveConsoleOutput(true)
.execute() .execute(1)
.assertExitCodeIs(1)
.getOutput(); .getOutput();
TKit.assertTextStream(script.expectedErrorMessage).apply(output.stream()); TKit.assertTextStream(script.expectedErrorMessage).apply(output.stream());
return; return;
@ -236,7 +235,7 @@ public final class MainClassTest {
.setDirectory(cmd.outputDir()) .setDirectory(cmd.outputDir())
.setExecutable(cmd.appLauncherPath()) .setExecutable(cmd.appLauncherPath())
.dumpOutput().saveOutput() .dumpOutput().saveOutput()
.execute().assertExitCodeIs(1).getOutput(); .execute(1).getOutput();
TKit.assertTextStream(String.format( TKit.assertTextStream(String.format(
"Error: Could not find or load main class %s", "Error: Could not find or load main class %s",
nonExistingMainClass)).apply(output.stream()); nonExistingMainClass)).apply(output.stream());
@ -289,7 +288,7 @@ public final class MainClassTest {
.addArguments("-v", "-c", "-M", "-f", jarFile.toString()) .addArguments("-v", "-c", "-M", "-f", jarFile.toString())
.addArguments("-C", workDir.toString(), ".") .addArguments("-C", workDir.toString(), ".")
.dumpOutput() .dumpOutput()
.execute().assertExitCodeIsZero(); .execute();
}); });
} }

View File

@ -24,6 +24,7 @@
package jdk.jpackage.tests; package jdk.jpackage.tests;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -71,7 +72,7 @@ public final class ModulePathTest {
} }
@Test @Test
public void test() { public void test() throws IOException {
final String moduleName = "com.foo"; final String moduleName = "com.foo";
JPackageCommand cmd = JPackageCommand.helloAppImage( JPackageCommand cmd = JPackageCommand.helloAppImage(
"benvenuto.jar:" + moduleName + "/com.foo.Hello"); "benvenuto.jar:" + moduleName + "/com.foo.Hello");
@ -88,45 +89,42 @@ public final class ModulePathTest {
String goodModulePath = Objects.requireNonNull(cmd.getArgumentValue( String goodModulePath = Objects.requireNonNull(cmd.getArgumentValue(
"--module-path")); "--module-path"));
cmd.removeArgumentWithValue("--module-path"); cmd.removeArgumentWithValue("--module-path");
TKit.withTempDirectory("empty-dir", emptyDir -> {
Path nonExistingDir = TKit.withTempDirectory("non-existing-dir",
unused -> {
});
Function<String, String> substitute = str -> { Path emptyDir = TKit.createTempDirectory("empty-dir");
String v = str; Path nonExistingDir = TKit.withTempDirectory("non-existing-dir", x -> {});
v = v.replace(GOOD_PATH, goodModulePath);
v = v.replace(EMPTY_DIR, emptyDir.toString());
v = v.replace(NON_EXISTING_DIR, nonExistingDir.toString());
return v;
};
boolean withGoodPath = modulePathArgs.stream().anyMatch( Function<String, String> substitute = str -> {
s -> s.contains(GOOD_PATH)); String v = str;
v = v.replace(GOOD_PATH, goodModulePath);
v = v.replace(EMPTY_DIR, emptyDir.toString());
v = v.replace(NON_EXISTING_DIR, nonExistingDir.toString());
return v;
};
cmd.addArguments(modulePathArgs.stream().map(arg -> Stream.of( boolean withGoodPath = modulePathArgs.stream().anyMatch(
"--module-path", substitute.apply(arg))).flatMap(s -> s).collect( s -> s.contains(GOOD_PATH));
Collectors.toList()));
if (withGoodPath) { cmd.addArguments(modulePathArgs.stream().map(arg -> Stream.of(
cmd.executeAndAssertHelloAppImageCreated(); "--module-path", substitute.apply(arg))).flatMap(s -> s).collect(
Collectors.toList()));
if (withGoodPath) {
cmd.executeAndAssertHelloAppImageCreated();
} else {
final String expectedErrorMessage;
if (modulePathArgs.isEmpty()) {
expectedErrorMessage = "Error: Missing argument: --runtime-image or --module-path";
} else { } else {
final String expectedErrorMessage; expectedErrorMessage = String.format(
if (modulePathArgs.isEmpty()) { "Error: Module %s not found", moduleName);
expectedErrorMessage = "Error: Missing argument: --runtime-image or --module-path";
} else {
expectedErrorMessage = String.format(
"Error: Module %s not found", moduleName);
}
List<String> output = cmd
.saveConsoleOutput(true)
.execute()
.assertExitCodeIs(1)
.getOutput();
TKit.assertTextStream(expectedErrorMessage).apply(output.stream());
} }
});
List<String> output = cmd
.saveConsoleOutput(true)
.execute(1)
.getOutput();
TKit.assertTextStream(expectedErrorMessage).apply(output.stream());
}
} }
private final List<String> modulePathArgs; private final List<String> modulePathArgs;

View File

@ -29,13 +29,20 @@ set_args ()
local arg_is_output_dir= local arg_is_output_dir=
local arg_is_mode= local arg_is_mode=
local output_dir_set= local output_dir_set=
local with_append_actions=yes
for arg in "$@"; do for arg in "$@"; do
if [ "$arg" == "-o" ]; then if [ "$arg" == "-o" ]; then
arg_is_output_dir=yes arg_is_output_dir=yes
output_dir_set=yes output_dir_set=yes
elif [ "$arg" == "-m" ]; then elif [ "$arg" == "-m" ]; then
arg_is_mode=yes arg_is_mode=yes
continue continue
elif [ "$arg" == '--' ]; then
append_actions
with_append_actions=
continue
elif ! case "$arg" in -Djpackage.test.action=*) false;; esac; then
continue
elif [ -n "$arg_is_output_dir" ]; then elif [ -n "$arg_is_output_dir" ]; then
arg_is_output_dir= arg_is_output_dir=
output_dir="$arg" output_dir="$arg"
@ -47,6 +54,13 @@ set_args ()
args+=( "$arg" ) args+=( "$arg" )
done done
[ -n "$output_dir_set" ] || args=( -o "$output_dir" "${args[@]}" ) [ -n "$output_dir_set" ] || args=( -o "$output_dir" "${args[@]}" )
[ -z "$with_append_actions" ] || append_actions
}
append_actions ()
{
args+=( '--' '-Djpackage.test.action=create,install,verify-install,uninstall,verify-uninstall' )
} }
@ -62,7 +76,3 @@ exec_command ()
set_args "$@" set_args "$@"
basedir="$(dirname $0)" basedir="$(dirname $0)"
exec_command "$basedir/run_tests.sh" -m create "${args[@]}" exec_command "$basedir/run_tests.sh" -m create "${args[@]}"
exec_command "$basedir/manage_packages.sh" -d "$output_dir"
exec_command "$basedir/run_tests.sh" -m verify-install "${args[@]}"
exec_command "$basedir/manage_packages.sh" -d "$output_dir" -u
exec_command "$basedir/run_tests.sh" -m verify-uninstall "${args[@]}"

View File

@ -26,6 +26,7 @@ import java.io.InputStream;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import jdk.jpackage.test.TKit; import jdk.jpackage.test.TKit;
import jdk.jpackage.test.HelloApp;
import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageCommand;
import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.Annotations.Test;
import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.Parameter;
@ -58,6 +59,10 @@ public class WinConsoleTest {
} }
cmd.executeAndAssertHelloAppImageCreated(); cmd.executeAndAssertHelloAppImageCreated();
checkSubsystem(cmd.appLauncherPath(), withWinConsole); checkSubsystem(cmd.appLauncherPath(), withWinConsole);
// Run launcher with a number of arguments to make sure they go through
// regardless the launcher has or doesn't have console.
HelloApp.executeLauncherAndVerifyOutput(cmd, "a", "b", "c");
} }
private static void checkSubsystem(Path path, boolean isConsole) throws private static void checkSubsystem(Path path, boolean isConsole) throws

View File

@ -70,7 +70,7 @@ public class WinScriptTest {
@Test @Test
@Parameter("0") @Parameter("0")
@Parameter("10") @Parameter("10")
public void test(int wsfExitCode) { public void test(int wsfExitCode) throws IOException {
final ScriptData appImageScriptData; final ScriptData appImageScriptData;
if (wsfExitCode != 0 && packageType == PackageType.WIN_EXE) { if (wsfExitCode != 0 && packageType == PackageType.WIN_EXE) {
appImageScriptData = new ScriptData(PackageType.WIN_MSI, 0); appImageScriptData = new ScriptData(PackageType.WIN_MSI, 0);
@ -81,29 +81,32 @@ public class WinScriptTest {
final ScriptData msiScriptData = new ScriptData(PackageType.WIN_EXE, wsfExitCode); final ScriptData msiScriptData = new ScriptData(PackageType.WIN_EXE, wsfExitCode);
test.setExpectedExitCode(wsfExitCode == 0 ? 0 : 1); test.setExpectedExitCode(wsfExitCode == 0 ? 0 : 1);
TKit.withTempDirectory("resources", tempDir -> {
test.addInitializer(cmd -> {
cmd.addArguments("--resource-dir", tempDir);
appImageScriptData.createScript(cmd); final Path tempDir = TKit.createTempDirectory("resources");
msiScriptData.createScript(cmd);
});
if (packageType == PackageType.WIN_MSI) { test.addInitializer(cmd -> {
cmd.addArguments("--resource-dir", tempDir);
appImageScriptData.createScript(cmd);
msiScriptData.createScript(cmd);
});
switch (packageType) {
case WIN_MSI:
test.addBundleVerifier((cmd, result) -> { test.addBundleVerifier((cmd, result) -> {
appImageScriptData.assertJPackageOutput(result.getOutput()); appImageScriptData.assertJPackageOutput(result.getOutput());
}); });
} break;
if (packageType == PackageType.WIN_EXE) { case WIN_EXE:
test.addBundleVerifier((cmd, result) -> { test.addBundleVerifier((cmd, result) -> {
appImageScriptData.assertJPackageOutput(result.getOutput()); appImageScriptData.assertJPackageOutput(result.getOutput());
msiScriptData.assertJPackageOutput(result.getOutput()); msiScriptData.assertJPackageOutput(result.getOutput());
}); });
} break;
}
test.run(); test.run();
});
} }
private static class ScriptData { private static class ScriptData {

View File

@ -21,9 +21,16 @@
* questions. * questions.
*/ */
import jdk.jpackage.test.TKit; import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
import jdk.jpackage.test.Annotations.Test;
import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageTest;
import jdk.jpackage.test.PackageType; import jdk.jpackage.test.PackageType;
import jdk.jpackage.test.WindowsHelper;
import jdk.jpackage.test.TKit;
/** /**
* Test both --win-upgrade-uuid and --app-version parameters. Output of the test * Test both --win-upgrade-uuid and --app-version parameters. Output of the test
@ -41,34 +48,155 @@ import jdk.jpackage.test.PackageType;
* @summary jpackage with --win-upgrade-uuid and --app-version * @summary jpackage with --win-upgrade-uuid and --app-version
* @library ../helpers * @library ../helpers
* @key jpackagePlatformPackage * @key jpackagePlatformPackage
* @requires (jpackage.test.SQETest != null)
* @build jdk.jpackage.test.* * @build jdk.jpackage.test.*
* @requires (os.family == "windows") * @requires (os.family == "windows")
* @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal
* @run main/othervm/timeout=360 -Xmx512m WinUpgradeUUIDTest * @compile WinUpgradeUUIDTest.java
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
* --jpt-run=WinUpgradeUUIDTest.test
*/
/*
* @test
* @summary jpackage with --win-upgrade-uuid and --app-version
* @library ../helpers
* @key jpackagePlatformPackage
* @requires (jpackage.test.SQETest == null)
* @build jdk.jpackage.test.*
* @requires (os.family == "windows")
* @modules jdk.jpackage/jdk.jpackage.internal
* @compile WinUpgradeUUIDTest.java
* @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main
* --jpt-run=WinUpgradeUUIDTest
*/ */
public class WinUpgradeUUIDTest { public class WinUpgradeUUIDTest {
public static void main(String[] args) {
TKit.run(args, () -> { @Test
PackageTest test = init(); public static void test() {
if (test.getAction() != PackageTest.Action.VERIFY_INSTALL) { Supplier<PackageTest> init = () -> {
test.run(); final UUID upgradeCode = UUID.fromString(
"F0B18E75-52AD-41A2-BC86-6BE4FCD50BEB");
return new PackageTest()
.forTypes(PackageType.WINDOWS)
.configureHelloApp()
.addInitializer(cmd -> cmd.addArguments("--win-upgrade-uuid",
upgradeCode.toString()))
.forTypes(PackageType.WIN_MSI)
.addBundlePropertyVerifier("UpgradeCode", value -> {
if (value.startsWith("{")) {
value = value.substring(1);
}
if (value.endsWith("}")) {
value = value.substring(0, value.length() - 1);
}
return UUID.fromString(value).equals(upgradeCode);
}, "is a match with");
};
// Replace real uninstall command for the first package with nop action.
// It will be uninstalled automatically when the second
// package will be installed.
// However uninstall verification for the first package will be executed.
PackageTest test1 = init.get().setPackageUninstaller(cmd -> {});
PackageTest test2 = init.get().addInitializer(cmd -> {
cmd.setArgumentValue("--app-version", "2.0");
cmd.setArgumentValue("--arguments", "bar");
});
new PackageTest.Group(test1, test2).run();
}
/**
* Running jpackage multiple times with the same parameters should produce
* MSI packages with the same UpgradeCode and ProductCode values.
*/
@Test
public static void testUUIDs() {
Supplier<PackageTest> init = () -> {
return new PackageTest()
.forTypes(PackageType.WIN_MSI)
.configureHelloApp()
.addInitializer(cmd -> {
cmd.setFakeRuntime();
cmd.setArgumentValue("--dest", TKit.createTempDirectory("output"));
});
};
PackageTest test1 = init.get();
PackageTest test2 = init.get();
PackageTest test3 = init.get().addInitializer(cmd -> {
cmd.addArguments("--app-version", "2.0");
});
PackageTest test4 = init.get().addInitializer(cmd -> {
cmd.addArguments("--app-version", "2.0");
cmd.addArguments("--vendor", "Foo Inc.");
});
PackageTest[] tests = new PackageTest[] { test1, test2, test3, test4 };
var productCodeVerifier = createPropertyVerifier("ProductCode", tests);
var upgradeCodeVerifier = createPropertyVerifier("UpgradeCode", tests);
List.of(tests).forEach(test -> {
test.run(PackageTest.Action.CREATE);
});
productCodeVerifier.assertEquals(test1, test2);
productCodeVerifier.assertNotEquals(test1, test3);
productCodeVerifier.assertNotEquals(test1, test4);
productCodeVerifier.assertNotEquals(test3, test4);
upgradeCodeVerifier.assertEquals(test1, test2);
upgradeCodeVerifier.assertEquals(test1, test3);
upgradeCodeVerifier.assertNotEquals(test1, test4);
}
private static PropertyVerifier createPropertyVerifier(String propertyName,
PackageTest... tests) {
Map<PackageTest, Map.Entry<String, String>> properties = new HashMap<>();
List.of(tests).forEach(test -> {
test.addBundleVerifier(cmd -> {
properties.put(test, Map.entry(cmd.getPrintableCommandLine(),
WindowsHelper.getMsiProperty(cmd, propertyName)));
});
});
return new PropertyVerifier() {
@Override
protected String propertyName() {
return propertyName;
} }
test = init(); @Override
test.addInitializer(cmd -> { protected Map<PackageTest, Map.Entry<String, String>> propertyValues() {
cmd.setArgumentValue("--app-version", "2.0"); return properties;
cmd.setArgumentValue("--arguments", "bar"); }
}); };
test.run();
});
} }
private static PackageTest init() { static abstract class PropertyVerifier {
return new PackageTest() void assertEquals(PackageTest x, PackageTest y) {
.forTypes(PackageType.WINDOWS) var entryX = propertyValues().get(x);
.configureHelloApp() var entryY = propertyValues().get(y);
.addInitializer(cmd -> cmd.addArguments("--win-upgrade-uuid", TKit.assertEquals(entryX.getValue(), entryY.getValue(),
"F0B18E75-52AD-41A2-BC86-6BE4FCD50BEB")); String.format(
"Check %s is the same for %s and %s command lines",
propertyName(), entryX.getKey(), entryY.getKey()));
}
void assertNotEquals(PackageTest x, PackageTest y) {
var entryX = propertyValues().get(x);
var entryY = propertyValues().get(y);
TKit.assertNotEquals(entryX.getValue(), entryY.getValue(),
String.format(
"Check %s is different for %s and %s command lines",
propertyName(), entryX.getKey(), entryY.getKey()));
}
protected abstract String propertyName();
protected abstract Map<PackageTest, Map.Entry<String, String>> propertyValues();
} }
} }