blob: 45bb5c50e9868e357822acf1a8a43f3ef8ded543 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2009 IBM Corporation and others. All rights reserved. This
* program and the accompanying materials are made available under the terms of
* the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors: IBM Corporation - initial API and implementation
* This file originally came from 'Eclipse Orbit' project then adapted to use
* in WTP and improved to use 'Manifest' to read manifest.mf, instead of reading
* it as a properties file.
******************************************************************************/
package org.eclipse.wtp.layout.tests;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.PatternSyntaxException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.eclipse.osgi.util.ManifestElement;
import org.osgi.framework.BundleException;
/**
* @since 3.3
*/
public class TestLayout extends TestCase {
private static final String EXTENSION_JAR = ".jar";
private static final String EXTENSION_ZIP = ".zip";
private static final String PROPERTY_BUNDLE_ID = "Bundle-SymbolicName";
private static final String CONFIG = "config.properties";
private static final String KEY_DFT_BIN_JAR = "default.binary.jar";
private static final String KEY_DFT_SRC_JAR = "default.source.jar";
private static final String KEY_DFT_BIN_ZIP = "default.binary.zip";
private static final String KEY_DFT_SRC_ZIP = "default.source.zip";
private static final String EOL = System.getProperty("line.separator", "\n");
private Properties config;
private List errors = new ArrayList();
public static Test suite() {
System.out.println("suite for layout");
return new TestSuite(TestLayout.class);
}
public void addError(String message) {
errors.add(message);
}
public void testBundleLayout() throws Exception {
dumpAllProperties();
String homeLocation = System.getProperty("org.eclipse.wtp.inputDir");
if (homeLocation == null) {
homeLocation = System.getProperty("eclipse.home.location");
}
if (!homeLocation.endsWith("/")) {
homeLocation = homeLocation + "/";
}
// given the nature of this test, we can just check all possible directories,
String dirLocation = homeLocation + "dropins/eclipse/plugins/";
checkDirectory(dirLocation);
dirLocation = homeLocation + "dropins/eclipse/features/";
checkDirectory(dirLocation);
dirLocation = homeLocation + "plugins/";
checkDirectory(dirLocation);
dirLocation = homeLocation + "features/";
checkDirectory(dirLocation);
if (errors.size() > 0) {
Collections.sort(errors);
FileWriter outfileWriter = null;
File outfile = null;
String testOutputDirName = System.getProperty("junit-report-output");
try {
outfile = new File(testOutputDirName, "layoutErrors.html");
outfileWriter = new FileWriter(outfile);
introduceLayoutErrors(outfileWriter);
listErrors(outfileWriter);
}
finally {
if (outfileWriter != null) {
outfileWriter.close();
}
}
fail("Errors in bundle layout. For list, see " + outfile.getAbsolutePath());
}
}
private void checkDirectory(String direcotoryPath) throws MalformedURLException {
URL url = new URL(direcotoryPath);
String directoryToCheck = url.getPath();
System.out.println("directory to check: " + directoryToCheck);
assertNotNull("Need to set inputDir for tests.", directoryToCheck);
File inputdir = new File(directoryToCheck);
assertTrue("inputDir (" + directoryToCheck + ") must exist.", inputdir.exists());
assertTrue("inputDir (" + directoryToCheck + ") must be an existing directory.", inputdir.exists() && inputdir.isDirectory());
File[] children = inputdir.listFiles();
for (int i = 0; i < children.length; i++) {
File child = children[i];
String id = getBundleId(child);
if (id != null) {
if (id.endsWith(".source") || id.endsWith(".infopop") || id.endsWith(".doc.user") || id.endsWith(".doc") || id.endsWith(".doc.isv") || id.endsWith(".doc.dev") || id.endsWith(".doc.api") || id.endsWith("standard.schemas") || id.endsWith(".branding"))
processBundle(child, getExpected(id, true, child.getName().endsWith(EXTENSION_ZIP)));
else
processBundle(child, getExpected(id, false, child.getName().endsWith(EXTENSION_ZIP)));
}
}
}
private void listErrors(FileWriter outfileWriter) throws IOException {
outfileWriter.write("<ul>" + EOL);
for (Iterator iter = errors.iterator(); iter.hasNext();) {
outfileWriter.write("<li>" + (String) iter.next() + "</li>" + EOL);
}
outfileWriter.write("</ul>" + EOL);
}
private void introduceLayoutErrors(FileWriter outfileWriter) throws IOException {
outfileWriter.write("<p>" + EOL);
outfileWriter.write("The layout test looks for files which are required, " + EOL);
outfileWriter.write("or common. Missing about.html files are considered blocking " + EOL);
outfileWriter.write("defects (for a release). Missing properties files indicates " + EOL);
outfileWriter.write("that strings are not externalized. Or, either of these may indicate a mistake " + EOL);
outfileWriter.write("in the build.properties file. The test for class or jar files is meant as a " + EOL);
outfileWriter.write("sanity check that the build worked as expected. There are many cases, though, " + EOL);
outfileWriter.write("where it is expected to have no class or jar files, in which case the " + EOL);
outfileWriter.write("bundle needs to be added to an explicit list of exceptions" + EOL);
outfileWriter.write("</p>" + EOL);
}
/*
* Check the configuration file and return a set of regular expressions
* which match the list of files that are expected to be in the bundle.
*/
private Set getExpected(String id, boolean source, boolean zip) {
// is the config cached?
if (config == null) {
config = new Properties();
InputStream input = null;
try {
input = this.getClass().getResourceAsStream(CONFIG);
assertNotNull("Unable to load configuration file.", input);
config.load(input);
}
catch (IOException e) {
e.printStackTrace();
fail(e.getMessage());
}
finally {
try {
if (input != null)
input.close();
}
catch (IOException e) {
// ignore
}
}
}
String line = config.getProperty(id);
if (line == null) {
if (source)
line = zip ? config.getProperty(KEY_DFT_SRC_ZIP) : config.getProperty(KEY_DFT_SRC_JAR);
else
line = zip ? config.getProperty(KEY_DFT_BIN_ZIP) : config.getProperty(KEY_DFT_BIN_JAR);
}
if (line == null)
fail("Unable to load settings for: " + id);
Set result = new HashSet();
for (StringTokenizer tokenizer = new StringTokenizer(line, ","); tokenizer.hasMoreTokens();)
result.add(tokenizer.nextToken().trim());
return result;
}
/*
* Process the bundle at the specified location, with the given set of
* expected results.
*/
private void processBundle(File file, Set expected) {
if (file.isDirectory()) {
String[] array = (String[]) expected.toArray(new String[expected.size()]);
processDir("", file, array);
for (int i = 0; i < array.length; i++)
if (array[i] != null)
addError("Missing pattern or file: " + array[i] + " in dir: " + file.getName());
}
else
processArchive(file, (String[]) expected.toArray(new String[expected.size()]));
}
/*
* The bundle is an archive. Make sure it has the right contents.
*/
private void processArchive(File file, String[] expected) {
ZipFile zip = null;
try {
zip = new ZipFile(file, ZipFile.OPEN_READ);
for (Enumeration e = zip.entries(); e.hasMoreElements();) {
ZipEntry entry = (ZipEntry) e.nextElement();
String name = entry.getName();
for (int i = 0; i < expected.length; i++) {
String pattern = expected[i];
if (pattern == null)
continue;
try {
if (name.matches(pattern))
expected[i] = null;
}
catch (PatternSyntaxException ex) {
ex.printStackTrace();
fail(ex.getMessage());
}
}
}
for (int i = 0; i < expected.length; i++) {
if (expected[i] != null)
addError("Missing pattern or file: " + expected[i] + " in file: " + file.getName());
}
}
catch (IOException e) {
e.printStackTrace();
fail(e.getMessage());
}
finally {
if (zip != null)
try {
zip.close();
}
catch (IOException e) {
// ignore
}
}
}
/*
* The bundle is in a directory.
*/
private void processDir(String root, File dir, String[] expected) {
File[] children = dir.listFiles();
for (int index = 0; index < children.length; index++) {
File child = children[index];
String name = root.length() == 0 ? child.getName() : root + '/' + child.getName();
if (child.isDirectory()) {
processDir(name, child, expected);
continue;
}
for (int i = 0; i < expected.length; i++) {
String pattern = expected[i];
if (pattern == null)
continue;
try {
if (name.matches(pattern))
expected[i] = null;
}
catch (PatternSyntaxException ex) {
ex.printStackTrace();
addError(ex.getMessage());
continue;
}
}
}
}
/*
* Return the bundle id from the manifest pointed to by the given input
* stream.
*/
private String getBundleIdFromManifest(InputStream input, String path) {
String id = null;
try {
Map attributes = ManifestElement.parseBundleManifest(input, null);
id = (String) attributes.get(PROPERTY_BUNDLE_ID);
if (id == null || id.length() == 0) {
addError("BundleSymbolicName header not set in manifest for bundle: " + path);
}
else {
int pos = id.indexOf(';');
if (pos > 0) {
id = id.substring(0, pos);
}
}
}
catch (BundleException e) {
e.printStackTrace();
addError(e.getMessage());
}
catch (IOException e) {
e.printStackTrace();
addError(e.getMessage());
}
finally {
if (input != null)
try {
input.close();
}
catch (IOException e) {
// ignore
}
}
return id;
}
/*
* Return the bundle identifier for the bundle contained in the given
* archive/directory.
*/
private String getBundleId(File file) {
String id = null;
if (file.isDirectory())
id = getBundleIdFromDir(file);
else if (file.getName().toLowerCase().endsWith(EXTENSION_ZIP))
id = getBundleIdFromZIP(file);
else if (file.getName().toLowerCase().endsWith(EXTENSION_JAR))
id = getBundleIdFromJAR(file);
return id;
}
private String getBundleIdFromZIP(File file) {
ZipFile zip = null;
try {
zip = new ZipFile(file);
for (Enumeration e = zip.entries(); e.hasMoreElements();) {
ZipEntry entry = (ZipEntry) e.nextElement();
if (entry.getName().matches("^.*/" + JarFile.MANIFEST_NAME)) {
InputStream input = zip.getInputStream(entry);
try {
return getBundleIdFromManifest(input, file.getAbsolutePath());
}
finally {
try {
input.close();
}
catch (IOException ex) {
// ignore
}
}
}
}
}
catch (IOException ex) {
ex.printStackTrace();
addError(ex.getMessage());
// ignore
}
finally {
try {
zip.close();
}
catch (IOException ex) {
// ignore
}
}
addError("Bundle manifest (MANIFEST.MF) not found in bundle: " + file.getAbsolutePath());
return null;
}
/*
* The given file points to an expanded bundle on disc. Look into the
* bundle manifest file to find the bundle identifier.
*/
private String getBundleIdFromDir(File dir) {
String id = null;
File manifestFile = new File(dir, JarFile.MANIFEST_NAME);
if (!manifestFile.exists() || !manifestFile.isFile()) {
addError("Bundle manifest (MANIFEST.MF) not found at: " + manifestFile.getAbsolutePath());
}
else {
try {
id = getBundleIdFromManifest(new FileInputStream(manifestFile), manifestFile.getAbsolutePath());
}
catch (FileNotFoundException e) {
e.printStackTrace();
addError(e.getMessage());
}
}
return id;
}
/*
* The given file points to a bundle contained in an archive. Look into
* the bundle manifest file to find the bundle identifier.
*/
private String getBundleIdFromJAR(File file) {
InputStream input = null;
JarFile jar = null;
try {
jar = new JarFile(file, false, ZipFile.OPEN_READ);
JarEntry entry = jar.getJarEntry(JarFile.MANIFEST_NAME);
if (entry == null) {
addError("Bundle does not contain a MANIFEST.MF file: " + file.getAbsolutePath());
return null;
}
input = jar.getInputStream(entry);
return getBundleIdFromManifest(input, file.getAbsolutePath());
}
catch (IOException e) {
e.printStackTrace();
addError(e.getMessage());
return null;
}
finally {
if (input != null)
try {
input.close();
}
catch (IOException e) {
// ignore
}
if (jar != null)
try {
jar.close();
}
catch (IOException e) {
// ignore
}
}
}
void dumpAllProperties() {
System.out.println();
System.out.println(" = = = Dump All Properties = = = ");
System.out.println();
java.util.Enumeration enumeration = System.getProperties().keys();
String key = null;
while (enumeration.hasMoreElements()) {
key = (String) enumeration.nextElement();
System.out.print(key);
for (int i = 0; i < (30 - key.length()); i++)
System.out.print(" ");
System.out.println("->" + System.getProperty(key));
}
System.out.println();
System.out.println("= = = Dump All Properties = = = ");
System.out.println();
}
}