blob: 98ccca6210d5a7d91da0df2edc1d997f9621e5b2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.zip.CRC32;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import org.netbeans.junit.NbTestCase;
/** Some infrastructure for module system tests.
* @author Jesse Glick
*/
public abstract class SetupHid extends NbTestCase {
public SetupHid(String name) {
super(name);
}
/** directory of data files for JARs */
protected File data;
/** directory full of JAR files to test */
protected File jars;
protected @Override void setUp() throws Exception {
Locale.setDefault(Locale.US);
clearWorkDir();
data = new File(getDataDir(), "jars");
jars = new File(getWorkDir(), "jars");
jars.mkdirs();
createTestJARs();
File ud = new File(getWorkDir(), "ud");
ud.mkdirs();
System.setProperty("netbeans.user", ud.getPath());
}
@Override
protected Level logLevel() {
return Level.FINE;
}
protected static void deleteRec(File f) throws IOException {
if (f.isDirectory()) {
File[] kids = f.listFiles();
if (kids == null) throw new IOException("Could not list: " + f);
for (int i = 0; i < kids.length; i++) {
deleteRec(kids[i]);
}
}
if (! f.delete()) throw new IOException("Could not delete: " + f);
}
/** same as FileUtil.copy */
protected static void copyStreams(InputStream is, OutputStream os) throws IOException {
final byte[] BUFFER = new byte[4096];
int len;
for (;;) {
len = is.read(BUFFER);
if (len == -1) {
return;
}
os.write(BUFFER, 0, len);
}
}
protected static void copy(File a, File b) throws IOException {
OutputStream os = new FileOutputStream(b);
try {
copyStreams(new FileInputStream(a), os);
} finally {
os.close();
}
}
/**
* Create a fresh JAR file.
* @param jar the file to create
* @param contents keys are JAR entry paths, values are text contents (will be written in UTF-8)
* @param manifest a manifest to store (key/value pairs for main section)
*/
public static void createJar(File jar, Map<String,String> contents, Map<String,String> manifest) throws IOException {
// XXX use TestFileUtils.writeZipFile
Manifest m = new Manifest();
m.getMainAttributes().putValue("Manifest-Version", "1.0"); // workaround for JDK bug
for (Map.Entry<String,String> line : manifest.entrySet()) {
m.getMainAttributes().putValue(line.getKey(), line.getValue());
}
jar.getParentFile().mkdirs();
OutputStream os = new FileOutputStream(jar);
try {
JarOutputStream jos = new JarOutputStream(os, m);
for (Map.Entry<String, String> entry : contents.entrySet()) {
String path = entry.getKey();
byte[] data = entry.getValue().getBytes("UTF-8");
JarEntry je = new JarEntry(path);
je.setSize(data.length);
CRC32 crc = new CRC32();
crc.update(data);
je.setCrc(crc.getValue());
jos.putNextEntry(je);
jos.write(data);
}
jos.close();
} finally {
os.close();
}
}
private void createTestJARs() throws IOException {
File simpleModule = createTestJAR("simple-module", null);
File dependsOnSimpleModule = createTestJAR("depends-on-simple-module", null, simpleModule);
createTestJAR("dep-on-dep-on-simple", null, simpleModule, dependsOnSimpleModule);
File cyclic1;
{ // cyclic-1
File cyclic1Src = new File(data, "cyclic-1");
File cyclic2Src = new File(data, "cyclic-2");
compile(Arrays.asList(
"-sourcepath", cyclic1Src + File.pathSeparator + cyclic2Src,
"-d", cyclic1Src.getAbsolutePath()),
findSourceFiles(cyclic1Src, cyclic2Src));
cyclic1 = new File(jars, "cyclic-1.jar");
OutputStream os = new FileOutputStream(cyclic1);
try {
JarOutputStream jos = new JarOutputStream(os, loadManifest(new File(data, "cyclic-1.mf")));
try {
jarUp(jos, new File(cyclic1Src, "org/foo"), "org/foo/");
} finally {
jos.close();
}
} finally {
os.close();
}
}
File cyclic2 = createTestJAR("cyclic-2", null, cyclic1);
createTestJAR("depends-on-cyclic-1", null, cyclic1, cyclic2);
File libraryUndecl = createTestJAR("library-undecl", "library-src");
createTestJAR("library-unvers", "library-src");
createTestJAR("library-vers", "library-src");
createTestJAR("library-vers-partial", "library-src");
createTestJAR("depends-on-lib-undecl", "depends-on-library-src", libraryUndecl);
createTestJAR("depends-on-lib-unvers", "depends-on-library-src", libraryUndecl);
createTestJAR("depends-on-lib-vers", "depends-on-library-src", libraryUndecl);
createTestJAR("depends-on-lib-vers-partial", "depends-on-library-src", libraryUndecl);
createTestJAR("fails-on-lib-undecl", "depends-on-library-src", libraryUndecl);
createTestJAR("fails-on-non-existing-package", "depends-on-library-src", libraryUndecl);
createTestJAR("fails-on-lib-unvers", "depends-on-library-src", libraryUndecl);
createTestJAR("fails-on-lib-old", "depends-on-library-src", libraryUndecl);
createTestJAR("prov-foo", null);
createTestJAR("req-foo", null);
createTestJAR("prov-foo-bar", null);
createTestJAR("req-foo-baz", null);
createTestJAR("prov-baz", null);
createTestJAR("prov-foo-req-bar", null);
createTestJAR("prov-bar-req-foo", null);
createTestJAR("prov-bar-dep-cyclic", null);
createTestJAR("rel-ver-2", null);
createTestJAR("dep-on-relvertest-1", null);
createTestJAR("dep-on-relvertest-1-2", null);
createTestJAR("dep-on-relvertest-1-2-nospec", null);
createTestJAR("dep-on-relvertest-2", null);
createTestJAR("dep-on-relvertest-2-3", null);
createTestJAR("dep-on-relvertest-2-3-late", null);
createTestJAR("dep-on-relvertest-2-impl", null);
createTestJAR("dep-on-relvertest-2-impl-wrong", null);
createTestJAR("dep-on-relvertest-2-late", null);
createTestJAR("dep-on-relvertest-3-4", null);
createTestJAR("dep-on-relvertest-some", null);
createTestJAR("depends-on-simple-module-2", null);
createTestJAR("dep-on-needs_foo-simple", null);
createTestJAR("needs-foo", null);
createTestJAR("recommends-foo", null);
createTestJAR("prov-foo-depends-needs_foo", "prov-foo");
createTestJAR("agent", "agent");
createTestJAR("api-mod-export-all", "exposes-api");
createTestJAR("api-mod-export-none", "exposes-api");
File exposesAPI = createTestJAR("api-mod-export-api", "exposes-api");
createTestJAR("api-mod-export-friend", "exposes-api");
createTestJAR("uses-api-simple-dep", "uses-api", exposesAPI);
createTestJAR("uses-api-impl-dep", "uses-api", exposesAPI);
createTestJAR("uses-api-impl-dep-for-friends", "uses-api", exposesAPI);
createTestJAR("uses-api-spec-dep", "uses-api", exposesAPI);
createTestJAR("dep-on-two-modules", null);
File usesAPI = createTestJAR("uses-and-exports-api", "uses-api", exposesAPI);
createTestJAR("uses-api-transitively", null, exposesAPI, usesAPI);
createTestJAR("uses-api-directly", "uses-api-transitively", exposesAPI, usesAPI);
createTestJAR("uses-api-transitively-old", "uses-api-transitively", exposesAPI, usesAPI);
createTestJAR("uses-api-directly-old", "uses-api-transitively", exposesAPI, usesAPI);
createTestJAR("look-for-myself", null);
createTestJAR("uses-api-friend", "uses-api", exposesAPI);
createTestJAR("little-manifest", null);
createTestJAR("medium-manifest", null);
createTestJAR("big-manifest", null);
createTestJAR("patchable", null);
createTestJAR("fragment-module", null);
createTestJAR("host-module", null);
{ // Make the patch JAR specially:
File src = new File(data, "patch");
String srcS = src.getAbsolutePath();
compile(Arrays.asList("-sourcepath", srcS, "-d", srcS), findSourceFiles(src));
File jar = new File(jars, "patches/pkg-subpkg/some-patch.jar");
jar.getParentFile().mkdirs();
OutputStream os = new FileOutputStream(jar);
try {
JarOutputStream jos = new JarOutputStream(os, loadManifest(null));
try {
jarUp(jos, src, "");
} finally {
jos.close();
}
} finally {
os.close();
}
}
File locale = new File(jars, "locale");
locale.mkdirs();
{
OutputStream os = new FileOutputStream(new File(jars, "localized-manifest.jar"));
try {
JarOutputStream jos = new JarOutputStream(os, loadManifest(new File(data, "localized-manifest.mf")));
try {
writeJarEntry(jos, "locmani/Bundle.properties", new File(data, "localized-manifest/locmani/Bundle.properties"));
} finally {
jos.close();
}
} finally {
os.close();
}
os = new FileOutputStream(new File(locale, "localized-manifest_cs.jar"));
try {
JarOutputStream jos = new JarOutputStream(os, loadManifest(null));
try {
writeJarEntry(jos, "locmani/Bundle_cs.properties", new File(data, "localized-manifest/locmani/Bundle_cs.properties"));
} finally {
jos.close();
}
} finally {
os.close();
}
}
{
OutputStream os = new FileOutputStream(new File(jars, "base-layer-mod.jar"));
try {
JarOutputStream jos = new JarOutputStream(os, loadManifest(new File(data, "base-layer-mod.mf")));
try {
writeJarEntry(jos, "baselayer/layer.xml", new File(data, "base-layer-mod/baselayer/layer.xml"));
} finally {
jos.close();
}
} finally {
os.close();
}
os = new FileOutputStream(new File(locale, "base-layer-mod_cs.jar"));
try {
JarOutputStream jos = new JarOutputStream(os, loadManifest(null));
try {
writeJarEntry(jos, "baselayer/layer_cs.xml", new File(data, "base-layer-mod/baselayer/layer_cs.xml"));
} finally {
jos.close();
}
} finally {
os.close();
}
os = new FileOutputStream(new File(locale, "base-layer-mod_foo.jar"));
try {
JarOutputStream jos = new JarOutputStream(os, loadManifest(null));
try {
writeJarEntry(jos, "baselayer/layer_foo.xml", new File(data, "base-layer-mod/baselayer/layer_foo.xml"));
} finally {
jos.close();
}
} finally {
os.close();
}
createTestJAR("override-layer-mod", null);
}
}
private static void compile(List<String> options, Iterable<File> files) throws IOException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager mgr = compiler.getStandardFileManager(null, null, null);
List<String> fullOptions = new ArrayList<String>(options);
fullOptions.addAll(Arrays.asList("-source", "8", "-target", "8"));
if (!compiler.getTask(null, mgr, null, fullOptions, null, mgr.getJavaFileObjectsFromFiles(files)).call()) {
throw new IOException("compilation failed");
}
}
private File createTestJAR(String name, String srcdir, File... classpath) throws IOException {
return createTestJAR(data, jars, name, srcdir, classpath);
}
public static File createTestJAR(File data, File jars, String name, String srcdir, File... classpath) throws IOException {
File srcdirF = null;
File d = new File(data, srcdir != null ? srcdir : name);
if (d.isDirectory()) {
srcdirF = d;
}
File manifestF = null;
File f = new File(data, name + ".mf");
if (f.isFile()) {
manifestF = f;
}
if (srcdirF != null) {
assert srcdirF.isDirectory();
List<File> sourceFiles = findSourceFiles(srcdirF);
if (!sourceFiles.isEmpty()) {
StringBuilder cp = new StringBuilder(System.getProperty("java.class.path")); // o.o.util, o.o.modules
for (File j : classpath) {
cp.append(File.pathSeparatorChar);
cp.append(j);
}
compile(Arrays.asList(
"-classpath", cp.toString(),
"-sourcepath", srcdirF.getAbsolutePath(),
"-d", srcdirF.getAbsolutePath()),
sourceFiles);
}
}
// XXX use TestFileUtils.writeZipFile (or JarBuilder)
File jar = new File(jars, name + ".jar");
jars.mkdirs();
OutputStream os = new FileOutputStream(jar);
try {
JarOutputStream jos = new JarOutputStream(os, loadManifest(manifestF));
try {
if (srcdirF != null) {
jarUp(jos, srcdirF, "");
}
} finally {
jos.close();
}
} finally {
os.close();
}
return jar;
}
private static Manifest loadManifest(File mani) throws IOException {
Manifest m = new Manifest();
if (mani != null) {
InputStream is = new FileInputStream(mani);
try {
m.read(is);
} finally {
is.close();
}
}
m.getMainAttributes().putValue("Manifest-Version", "1.0"); // workaround for JDK bug
return m;
}
private static List<File> findSourceFiles(File... roots) {
List<File> sourceFiles = new ArrayList<File>();
for (File root : roots) {
doFindSourceFiles(sourceFiles, root);
}
return sourceFiles;
}
private static void doFindSourceFiles(List<File> sourceFiles, File srcdir) {
for (File k : srcdir.listFiles()) {
if (k.getName().endsWith(".java")) {
sourceFiles.add(k);
} else if (k.isDirectory()) {
doFindSourceFiles(sourceFiles, k);
}
}
}
private static void jarUp(JarOutputStream jos, File dir, String prefix) throws IOException {
for (File f : dir.listFiles()) {
String path = prefix + f.getName();
if (f.getName().endsWith(".java")) {
continue;
} else if (f.isDirectory()) {
jarUp(jos, f, path + "/");
} else {
writeJarEntry(jos, path, f);
}
}
}
private static void writeJarEntry(JarOutputStream jos, String path, File f) throws IOException, FileNotFoundException {
JarEntry je = new JarEntry(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = new FileInputStream(f);
try {
copyStreams(is, baos);
} finally {
is.close();
}
byte[] data = baos.toByteArray();
je.setSize(data.length);
CRC32 crc = new CRC32();
crc.update(data);
je.setCrc(crc.getValue());
jos.putNextEntry(je);
jos.write(data);
}
protected File changeManifest(File orig, String manifest) throws IOException {
File f = new File(getWorkDir(), orig.getName());
Manifest mf = new Manifest(new ByteArrayInputStream(manifest.getBytes("utf-8")));
mf.getMainAttributes().putValue("Manifest-Version", "1.0");
JarOutputStream os = new JarOutputStream(new FileOutputStream(f), mf);
JarFile jf = new JarFile(orig);
Enumeration<JarEntry> en = jf.entries();
InputStream is;
while (en.hasMoreElements()) {
JarEntry e = en.nextElement();
if (e.getName().equals("META-INF/MANIFEST.MF")) {
continue;
}
os.putNextEntry(e);
is = jf.getInputStream(e);
byte[] arr = new byte[4096];
for (;;) {
int len = is.read(arr);
if (len == -1) {
break;
}
os.write(arr, 0, len);
}
is.close();
os.closeEntry();
}
os.close();
return f;
}
}