blob: d9aa39ad2008a79bc8f22491411e50dfcc8c8300 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id$
*/
/*
*
* Logger.java
*
*/
package org.apache.qetest;
import java.util.Hashtable;
import java.util.Properties;
/**
* Interface defining a utility that can log out test results.
* This interface defnines a standalone utility that can be used
* to report the results of a test. It would commonly be used by a
* testing utility library to produce actual output from the run
* of a test file.
* <p>The Logger defines a minimal interface for expressing the result
* of a test in a generic manner. Different Loggers can be written
* to both express the results in different places (on a live console,
* in a persistent file, over a network) and in different formats -
* perhaps an XMLTestLogger would express the results in an
* XML file or object.</p>
* <p>In many cases, tests will actually call a Reporter, which
* acts as a composite for Logger objects, and includes numerous
* useful utility and convenience methods.</p>
* <ul>Loggers explicitly have a restricted set of logging calls for
* two main reasons:
* <li>To help keep tests structured</li>
* <li>To make it easier to generate 'reports' based on test output
* (i.e. number of tests passed/failed, graphs of results, etc.)</li>
* </ul>
* <p>While there are a number of very good general purpose logging
* utilities out there, I prefer to use this Logger or a similar
* class to report the results of tests because this class
* specifically constrains the format and manner that a test reports
* results. This should help to keep tests more structured and
* easier for other users to understand. Specific Logger
* implementations may of course choose to store or log out their
* results in any manner they like; in fact I plan on implementing
* a <a href="http://jakarta.apache.org/log4j/docs/index.html">
* Log4JLogger</a> class in the future.</p>
* @author Shane_Curcuru@lotus.com
* @version $Id$
*/
public interface Logger
{
//-----------------------------------------------------
//-------- Constants for common input params --------
//-----------------------------------------------------
/**
* Parameter: FQCN of Logger(s) to use.
* <p>Default: usually none, but implementers may choose to call
* setupDefaultLogger(). Will accept multiple classnames separated
* by ";" semicolon. Format:
* <code>-reporters org.apache.qetest.ConsoleLogger;org.apache.qetest.SomeOtherLogger</code></p>
*/
public static final String OPT_LOGGERS = "loggers";
/** Constant ';' semicolon for delimiter in OPT_LOGGERS field. */
public static final String LOGGER_SEPARATOR = ";";
/**
* A default Logger classname - ConsoleLogger.
* //@todo have a factory creation service for this so that any
* test or testlet that wishes a default logger at startup
* can simply ask a common place for one.
*/
public static final String DEFAULT_LOGGER =
"org.apache.qetest.ConsoleLogger";
/**
* Parameter: level of output to log, int 0-99.
* {@link #CRITICALMSG CRITICALMSG = 0}, only very important
* messages are output from the test or shown in a report, to
* {@link #TRACEMSG TRACEMSG}, where all messages are output
* or shown in the result.
* {@link #CRITICALMSG See CRITICALMSG for a chart of levels.}
*/
public static final String OPT_LOGGINGLEVEL = "loggingLevel";
/**
* Parameter: if we should log performance data, true/false.
*/
public static final String OPT_PERFLOGGING = "perfLogging";
/**
* Parameter: if we should dump debugging info to System.err.
* <p>This is primarily for debugging the test framework itself,
* not necessarily for debugging the application under test.</p>
*/
public static final String OPT_DEBUG = "debug";
/**
* Parameter: Name of test results file for file-based Loggers.
* <p>File-based loggers should use this key as an initializer
* for the name of their output file.</p>
* <p>Commandline Format: <code>-logFile path\to\ResultsFileName.ext</code></p>
* <p>Properties file Format: <code>logFile=path\\to\\ResultsFileName.ext</code></p>
* //@todo Need more doc about platform separator differences
*/
public static final String OPT_LOGFILE = "logFile";
/**
* Parameter: Indent depth for console or HTML/XML loggers.
* <p>Loggers may use this as an integer number of spaces to
* indent, as applicable to their situation.</p>
* <p>Commandline Format: <code>-indent <i>nn</i></code></p>
* <p>Properties file Format: <code>indent=<i>nn</i></code></p>
*/
public static final String OPT_INDENT = "indent";
//-----------------------------------------------------
//-------- Constants for Logger and Reporter interactions --------
//-----------------------------------------------------
/**
* This determines the amount of data actually logged out to results.
* <p>Loggers merely use these constants in their output formats.
* Reporters will only call contained Loggers to report messages
* at the current logging level and higher.
* For example, if you <code>setLoggingLevel(ERRORMSG)</code>
* (currently found in the {@link Reporter Reporter} subclass) then INFOMSGs
* will not be reported, presumably speeding execution time and saving
* output log space. These levels are also coded into most Logger output,
* allowing for easy reporting of various levels of results.</p>
* <ul>Allowable values are:
* <li>CRITICALMSG - Must-be-printed messages; may print only selected
* fails (and skip printing most passes).</li>
* <li>ERRORMSG - Logs an error and (optionally) a fail.</li>
* <li>FAILSONLY - Skips logging out most pass messages (still
* reports testFileResults) but prints out all fails.</li>
* <li>WARNINGMSG - Used for non-fail warnings - the test will
* continue, hopefully sucessfully.</li>
* <li>STATUSMSG - Reports on basic status of the test, when you
* want to include more detail than in a check() call</li>
* <li>INFOMSG - For more basic test debugging messages.</li>
* <li>TRACEMSG - Tracing all operations, detailed debugging information.</li>
* </ul>
* <p>Note that Logger implementations should also generally
* put the actual level that each call is logged at into any
* persistent outputs. This will enable you to use results \
* analysis tools later on against the results and screen
* against loggingLevel then as well.</p>
* <p>A common technique is to run a set of tests with a very
* high loggingLevel, so most or all output is stored by Loggers,
* and then analyze the results with a low logging level. If
* those results show some problems, you can always re-analyze
* the results with a higher logging level without having to
* re-run the test.</p>
* @see #logMsg(int, java.lang.String)
*/
// Levels are separated in actual values in case you wish to add your own levels in between
public static final int CRITICALMSG = 0; // Lowest possible loggingLevel
/**
* ERRORMSG - Logs an error and (optionally) a fail.
* {@link #CRITICALMSG See CRITICALMSG for a chart.}
*/
public static final int ERRORMSG = 10;
/**
* FAILSONLY - Skips logging out most pass messages.
* {@link #CRITICALMSG See CRITICALMSG for a chart.}
*/
public static final int FAILSONLY = 20;
/**
* WARNINGMSG - Used for non-fail warnings.
* {@link #CRITICALMSG See CRITICALMSG for a chart.}
*/
public static final int WARNINGMSG = 30;
/**
* STATUSMSG - Reports on basic status of the test.
* {@link #CRITICALMSG See CRITICALMSG for a chart.}
*/
public static final int STATUSMSG = 40;
/**
* INFOMSG - For more basic test debugging messages.
* {@link #CRITICALMSG See CRITICALMSG for a chart.}
*/
public static final int INFOMSG = 50;
/**
* TRACEMSG - Tracing all operations.
* {@link #CRITICALMSG See CRITICALMSG for a chart.}
*/
public static final int TRACEMSG = 60; // Highest possible loggingLevel
/** Default level is {@link #STATUSMSG STATUSMSG}. */
public static final int DEFAULT_LOGGINGLEVEL = STATUSMSG;
/**
* Constants for tracking results by testcase or testfile.
* <p>Testfiles default to an incomplete or INCP_RESULT. If a
* test never successfully calls a check* method, it's result
* will be incomplete.</p>
* <p>Note that a test cannot explicitly reset it's result to be INCP.</p>
*/
// Note: implementations should never rely on the actual values
// of these constants, except possibly to ensure that
// overriding values are > greater than other values
public static final int INCP_RESULT = 0;
// Note: string representations are explicitly set to all be
// 4 characters long to make it simpler to parse results
/**
* Constant "Incp" for result files.
* @see #INCP_RESULT
*/
public static final String INCP = "Incp";
/**
* Constants for tracking results by testcase or testfile.
* <p>A PASS_RESULT signifies that a specific test point (or a testcase,
* or testfile) has perfomed an operation correctly and has been verified.</p>
* <p>A pass overrides an incomplete.</p>
* @see #checkPass(java.lang.String)
*/
public static final int PASS_RESULT = 2;
/**
* Constant "Pass" for result files.
* @see #PASS_RESULT
*/
public static final String PASS = "Pass";
/**
* Constants for tracking results by testcase or testfile.
* <p>An AMBG_RESULT or ambiguous result signifies that a specific test
* point (or a testcase, or testfile) has perfomed an operation but
* that it has not been verified.</p>
* <p>Ambiguous results can be used when the test may not have access
* to baseline, or verified 'gold' result data. It may also be used
* during test file creation when the tester has not yet specified the
* expected behavior of a test.</p>
* <p>Ambiguous overrides both pass and incomplete.</p>
* @see #checkAmbiguous(java.lang.String)
*/
public static final int AMBG_RESULT = 5;
/**
* Constant "Ambg" for result files.
* @see #AMBG_RESULT
*/
public static final String AMBG = "Ambg";
/**
* Constants for tracking results by testcase or testfile.
* <p>A FAIL_RESULT signifies that a specific test point (or a testcase,
* or testfile) has perfomed an operation incorrectly.</p>
* <p>A fail in one test point does not necessarily mean that other test
* points are invalid - well written tests should be able to continue
* and produce valid results for the rest of the test file.</p>
* <p>A fail overrides any of incomplete, pass or ambiguous.</p>
* @see #checkFail(java.lang.String)
*/
public static final int FAIL_RESULT = 8;
/**
* Constant "Fail" for result files.
* @see #FAIL_RESULT
*/
public static final String FAIL = "Fail";
/**
* Constants for tracking results by testcase or testfile.
* <p>An ERRR_RESULT signifies that some part of the testfile
* has caused an unexpected error, exception, or other Really Bad Thing.</p>
* <p>Errors signify that something unexpected happened and that the test
* may not produce valid results. It would most commonly be used for
* problems relating to setting up test data or errors with other software
* being used (i.e. not problems with the actual software code that the
* test is attempting to verify).</p>
* <p>An error overrides <B>any</B> other result.</p>
* @see #checkErr(java.lang.String)
*/
public static final int ERRR_RESULT = 9;
/**
* Constant "Errr" for result files.
* Note all constant strings for results are 4 chars long
* to make fixed-length record file-based results simpler.
* @see #ERRR_RESULT
*/
public static final String ERRR = "Errr";
/**
* Testfiles and testcases should default to incomplete.
*/
public final int DEFAULT_RESULT = INCP_RESULT;
//-----------------------------------------------------
//-------- Control and utility routines --------
//-----------------------------------------------------
/**
* Return a description of what this Logger/Reporter does.
* @author Shane_Curcuru@lotus.com
* @return description of how this Logger outputs results, OR
* how this Reporter uses Loggers, etc..
*/
public abstract String getDescription();
/**
* Returns information about the Property name=value pairs that
* are understood by this Logger/Reporter.
* @author Shane_Curcuru@lotus.com
* @return same as {@link java.applet.Applet#getParameterInfo()}.
*/
public abstract String[][] getParameterInfo();
/**
* Accessor methods for a properties block.
* @return our Properties block.
*/
public abstract Properties getProperties();
/**
* Accessor methods for a properties block.
* Always having a Properties block allows users to pass common
* options to a Logger/Reporter without having to know the specific
* 'properties' on the object.
* <p>Much like in Applets, users can call getParameterInfo() to
* find out what kind of properties are available. Callers more
* commonly simply call initialize(p) instead of setProperties(p)</p>
* @author Shane_Curcuru@lotus.com
* @param p Properties to set (should be cloned).
*/
public abstract void setProperties(Properties p);
/**
* Call once to initialize this Logger/Reporter from Properties.
* <p>Simple hook to allow Logger/Reporters with special output
* items to initialize themselves.</p>
*
* @author Shane_Curcuru@lotus.com
* @param p Properties block to initialize from.
* @return status, true if OK, false if an error occoured.
*/
public abstract boolean initialize(Properties p);
/**
* Is this Logger/Reporter ready to log results?
* Obviously the meaning of 'ready' may be slightly different
* between different kinds of Loggers. A ConsoleLogger may
* simply always be ready, since it probably just sends it's
* output to System.out. File-based Loggers may not be ready
* until they've opened their file, and if IO errors happen,
* may have to report not ready later on as well.
* @author Shane_Curcuru@lotus.com
* @return status - true if it's ready to report, false otherwise
*/
public abstract boolean isReady();
/**
* Flush this Logger/Reporter - should ensure all output is flushed.
* Note that the flush operation is not necessarily pertinent to
* all types of Logger/Reporter - console-type Loggers no-op this.
* @author Shane_Curcuru@lotus.com
*/
public abstract void flush();
/**
* Close this Logger/Reporter - should include closing any OutputStreams, etc.
* Logger/Reporters should return {@link #isReady()} = false
* after being closed; presumably they will not actually log
* out any further data.
* @author Shane_Curcuru@lotus.com
*/
public abstract void close();
//-----------------------------------------------------
//-------- Testfile / Testcase start and stop routines --------
//-----------------------------------------------------
/**
* Report that a testfile has started.
* Implementing Loggers must output/store/report a message
* that the test file has started.
* @author Shane_Curcuru@lotus.com
* @param name file name or tag specifying the test.
* @param comment comment about the test.
*/
public abstract void testFileInit(String name, String comment);
/**
* Report that a testfile has finished, and report it's result.
* Implementing Loggers must output a message that the test is
* finished, and print the results.
* @author Shane_Curcuru@lotus.com
* @param msg message to log out
* @param result result of testfile
*/
public abstract void testFileClose(String msg, String result);
/**
* Report that a testcase has started.
* @author Shane_Curcuru@lotus.com
* @param comment short description of this test case's objective.
*/
public abstract void testCaseInit(String comment);
/**
* Report that a testcase has finished, and report it's result.
* Implementing classes must output a message that a testcase is
* finished, and print the results.
* @author Shane_Curcuru@lotus.com
* @param msg message of name of test case to log out
* @param result result of testfile
*/
public abstract void testCaseClose(String msg, String result);
//-----------------------------------------------------
//-------- Test results logging routines --------
//-----------------------------------------------------
/**
* Report a comment to result file with specified severity.
* Print out the message, optionally along with the level (depends
* on your storage mechanisim: console output probably doesn't need
* the level, but a file output probably would want it.)
* <p>Note that some Loggers may limit the comment string,
* either in overall length or by stripping any linefeeds, etc.
* This is to allow for optimization of file or database-type
* reporters with fixed fields. Users who need to log out
* special string data should use logArbitrary() instead.</p>
* <p>Remember, use {@link #checkPass(String)}, or
* {@link #checkFail(String)}, etc. to report the actual
* results of your tests.</p>
* @author Shane_Curcuru@lotus.com
* @param level severity of message; from {@link #CRITICALMSG}
* to {@link #TRACEMSG}
* @param msg comment to log out.
*/
public abstract void logMsg(int level, String msg);
/**
* Report an arbitrary String to result file with specified severity.
* Log out the String provided exactly as-is.
* @author Shane_Curcuru@lotus.com
* @param level severity of message; from {@link #CRITICALMSG}
* to {@link #TRACEMSG}
* @param msg arbitrary String to log out.
*/
public abstract void logArbitrary(int level, String msg);
/**
* Logs out statistics to result file with specified severity.
* This is a general-purpose way to log out numeric statistics. We accept
* both a long and a double to allow users to save whatever kind of numbers
* they need to, with the simplest API. The actual meanings of the numbers
* are dependent on the implementer.
* @author Shane_Curcuru@lotus.com
* @param level severity of message; from {@link #CRITICALMSG}
* to {@link #TRACEMSG}
* @param lVal statistic in long format, if available.
* @param dVal statistic in double format, if available.
* @param msg comment to log out.
*/
public abstract void logStatistic(int level, long lVal, double dVal,
String msg);
/**
* Logs out Throwable.toString() and a stack trace of the
* Throwable with the specified severity.
* <p>Works in conjunction with {@link Reporter#setLoggingLevel(int) setLoggingLevel};
* only outputs messages that are more severe than the current
* logging level. Different Logger or Reporter implementations
* may implement loggingLevels in different ways currently.</p>
* <p>This uses logArbitrary to log out your msg - message,
* a newline, throwable.toString(), a newline,
* and then throwable.printStackTrace().</p>
* <p>Note that this does not imply a failure or problem in
* a test in any way: many tests may want to verify that
* certain exceptions are thrown, etc.</p>
* @author Shane_Curcuru@lotus.com
* @param level severity of message.
* @param throwable throwable/exception to log out.
* @param msg description of the throwable.
*/
public abstract void logThrowable(int level, Throwable throwable, String msg);
/**
* Logs out a element to results with specified severity.
* This method is primarily for Loggers that output to fixed
* structures, like files, XML data, or databases.
* @author Shane_Curcuru@lotus.com
* @param level severity of message; from {@link #CRITICALMSG}
* to {@link #TRACEMSG}
* @param element name of enclosing element
* @param attrs hash of name=value attributes
* @param msg Object to log out; up to Loggers to handle
* processing of this; usually logs just .toString().
*/
public abstract void logElement(int level, String element,
Hashtable attrs, Object msg);
/**
* Logs out contents of a Hashtable with specified severity.
* <p>Loggers should store or log the full contents of the hashtable.</p>
* @param level severity of message; from {@link #CRITICALMSG}
* to {@link #TRACEMSG}
* @param hash Hashtable to log the contents of.
* @param msg decription of the Hashtable.
*/
public abstract void logHashtable(int level, Hashtable hash, String msg);
//-----------------------------------------------------
//-------- Test results reporting check* routines --------
//-----------------------------------------------------
// There is no public void checkIncp(String comment) method
/**
* Writes out a Pass record with comment.
* @author Shane_Curcuru@lotus.com
* @param comment comment to log with the pass record.
* @see #PASS_RESULT
*/
public abstract void checkPass(String comment);
/**
* Writes out an ambiguous record with comment.
* @author Shane_Curcuru@lotus.com
* @param comment to log with the ambg record.
* @see #AMBG_RESULT
*/
public abstract void checkAmbiguous(String comment);
/**
* Writes out a Fail record with comment.
* @author Shane_Curcuru@lotus.com
* @param comment comment to log with the fail record.
* @see #FAIL_RESULT
*/
public abstract void checkFail(String comment);
/**
* Writes out an Error record with comment.
* @author Shane_Curcuru@lotus.com
* @param comment comment to log with the error record.
* @see #ERRR_RESULT
*/
public abstract void checkErr(String comment);
/* EXPERIMENTAL: have duplicate set of check*() methods
that all output some form of ID as well as comment.
Leave the non-ID taking forms for both simplicity to the
end user who doesn't care about IDs as well as for
backwards compatibility.
*/
/**
* Writes out a Pass record with comment and ID.
* @author Shane_Curcuru@lotus.com
* @param comment comment to log with the pass record.
* @param ID token to log with the pass record.
* @see #PASS_RESULT
*/
public abstract void checkPass(String comment, String id);
/**
* Writes out an ambiguous record with comment and ID.
* @author Shane_Curcuru@lotus.com
* @param comment to log with the ambg record.
* @param ID token to log with the pass record.
* @see #AMBG_RESULT
*/
public abstract void checkAmbiguous(String comment, String id);
/**
* Writes out a Fail record with comment and ID.
* @author Shane_Curcuru@lotus.com
* @param comment comment to log with the fail record.
* @param ID token to log with the pass record.
* @see #FAIL_RESULT
*/
public abstract void checkFail(String comment, String id);
/**
* Writes out an Error record with comment and ID.
* @author Shane_Curcuru@lotus.com
* @param comment comment to log with the error record.
* @param ID token to log with the pass record.
* @see #ERRR_RESULT
*/
public abstract void checkErr(String comment, String id);
} // end of class Logger