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 jakarta.servlet.*; 013import jakarta.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 Arrays.sort(files); 091 092 if (files.length == 0) 093 log.warn("No *.mp test files found in directory: ", testdir.getAbsolutePath()); 094 095 Watch wp = new Watch("parse"); 096 Watch wc = new Watch("compile"); 097 098 int crap_count = 0; 099 int generate_count = 0; 100 for (int n = 0; n < files.length; n++) 101 { 102 File test = files[n]; 103 log.bug("Processing file: ", test.getName()); 104 105 //some files are not expected to compile (as part of the test) 106 //those files start with "crap..." 107 boolean crap = test.getName().toLowerCase().startsWith("crap"); 108 if (crap) { 109 crap_count++; 110 } 111 112 String result_file_str = test.getName(); 113 int i = result_file_str.lastIndexOf("."); 114 String outbase = result_file_str.substring(0, i); 115 String outname = outbase + ".java"; 116 117 File expected = new File(testdir, outname); 118 File out = new File(tmpdir, outname); 119 120 PageParser p; 121 PageCompiler pc; 122 CharArrayWriter buf = new CharArrayWriter(); 123 124 //generate 125 if (generate) 126 { 127 try { 128 buf.reset(); 129 buf.append("Generate: ").append( 130 String.format("%-20s",test.getName())); 131 132 wp.start(); 133 p = new PageParser(testdir, test, expected, outbase); 134 p.parse(); 135 wp.stop(); 136 137 buf.append(" [parsed in:"). 138 append( 139 String.format("%3s", 140 String.valueOf(wp.getTime()))) 141 .append( " ms, "); 142 //successful parse, no parse exceptions 143 //there may be compile time errors though (expected for 144 //crap files) 145 wc.start(); 146 pc = new PageCompiler(expected, System.getProperty("java.class.path"), null); 147 boolean compiled = pc.compile(); 148 wc.stop(); 149 150 if (! compiled) { 151 buf.append(" compile error...possibly expected]"); 152 log.bug(pc.getError()); 153 } 154 else{ 155 buf.append(" compiled in: "). 156 append( 157 String.format("%5s", 158 String.valueOf(wc.getTime()))) 159 .append(" ms]"); 160 //remove class file 161 File f = new File(testdir, outbase + ".class"); 162 if (f.exists()) { 163 f.delete(); 164 } 165 //compiled ok too. 166 generate_count++; 167 } 168 } 169 catch (Exception e) 170 { 171 buf.append(" [parse error...possibly expected]"); 172// if (! crap) { //crap files are expected to have errors 173 msg(test.getName(), "Parse Error: " + e.toString()); 174// } 175 } 176 177 log.info(buf.toString()); 178 continue; 179 } //end if (generate) 180 181 //test 182 183 //we need an expected file to compare with 184 if (! expected.exists()) 185 { 186 if (! crap) { //wont exist for crap files cause parse error 187 msg(test.getName(), "FAILED [expected file: " + expected.getAbsolutePath() + " does not exist]"); 188 continue; 189 } 190 } 191 192 p = new PageParser(testdir, test, out, outbase); 193 194 try { 195 p.parse(); 196 //crap files will cause a parse exception 197 //if here, then parse successful 198 if (! compare(expected, out)) { 199 msg(test.getName(), "FAIL [expected != actual]"); 200 } 201 else{ 202 msg(test.getName(), "OK"); 203 } 204 } 205 catch (Exception e) { 206 if (crap) { 207 //we wanted the expected parse exception 208 msg(test.getName(), "OK"); 209 } 210 else{ 211 msg(test.getName(), "FAIL [ParseException] " + e.toString()); 212 } 213 } 214 215 } //end for loop 216 217 if (generate) { 218 System.out.println(); 219 System.out.println((files.length - crap_count) + " total (non-crap) *.mp files in " + testdir.getAbsolutePath()); 220 System.out.println(generate_count + " expected files were generated."); 221 } 222 223 //print results 224 System.out.println(); 225 TablePrinter.PrintConfig pconfig = new TablePrinter.PrintConfig(); 226 pconfig.setAutoFit(false).setPrintBorders(true).setCellPadding(1); 227 if (generate) { 228 pconfig.setCellWidthForColumn(1,55); 229 System.out.println("The following errors are expected!"); 230 } 231 TablePrinter printer = new TablePrinter(2, System.out, pconfig); 232 printer.startTable(); 233 for (int n = 0; n < results.size(); n++) { 234 printer.startRow(); 235 Object[] arr = (Object[]) results.get(n); 236 printer.printCell(arr[0].toString()); 237 printer.printCell(arr[1].toString()); 238 printer.endRow(); 239 } 240 printer.endTable(); 241 } 242 243static void msg(Object testName, Object result) 244 { 245 results.add(new Object[] {testName, result}); 246 } 247 248static boolean compare(File expected, File output) throws Exception 249 { 250 if (! output.exists()) 251 return false; 252 253 byte[] expected_b = IOUtil.fileToByteArray(expected); 254 byte[] output_b = IOUtil.fileToByteArray(output); 255 256 return Arrays.equals(expected_b, output_b); 257 } 258 259}