001 // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org)
002 // The Molly framework is freely distributable under the terms of an
003 // MIT-style license. For details, see the molly pages web site at:
004 // http://www.mollypages.org/. Use, modify, have fun !
005
006 package fc.web.page;
007
008 import java.io.*;
009 import java.util.*;
010 import java.net.URL;
011
012 import javax.servlet.*;
013 import javax.servlet.http.*;
014
015 import fc.util.*;
016 import fc.io.*;
017 import fc.io.fileselectors.*;
018
019 /**
020 Regression testing since eyeballing parser output just ain't cutting it.
021 For developers only, useful when the molly parser is hacked/changed and
022 we want to ensure that the new parser does not break any existing behavior.
023 <p>
024 All regression tests are in the <pre>test</pre> subdirectory (relative to the java
025 source of this class). These tests exist as *.mp files. This program will
026 run the parser on each file and a) either check actual output with expected
027 output and/or b) see if an expected error occurs.
028 <p>
029 The expected/canonical output (generated by the good working parser) also
030 always exists in the <pre>test</pre> subdirectory. The <i>actual</i>
031 output generated by the latest hacked parser is output in a temporary
032 directory (the location of which is specified on the command line). The
033 output are then compared and should be identical.
034 <p>
035 The expected output can be regenerated via the <pre>generateExpected</pre>
036 argument but this should be used by caution. Future/actual output of the
037 changed/hacked parser is compared with the last good generated expected
038 output so the expected output should only be updated when the parser is
039 in a known working state.
040
041 @author hursh jain
042 */
043 public class PageParserTest
044 {
045 static List results = new ArrayList();
046
047 public static void main (String args[]) throws Exception
048 {
049 Args myargs = new Args(args);
050 Log log = Log.getDefault();
051 myargs.setUsage("java " + myargs.getMainClassName()
052 + "\n---- Specify ---"
053 + "\n -test <if specified, run all unit tests with the latest parser>"
054 + "\n -tmpdir <location to a tmp dir used for generated files"
055 + "\n----- or ----"
056 + "\n -generateExpected if specified, expected/canonical parser output "
057 + "\n is generated. Use with caution. The servlet API"
058 + "\n along with all the molly classes must be on the"
059 + "\n system classpath for this option to work fully."
060 );
061
062 boolean testing = myargs.flagExists("test");
063 boolean generate = myargs.flagExists("generateExpected");
064 File tmpdir = null;
065
066 if (! testing && ! generate)
067 myargs.showError();
068
069 if (testing)
070 {
071 tmpdir = new File(myargs.getRequired("tmpdir"));
072 if (! tmpdir.exists()) {
073 log.error("The specified temp directory: " + tmpdir.getAbsolutePath() + " does not exist");
074 return;
075 }
076 }
077
078 Class c = new PageParserTest().getClass();
079 URL url = c.getResource("test");
080 //System.out.println(url + ";" + url.getFile());
081 File testdir = new File(url.toURI());
082 if (! testdir.isDirectory()) {
083 log.error(testdir + " is not a directory. This test does not work from within jars. For development, you need to explode the jar");
084 return;
085 }
086
087 log.bug("Regression test directory: " + testdir.getAbsolutePath());
088
089 File[] files = testdir.listFiles(new SuffixSelector(".mp"));
090
091 if (files.length == 0)
092 log.warn("No *.mp test files found in directory: ", testdir.getAbsolutePath());
093
094 Watch wp = new Watch("parse");
095 Watch wc = new Watch("compile");
096
097 int crap_count = 0;
098 int generate_count = 0;
099 for (int n = 0; n < files.length; n++)
100 {
101 File test = files[n];
102 log.bug("Processing file: ", test.getName());
103
104 //some files are not expected to compile (as part of the test)
105 //those files start with "crap..."
106 boolean crap = test.getName().toLowerCase().startsWith("crap");
107 if (crap) {
108 crap_count++;
109 }
110
111 String result_file_str = test.getName();
112 int i = result_file_str.lastIndexOf(".");
113 String outbase = result_file_str.substring(0, i);
114 String outname = outbase + ".java";
115
116 File expected = new File(testdir, outname);
117 File out = new File(tmpdir, outname);
118
119 PageParser p;
120 PageCompiler pc;
121 CharArrayWriter buf = new CharArrayWriter();
122
123 //generate
124 if (generate)
125 {
126 try {
127 buf.reset();
128 buf.append("Generate: ").append(
129 String.format("%-20s",test.getName()));
130
131 wp.start();
132 p = new PageParser(testdir, test, expected, outbase);
133 p.parse();
134 wp.stop();
135
136 buf.append(" [parsed in:").
137 append(
138 String.format("%3s",
139 String.valueOf(wp.getTime())))
140 .append( " ms, ");
141 //successful parse, no parse exceptions
142 //there may be compile time errors though (expected for
143 //crap files)
144 wc.start();
145 pc = new PageCompiler(expected, System.getProperty("java.class.path"), null);
146 boolean compiled = pc.compile();
147 wc.stop();
148
149 if (! compiled) {
150 buf.append(" compile error...possibly expected]");
151 log.bug(pc.getError());
152 }
153 else{
154 buf.append(" compiled in: ").
155 append(
156 String.format("%5s",
157 String.valueOf(wc.getTime())))
158 .append(" ms]");
159 //remove class file
160 File f = new File(testdir, outbase + ".class");
161 if (f.exists()) {
162 f.delete();
163 }
164 //compiled ok too.
165 generate_count++;
166 }
167 }
168 catch (Exception e)
169 {
170 buf.append(" [parse error...possibly expected]");
171 // if (! crap) { //crap files are expected to have errors
172 msg(test.getName(), "Parse Error: " + e.toString());
173 // }
174 }
175
176 log.info(buf.toString());
177 continue;
178 } //end if (generate)
179
180 //test
181
182 //we need an expected file to compare with
183 if (! expected.exists())
184 {
185 if (! crap) { //wont exist for crap files cause parse error
186 msg(test.getName(), "FAILED [expected file: " + expected.getAbsolutePath() + " does not exist]");
187 continue;
188 }
189 }
190
191 p = new PageParser(testdir, test, out, outbase);
192
193 try {
194 p.parse();
195 //crap files will cause a parse exception
196 //if here, then parse successful
197 if (! compare(expected, out)) {
198 msg(test.getName(), "FAIL [expected != actual]");
199 }
200 else{
201 msg(test.getName(), "OK");
202 }
203 }
204 catch (Exception e) {
205 if (crap) {
206 //we wanted the expected parse exception
207 msg(test.getName(), "OK");
208 }
209 else{
210 msg(test.getName(), "FAIL [ParseException] " + e.toString());
211 }
212 }
213
214 } //end for loop
215
216 if (generate) {
217 System.out.println();
218 System.out.println((files.length - crap_count) + " total (non-crap) *.mp files in " + testdir.getAbsolutePath());
219 System.out.println(generate_count + " expected files were generated.");
220 }
221
222 //print results
223 System.out.println();
224 TablePrinter.PrintConfig pconfig = new TablePrinter.PrintConfig();
225 pconfig.setAutoFit(false).setPrintBorders(true).setCellPadding(1);
226 if (generate) {
227 pconfig.setCellWidthForColumn(1,55);
228 System.out.println("The following errors are expected!");
229 }
230 TablePrinter printer = new TablePrinter(2, System.out, pconfig);
231 printer.startTable();
232 for (int n = 0; n < results.size(); n++) {
233 printer.startRow();
234 Object[] arr = (Object[]) results.get(n);
235 printer.printCell(arr[0].toString());
236 printer.printCell(arr[1].toString());
237 printer.endRow();
238 }
239 printer.endTable();
240 }
241
242 static void msg(Object testName, Object result)
243 {
244 results.add(new Object[] {testName, result});
245 }
246
247 static boolean compare(File expected, File output) throws Exception
248 {
249 if (! output.exists())
250 return false;
251
252 byte[] expected_b = IOUtil.fileToByteArray(expected);
253 byte[] output_b = IOUtil.fileToByteArray(output);
254
255 return Arrays.equals(expected_b, output_b);
256 }
257
258 }