001: /*
002: * Copyright 2002-2008 Andy Clark
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.cyberneko.html;
018:
019: import org.cyberneko.html.HTMLConfiguration;
020:
021: import java.io.*;
022: import java.util.*;
023:
024: import org.apache.tools.ant.BuildException;
025: import org.apache.tools.ant.DirectoryScanner;
026: import org.apache.tools.ant.Project;
027: import org.apache.tools.ant.Task;
028: import org.apache.tools.ant.types.FileSet;
029:
030: import org.apache.xerces.xni.parser.XMLDocumentFilter;
031: import org.apache.xerces.xni.parser.XMLInputSource;
032: import org.apache.xerces.xni.parser.XMLParserConfiguration;
033:
034: /**
035: * A simple regression tester written as an Ant task. This task
036: * generates canonical output using the <code>Writer</code> class
037: * and compares it against the expected canonical output. Simple
038: * as that.
039: *
040: * @author Andy Clark
041: */
042: public class Tester extends Task {
043:
044: //
045: // Data
046: //
047:
048: /** Canonical test directory. */
049: protected String fCanonicalDir;
050:
051: /** Output directory for generated files. */
052: protected String fOutputDir;
053:
054: /** List of test filesets. */
055: protected Vector fFileSets = new Vector();
056:
057: //
058: // Public methods
059: //
060:
061: /** Sets the canonical test directory. */
062: public void setCanonDir(String canondir) {
063: fCanonicalDir = canondir;
064: } // setCanonDir(String)
065:
066: /** Sets the output directory for generated files. */
067: public void setOutputDir(String outdir) {
068: fOutputDir = outdir;
069: } // setOutputDir(String)
070:
071: /** Adds a fileset to the list of test filesets. */
072: public void addFileSet(FileSet fileset) {
073: fFileSets.addElement(fileset);
074: } // addFileSet(FileSet)
075:
076: //
077: // Task methods
078: //
079:
080: /** Performs the test. */
081: public void execute() throws BuildException {
082:
083: // check params
084: String canonicaldir = fCanonicalDir;
085: if (canonicaldir == null) {
086: canonicaldir = ".";
087: log(
088: "Canonical directory not specified. Assuming current directory.",
089: Project.MSG_WARN);
090: }
091: String outputdir = fOutputDir;
092: if (outputdir == null) {
093: outputdir = ".";
094: log(
095: "Output directory not specified. Assuming current directory.",
096: Project.MSG_WARN);
097: }
098: if (fFileSets.size() == 0) {
099: throw new BuildException(
100: "must specify at least one fileset");
101: }
102:
103: // parse input files and produce output files
104: log("Parsing test files and generating output...");
105: File outdir = new File(outputdir);
106: int size = fFileSets.size();
107: for (int i = 0; i < size; i++) {
108: FileSet fileset = (FileSet) fFileSets.elementAt(i);
109: DirectoryScanner dirscanner = fileset
110: .getDirectoryScanner(getProject());
111: File indir = dirscanner.getBasedir();
112: String[] files = dirscanner.getIncludedFiles();
113: for (int j = 0; j < files.length; j++) {
114: File infile = new File(indir, files[j]);
115: File outfile = new File(outdir, files[j]);
116: log(" " + outfile, Project.MSG_VERBOSE);
117: OutputStream out = null;
118: try {
119: // create filters
120: out = new FileOutputStream(outfile);
121: XMLDocumentFilter[] filters = { new Writer(out) };
122:
123: // create parser
124: XMLParserConfiguration parser = new HTMLConfiguration();
125:
126: // parser settings
127: parser
128: .setProperty(
129: "http://cyberneko.org/html/properties/filters",
130: filters);
131: String infilename = infile.toString();
132: File insettings = new File(infilename + ".settings");
133: if (insettings.exists()) {
134: BufferedReader settings = new BufferedReader(
135: new FileReader(insettings));
136: String settingline;
137: while ((settingline = settings.readLine()) != null) {
138: StringTokenizer tokenizer = new StringTokenizer(
139: settingline);
140: String type = tokenizer.nextToken();
141: String id = tokenizer.nextToken();
142: String value = tokenizer.nextToken();
143: if (type.equals("feature")) {
144: parser.setFeature(id, value
145: .equals("true"));
146: } else {
147: parser.setProperty(id, value);
148: }
149: }
150: settings.close();
151: }
152:
153: // parse
154: parser.parse(new XMLInputSource(null, infilename,
155: null));
156: } catch (Exception e) {
157: log(" error parsing input file, " + infile);
158: throw new BuildException(e);
159: } finally {
160: try {
161: out.close();
162: } catch (Exception e) {
163: log(" error closing output file, " + outfile);
164: throw new BuildException(e);
165: }
166: }
167: }
168: }
169:
170: // compare against canonical output
171: log("Comparing parsed output against canonical output...");
172: File canondir = new File(canonicaldir);
173: int errors = 0;
174: for (int i = 0; i < size; i++) {
175: FileSet fileset = (FileSet) fFileSets.elementAt(i);
176: DirectoryScanner dirscanner = fileset
177: .getDirectoryScanner(getProject());
178: String[] files = dirscanner.getIncludedFiles();
179: for (int j = 0; j < files.length; j++) {
180: File canonfile = new File(canondir, files[j]);
181: if (!canonfile.exists()) {
182: errors++;
183: log(" canonical file missing, " + canonfile);
184: continue;
185: }
186: File outfile = new File(outdir, files[j]);
187: if (!outfile.exists()) {
188: errors++;
189: log(" output file missing, " + outfile);
190: continue;
191: }
192: log(" comparing " + canonfile + " and " + outfile,
193: Project.MSG_VERBOSE);
194: try {
195: if (compare(canonfile, outfile)) {
196: errors++;
197: }
198: } catch (IOException e) {
199: errors++;
200: log("i/o error");
201: }
202: }
203: }
204:
205: // finished
206: if (errors > 0) {
207: log("Finished with errors.");
208: throw new BuildException();
209: }
210: log("Done.");
211:
212: } // execute()
213:
214: //
215: // Protected methods
216: //
217:
218: /** Compares two files. */
219: protected boolean compare(File f1, File f2) throws IOException {
220: BufferedReader i1 = new BufferedReader(new InputStreamReader(
221: new UTF8BOMSkipper(new FileInputStream(f1)), "UTF8"));
222: BufferedReader i2 = new BufferedReader(new InputStreamReader(
223: new FileInputStream(f2), "UTF8"));
224: String l1;
225: String l2;
226: int errors = 0;
227: long n = 0;
228: while ((l1 = i1.readLine()) != null) {
229: n++;
230: if ((l2 = i2.readLine()) == null) {
231: errors++;
232: log(" file lengths don't match (" + f1 + ")");
233: break;
234: }
235: if (compare(f1.getName(), n, l1, l2)) {
236: errors++;
237: break;
238: }
239: }
240: if (errors == 0 && (l2 = i2.readLine()) != null) {
241: errors++;
242: log(" file lengths don't match (" + f1 + ")");
243: }
244: i1.close();
245: i2.close();
246: return errors > 0;
247: } // compare(File,File):boolean
248:
249: /** Compares two strings. */
250: protected boolean compare(String f, long n, String s1, String s2) {
251: int l1 = s1.length();
252: int l2 = s2.length();
253: boolean error = false;
254: if (l1 < l2) {
255: error = true;
256: log(" " + f + ':' + n + " output string too long");
257: } else if (l1 > l2) {
258: error = true;
259: log(" " + f + ':' + n + " output string too short");
260: } else if (!s1.equals(s2)) {
261: error = true;
262: log(" " + f + ':' + n + " strings don't match");
263: }
264: if (error) {
265: log(" [in: " + s1 + ']');
266: log(" [out: " + s2 + ']');
267: }
268: return error;
269: } // compare(String,long,String,String):boolean
270:
271: } // class Tester
|