       1 /*
       2  * $Id$
       3  *
       4  * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
       5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       6  *
       7  * This code is free software; you can redistribute it and/or modify it
       8  * under the terms of the GNU General Public License version 2 only, as
       9  * published by the Free Software Foundation.  Oracle designates this
      10  * particular file as subject to the "Classpath" exception as provided
      11  * by Oracle in the LICENSE file that accompanied this code.
      12  *
      13  * This code is distributed in the hope that it will be useful, but WITHOUT
      14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      16  * version 2 for more details (a copy is included in the LICENSE file that
      17  * accompanied this code).
      18  *
      19  * You should have received a copy of the GNU General Public License version
      20  * 2 along with this work; if not, write to the Free Software Foundation,
      21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      22  *
      23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      24  * or visit www.oracle.com if you need additional information or have any
      25  * questions.
      26  */
      27 package com.sun.javatest.audit;
      28
      29 import java.io.PrintStream;
      30 import java.text.DateFormat;
      31 import java.text.ParseException;
      32 import java.text.SimpleDateFormat;
      33 import java.util.Arrays;
      34 import java.util.Date;
      35 import java.util.Enumeration;
      36 import java.util.Hashtable;
      37 import java.util.Iterator;
      38 import java.util.Locale;
      39 import java.util.Map;
      40 import java.util.SortedSet;
      41 import java.util.TreeSet;
      42 import java.util.Vector;
      43
      44 import com.sun.javatest.ExcludeList;
      45 import com.sun.javatest.Parameters;
      46 import com.sun.javatest.Status;
      47 import com.sun.javatest.TestDescription;
      48 import com.sun.javatest.TestFilter;
      49 import com.sun.javatest.TestFinder;
      50 import com.sun.javatest.TestFinderQueue;
      51 import com.sun.javatest.TestResult;
      52 import com.sun.javatest.TestSuite;
      53 import com.sun.javatest.WorkDirectory;
      54 import com.sun.javatest.util.I18NResourceBundle;
      55 import com.sun.javatest.util.StringArray;
      56
      57 /**
      58  * Analyze a set of test results for validity.
      59  * Based on the given parameters, a test finder is run over the test suite
      60  * to determine which tests are supposed to have been run, and the work
      61  * directory is checked to see that the corresponding test has been run,
      62  * successfully. Various statistics are collected and can be printed or
      63  * accessed for external analysis.
      64  */
      65 public class Audit
      66 {
      67     /**
      68      * Analyze a set of test results for validity, based on the given parameters.
      69      * @param params Parameters to define the test finder and work directory
      70      * used in the analysis.
      71      * @throws TestSuite.Fault if there is a problem opening the test suite given in the parameters
      72      */
      73     public Audit(Parameters params)
      74     {
      75         this(getTestFinderQueue(params),
      76              params.getExcludeList(),
      77              params.getWorkDirectory());
      78     }
      79
      80     /**
      81      * Create a test finder queue based on the info in the parameters
      82      */
      83     private static TestFinderQueue getTestFinderQueue(Parameters params)
      84     {
      85         TestSuite ts = params.getTestSuite();
      86         TestFinder tf = ts.getTestFinder();
      87
      88         TestFinderQueue tfq = new TestFinderQueue();
      89         tfq.setTestFinder(tf);
      90
      91         String[] tests = params.getTests();
      92         tfq.setTests(tests);
      93
      94         TestFilter[] filters = params.getFilters();
      95         tfq.setFilters(filters);
      96
      97         return tfq;
      98     }
      99
     100
     101     /**
     102      * Analyze a set of test results for validity, based on the given parameters.
     103      * @param tfq An enumerator for the set of tests to be run
     104      * @param excludeList The excludeList against which to check excluded test cases
     105      * @param workDir The set of results
     106      */
     107     public Audit(TestFinderQueue tfq, ExcludeList excludeList, WorkDirectory workDir)
     108     {
     109         Vector<TestResult> badChecksumTestsV = new Vector<>();
     110         Vector<TestResult> badTestDescriptionsV = new Vector<>();
     111         Vector<TestResult> badTestCaseTestsV = new Vector<>();
     112         Vector<TestDescription> badTestsV = new Vector<>();
     113
     114         workDir.getTestResultTable().waitUntilReady();
     115
     116         TestDescription td;
     117         while ((td = tfq.next()) != null) {
     118             try {
     119                 TestResult tr = new TestResult(workDir, TestResult.getWorkRelativePath(td));
     120
     121                 testCount++;
     122                 statusCounts[tr.getStatus().getType()]++;
     123                 checksumCounts[tr.getChecksumState()]++;
     124
     125                 if (tr.getChecksumState() == TestResult.BAD_CHECKSUM)
     126                     badChecksumTestsV.addElement(tr);
     127
     128                 if (!equal(td, tr.getDescription()))
     129                     badTestDescriptionsV.addElement(tr);
     130
     131                 if (!checkTestCases(tr, excludeList))
     132                     badTestCaseTestsV.addElement(tr);
     133
     134                 Map trEnv = tr.getEnvironment();
     135                 for (Iterator i = trEnv.entrySet().iterator(); i.hasNext(); ) {
     136                     Map.Entry e = (Map.Entry) (i.next());
     137                     String key = (String) (e.getKey());
     138                     String value = (String) (e.getValue());
     139                     Vector<String> allValuesForKey = envTable.get(key);
     140                     if (allValuesForKey == null) {
     141                         allValuesForKey = new Vector<>();
     142                         envTable.put(key, allValuesForKey);
     143                     }
     144                     if (!allValuesForKey.contains(value))
     145                         allValuesForKey.addElement(value);
     146                 }
     147
     148                 String start = tr.getProperty(TestResult.START);
     149                 if (start == null) {
     150                     badDates = true;
     151                 }
     152                 else {
     153                     Date d = parseDate(start);
     154                     if (d == null)
     155                         badDates = true;
     156                     else {
     157                         if (earliestStart == null || d.before(earliestStart))
     158                             earliestStart = d;
     159                         if (latestStart == null || d.after(latestStart))
     160                             latestStart = d;
     161                     }
     162                 }
     163             }
     164             catch (TestResult.Fault e) {
     165                 //System.err.println(td.getRootRelativeURL() + " " + TestResult.getWorkRelativePath(td) + " " + e);
     166                 badTestsV.addElement(td);
     167             }
     168         }
     169
     170         for (Enumeration e = envTable.keys(); e.hasMoreElements(); ) {
     171             String key = (String)(e.nextElement());
     172             Vector allValuesForKey = envTable.get(key);
     173             envCounts[allValuesForKey.size() == 1 ? 0 : 1]++;
     174         }
     175
     176         if (badChecksumTestsV.size() > 0) {
     177             badChecksumTests = new TestResult[badChecksumTestsV.size()];
     178             badChecksumTestsV.copyInto(badChecksumTests);
     179         }
     180
     181         if (badTestDescriptionsV.size() > 0) {
     182             badTestDescriptions = new TestResult[badTestDescriptionsV.size()];
     183             badTestDescriptionsV.copyInto(badTestDescriptions);
     184         }
     185
     186         if (badTestCaseTestsV.size() > 0) {
     187             badTestCaseTests = new TestResult[badTestCaseTestsV.size()];
     188             badTestCaseTestsV.copyInto(badTestCaseTests);
     189         }
     190
     191         if (badTestsV.size() > 0) {
     192             badTests = new TestDescription[badTestsV.size()];
     193             badTestsV.copyInto(badTests);
     194         }
     195     }
     196
     197     /**
     198      * Get a list of tests that had bad checksums during the analysis.
     199      * @return A set of tests, or null if none.
     200      */
     201     public TestResult[] getBadChecksumTests() {
     202         return badChecksumTests;
     203     }
     204
     205     /**
     206      * Get a list of tests that had bad test descriptions during the analysis.
     207      * @return A set of tests, or null if none.
     208      */
     209     public TestResult[] getBadTestDescriptions() {
     210         return badTestDescriptions;
     211     }
     212
     213     /**
     214      * Get a list of tests that gave problems during the analysis.
     215      * @return A set of tests, or null if none.
     216      */
     217     public TestDescription[] getBadTests() {
     218         return badTests;
     219     }
     220
     221     /**
     222      * Get a list of tests that were executed with too many test cases
     223      * excluded.
     224      * @return A set of tests, or null if none.
     225      */
     226     public TestResult[] getBadTestCaseTests() {
     227         return badTestCaseTests;
     228     }
     229
     230     /**
     231      * Get the statistics about the test result checksums.
     232      * @return An array of counts, indexed by
     233      * TestResult.GOOD_CHECKSUM, TestResult.BAD_CHECKSUM, etc
     234      */
     235     public int[] getChecksumCounts() {
     236         return checksumCounts;
     237     }
     238
     239     /**
     240      * Get the statistics about the environment values used by the test results.
     241      * @return An array of two integers; the first gives the number of env
     242      * values that were uniquely defined across all test results, the second gives
     243      * the number that were multiply defined across the results.
     244      */
     245     public int[] getEnvCounts() {
     246         return envCounts;
     247     }
     248
     249     /**
     250      * Get the composite set of environment values used by the tests.
     251      * @return A table of values.
     252      * The keys to the table are strings; the values are vectors of strings
     253      * containing the various values for that key.
     254      */
     255     public Hashtable getEnvTable() {
     256         return envTable;
     257     }
     258
     259     /**
     260      * Get the statistics about the test results.
     261      * @return An array of counts, indexed by Status.PASSED, Status.FAILED, etc
     262      */
     263     public int[] getStatusCounts() {
     264         return statusCounts;
     265     }
     266
     267     /**
     268      * Get earliest recorded start time for a test.
     269      * @return The earliest recorded valid start time for a test,
     270      * or null, if none was found.
     271      */
     272     public Date getEarliestStartTime() {
     273         return earliestStart;
     274     }
     275
     276     /**
     277      * Get latest recorded start time for a test.
     278      * @return The latest recorded valid start time for a test,
     279      * or null, if none was found.
     280      */
     281     public Date getLatestStartTime() {
     282         return latestStart;
     283     }
     284
     285     public boolean hasBadStartTimes() {
     286         return badDates;
     287     }
     288
     289     /**
     290      * Get an overall thumps-up/thumbs-down for the analysis.
     291      * It is a composite of the other isXXXOK() methods.
     292      * @return true if all checks are OK
     293      */
     294     public boolean isOK() {
     295         return (isStatusCountsOK()
     296                 && isChecksumCountsOK()
     297                 && isDateStampsOK()
     298                 && isAllTestsOK()
     299                 && isAllTestCasesOK())
     300                 && isAllTestDescriptionsOK();
     301     }
     302
     303     /**
     304      * Determine if all tests were analyzed successfully.
     305      * @return true if all tests were analyzed successfully.
     306      */
     307     public boolean isAllTestsOK() {
     308         return (badTests == null);
     309     }
     310
     311     /**
     312      * Determine if all test cases were correctly executed for those
     313      * tests that support test cases.
     314      * @return true if all test cases were correctly executed for those
     315      * tests that support test cases.
     316      */
     317     public boolean isAllTestCasesOK() {
     318         return (badTestCaseTests == null);
     319     }
     320
     321     /**
     322      * Determine if all test descriptions were OK.
     323      * @return true is all test descriptions were OK.
     324      */
     325     public boolean isAllTestDescriptionsOK() {
     326         return (badTestDescriptions == null);
     327     }
     328
     329     /**
     330      * Determine if the checksum counts are acceptable.
     331      * This is currently defined as "no bad checksums";
     332      * in time, it should become "all good checksums".
     333      * @return true is there were no test results with bad checksums.
     334      */
     335     public boolean isChecksumCountsOK() {
     336         return (checksumCounts[TestResult.BAD_CHECKSUM] == 0);
     337     }
     338
     339     /**
     340      * Determine if all the test results have valid date stamps.
     341      * "Valid" just means present and parseable dates: no restriction
     342      * on the value is currently imposed.
     343      * @return true if no dates have invalid datestamps.
     344      */
     345     public boolean isDateStampsOK() {
     346         return (earliestStart != null && latestStart != null && !badDates);
     347     }
     348
     349     /**
     350      * Determine if the test result outcomes are acceptable.
     351      * All must pass, none must fail.
     352      * @return true if all necessary tests passed.
     353      */
     354     public boolean isStatusCountsOK() {
     355         if (testCount == 0)
     356             return false;
     357
     358         for (int i = 0; i < statusCounts.length; i++) {
     359             if (i != Status.PASSED && statusCounts[i] != 0)
     360                 return false;
     361         }
     362         return (statusCounts[Status.PASSED] == testCount);
     363     }
     364
     365     /**
     366      * Print out a report of the analysis.
     367      * @param out  A stream to which to write the output.
     368      * @param showAllEnvValues Include a listing of all environment values used
     369      *          by the collected set of tests.
     370      * @param showMultipleEnvValues Include a listing of those environment values
     371      *          which were multiply defined by the collected set of tests.
     372      */
     373     public synchronized void report(PrintStream out,
     374                                     boolean showAllEnvValues,
     375                                     boolean showMultipleEnvValues) {
     376         this.out = out;
     377
     378         showResultCounts();
     379         showChecksumCounts();
     380         showDateStampInfo();
     381         showEnvCounts();
     382
     383         showBadChecksums();
     384         showBadTestDescriptions();
     385         showBadTestCaseTests();
     386         showBadTests();
     387
     388         if (showAllEnvValues || showMultipleEnvValues)
     389             showEnvValues(showAllEnvValues);
     390     }
     391
     392     /**
     393      * Print out a short summary about any tests with bad checksums
     394      */
     395     private void showBadChecksums() {
     396         if (badChecksumTests != null) {
     397             out.println("The following " + badChecksumTests.length + " tests had bad checksums.");
     398             for (int i = 0; i < badChecksumTests.length; i++) {
     399                 TestResult tr = badChecksumTests[i];
     400                 out.println(tr.getWorkRelativePath());
     401             }
     402         }
     403     }
     404
     405     /**
     406      * Print out a short summary about any tests with bad test descriptions
     407      */
     408     private void showBadTestDescriptions() {
     409         if (badTestDescriptions != null) {
     410             out.println("The following " + badTestDescriptions.length + " tests had bad test descriptions.");
     411             for (int i = 0; i < badTestDescriptions.length; i++) {
     412                 TestResult tr = badTestDescriptions[i];
     413                 out.println(tr.getWorkRelativePath());
     414             }
     415         }
     416     }
     417
     418     /**
     419      * Print out a short summary report about any tests with too many test cases
     420      * excluded.
     421      */
     422     private void showBadTestCaseTests() {
     423         if (badTestCaseTests != null) {
     424             out.println(i18n.getString("adt.tooManyTestCases", new Integer(badTestCaseTests.length)));
     425             for (int i = 0; i < badTestCaseTests.length; i++) {
     426                 TestResult tr = badTestCaseTests[i];
     427                 out.println(tr.getWorkRelativePath());
     428             }
     429         }
     430     }
     431
     432     /**
     433      * Print out a short summary report about any tests that gave problems
     434      * during the analysis.
     435      */
     436     private void showBadTests() {
     437         if (badTests != null) {
     438             out.println(i18n.getString("adt.badTests", new Integer(badTests.length)));
     439             for (int i = 0; i < badTests.length; i++) {
     440                 TestDescription td = badTests[i];
     441                 out.println(TestResult.getWorkRelativePath(td));
     442             }
     443         }
     444     }
     445
     446     /**
     447      * Print out a short summary report about the earliest and latest
     448      * start times for the test results.
     449      */
     450     private void showDateStampInfo() {
     451         if (earliestStart == null || latestStart == null) {
     452             out.println(i18n.getString("adt.noDateStamps"));
     453         }
     454         else {
     455             Integer b = new Integer(badDates ? 1 : 0);
     456             out.println(i18n.getString("adt.earliestResult",
     457                                     new Object[] {earliestStart, b}));
     458             out.println(i18n.getString("adt.latestResult",
     459                                     new Object[] {latestStart, b}));
     460             if (badDates)
     461                 out.println(i18n.getString("adt.badDateStamps"));
     462         }
     463     }
     464
     465     /**
     466      * Print out a short summary report of the environment statistics.
     467      * @param out  A stream to which to write the output.
     468      */
     469     private void showEnvCounts() {
     470         int u = envCounts[0];
     471         int m = envCounts[1];
     472         if (u + m > 0) {
     473             if (m == 0)
     474                 out.println(i18n.getString("adt.env.allOK"));
     475             else {
     476                 out.println(i18n.getString("adt.env.count",
     477                                            new Object[] {
     478                                                new Integer(u),
     479                                                new Integer((u > 0 && m > 0) ? 1 : 0),
     480                                                new Integer(m)
     481                                                    } ));
     482             }
     483         }
     484     }
     485
     486     /**
     487      * Print out a listing of some or all of the env values used by the tests.
     488      * @param showAll show all environment values (uniquely and multiply defined.)
     489      * The default is to just show the multiple defined values.
     490      */
     491     private void showEnvValues(boolean showAll) {
     492         out.println();
     493         out.print(i18n.getString("adt.envList.title"));
     494
     495         SortedSet<String> ss = new TreeSet<>();
     496         for (Enumeration e = envTable.keys(); e.hasMoreElements(); ) {
     497             String key = (String)(e.nextElement());
     498             ss.add(key);
     499         }
     500
     501         for (Iterator iter = ss.iterator(); iter.hasNext(); ) {
     502             String key = (String) (iter.next());
     503             Vector allValuesForKey = envTable.get(key);
     504             if (allValuesForKey.size() == 1) {
     505                 if (showAll)
     506                     out.println(i18n.getString("adt.envKeyValue",
     507                                             new Object[] {key, allValuesForKey.elementAt(0)}));
     508             }
     509             else {
     510                 out.println(i18n.getString("adt.envKey", key));
     511                 for (int j = 0; j < allValuesForKey.size(); j++) {
     512                     out.println(i18n.getString("adt.envValue",
     513                                             allValuesForKey.elementAt(j)));
     514                 }
     515             }
     516         }
     517     }
     518
     519     /**
     520      * Print out a short summary report of the checksum statistics.
     521      */
     522     private void showChecksumCounts() {
     523         if (testCount > 0) {
     524             int g = checksumCounts[TestResult.GOOD_CHECKSUM];
     525             int b = checksumCounts[TestResult.BAD_CHECKSUM];
     526             int n = checksumCounts[TestResult.NO_CHECKSUM];
     527             if (b == 0 && n == 0)
     528                 out.println(i18n.getString("adt.cs.allOK"));
     529             else
     530                 out.println(i18n.getString("adt.cs.count",
     531                                            new Object[] {
     532                                                new Integer(g),
     533                                                new Integer((g > 0) && (b + n > 0) ? 1 : 0),
     534                                                new Integer(b),
     535                                                new Integer((b > 0) && (n > 0) ? 1 : 0),
     536                                                new Integer(n)
     537                                                    }));
     538         }
     539     }
     540
     541     /**
     542      * Print out a short summary report of the test result status statistics.
     543      */
     544     private void showResultCounts() {
     545         if (testCount == 0)
     546             out.println(i18n.getString("adt.status.noTests"));
     547         else {
     548             int p = statusCounts[Status.PASSED];
     549             int f = statusCounts[Status.FAILED];
     550             int e = statusCounts[Status.ERROR];
     551             int nr = statusCounts[Status.NOT_RUN];
     552
     553             if (p == testCount)
     554                 out.println(i18n.getString("adt.status.allOK"));
     555             else
     556                 out.println(i18n.getString("adt.status.count",
     557                                            new Object[] {
     558                                                new Integer(p),
     559                                                new Integer((p > 0) && (f + e + nr > 0) ? 1 : 0),
     560                                                new Integer(f),
     561                                                new Integer((f > 0) && (e + nr > 0) ? 1 : 0),
     562                                                new Integer(e),
     563                                                new Integer((e > 0) && (nr > 0) ? 1 : 0),
     564                                                new Integer(nr)
     565                                                    }));
     566         }
     567     }
     568
     569     /**
     570      * Print out a labelled count if non-zero
     571      * @param needSep Need a leading separator (comma)
     572      * @param label The label to display
     573      * @param count The count to display, if non-zero
     574      * @return true if a separator will be needed for the next value to be shown
     575      */
     576     private boolean showCount(String msg, boolean needSep, int count) {
     577         if (count == 0)
     578             return needSep;
     579         else {
     580             out.print(i18n.getString(msg,
     581                                   new Object[] {new Integer(needSep ? 1 : 0), new Integer(count)}));
     582             return true;
     583         }
     584     }
     585
     586     private boolean checkTestCases(TestResult tr, ExcludeList excludeList)
     587         throws TestResult.Fault {
     588         // If no test cases excluded when test was run, then OK.
     589         // (This is the typical case.)
     590         String[] etcTest;
     591         String etcTestProp = tr.getProperty("excludedTestCases");
     592         if (etcTestProp == null)
     593             return true;
     594         else
     595             etcTest = StringArray.split(etcTestProp);
     596
     597         // This next test is probably redundant, assuming the excludeList is
     598         // the same as that used to locate the tests, but check anyway:
     599         // if test is now completely excluded, then OK.
     600         if (excludeList.excludesAllOf(tr.getDescription()))
     601             return true;
     602
     603         String[] etcTable = excludeList.getTestCases(tr.getDescription());
     604         // null indicates test not found in table, or the entire test is excluded
     605         // (without specifying test cases.)  Because of the previous check, the
     606         // latter cannot be the case, so null means the test is not present
     607         // in the exclude list. Since we also have checked that the test has
     608         // excluded test cases that means we have a problem...
     609         if (etcTable == null)
     610             return false;
     611
     612         // now check that etcTest is a subset of etcTable
     613     nextTestCase:
     614         for (int i = 0; i < etcTest.length; i++) {
     615             for (int j = 0; j < etcTable.length; j++) {
     616                 if (etcTest[i].equals(etcTable[j]))
     617                     continue nextTestCase;
     618             }
     619             // etcTest[i] was not found in etcTable;
     620             // that means we have a problem
     621             return false;
     622         }
     623
     624         // if we're here, we found all the test cases that were actually
     625         // excluded were all validly excluded, according to excludedTestCases.
     626         return true;
     627     }
     628
     629     private static boolean equal(TestDescription a, TestDescription b) {
     630         if (a == null || b == null)
     631             return (a == b);
     632
     633         //if (!a.rootRelativeFile.equals(b.rootRelativeFile))
     634         //    return false;
     635
     636         Iterator eA = a.getParameterKeys();
     637         Iterator eB = b.getParameterKeys();
     638         while (eA.hasNext() && eB.hasNext()) {
     639             String keyA = (String)eA.next();
     640             String keyB = (String)eB.next();
     641             if (!keyA.equals(keyB)) {
     642                 //System.err.println("mismatch " + a.getRootRelativePath() + " a:" + keyA + " b:" + keyB);
     643                 return false;
     644             }
     645
     646             String valA = a.getParameter(keyA);
     647             String valB = a.getParameter(keyB);
     648             if (!(valA.equals(valB) || (keyA.equals("keywords") && keywordMatch(valA, valB)))) {
     649                 //System.err.println("mismatch " + a.getRootRelativePath() + " key:" + keyA + " a:" + valA + " b:" + valB);
     650                 return false;
     651             }
     652         }
     653
     654         return true;
     655     }
     656
     657     private final static boolean keywordMatch(String a, String b) {
     658         // eek, not very efficient!
     659         String[] aa = StringArray.split(a);
     660         Arrays.sort(aa);
     661         String[] bb = StringArray.split(b);
     662         Arrays.sort(bb);
     663         return Arrays.equals(aa, bb);
     664     }
     665
     666     private Date parseDate(String s) {
     667         if (dateFormats == null)
     668             initDateFormats();
     669
     670         for (int i = 0; i < dateFormats.length; i++) {
     671             try {
     672                 Date d = dateFormats[i].parse(s);
     673                 // successfully parsed the date; shuffle the format to the front
     674                 // to speed up future parses, assuming dates will likely be similar
     675                 if (i > 0) {
     676                     DateFormat tmp = dateFormats[i];
     677                     System.arraycopy(dateFormats, 0, dateFormats, 1, i);
     678                     dateFormats[0] = tmp;
     679                 }
     680                 return d;
     681             }
     682             catch (ParseException e) {
     683                 //System.err.println("pattern: " + ((SimpleDateFormat)dateFormats[i]).toPattern());
     684                 //System.err.println("  value: " + s);
     685                 //System.err.println("example: " + dateFormats[i].format(new Date()));
     686             }
     687         }
     688         return null;
     689     }
     690
     691     private void initDateFormats() {
     692         // Create an array of possible date formats to parse dates in .jtr files.
     693         // Most likely is Unix C time in English; the array will be reordered in use
     694         // by moving the recently used entries to the front of the array.
     695         Vector<DateFormat> v = new Vector<>();
     696
     697         // generic Java default
     698         // 10-Sep-99 3:25:11 PM
     699         v.addElement(DateFormat.getDateTimeInstance());
     700         v.addElement(DateFormat.getDateTimeInstance(DateFormat.DEFAULT,
     701                                                     DateFormat.DEFAULT,
     702                                                     Locale.ENGLISH));
     703
     704         // standard IETF date syntax
     705         // Fri, 10 September 1999 03:25:12 PDT
     706         v.addElement(new SimpleDateFormat("EEE, dd MMMM yyyy HH:mm:ss zzz"));
     707         v.addElement(new SimpleDateFormat("EEE, dd MMMM yyyy HH:mm:ss zzz", Locale.ENGLISH));
     708
     709         // Unix C time
     710         // Fri Sep 10 14:41:37 PDT 1999
     711         v.addElement(new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy"));
     712         v.addElement(new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH));
     713
     714         // allow user-specified format
     715         String s = System.getProperty("javatest.date.format");
     716         if (s != null)
     717             v.addElement(new SimpleDateFormat(s));
     718
     719         dateFormats = new DateFormat[v.size()];
     720         v.copyInto(dateFormats);
     721     }
     722
     723     private int testCount;
     724     private int[] checksumCounts = new int[TestResult.NUM_CHECKSUM_STATES];
     725     private int[] envCounts = new int[2];
     726     private int[] statusCounts = new int[5];
     727     private boolean badDates = false;
     728     private TestDescription[] badTests;
     729     private TestResult[] badTestCaseTests;
     730     private TestResult[] badTestDescriptions;
     731     private TestResult[] badChecksumTests;
     732     private Date earliestStart;
     733     private Date latestStart;
     734     private DateFormat[] dateFormats;
     735
     736     private Hashtable<String, Vector<String>> envTable = new Hashtable<>();
     737     private PrintStream out;
     738     private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(Audit.class);
     739 }