Skip to content
Permalink
Browse files
8233244: Add tests for icons configuration in rpm/deb packages
Reviewed-by: herrick, almatvee
  • Loading branch information
Alexey Semenyuk committed Jun 11, 2020
1 parent e3cb4df commit 609819173eb17cd804a58de66a11dae9b572e8b3
Showing 3 changed files with 179 additions and 39 deletions.
@@ -25,6 +25,7 @@
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import jdk.incubator.jpackage.internal.IOUtils;


final public class FileAssociations {
@@ -65,6 +66,13 @@ public FileAssociations setIcon(Path v) {
return this;
}

Path getLinuxIconFileName() {
if (icon == null) {
return null;
}
return Path.of(getMime().replace('/', '-') + IOUtils.getSuffix(icon));
}

Path getPropertiesFile() {
return file;
}
@@ -25,12 +25,23 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.incubator.jpackage.internal.IOUtils;
import jdk.jpackage.test.PackageTest.PackageHandlers;



public class LinuxHelper {
private static String getRelease(JPackageCommand cmd) {
return cmd.getArgumentValue("--linux-app-release", () -> "1");
@@ -280,27 +291,6 @@ static void addBundleDesktopIntegrationVerifier(PackageTest test,
boolean integrated) {
final String xdgUtils = "xdg-utils";

test.addBundleVerifier(cmd -> {
List<String> prerequisites = getPrerequisitePackages(cmd);
boolean xdgUtilsFound = prerequisites.contains(xdgUtils);
if (integrated) {
TKit.assertTrue(xdgUtilsFound, String.format(
"Check [%s] is in the list of required packages %s",
xdgUtils, prerequisites));
} else {
TKit.assertFalse(xdgUtilsFound, String.format(
"Check [%s] is NOT in the list of required packages %s",
xdgUtils, prerequisites));
}
});

test.forTypes(PackageType.LINUX_DEB, () -> {
addDebBundleDesktopIntegrationVerifier(test, integrated);
});
}

private static void addDebBundleDesktopIntegrationVerifier(PackageTest test,
boolean integrated) {
Function<List<String>, String> verifier = (lines) -> {
// Lookup for xdg commands
return lines.stream().filter(line -> {
@@ -312,21 +302,29 @@ private static void addDebBundleDesktopIntegrationVerifier(PackageTest test,
};

test.addBundleVerifier(cmd -> {
TKit.withTempDirectory("dpkg-control-files", tempDir -> {
// Extract control Debian package files into temporary directory
Executor.of("dpkg", "-e")
.addArgument(cmd.outputBundle())
.addArgument(tempDir)
.execute();
// Verify dependencies.
List<String> prerequisites = getPrerequisitePackages(cmd);
boolean xdgUtilsFound = prerequisites.contains(xdgUtils);
TKit.assertTrue(xdgUtilsFound == integrated, String.format(
"Check [%s] is%s in the list of required packages %s",
xdgUtils, integrated ? "" : " NOT", prerequisites));

Path controlFile = Path.of("postinst");
Map<Scriptlet, List<String>> scriptlets = getScriptlets(cmd);
if (integrated) {
Set<Scriptlet> requiredScriptlets = Stream.of(Scriptlet.values()).sorted().collect(
Collectors.toSet());
TKit.assertTrue(scriptlets.keySet().containsAll(
requiredScriptlets), String.format(
"Check all required scriptlets %s found in the package. Package scriptlets: %s",
requiredScriptlets, scriptlets.keySet()));
}

// Lookup for xdg commands in postinstall script
String lineWithXsdCommand = verifier.apply(
Files.readAllLines(tempDir.resolve(controlFile)));
// Lookup for xdg commands in scriptlets.
scriptlets.entrySet().forEach(scriptlet -> {
String lineWithXsdCommand = verifier.apply(scriptlet.getValue());
String assertMsg = String.format(
"Check if %s@%s control file uses xdg commands",
cmd.outputBundle(), controlFile);
"Check if [%s] scriptlet uses xdg commands",
scriptlet.getKey());
if (integrated) {
TKit.assertNotNull(lineWithXsdCommand, assertMsg);
} else {
@@ -411,6 +409,19 @@ static void addFileAssociationsVerifier(PackageTest test, FileAssociations fa) {
fa.getMime()));
});
});

test.addBundleVerifier(cmd -> {
final Path mimeTypeIconFileName = fa.getLinuxIconFileName();
if (mimeTypeIconFileName != null) {
// Verify there are xdg registration commands for mime icon file.
Path mimeTypeIcon = cmd.appLayout().destktopIntegrationDirectory().resolve(
mimeTypeIconFileName);

Map<Scriptlet, List<String>> scriptlets = getScriptlets(cmd);
scriptlets.entrySet().stream().forEach(e -> verifyIconInScriptlet(
e.getKey(), e.getValue(), mimeTypeIcon));
}
});
}

private static String queryFileMimeType(Path file) {
@@ -423,6 +434,122 @@ private static String queryMimeTypeDefaultHandler(String mimeType) {
.executeAndGetFirstLineOfOutput();
}

private static void verifyIconInScriptlet(Scriptlet scriptletType,
List<String> scriptletBody, Path iconPathInPackage) {
final String dashMime = IOUtils.replaceSuffix(
iconPathInPackage.getFileName(), null).toString();
final String xdgCmdName = "xdg-icon-resource";

Stream<String> scriptletBodyStream = scriptletBody.stream()
.filter(str -> str.startsWith(xdgCmdName))
.filter(str -> Pattern.compile(
"\\b" + dashMime + "\\b").matcher(str).find());
if (scriptletType == Scriptlet.PostInstall) {
scriptletBodyStream = scriptletBodyStream.filter(str -> List.of(
str.split("\\s+")).contains(iconPathInPackage.toString()));
}

scriptletBodyStream.peek(xdgCmd -> {
Matcher m = XDG_CMD_ICON_SIZE_PATTERN.matcher(xdgCmd);
TKit.assertTrue(m.find(), String.format(
"Check icon size is specified as a number in [%s] xdg command of [%s] scriptlet",
xdgCmd, scriptletType));
int iconSize = Integer.parseInt(m.group(1));
TKit.assertTrue(XDG_CMD_VALID_ICON_SIZES.contains(iconSize),
String.format(
"Check icon size [%s] is one of %s values",
iconSize, XDG_CMD_VALID_ICON_SIZES));
})
.findFirst().orElseGet(() -> {
TKit.assertUnexpected(String.format(
"Failed to find [%s] command in [%s] scriptlet for [%s] icon file",
xdgCmdName, scriptletType, iconPathInPackage));
return null;
});
}

private static Map<Scriptlet, List<String>> getScriptlets(
JPackageCommand cmd, Scriptlet... scriptlets) {
cmd.verifyIsOfType(PackageType.LINUX);

Set<Scriptlet> scriptletSet = Set.of(
scriptlets.length == 0 ? Scriptlet.values() : scriptlets);
switch (cmd.packageType()) {
case LINUX_DEB:
return getDebScriptlets(cmd, scriptletSet);

case LINUX_RPM:
return getRpmScriptlets(cmd, scriptletSet);
}

// Unreachable
return null;
}

private static Map<Scriptlet, List<String>> getDebScriptlets(
JPackageCommand cmd, Set<Scriptlet> scriptlets) {
Map<Scriptlet, List<String>> result = new HashMap<>();
TKit.withTempDirectory("dpkg-control-files", tempDir -> {
// Extract control Debian package files into temporary directory
Executor.of("dpkg", "-e")
.addArgument(cmd.outputBundle())
.addArgument(tempDir)
.execute();

for (Scriptlet scriptlet : scriptlets) {
Path controlFile = Path.of(scriptlet.deb);
result.put(scriptlet, Files.readAllLines(tempDir.resolve(
controlFile)));
}
});
return result;
}

private static Map<Scriptlet, List<String>> getRpmScriptlets(
JPackageCommand cmd, Set<Scriptlet> scriptlets) {
List<String> output = Executor.of("rpm", "-qp", "--scripts",
cmd.outputBundle().toString()).executeAndGetOutput();

Map<Scriptlet, List<String>> result = new HashMap<>();
List<String> curScriptletBody = null;
for (String str : output) {
Matcher m = Scriptlet.RPM_HEADER_PATTERN.matcher(str);
if (m.find()) {
Scriptlet scriptlet = Scriptlet.RPM_MAP.get(m.group(1));
if (scriptlets.contains(scriptlet)) {
curScriptletBody = new ArrayList<>();
result.put(scriptlet, curScriptletBody);
} else if (curScriptletBody != null) {
curScriptletBody = null;
}
} else if (curScriptletBody != null) {
curScriptletBody.add(str);
}
}

return result;
}

private static enum Scriptlet {
PostInstall("postinstall", "postinst"),
PreUninstall("preuninstall", "prerm");

Scriptlet(String rpm, String deb) {
this.rpm = rpm;
this.deb = deb;
}

private final String rpm;
private final String deb;

static final Pattern RPM_HEADER_PATTERN = Pattern.compile(String.format(
"(%s) scriptlet \\(using /bin/sh\\):", Stream.of(values()).map(
v -> v.rpm).collect(Collectors.joining("|"))));

static final Map<String, Scriptlet> RPM_MAP = Stream.of(values()).collect(
Collectors.toMap(v -> v.rpm, v -> v));
};

public static String getDefaultPackageArch(PackageType type) {
if (archs == null) {
archs = new HashMap<>();
@@ -449,5 +576,10 @@ public static String getDefaultPackageArch(PackageType type) {
static final Set<Path> CRITICAL_RUNTIME_FILES = Set.of(Path.of(
"lib/server/libjvm.so"));

static private Map<PackageType, String> archs;
private static Map<PackageType, String> archs;

private final static Pattern XDG_CMD_ICON_SIZE_PATTERN = Pattern.compile("\\s--size\\s+(\\d+)\\b");

// Values grabbed from https://linux.die.net/man/1/xdg-icon-resource
private final static Set<Integer> XDG_CMD_VALID_ICON_SIZES = Set.of(16, 22, 32, 48, 64, 128);
}
@@ -239,6 +239,10 @@ PackageTest addHelloAppFileAssociationsVerifier(FileAssociations fa,
// running check of type of environment.
addHelloAppInitializer(null);

forTypes(PackageType.LINUX, () -> {
LinuxHelper.addFileAssociationsVerifier(this, fa);
});

String noActionMsg = "Not running file associations test";
if (GraphicsEnvironment.isHeadless()) {
TKit.trace(String.format(
@@ -275,10 +279,6 @@ PackageTest addHelloAppFileAssociationsVerifier(FileAssociations fa,
});
});

forTypes(PackageType.LINUX, () -> {
LinuxHelper.addFileAssociationsVerifier(this, fa);
});

return this;
}

0 comments on commit 6098191

Please sign in to comment.