001    /*
002     * Copyright (c) 2007 Henri Sivonen
003     *
004     * Permission is hereby granted, free of charge, to any person obtaining a 
005     * copy of this software and associated documentation files (the "Software"), 
006     * to deal in the Software without restriction, including without limitation 
007     * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
008     * and/or sell copies of the Software, and to permit persons to whom the 
009     * Software is furnished to do so, subject to the following conditions:
010     *
011     * The above copyright notice and this permission notice shall be included in 
012     * all copies or substantial portions of the Software.
013     *
014     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
015     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
016     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
017     * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
018     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
019     * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
020     * DEALINGS IN THE SOFTWARE.
021     */
022    
023    package nu.validator.htmlparser.test;
024    
025    import java.io.BufferedInputStream;
026    import java.io.BufferedReader;
027    import java.io.FileInputStream;
028    import java.io.IOException;
029    import java.io.InputStream;
030    import java.io.InputStreamReader;
031    import java.io.StringWriter;
032    import java.util.LinkedList;
033    
034    import nu.validator.htmlparser.common.XmlViolationPolicy;
035    import nu.validator.htmlparser.sax.HtmlParser;
036    
037    import org.xml.sax.InputSource;
038    import org.xml.sax.SAXException;
039    import org.xml.sax.SAXParseException;
040    
041    public class TreeTester {
042    
043        private final BufferedInputStream aggregateStream;
044    
045        private boolean streaming = false;
046    
047        /**
048         * @param aggregateStream
049         */
050        public TreeTester(InputStream aggregateStream) {
051            this.aggregateStream = new BufferedInputStream(aggregateStream);
052        }
053    
054        private void runTests() throws Throwable {
055            if (aggregateStream.read() != '#') {
056                System.err.println("No hash at start!");
057                return;
058            }
059            while (runTest()) {
060                // spin
061            }
062        }
063    
064        private boolean runTest() throws Throwable {
065            UntilHashInputStream stream = null;
066            try {
067                String context = null;
068                aggregateStream.mark(4096);
069                if (skipLabel()) { // #data
070                    return false;
071                }
072                stream = new UntilHashInputStream(aggregateStream);
073                while (stream.read() != -1) {
074                    // spin
075                }
076                if (skipLabel()) { // #errors
077                    System.err.println("Premature end of test data.");
078                    return false;
079                }
080                stream = new UntilHashInputStream(aggregateStream);
081                while (stream.read() != -1) {
082                    // spin
083                }
084    
085                StringBuilder sb = new StringBuilder();
086                int c;
087                while ((c = aggregateStream.read()) != '\n') {
088                    sb.append((char) c);
089                }
090                String label = sb.toString();
091                if ("document-fragment".equals(label)) {
092                    sb.setLength(0);
093                    while ((c = aggregateStream.read()) != '\n') {
094                        sb.append((char) c);
095                    }
096                    context = sb.toString();
097                }
098                aggregateStream.reset();
099                if (skipLabel()) { // #data
100                    System.err.println("Premature end of test data.");
101                    return false;
102                }
103                stream = new UntilHashInputStream(aggregateStream);
104                InputSource is = new InputSource(stream);
105                is.setEncoding("UTF-8");
106                StringWriter sw = new StringWriter();
107                ListErrorHandler leh = new ListErrorHandler();
108                TreeDumpContentHandler treeDumpContentHandler = new TreeDumpContentHandler(
109                        sw);
110                HtmlParser htmlParser = new HtmlParser(XmlViolationPolicy.ALLOW);
111                if (streaming) {
112                    htmlParser.setStreamabilityViolationPolicy(XmlViolationPolicy.FATAL);
113                }
114                htmlParser.setContentHandler(treeDumpContentHandler);
115                htmlParser.setLexicalHandler(treeDumpContentHandler);
116                htmlParser.setErrorHandler(leh);
117                htmlParser.setScriptingEnabled(true);
118                try {
119                    if (context == null) {
120                        htmlParser.parse(is);
121                    } else {
122                        htmlParser.parseFragment(is, context);
123                        treeDumpContentHandler.endDocument();
124                    }
125                } catch (SAXParseException e) {
126                    // ignore
127                }
128                stream.close();
129    
130                if (skipLabel()) { // #errors
131                    System.err.println("Premature end of test data.");
132                    return false;
133                }
134                LinkedList<String> expectedErrors = new LinkedList<String>();
135                BufferedReader br = new BufferedReader(new InputStreamReader(
136                        new UntilHashInputStream(aggregateStream), "UTF-8"));
137                String line = null;
138                while ((line = br.readLine()) != null) {
139                    expectedErrors.add(line);
140                }
141    
142                if (context != null) {
143                    if (skipLabel()) { // #document-fragment
144                        System.err.println("Premature end of test data.");
145                        return false;
146                    }
147                    UntilHashInputStream stream2 = new UntilHashInputStream(aggregateStream);
148                    while (stream2.read() != -1) {
149                        // spin
150                    }
151                }
152    
153    
154                if (skipLabel()) { // #document
155                    System.err.println("Premature end of test data.");
156                    return false;
157                }
158    
159                StringBuilder expectedBuilder = new StringBuilder();
160                br = new BufferedReader(new InputStreamReader(
161                        new UntilHashInputStream(aggregateStream), "UTF-8"));
162                while ((line = br.readLine()) != null) {
163                    expectedBuilder.append(line);
164                    expectedBuilder.append('\n');
165                }
166                String expected = expectedBuilder.toString();
167                String actual = sw.toString();
168    
169                LinkedList<String> actualErrors = leh.getErrors();
170    
171                if (expected.equals(actual) || (streaming && leh.isFatal()) /*
172                 * && expectedErrors.size() ==
173                 * actualErrors.size()
174                 */) {
175                    System.err.println("Success.");
176                    // System.err.println(stream);
177                } else {
178                    System.err.print("Failure.\nData:\n" + stream + "\nExpected:\n"
179                            + expected + "Got: \n" + actual);
180                    System.err.println("Expected errors:");
181                    for (String err : expectedErrors) {
182                        System.err.println(err);
183                    }
184                    System.err.println("Actual errors:");
185                    for (String err : actualErrors) {
186                        System.err.println(err);
187                    }
188                }
189            } catch (Throwable t) {
190                System.err.println("Failure.\nData:\n" + stream);
191                throw t;
192            }
193            return true;
194        }
195    
196        private boolean skipLabel() throws IOException {
197            int b = aggregateStream.read();
198            if (b == -1) {
199                return true;
200            }
201            for (;;) {
202                b = aggregateStream.read();
203                if (b == -1) {
204                    return true;
205                } else if (b == 0x0A) {
206                    return false;
207                }
208            }
209        }
210    
211        /**
212         * @param args
213         * @throws Throwable
214         */
215        public static void main(String[] args) throws Throwable {
216            for (int i = 0; i < args.length; i++) {
217                TreeTester tester = new TreeTester(new FileInputStream(args[i]));
218                tester.runTests();
219            }
220        }
221    
222    }