blob: 7d55289505bb248a9ee378be4deed7a014789c6a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2010 VMware Inc.
* 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:
* VMware Inc. - initial contribution
*******************************************************************************/
package org.eclipse.virgo.util.parser.manifest;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.eclipse.virgo.util.parser.manifest.ManifestContents;
import org.eclipse.virgo.util.parser.manifest.ManifestParser;
import org.eclipse.virgo.util.parser.manifest.ManifestProblem;
import org.eclipse.virgo.util.parser.manifest.ManifestProblemKind;
import org.eclipse.virgo.util.parser.manifest.RecoveringManifestParser;
import org.eclipse.virgo.util.parser.manifest.internal.RecoveringManifestLexer;
import org.eclipse.virgo.util.parser.manifest.internal.TestVisitor;
import junit.framework.TestCase;
public class RecoveringManifestParserTests extends TestCase {
private static final String ABC = "abcdefghijklmnopqrstuvwxyz";
TestVisitor v;
RecoveringManifestParser mp;
/**
*
* Example of how to basically create a parser, parse a file then check for the problems.
*
* @throws Exception sometimes
*/
public void testBasicParserUsage() throws Exception {
ManifestParser mParser = new RecoveringManifestParser();
try (InputStream broken = new FileInputStream("build/resources/test/broken001.mf")) {
ManifestContents contents = mParser.parse(new InputStreamReader(broken));
// has errors but recoverable
assertTrue(mParser.foundProblems());
Map<String, String> mainAttrs = contents.getMainAttributes();
assertEquals("toys", mainAttrs.get("Bundle-Name"));
assertEquals("1.0", contents.getVersion());
Map<String, String> secondaryAttrs = contents.getAttributesForSection("secondSection");
assertEquals("secondSection", secondaryAttrs.get("Name"));
List<String> sectionNames = contents.getSectionNames();
assertEquals("secondSection", sectionNames.get(0));
}
}
public void testNameTooLong() {
StringBuffer sb = new StringBuffer();
sb.append("Manifest-Version: 1.0\n");
while (sb.length() < (RecoveringManifestLexer.MAX_TOKEN_LENGTH + 10000)) {
sb.append(ABC);
}
sb.append("abc: value\n");
ManifestParser mParser = new RecoveringManifestParser();
ManifestContents contents = mParser.parse(sb.toString());
assertNotNull(contents);
assertTrue(mParser.foundProblems());
assertEquals(ManifestProblemKind.NAME_TOO_LONG, mParser.getProblems().get(0).getKind());
}
public void testValueTooLong() throws Exception {
StringBuffer sb = new StringBuffer();
sb.append("Manifest-Version: 1.0\nName:\n");
while (sb.length() < (RecoveringManifestLexer.MAX_TOKEN_LENGTH + 10000)) {
sb.append(" " + ABC).append("\n");
}
ManifestParser mParser = new RecoveringManifestParser();
ManifestContents contents = mParser.parse(sb.toString());
assertNotNull(contents);
assertTrue(mParser.foundProblems());
assertEquals(ManifestProblemKind.VALUE_TOO_LONG, mParser.getProblems().get(0).getKind());
}
// Checking the isXXX methods in the lexer now by passing chars with a value
// > 256 (this means the lookup tables won't be used)
public void testFunkyChars() {
ManifestParser mParser = new RecoveringManifestParser();
mParser.parse("Manifest-Version: 1.0\nName: " + new Character((char) 257));
mParser = new RecoveringManifestParser();
mParser.parse("Manifest-Version: 1.0\n" + new Character((char) 257) + "ame: value");
mParser = new RecoveringManifestParser();
mParser.parse("Manifest-Version: 1.0\nN" + new Character((char) 257) + "ame: value");
}
public void testSkippingSections() {
TestVisitor tv = new TestVisitor();
tv.setTerminateAfterMainSection(true);
ManifestParser mParser = new RecoveringManifestParser(tv);
ManifestContents contents = mParser.parse("Manifest-Version: 1.0\na: b\n\nName: Second\nc: d\n");
assertFalse(mParser.foundProblems());
// Attributes are "Manifest-Version" and "a"
assertEquals(2, contents.getMainAttributes().size());
assertTrue(contents.getSectionNames().isEmpty());
}
public void testSkippingSections2() {
ManifestParser mParser = new RecoveringManifestParser();
mParser.setTerminateAfterMainSection(true);
ManifestContents contents = mParser.parse("Manifest-Version: 1.0\na: b\n\nName: Second\nc: d\n\nName: Third\ne: f\n");
assertFalse(mParser.foundProblems());
// Attributes are "Manifest-Version" and "a"
assertEquals(2, contents.getMainAttributes().size());
// Nothing there:
assertEquals(0, contents.getSectionNames().size());
}
public void testTheMostBasic() throws Exception {
parse("Manifest-Version: 1.0");
assertEquals("1.0", v.getVersion());
parse("Manifest-Version:\n 1.0");
assertEquals("1.0", v.getVersion());
}
// public void testRogueNewlines() throws Exception {
// parse("\n\nManifest-Version: 1.0");
// assertEquals("1.0", v.getVersion());
// assertTrue(mp.foundProblems());
// assertEquals(1, mp.getProblems().size());
// }
public void testErrorScenarios() throws Exception {
parse("Manifest-Version\n 1.0");
assertEquals("1.0", v.getVersion());
// MP002:[line 1, col 0]: Name header ended prematurely when a newline
// was encountered. Expected form is Name: Value
assertTrue(mp.foundProblems());
assertEquals(1, mp.getProblems().size());
}
public void testErrorScenarios2() throws Exception {
// Here the manifest-version is not found, because there is no space on
// the second line ahead of 1.0 - so we assume it is a
// new header
parse("Manifest-Version\n1.0");
// assertEquals("1.0", v.getVersion());
// Printing problems: #5
// Manifest-Version
// ^ ^
// MP002:[line 1, col 0]: Name header ended prematurely when a newline
// was encountered. Expected form is Name: Value
// Manifest-Version
// ^ ^
// MP004:[line 1, col 0]: The value appears to be missing for the header
// name 'Manifest-Version'
// 1.0
// ^
// MP005:[line 2, col 1]: The name of a header cannot contain the
// character '.'
// 1.0
// ^ ^
// MP002:[line 2, col 0]: Name header ended prematurely when a newline
// was encountered. Expected form is Name: Value
// 1.0
// ^ ^
// MP004:[line 2, col 0]: The value appears to be missing for the header
// name '1.0'
assertTrue(mp.foundProblems());
assertEquals(5, mp.getProblems().size());
}
public void testErrorScenarios3() throws Exception {
// Now we work out that they have missed the colon and the value is on
// the second line
parse("Manifest-Version\n 1.0");
// Manifest-Version
// ^ ^
// MP002:[line 1, col 0]: Name header ended prematurely when a newline
// was encountered. Expected form is Name: Value
assertEquals("1.0", v.getVersion());
assertTrue(mp.foundProblems());
assertEquals(1, mp.getProblems().size());
}
public void testSimpleSetOfNameValueHeaders() throws Exception {
parse("Manifest-Version: 1.0\na: b\nc: d");
v.assertHeaderCount(3);
}
public void testTwoSectionManifest() throws Exception {
parse("Manifest-Version: 1.0\na: b\nc: d\n\nfoo: bar\nWibble: wobble");
v.assertSecondarySectionsCount(1);
}
private void readFromJar(String manifestName) {
try (ZipFile manifestTestDataZip = new ZipFile("build/resources/test/manifests.zip")) {
ZipEntry manifestZipEntry = manifestTestDataZip.getEntry(manifestName);
InputStreamReader inputStreamReader = new InputStreamReader(manifestTestDataZip.getInputStream(manifestZipEntry));
parse(inputStreamReader);
} catch (IOException e) {
e.printStackTrace();
fail("Unexpected IOException " + e.getMessage());
}
}
// --- tests involving testdata on the disk
public void testBasicOsgiManifest() throws Exception {
readFromJar("manifest0000.mf");
assertFalse(mp.foundProblems());
assertContainsHeaderNameValuePair("Bundle-Localization", "plugin");
}
// public void testBasicOsgiManifest0011() throws Exception {
// parse(readFromJar("manifest0011.mf"));
// assertTrue(mp.foundProblems());
// assertProblem(ManifestProblemKind.UNEXPECTED_NAME);
// }
// public void testBasicOsgiManifest0210() throws Exception {
// parse(readFromJar("manifest0210.mf"));
// assertTrue(mp.foundProblems());
// assertProblem(ManifestProblemKind.UNEXPECTED_BLANK_LINES_AT_START_OF_MANIFEST);
// // assertProblem(ManifestProblemKind.UNEXPECTED_TOKEN_KIND);
// // assertProblem(ManifestProblemKind.UNEXPECTED_EOM);
// }
public void testStreamParsing() throws IOException {
Reader iStream = new StringReader(
"Manifest-Version: 1.0\r\nGrobble: gribble\r\n\r\n\r\nName: wobble\r\nFlibble:\r\n Fl\r\n ob\r\n le");
mp = new RecoveringManifestParser(v = new TestVisitor());
mp.parse(iStream);
// printProblems();
assertFalse(mp.foundProblems());
}
public void testBrokenManifests001() throws Exception {
parse(new File("build/resources/test/broken001.mf"));
assertTrue(mp.foundProblems());
// printProblems();
assertProblem(ManifestProblemKind.NAME_ENDED_WITH_SPACE_RATHER_THAN_COLON);
}
public void testMessageFormatting() {
String fmtd = ManifestProblemKind.NAME_MUST_START_WITH_ALPHANUMERIC.format(0, 0, "test");
assertNotNull(fmtd);
}
public void testBrokenManifests002() {
parse(getTestFile("broken002.mf"));
assertTrue(mp.foundProblems());
// printProblems();
// printHeaders();
assertProblem(ManifestProblemKind.NAME_ENDED_WITH_SPACE_RATHER_THAN_COLON);
assertProblem(ManifestProblemKind.VALUE_MUST_START_WITH_SPACE);
assertProblem(ManifestProblemKind.VALUE_MUST_IMMEDIATELY_FOLLOW_NAME);
assertProblem(ManifestProblemKind.ILLEGAL_NAME_CHAR);
}
public void testBrokenManifests003() {
parse(getTestFile("broken003.mf"));
assertTrue(mp.foundProblems());
// Name = secondSection
// intendedValueContinuationForLastLine =
// MissingColon = abcde
// OrdinaryName = OrdinaryValue
assertTrue(mp.getManifestContents().getAttributesForSection("secondSection").size() == 4);
assertProblem(ManifestProblemKind.NAME_ENDED_PREMATURELY_WITH_NEWLINE);
assertProblem(ManifestProblemKind.MISSING_VALUE);
}
// Funky package name char
public void testBasicOsgiManifest004() {
parse(getTestFile("broken004.mf"));
String myway = v.getMainAttributes().get("Export-Package");
assertEquals(".,p!yuck, a^f", myway);
assertFalse(mp.foundProblems());
}
public void testAllManifestTestData() throws Exception {
// few warmups before the monitored run
runPerformanceTest(false);
runPerformanceTest(false);
runPerformanceTest(false);
runPerformanceTest(true);
}
public void runPerformanceTest(boolean measure) throws Exception {
ZipFile manifestTestDataZip = new ZipFile("build/resources/test/manifests.zip");
try {
Enumeration<? extends ZipEntry> manifestsFromZip = manifestTestDataZip.entries();
int c = 0;
long stime = System.currentTimeMillis();
while (manifestsFromZip.hasMoreElements()) {
ZipEntry manifestEntry = manifestsFromZip.nextElement();
String manifestName = manifestEntry.getName();
if (!manifestName.endsWith(".mf") || !manifestName.startsWith("manifest")) {
continue;
}
InputStream fis = manifestTestDataZip.getInputStream(manifestEntry);
try {
c++;
Manifest mf = new Manifest(fis);
mf.getEntries();
} catch (IOException e) {
e.printStackTrace();
}
fis.close();
}
long etime = System.currentTimeMillis();
if (measure) {
System.out.println("JDK processed " + c + " manifests in " + (etime - stime) + "ms");
}
boolean pauseToAttachProfiler = false;
if (measure && pauseToAttachProfiler) {
System.in.read();
}
manifestsFromZip = manifestTestDataZip.entries();
stime = System.currentTimeMillis();
c = 0;
while (manifestsFromZip.hasMoreElements()) {
ZipEntry manifestEntry = manifestsFromZip.nextElement();
String manifestName = manifestEntry.getName();
if (!manifestName.endsWith(".mf") || !manifestName.startsWith("manifest")) {
continue;
}
c++;
Reader r = new InputStreamReader(manifestTestDataZip.getInputStream(manifestEntry));
try {
parse(r);
} catch (Exception e) {
e.printStackTrace();
fail("Failed on manifest " + manifestName);
}
r.close();
}
etime = System.currentTimeMillis();
if (measure) {
System.out.println("RecoverableParser processed " + c + " manifests in " + (etime - stime) + "ms");
}
if (measure && pauseToAttachProfiler) {
try {
Thread.sleep(10000);
} catch (Exception e) {
}
}
} finally {
manifestTestDataZip.close();
}
}
// ---
private void assertContainsHeaderNameValuePair(String name, String value) {
Map<String, String> headers = v.getAllHeaders();
boolean found = false;
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
String headerName = headerEntry.getKey();
String headerValue = headerEntry.getValue();
if (headerName.equals(name) && headerValue.equals(headerValue)) {
found = true;
break;
}
}
if (!found) {
fail("Failed to find header name value pair: " + name + ": " + value);
}
}
private void parse(Reader r) {
try {
StringBuilder fileData = new StringBuilder(512);
char[] buf = new char[4096];
int numRead = 0;
while ((numRead = r.read(buf)) != -1) {
fileData.append(new String(buf, 0, numRead));
}
r.close();
parse(fileData.toString());
} catch (Exception e) {
throw new RuntimeException("Problem during parsing", e);
}
}
private void parse(File f) {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f))) {
StringBuilder fileData = new StringBuilder(512);
byte[] buf = new byte[4096];
int numRead = 0;
while ((numRead = bis.read(buf)) != -1) {
fileData.append(new String(buf, 0, numRead));
}
parse(fileData.toString());
} catch (Exception e) {
throw new RuntimeException("Problem during parsing", e);
}
}
private void parse(String manifest) throws Exception {
// long stime = System.currentTimeMillis();
try {
mp = new RecoveringManifestParser(v = new TestVisitor());
mp.parse(manifest);
} catch (Exception e) {
printProblems();
throw e;
}
// long etime = System.currentTimeMillis();
// System.out.println("Parsed in " + (etime - stime) + "ms");
// printProblems();
// printManifest();
}
private void printProblems() {
List<ManifestProblem> problems = mp.getProblems();
System.out.println("Printing problems: #" + problems.size());
for (ManifestProblem manifestProblem : problems) {
System.out.println(manifestProblem.toStringWithContext());
}
}
private File getTestFile(String name) {
return new File("build/resources/test/" + name);
}
private ManifestProblem assertProblem(ManifestProblemKind expectedProblem) {
List<ManifestProblem> problems = mp.getProblems();
for (ManifestProblem manifestProblem : problems) {
if (manifestProblem.getKind() == expectedProblem) {
return manifestProblem;
}
}
printProblems();
fail("Did not find the expected problem " + expectedProblem);
return null;
}
// private void printManifest() {
// Map<String, String> allHeaders = v.getAllHeaders();
// for (Map.Entry<String, String> header : allHeaders.entrySet()) {
// System.out.println(header.getKey() + ": " + header.getValue());
// }
// }
// private void printHeaders() {
// Map<String, String> attrs = v.getAttributesForSection("secondSection");
// for (Map.Entry<String, String> attr : attrs.entrySet()) {
// System.out.println(attr.getKey() + " = " + attr.getValue());
// }
// }
}