1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 /* 19 * $Id$ 20 */ 21 22 package org.apache.qetest.xsl; 23 import java.io.File; 24 import java.io.FileNotFoundException; 25 import java.io.FileOutputStream; 26 import java.io.FilenameFilter; 27 import java.io.PrintWriter; 28 29 import javax.xml.transform.Templates; 30 import javax.xml.transform.Transformer; 31 import javax.xml.transform.TransformerException; 32 import javax.xml.transform.TransformerFactory; 33 import javax.xml.transform.stream.StreamResult; 34 import javax.xml.transform.stream.StreamSource; 35 36 /** 37 * Scans a directory tree of result.xml and assorted files 38 * and creates a rolled-up overview of test problems. 39 * 40 * <p>Currently fairly tightly coupled to it's stylesheet and to 41 * XMLFileLogger and Reporter. Scans a directory tree (default is 42 * results-alltest) looking for Pass-/Fail-*.xml results marker 43 * files from test runs. Then runs the stylesheet over those, 44 * which produces a single output .html file that lists just fails 45 * and related messages.</p> 46 * 47 * @author shane_curcuru@lotus.com 48 * @version $Id$ 49 */ 50 public class ResultScanner 51 { 52 53 /** 54 * Scan a directory tree for result files and create report. 55 * 56 * @param resultDir directory to start scanning in 57 * @param baseName of output filename (.xml/.html added) 58 * @throws any underlying exceptions, including 59 * FileNotFoundException if if can't find any results 60 */ scanResults(String resultName, String baseName)61 public void scanResults(String resultName, String baseName) 62 throws Exception 63 { 64 File resultDir = new File(resultName); 65 if (!resultDir.exists() || !resultDir.isDirectory()) 66 { 67 throw new FileNotFoundException("ResultScanner: resultDir " + resultDir + " does not exist!"); 68 } 69 70 // Create file to hold list of all results found 71 String listFilename = baseName + ".xml"; 72 FileOutputStream fos = new FileOutputStream(listFilename); 73 PrintWriter writer = new PrintWriter(fos); 74 75 // Scan for result files in the dir tree 76 try 77 { 78 logStartTag(writer, listFilename); 79 scanDir(resultDir, writer, 1); // 1 = starting recursion 80 } 81 catch (Exception e) 82 { 83 logError(writer, "scanResults threw: " + e.toString()); 84 } 85 finally 86 { 87 logEndTag(writer); 88 writer.close(); 89 } 90 91 // Transform the list into a master overview of fails 92 transformScan(resultDir, listFilename, baseName + ".html"); 93 } 94 95 96 /** 97 * Scan a directory tree for result files and create report. 98 * 99 * 100 * @param resultDir directory to start scanning in 101 * @param writer to println results to 102 * @param recursion depth of this method 103 * @throws any underlying exceptions, including 104 * FileNotFoundException if if can't find any results 105 */ scanDir(File resultDir, PrintWriter writer, int depth)106 protected void scanDir(File resultDir, PrintWriter writer, int depth) 107 throws Exception 108 { 109 depth++; 110 if (depth > MAX_DEPTH) 111 { 112 logError(writer, "scanDir: MAX_DEPTH exceeded, returning!"); 113 return; 114 } 115 116 if (!resultDir.exists() || !resultDir.isDirectory()) 117 { 118 logError(writer, "scanDir: attempt to scan non-directory, returning!"); 119 return; 120 } 121 122 // Get list of teststatus files that definitely have problems 123 String badResults[] = resultDir.list( 124 new FilenameFilter() // anonymous class 125 { 126 public boolean accept(File dir, String name) 127 { 128 // Shortcuts for bogus filenames and dirs 129 if (name == null || dir == null) 130 return false; 131 // Skip already-rolledup Harness reports 132 if (name.endsWith("Harness.xml")) 133 return false; 134 return (name.startsWith("Fail-") 135 || name.startsWith("Errr-") 136 || name.startsWith("Incp-")); 137 } 138 } 139 ); 140 141 // Get list of teststatus files that passed or were 142 // ambiguous, which means they didn't fail 143 String okResults[] = resultDir.list( 144 new FilenameFilter() // anonymous class 145 { 146 public boolean accept(File dir, String name) 147 { 148 // Shortcuts for bogus filenames and dirs 149 if (name == null || dir == null) 150 return false; 151 return (name.startsWith("Pass-") 152 || name.startsWith("Ambg-")); 153 } 154 } 155 ); 156 157 // Output references to both sets of files if needed 158 if ((null != okResults) && (null != badResults) 159 && (okResults.length + badResults.length > 0)) 160 { 161 logTestGroup(writer, resultDir.getPath(), okResults, badResults); 162 } 163 164 // Traverse down directories 165 String subdirs[] = resultDir.list( 166 new FilenameFilter() // anonymous class 167 { 168 public boolean accept(File dir, String name) 169 { 170 // Shortcuts for bogus filenames and dirs and CVS junk 171 if (null == name || null == dir || "CVS".equals(name)) 172 return false; 173 return (new File(dir, name)).isDirectory(); 174 } 175 } 176 ); 177 if (null != subdirs) 178 { 179 for (int i=0; i < subdirs.length; i++) 180 { 181 scanDir(new File(resultDir, subdirs[i]), writer, depth + 1); 182 } 183 } 184 185 } 186 187 188 /** 189 * Transform a resultfilelist into a real report. 190 * 191 * 192 * @param resultDir directory to start scanning in 193 * @param filename name of listfile 194 * @throws any underlying exceptions 195 */ transformScan(File resultDir, String xmlName, String outName)196 protected void transformScan(File resultDir, String xmlName, String outName) 197 throws TransformerException 198 { 199 Templates templates = getTemplates(); 200 Transformer transformer = templates.newTransformer(); 201 transformer.transform(new StreamSource(xmlName), 202 new StreamResult(outName)); 203 } 204 205 206 /** 207 * Find the appropriate stylesheet to use. 208 * 209 * Worker method for future expansion 210 * 211 * @return Templates object to use for transform 212 * @throws any underlying TransformerException 213 */ getTemplates()214 public Templates getTemplates() 215 throws TransformerException 216 { 217 //@todo assumption: it's in current dir 218 String xslName = "ResultScanner.xsl"; 219 StreamSource xslSource = new StreamSource(xslName); 220 TransformerFactory factory = TransformerFactory.newInstance(); 221 return factory.newTemplates(xslSource); 222 } 223 224 225 /** 226 * Write an xml decl and start tag to our list file. 227 * 228 * @param writer where to write to 229 */ logStartTag(PrintWriter writer, String name)230 public static void logStartTag(PrintWriter writer, String name) 231 { 232 writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); 233 writer.println("<resultfilelist logFile=\"" + name + "\">"); 234 writer.flush(); 235 } 236 237 238 /** 239 * Write the end tag to our list file. 240 * 241 * @param writer where to write to 242 */ logEndTag(PrintWriter writer)243 public static void logEndTag(PrintWriter writer) 244 { 245 writer.println("</resultfilelist>"); 246 writer.flush(); 247 } 248 249 250 /** 251 * Write an group of tests to our list file. 252 * 253 * @param writer where to write to 254 * @param base basedir where these are found 255 * @param okTests list of passing tests to write 256 * @param badTests list of failing tests to write 257 */ logTestGroup(PrintWriter writer, String base, String[] okTests, String[] badTests)258 public static void logTestGroup(PrintWriter writer, String base, 259 String[] okTests, String[] badTests) 260 { 261 writer.println("<testgroup href=\"" + base + "\" >"); 262 if ((null != okTests) && (okTests.length > 0)) 263 { 264 for (int i = 0; i < okTests.length; i++) 265 { 266 writer.println("<teststatus href=\"" + okTests[i] + "\" status=\"ok\" />"); 267 } 268 } 269 if ((null != badTests) && (badTests.length > 0)) 270 { 271 for (int i = 0; i < badTests.length; i++) 272 { 273 writer.println("<teststatus href=\"" + badTests[i] + "\" status=\"notok\" />"); 274 } 275 } 276 writer.println("</testgroup>"); 277 } 278 279 280 /** 281 * Write an error message to our list file. 282 * 283 * @param writer where to write to 284 * @param error string to write out 285 */ logError(PrintWriter writer, String error)286 public static void logError(PrintWriter writer, String error) 287 { 288 writer.println("<error>" + error + "</error>"); 289 writer.flush(); 290 } 291 292 293 /** 294 * Defensive coding: limit directory depth recursion. 295 */ 296 protected static final int MAX_DEPTH = 10; 297 298 299 /** 300 * Bottleneck output for future redirection. 301 */ 302 protected PrintWriter outWriter = new PrintWriter(System.out); 303 304 305 /** 306 * Main method to run from the command line; sample usage. 307 * @param args cmd line arguments 308 */ main(String[] args)309 public static void main(String[] args) 310 { 311 String resultDir = "results-alltest"; 312 if (args.length >= 1) 313 { 314 resultDir = args[0]; 315 } 316 String logFile = resultDir + File.separator + "ResultReport"; 317 if (args.length >= 2) 318 { 319 logFile = args[1]; 320 } 321 //@todo add more arguments; filtering, options 322 // to pass to stylesheets etc. 323 try 324 { 325 ResultScanner app = new ResultScanner(); 326 app.scanResults(resultDir, logFile); 327 System.out.println("ResultScanner of " + resultDir + " complete in: " + logFile + ".html"); 328 } 329 catch (Exception e) 330 { 331 System.out.println("ResultScanner error on: " + resultDir); 332 e.printStackTrace(); 333 } 334 } 335 336 } 337