package org.graalvm.component.installer.persist;
import java.io.BufferedReader;
import org.graalvm.component.installer.MetadataException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.jar.JarFile;
import org.graalvm.component.installer.BundleConstants;
import org.graalvm.component.installer.CommonConstants;
import org.graalvm.component.installer.FailedOperationException;
import org.graalvm.component.installer.TestBase;
import org.graalvm.component.installer.jar.JarMetaLoader;
import org.graalvm.component.installer.model.ComponentInfo;
import org.graalvm.component.installer.model.DistributionType;
import org.junit.After;
import org.junit.Assert;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.ExternalResource;
import org.junit.rules.TestName;
public class ComponentPackageLoaderTest extends TestBase {
@Rule public TestName name = new TestName();
@Rule public ExpectedException exception = ExpectedException.none();
private Properties data = new Properties();
private JarFile jarData;
public ComponentPackageLoaderTest() {
}
private JarFile jf;
@Rule public ExternalResource jarFileResource = new ExternalResource() {
@Override
protected void after() {
if (jf != null) {
try {
jf.close();
} catch (IOException ex) {
}
}
super.after();
}
};
@Before
public void setUp() throws Exception {
defaultBundle = ResourceBundle.getBundle("org.graalvm.component.installer.persist.Bundle");
String s = name.getMethodName();
if (s.startsWith("test") && s.length() > 4) {
s = Character.toLowerCase(s.charAt(4)) + s.substring(5);
}
Path dp = dataFile("data/" + s + ".jar");
if (Files.exists(dp)) {
this.jarData = new JarFile(dp.toFile());
}
try (InputStream istm = getClass().getResourceAsStream("data/" + s + ".properties")) {
if (istm != null) {
data.load(istm);
}
}
}
@After
public void tearDown() throws Exception {
if (jarData != null) {
jarData.close();
}
}
ComponentInfo info() throws IOException {
if (jarData == null) {
return new ComponentPackageLoader(data::getProperty, this).createComponentInfo();
} else {
return new JarMetaLoader(jarData, this).createComponentInfo();
}
}
@Test
@SuppressWarnings({"rawtypes", "unchecked"})
public void testRequiredKeys() throws Exception {
info();
Properties save = new Properties();
save.putAll(this.data);
List<String> sorted = new ArrayList<>((Set) save.keySet());
Collections.sort(sorted);
for (String s : save.stringPropertyNames()) {
data = new Properties();
data.putAll(save);
data.remove(s);
try {
info();
Assert.fail("Pasrer must reject component without key " + s);
} catch (MetadataException ex) {
}
}
}
@Test
public void testWorkingDirectories() throws Exception {
info = info();
assertTrue(info.getWorkingDirectories().contains("jre/languages/test/scrap"));
assertTrue(info.getWorkingDirectories().contains("jre/lib/test/scrapdir"));
}
@Test
public void testCollectErrors() throws Exception {
File f = dataFile("broken1.zip").toFile();
jf = new JarFile(f);
loader = new JarMetaLoader(jf, this).infoOnly(true);
info = loader.createComponentInfo();
List<String> errs = new ArrayList<>();
loader.getErrors().forEach((e) -> errs.add(((MetadataException) e).getOffendingHeader()));
Collections.sort(errs);
assertEquals("org.graalvm.ruby", info.getId());
assertEquals(Arrays.asList(
BundleConstants.BUNDLE_NAME,
BundleConstants.BUNDLE_REQUIRED,
BundleConstants.BUNDLE_VERSION), errs);
}
private ComponentPackageLoader loader;
private ComponentInfo info;
private void setupLoader() throws IOException {
File f = dataFile("data/truffleruby2.jar").toFile();
jf = new JarFile(f);
loader = new JarMetaLoader(jf, this);
info = loader.createComponentInfo();
}
@Test
public void testLoadPaths() throws Exception {
setupLoader();
assertTrue(info.getPaths().isEmpty());
loader.loadPaths();
assertFalse(info.getPaths().isEmpty());
assertTrue(info.getPaths().contains("jre/bin/ruby"));
}
@Test
public void testLoadSymlinks() throws Exception {
setupLoader();
loader.loadPaths();
Map<String, String> slinks = loader.loadSymlinks();
assertNotNull(slinks);
assertNotNull(slinks.get("bin/ruby"));
loader.parseSymlinks(new Properties());
}
@Test
public void testComponetInfoFromJar() throws Exception {
setupLoader();
assertNotNull(info.getId());
assertEquals("1.0", info.getVersionString());
Map<String, String> caps = info.getRequiredGraalValues();
assertNotNull(caps);
assertNotNull(caps.get(CommonConstants.CAP_GRAALVM_VERSION));
}
@Test
public void testParseChainedSymlinks() throws Exception {
setupLoader();
loader.loadPaths();
Properties links = new Properties();
try (InputStream istm = new FileInputStream(dataFile("chainedSymlinks.properties").toFile())) {
links.load(istm);
}
loader.parseSymlinks(links);
}
@Test
public void testParseCircularSymlink() throws Exception {
setupLoader();
loader.loadPaths();
Properties links = new Properties();
try (InputStream istm = new FileInputStream(dataFile("circularSymlinks.properties").toFile())) {
links.load(istm);
}
exception.expect(FailedOperationException.class);
exception.expectMessage("ERROR_CircularSymlink");
loader.parseSymlinks(links);
}
@Test
public void testBrokenSymlink() throws Exception {
setupLoader();
loader.loadPaths();
Properties links = new Properties();
try (InputStream istm = new FileInputStream(dataFile("brokenSymlinks.properties").toFile())) {
links.load(istm);
}
exception.expect(FailedOperationException.class);
exception.expectMessage("ERROR_BrokenSymlink");
loader.parseSymlinks(links);
}
@Test
public void testBrokenChainedSymlink() throws Exception {
setupLoader();
loader.loadPaths();
Properties links = new Properties();
try (InputStream istm = new FileInputStream(dataFile("brokenChainedSymlinks.properties").toFile())) {
links.load(istm);
}
exception.expect(FailedOperationException.class);
exception.expectMessage("ERROR_BrokenSymlink");
loader.parseSymlinks(links);
}
@Test
public void testLoadInvalidPermssions() throws Exception {
setupLoader();
exception.expect(FailedOperationException.class);
exception.expectMessage("ERROR_PermissionFormat");
try (InputStream istm = Files.newInputStream(dataFile("brokenPermissions.properties"));
BufferedReader br = new BufferedReader(new InputStreamReader(istm))) {
loader.parsePermissions(br);
}
}
@Test
public void testLoadPermssions() throws Exception {
setupLoader();
Map<String, String> permissions = loader.loadPermissions();
String v = permissions.get("./jre/languages/ruby/bin/gem");
assertEquals("", v);
v = permissions.get("./jre/bin/ruby");
assertEquals("r-xr-xr-x", v);
}
@Test
public void testPostinstMessage() throws Exception {
info = info();
String[] slashes = info.getPostinstMessage().split("\\\\");
assertEquals(3, slashes.length);
String[] lines = info.getPostinstMessage().split("\n");
assertEquals(4, lines.length);
}
@Test
public void testFastr() throws Exception {
info = info();
assertEquals(1, info.getDependencies().size());
assertEquals("org.graalvm.llvm-toolchain", info.getDependencies().iterator().next());
}
@Test
public void testDistributionTypeMissing() throws Exception {
info = info();
assertEquals(DistributionType.OPTIONAL, info.getDistributionType());
}
@Test
public void testDistributionTypeBundled() throws Exception {
info = info();
assertEquals(DistributionType.BUNDLED, info.getDistributionType());
}
@Test
public void testDistributionTypeInvalid() throws Exception {
exception.expect(MetadataException.class);
info = info();
}
}