| /* |
| * 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 xinclude; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.FileReader; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.LineNumberReader; |
| import java.io.OutputStream; |
| import java.io.PrintStream; |
| import java.io.PrintWriter; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.io.StringWriter; |
| import java.util.StringTokenizer; |
| |
| import org.apache.xerces.parsers.XIncludeParserConfiguration; |
| import org.apache.xerces.xni.XNIException; |
| import org.apache.xerces.xni.parser.XMLErrorHandler; |
| import org.apache.xerces.xni.parser.XMLInputSource; |
| import org.apache.xerces.xni.parser.XMLParseException; |
| import org.apache.xerces.xni.parser.XMLParserConfiguration; |
| |
| import xni.Writer; |
| |
| /** |
| * Tests for XInclude implementation. |
| * Use -f option to see the error message log |
| * @author Peter McCracken, IBM |
| */ |
| public class Test implements XMLErrorHandler { |
| /** Namespaces feature id (http://xml.org/sax/features/namespaces). */ |
| protected static final String NAMESPACES_FEATURE_ID = |
| "http://xml.org/sax/features/namespaces"; |
| |
| /** Validation feature id (http://xml.org/sax/features/validation). */ |
| protected static final String VALIDATION_FEATURE_ID = |
| "http://xml.org/sax/features/validation"; |
| |
| /** Schema validation feature id (http://apache.org/xml/features/validation/schema). */ |
| protected static final String SCHEMA_VALIDATION_FEATURE_ID = |
| "http://apache.org/xml/features/validation/schema"; |
| |
| /** Schema full checking feature id (http://apache.org/xml/features/validation/schema-full-checking). */ |
| protected static final String SCHEMA_FULL_CHECKING_FEATURE_ID = |
| "http://apache.org/xml/features/validation/schema-full-checking"; |
| |
| /** Property identifier: error handler. */ |
| protected static final String ERROR_HANDLER = |
| "http://apache.org/xml/properties/internal/error-handler"; |
| |
| // this array contains whether the test number NN (contained in file testNN.xml) |
| // is meant to be a pass or fail test |
| // true means the test should pass |
| private static final int NUM_TESTS = 41; |
| private static boolean[] TEST_RESULTS = new boolean[] { |
| // one value for each test |
| true, true, true, true, true, true, false, true, false, true, // 10 |
| false, false, false, false, true, true, true, false, true, true, // 20 |
| true, false, true, false, false, false, true, true, false, true, // 30 |
| true, false, true, true, true, true, true, true, false, false, // 40 |
| true, }; |
| |
| private String fOutputDirectory = "tests/xinclude/output"; |
| |
| public static void main(String[] args) { |
| Test tester = new Test(); |
| boolean testsSpecified = false; |
| for (int i = 0; i < args.length; i++) { |
| if (args[i].charAt(0) == '-' && args[i].length() > 1) { |
| switch (args[i].charAt(1)) { |
| case 'g' : |
| tester.fGenerate = true; |
| if (args.length > i + 1 |
| && args[i + 1].charAt(0) != '-') { |
| try { |
| Integer.parseInt(args[i + 1]); |
| // if it parses as an integer, we'll assume it's a test number |
| tester.setLogFile(System.err); |
| } |
| catch (NumberFormatException e) { |
| tester.fOutputDirectory = args[++i]; |
| } |
| } |
| break; |
| case 'h' : |
| printUsage(); |
| return; |
| case 'f' : |
| if (args.length > i + 1 |
| && args[i + 1].charAt(0) != '-') { |
| try { |
| Integer.parseInt(args[i + 1]); |
| // if it parses as an integer, we'll assume it's a test number |
| tester.setLogFile(System.err); |
| } |
| catch (NumberFormatException e) { |
| // if it doesn't parse as an integer, |
| // we assume it's the log file name |
| try { |
| tester.setLogFile( |
| new PrintStream( |
| new FileOutputStream(args[++i]))); |
| } |
| catch (IOException ioe) { |
| System.err.println( |
| "Couldn't open log file: " + args[i]); |
| } |
| } |
| } |
| else { |
| tester.setLogFile(System.err); |
| } |
| break; |
| default : |
| System.err.println("Unrecognized option: " + args[i]); |
| } |
| } |
| else { |
| testsSpecified = true; |
| tester.addTest(Integer.parseInt(args[i])); |
| } |
| } |
| |
| if (!testsSpecified) { |
| for (int i = 1; i <= NUM_TESTS; i++) { |
| tester.addTest(i); |
| } |
| } |
| |
| // Create output directory if it does not already exist. |
| if (tester.fGenerate) { |
| File outputDir = new File(tester.fOutputDirectory); |
| if (!outputDir.exists()) { |
| outputDir.mkdirs(); |
| } |
| } |
| tester.runTests(); |
| } |
| |
| private final PrintStream DEFAULT_LOG_STREAM = |
| new PrintStream(new OutputStream() { |
| public void write(int b) throws IOException { |
| } |
| }); |
| |
| private Writer fWriter; |
| private String fResults; |
| private PrintWriter fOutputWriter; |
| private int[] fTests = new int[NUM_TESTS]; |
| private int fNumTests = 0; |
| public boolean fGenerate; |
| public PrintStream fLogStream; |
| |
| public Test() throws XNIException { |
| XMLParserConfiguration parserConfig = new XIncludeParserConfiguration(); |
| parserConfig.setFeature(NAMESPACES_FEATURE_ID, true); |
| parserConfig.setFeature(SCHEMA_VALIDATION_FEATURE_ID, true); |
| parserConfig.setFeature(SCHEMA_FULL_CHECKING_FEATURE_ID, true); |
| fWriter = new Writer(parserConfig); |
| |
| // this has to be done AFTER fWriter is created |
| parserConfig.setProperty(ERROR_HANDLER, this); |
| |
| fGenerate = false; |
| |
| // squelch output by default |
| fLogStream = DEFAULT_LOG_STREAM; |
| } |
| |
| public void addTest(int t) { |
| fTests[fNumTests++] = t; |
| } |
| |
| public void setLogFile(PrintStream stream) { |
| fLogStream = stream; |
| } |
| |
| public void runTests() { |
| int totalFailures = 0; |
| |
| for (int i = 0; i < fNumTests; i++) { |
| if (!runTest(fTests[i])) { |
| totalFailures++; |
| } |
| } |
| |
| if (fLogStream != null && fLogStream != System.err) { |
| fLogStream.close(); |
| } |
| |
| if (totalFailures == 0) { |
| System.out.println("All XInclude Tests Passed"); |
| } |
| else { |
| System.err.println( |
| "Total failures for XInclude: " |
| + totalFailures |
| + "/" |
| + fNumTests); |
| printDetailsMessage(); |
| System.exit(1); |
| } |
| } |
| |
| private static final String XML_EXTENSION = ".xml"; |
| private static final String TXT_EXTENSION = ".txt"; |
| |
| private boolean runTest(int testnum) { |
| String testname = "tests/xinclude/tests/test"; |
| String outputFilename = fOutputDirectory + "/test"; |
| String expectedOutputFilename = "tests/xinclude/output/test"; |
| if (testnum < 10) { |
| testname += "0" + testnum; |
| outputFilename += "0" + testnum; |
| expectedOutputFilename += "0" + testnum; |
| } |
| else { |
| testname += testnum; |
| outputFilename += testnum; |
| expectedOutputFilename += testnum; |
| } |
| testname += XML_EXTENSION; |
| // we output to an .xml file if we expect success, |
| // or a .txt file if we expect failure |
| if (TEST_RESULTS[testnum - 1]) { |
| outputFilename += XML_EXTENSION; |
| expectedOutputFilename += XML_EXTENSION; |
| } |
| else { |
| outputFilename += TXT_EXTENSION; |
| expectedOutputFilename += TXT_EXTENSION; |
| } |
| |
| boolean passed = true; |
| StringBuffer buffer = null; |
| try { |
| fLogStream.println("TEST: " + testname); |
| java.io.Writer myWriter = new StringWriter(); |
| buffer = ((StringWriter)myWriter).getBuffer(); |
| fOutputWriter = new PrintWriter(myWriter); |
| fWriter.setOutput(myWriter); |
| fWriter.parse(new XMLInputSource(null, testname, null)); |
| } |
| catch (XNIException e) { |
| passed = false; |
| } |
| catch (IOException e) { |
| fLogStream.println("Unexpected IO problem: " + e); |
| fLogStream.println("Result: FAIL"); |
| return false; |
| } |
| |
| fResults = stripUserDir(buffer); |
| return processTestResults( |
| passed, |
| TEST_RESULTS[testnum - 1], |
| outputFilename, |
| expectedOutputFilename); |
| } |
| |
| private boolean processTestResults( |
| boolean passed, |
| boolean expectedPass, |
| String outputFilename, |
| String expectedOutputFile) { |
| if (fGenerate) { |
| try { |
| fLogStream.println("Generated: " + outputFilename); |
| PrintWriter outputFile = |
| new PrintWriter(new FileWriter(outputFilename)); |
| outputFile.print(fResults); |
| outputFile.close(); |
| } |
| catch (IOException e) { |
| fLogStream.println( |
| "IOException generating results: " + e.getMessage()); |
| return false; |
| } |
| return true; |
| } |
| |
| try { |
| if (passed == expectedPass) { |
| if (compareOutput(new FileReader(expectedOutputFile), |
| new StringReader(fResults))) { |
| fLogStream.println("Result: PASS"); |
| return true; |
| } |
| else { |
| fLogStream.println("Result: FAIL"); |
| return false; |
| } |
| } |
| else { |
| compareOutput(new FileReader(expectedOutputFile), |
| new StringReader(fResults)); |
| fLogStream.println(fResults); |
| fLogStream.println("Result: FAIL"); |
| return false; |
| } |
| } |
| catch (IOException e) { |
| fLogStream.println( |
| "Unexpected IO problem attempting to verify results: " + e); |
| fLogStream.println("Result: FAIL"); |
| return false; |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.xni.parser.XMLErrorHandler#error(java.lang.String, java.lang.String, org.apache.xerces.xni.parser.XMLParseException) |
| */ |
| public void error(String domain, String key, XMLParseException exception) |
| throws XNIException { |
| printError("Error", exception); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.xni.parser.XMLErrorHandler#fatalError(java.lang.String, java.lang.String, org.apache.xerces.xni.parser.XMLParseException) |
| */ |
| public void fatalError( |
| String domain, |
| String key, |
| XMLParseException exception) |
| throws XNIException { |
| printError("Fatal Error", exception); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.xni.parser.XMLErrorHandler#warning(java.lang.String, java.lang.String, org.apache.xerces.xni.parser.XMLParseException) |
| */ |
| public void warning(String domain, String key, XMLParseException exception) |
| throws XNIException { |
| printError("Warning", exception); |
| } |
| |
| private static void printUsage() { |
| System.out.println("java xinclude.Test [OPTIONS] [TESTS]"); |
| System.out.println("OPTIONS:"); |
| System.out.println(" -f [file] : Specifies a log file to print detailed error messages to."); |
| System.out.println(" Omitting the FILE parameter makes messages print to "); |
| System.out.println(" standard error. If this option is absent, the messages"); |
| System.out.println(" will not be output."); |
| System.out.println(""); |
| System.out.println(" -g [directory] : Generates the expected output files in the "); |
| System.out.println(" given directory if specified, otherwise the files"); |
| System.out.println(" are written to the expected output directory."); |
| System.out.println(" Only use this option without a target when the output "); |
| System.out.println(" is sure to be correct. The previous expected output files "); |
| System.out.println(" will be overwritten."); |
| System.out.println(""); |
| System.out.println(" -h : Prints this help message and exits."); |
| System.out.println("TESTS:"); |
| System.out.println( |
| " A whitespace separated list of tests to run, specified by test number."); |
| System.out.println(" If this is absent, all tests will be run."); |
| } |
| |
| private void printDetailsMessage() { |
| if (fLogStream != DEFAULT_LOG_STREAM) { |
| System.err.println("See log output for details"); |
| } |
| else { |
| System.err.println("Re-run with -f option to get details."); |
| } |
| } |
| |
| /** Prints the error message. */ |
| protected void printError(String type, XMLParseException ex) { |
| fOutputWriter.print("["); |
| fOutputWriter.print(type); |
| fOutputWriter.print("] "); |
| String systemId = ex.getExpandedSystemId(); |
| if (systemId != null) { |
| int index = systemId.lastIndexOf('/'); |
| if (index != -1) |
| systemId = systemId.substring(index + 1); |
| fOutputWriter.print(systemId); |
| } |
| fOutputWriter.print(':'); |
| fOutputWriter.print(ex.getLineNumber()); |
| fOutputWriter.print(':'); |
| fOutputWriter.print(ex.getColumnNumber()); |
| /* |
| * The String returned by getMessage() is not stable across JDKs, so we |
| * don't print it. Unfortunately, there doesn't seem to be a way to get |
| * the underlying type of exception (like FileNotFoundException) so |
| * we just don't print anything. |
| fOutputWriter.print(": "); |
| fOutputWriter.print(ex.getMessage()); |
| */ |
| fOutputWriter.println(); |
| fOutputWriter.flush(); |
| } // printError(String,XMLParseException) |
| |
| protected boolean compareOutput(Reader expected, Reader actual) |
| throws IOException { |
| LineNumberReader expectedOutput = new LineNumberReader(expected); |
| LineNumberReader actualOutput = new LineNumberReader(actual); |
| |
| while (expectedOutput.ready() && actualOutput.ready()) { |
| String expectedLine = expectedOutput.readLine(); |
| String actualLine = actualOutput.readLine(); |
| if (!expectedLine.equals(actualLine)) { |
| fLogStream.println( |
| "Mismatch on line: " + expectedOutput.getLineNumber()); |
| fLogStream.println("Expected: " + expectedLine); |
| fLogStream.println(" Actual: " + actualLine); |
| return false; |
| } |
| } |
| if (expectedOutput.ready() && !actualOutput.ready()) { |
| String expectedLine = expectedOutput.readLine(); |
| if (expectedLine != null) { |
| fLogStream.println( |
| "Actual output contains fewer lines than expected output."); |
| fLogStream.println( |
| "Line " |
| + expectedOutput.getLineNumber() |
| + ": " |
| + expectedLine); |
| fLogStream.println("Above line has no match in actual output."); |
| return false; |
| } |
| } |
| else if (!expectedOutput.ready() && actualOutput.ready()) { |
| String actualLine = actualOutput.readLine(); |
| if (actualLine != null) { |
| fLogStream.println( |
| "Actual output contains more lines than expected output."); |
| fLogStream.println( |
| "Line " + actualOutput.getLineNumber() + ": " + actualLine); |
| fLogStream.println( |
| "Above line has no match in expected output."); |
| return false; |
| } |
| } |
| |
| expectedOutput.close(); |
| actualOutput.close(); |
| return true; |
| } |
| |
| private String stripUserDir(StringBuffer buf) { |
| String userDir = System.getProperty("user.dir"); |
| String userURI = "file://"; |
| if (userDir.charAt(0) != '/') { |
| userURI += "/"; |
| } |
| userURI += userDir.replace('\\', '/'); |
| String str = getPathWithoutEscapes(buf.toString()); |
| |
| int start = 0, end = 0; |
| // strip ones in URI form |
| while ((start = str.indexOf(userURI, start)) != -1) { |
| end = start + userURI.length(); |
| // we add one, to get rid of the '/' after the user directory path |
| str = str.substring(0, start) + str.substring(end+1); |
| } |
| |
| while ((start = str.indexOf(userDir, start)) != -1) { |
| end = start + userDir.length(); |
| // we add one, to get rid of the '/' after the user directory path |
| str = str.substring(0, start) + str.substring(end+1); |
| } |
| return str; |
| } |
| |
| private static String getPathWithoutEscapes(String origPath) { |
| if (origPath != null && origPath.length() != 0 && origPath.indexOf('%') != -1) { |
| // Locate the escape characters |
| StringTokenizer tokenizer = new StringTokenizer(origPath, "%"); |
| StringBuffer result = new StringBuffer(origPath.length()); |
| int size = tokenizer.countTokens(); |
| result.append(tokenizer.nextToken()); |
| for(int i = 1; i < size; ++i) { |
| String token = tokenizer.nextToken(); |
| // Decode the 2 digit hexadecimal number following % in '%nn' |
| result.append((char)Integer.valueOf(token.substring(0, 2), 16).intValue()); |
| result.append(token.substring(2)); |
| } |
| return result.toString(); |
| } |
| return origPath; |
| } |
| } |