/*
* $Id$
*
* Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.javatest;
import com.sun.javatest.util.DynamicArray;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import com.sun.javatest.util.I18NResourceBundle;
/**
* Support class to read and process a list of tests and test cases which are
* known to fail during execution. The intent is to allow better post-run
* analysis of repetitive test runs, making is easier to find out what has
* "changed" since the list was made. This class is loosely based on the
* exclude list, making it easy to interchange the files and tools.
*
* File format:
* Test_URL[Test_Cases] BugID_List
* The test URL rules are defined elsewhere, but it is critical that the test
* names do not contain spaces and nothing before the BugID_List has any
* whitespace. The exact format of the BugID_List must simply conform to being
* comma separated values, no whitespace or non-printable characters.
* @since 4.4
*/
public class KnownFailuresList
{
public void addEntry(Entry e) throws Fault {
synchronized (table) {
Key key = new Key(e.relativeURL);
Object o = table.get(key);
if (o == null) {
// easy case: nothing already exists in the table, so just
// add this one
table.put(key, e);
}
else if (o instanceof Entry) {
// a single entry exists in the table, so need to check for
// invalid combinations of test cases and tests
Entry curr = (Entry)o;
if (curr.testCase == null) {
if (e.testCase == null)
// overwrite existing entry for entire test
table.put(key, e);
else {
if (strict) {
// can't record test case when entire test already listed
throw new Fault(i18n, "kfl.cantListCase", e.relativeURL);
}
// else ignore new entry since entire test is already listed
}
}
else {
if (e.testCase == null) {
if (strict) {
// can't record entire test when test case already listed
throw new Fault(i18n, "kfl.cantListTest", e.relativeURL);
}
else {
// overwrite existing entry for a test case with
// new entry for entire test
table.put(key, e);
}
}
else if (curr.testCase.equals(e.testCase)) {
// overwrite existing entry for the same test case
table.put(key, e);
}
else {
// already excluded one test case, now we need to exclude
// another; make an array to hold both entries against the
// one key
table.put(key, new Entry[] {curr, e});
}
}
}
else {
// if there is an array, it must be for unique test cases
if (e.testCase == null) {
if (strict) {
// can't exclude entire test when selected test cases already excluded
throw new Fault(i18n, "kfl.cantListTest", e.relativeURL);
}
else {
// overwrite existing entry for list of test cases with
// new entry for entire test
table.put(key, e);
}
}
else {
Entry[] curr = (Entry[])o;
for (int i = 0; i < curr.length; i++) {
if (curr[i].testCase.equals(e.testCase)) {
curr[i] = e;
return;
}
}
// must be a new test case, add it into the array
table.put(key, DynamicArray.append(curr, e));
}
}
}
}
/**
* This exception is used to report problems manipulating an exclude list.
*/
public static class Fault extends Exception
{
Fault(I18NResourceBundle i18n, String s, Object o) {
super(i18n.getString(s, o));
}
}
/**
* Test if a file appears to be for an exclude list, by checking the extension.
* @param f The file to be tested.
* @return true if the file appears to be a known failures list.
*/
public static boolean isKflFile(File f) {
return f.getPath().endsWith(KFLFILE_EXTN);
}
/**
* Create a new, empty KFL object.
*/
public KnownFailuresList() {
}
/**
* Create an KnownFailuresList from the data contained in a file.
* @param f The file to be read.
* @throws FileNotFoundException if the file cannot be found
* @throws IOException if any problems occur while reading the file
* @throws KnownFailuresList.Fault if the data in the file is inconsistent
* @see #KnownFailuresList(File[])
*/
public KnownFailuresList(File f)
throws FileNotFoundException, IOException, Fault
{
this(f, false);
}
/**
* Create an KnownFailuresList from the data contained in a file.
* @param f The file to be read.
* @param strict Indicate if strict data checking rules should be used.
* @throws FileNotFoundException if the file cannot be found
* @throws IOException if any problems occur while reading the file
* @throws KnownFailuresList.Fault if the data in the file is inconsistent
* @see #KnownFailuresList(File[])
* @see #setStrictModeEnabled(boolean)
*/
public KnownFailuresList(File f, boolean strict)
throws FileNotFoundException, IOException, Fault
{
setStrictModeEnabled(strict);
if (f != null) {
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8));
Parser p = new Parser(in);
try {
Entry e;
while ((e = p.readEntry()) != null)
addEntry(e);
}
finally {
in.close();
}
title = p.getTitle();
}
}
/**
* Create a KnownFailuresList from the data contained in a series of files.
* @param files The file to be read.
* @throws FileNotFoundException if any of the files cannot be found
* @throws IOException if any problems occur while reading the files.
* @throws KnownFailuresList.Fault if the data in the files is inconsistent
* @see #KnownFailuresList(File)
*/
public KnownFailuresList(File[] files)
throws FileNotFoundException, IOException, Fault
{
this(files, false);
}
/**
* Create a KnownFailuresList from the data contained in a series of files.
* @param files The file to be read.
* @param strict Indicate if strict data checking rules should be used.
* @throws FileNotFoundException if any of the files cannot be found
* @throws IOException if any problems occur while reading the files.
* @throws KnownFailuresList.Fault if the data in the files is inconsistent
* @see #KnownFailuresList(File)
* @see #setStrictModeEnabled(boolean)
*/
public KnownFailuresList(File[] files, boolean strict)
throws FileNotFoundException, IOException, Fault
{
setStrictModeEnabled(strict);
for (int i = 0; i < files.length; i++) {
KnownFailuresList kfl = new KnownFailuresList(files[i], strict);
merge(kfl);
}
}
/**
* Specify whether strict mode is on or not. In strict mode, calls to addEntry
* may generate an exception in the case of conflicts, such as adding an entry
* to exclude a specific test case when the entire test is already excluded.
* @param on true if strict mode should be enabled, and false otherwise
* @see #isStrictModeEnabled
*/
public void setStrictModeEnabled(boolean on) {
//System.err.println("EL.setStrictModeEnabled " + on);
strict = on;
}
/**
* Check whether strict mode is enabled or not. In strict mode, calls to addEntry
* may generate an exception in the case of conflicts, such as adding an entry
* to exclude a specific test case when the entire test is already excluded.
* @return true if strict mode is enabled, and false otherwise
* @see #setStrictModeEnabled
*/
public boolean isStrictModeEnabled() {
return strict;
}
/**
* Iterate over the contents of the table.
* @param group if true, entries for the same relative
* URL are grouped together, and if more than one, returned in an
* array; if false, the iterator always returns
* separate entries.
* @see Entry
* @return an iterator for the table: the entries are either
* single instances of @link(Entry) or a mixture of @link(Entry)
* and @link(Entry)[], depending on the group
* parameter.
*/
public Iterator getIterator(boolean group) {
if (group)
return table.values().iterator();
else {
// flatten the enumeration into a vector, then
// enumerate that
List v = new ArrayList<>(table.size());
for (Iterator