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 006package fc.web.page; 007 008import java.io.*; 009import java.util.*; 010import java.net.URL; 011 012import javax.servlet.*; 013import javax.servlet.http.*; 014 015import fc.util.*; 016import fc.io.*; 017import fc.io.fileselectors.*; 018 019/** 020Regression testing since eyeballing parser output just ain't cutting it. 021For developers only, useful when the molly parser is hacked/changed and 022we want to ensure that the new parser does not break any existing behavior. 023<p> 024All regression tests are in the <pre>test</pre> subdirectory (relative to the java 025source of this class). These tests exist as *.mp files. This program will 026run the parser on each file and a) either check actual output with expected 027output and/or b) see if an expected error occurs. 028<p> 029The expected/canonical output (generated by the good working parser) also 030always exists in the <pre>test</pre> subdirectory. The <i>actual</i> 031output generated by the latest hacked parser is output in a temporary 032directory (the location of which is specified on the command line). The 033output are then compared and should be identical. 034<p> 035The expected output can be regenerated via the <pre>generateExpected</pre> 036argument but this should be used by caution. Future/actual output of the 037changed/hacked parser is compared with the last good generated expected 038output so the expected output should only be updated when the parser is 039in a known working state. 040 041@author hursh jain 042*/ 043public class PageParserTest 044{ 045static List results = new ArrayList(); 046 047public 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 242static void msg(Object testName, Object result) 243 { 244 results.add(new Object[] {testName, result}); 245 } 246 247static 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}