blob: 53110163a913d520747e55e8d31c60eae6949f3e [file]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2000-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2000, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
*
* TestImpl.java
*
*/
package org.apache.qetest;
import java.util.Properties;
/**
* Minimal class defining a test implementation, using a Reporter.
* <p>TestImpls generally interact with a Reporter, which reports
* out in various formats the results from this test.
* Most test classes should subclass from this test, as it adds
* structure that helps to define the conceptual logic of running
* a 'test'. It also provides useful default implementations.</p>
* <p>Users wishing a much simpler testing framework can simply
* implement the minimal methods in the Test interface, and use a
* Logger to report results instead of a Reporter.</p>
* @author Shane_Curcuru@lotus.com
* @version $Id$
*/
public class TestImpl implements Test
{
/**
* Name (and description) of the current test.
* <p>Note that these are merely convenience variables - you do not need
* to use them. If you do use them, they should be initialized at
* construction time.</p>
*/
protected String testName = null;
/**
* Accesor method for the name of this test.
*
* NEEDSDOC ($objectName$) @return
*/
public String getTestName()
{
return testName;
}
/** (Name and) description of the current test. */
protected String testComment = null;
/**
* Accesor method for a brief description of this test.
*
* NEEDSDOC ($objectName$) @return
*/
public String getTestDescription()
{
return testComment;
}
/**
* Default constructor - initialize testName, Comment.
*/
public TestImpl()
{
// Only set them if they're not set
if (testName == null)
testName = "TestImpl.defaultName";
if (testComment == null)
testComment = "TestImpl.defaultComment";
}
/** Our Logger, who we tell all our secrets to. */
protected Logger logger = null;
/**
* Accesor methods for our Logger.
*
* NEEDSDOC @param l
*/
public void setLogger(Logger l)
{ // no-op: our implementation always uses a Reporter
}
/**
* Accesor methods for our Logger.
*
* NEEDSDOC ($objectName$) @return
*/
public Logger getLogger()
{
return null;
}
/** Our Reporter, who we tell all our secrets to. */
protected Reporter reporter;
/**
* Accesor methods for our Reporter.
*
* NEEDSDOC @param r
*/
public void setReporter(Reporter r)
{
if (r != null)
reporter = r;
}
/**
* Accesor methods for our Reporter.
*
* NEEDSDOC ($objectName$) @return
*/
public Reporter getReporter()
{
return reporter;
}
/** Flag to indicate a serious enough error that we should just give up. */
protected boolean abortTest = false;
/**
* Accesor methods for our abort flag.
*
* NEEDSDOC @param a
*/
public void setAbortTest(boolean a)
{
abortTest = a;
}
/**
* Accesor methods for our abort flag.
*
* NEEDSDOC ($objectName$) @return
*/
public boolean getAbortTest()
{
return (abortTest);
}
/**
* Run this test: main interface to cause the test to run itself.
* <p>A major goal of the TestImpl class is to separate the act and process
* of writing a test from it's actual runtime implementation. Testwriters
* should not need to know how their test is being executed.</p>
* <ul>They should simply focus on defining:
* <li>doTestFileInit: what setup has to be done before running the test</li>
* <li>testCase1, 2, ... n: individual, independent test cases</li>
* <li>doTestFileClose: what cleanup has to be done after running the test</li>
* </ul>
* <p>This method returns a simple boolean status as a convenience. In cases
* where you have a harness that runs a great many tests that normally pass, the
* harness can simply check this value for each test: if it's true, you could
* even delete any result logs then, and simply print out a meta-log stating
* that the test passed. Note that this does not provide any information about
* why a test failed (or caused an error, or whatever) - that's what the info in
* any Reporter's logs are for.</p>
* <p>If a test is aborted, then any containing harness needs not
* finish executing the test. Otherwise, even if part of a test fails,
* you should let the whole test run through.</p>
* <p>Harnesses should generally simply call runTest() to ask the
* test to run itself. In some cases a Harness might want to control
* the process more closely, in which case it should call:
* <code>
* test.setReporter(); // optional, depending on the test
* test.testFileInit();
* test.runTestCases();
* test.testFileClose();
* </code> instead.
* @todo return TestResult instead of boolean flag
* @author Shane_Curcuru@lotus.com
*
* NEEDSDOC @param p
* @return status - true if test ran to completion and <b>all</b>
* cases passed, false otherwise
*/
public boolean runTest(Properties p)
{
boolean status = testFileInit(p);
if (getAbortTest())
return status;
status &= runTestCases(p);
if (getAbortTest())
return status;
status &= testFileClose(p);
return status;
}
/**
* Initialize this test - called once before running testcases.
* Predefined behavior - subclasses should <b>not</b> override this method.
* <p>This method is basically a composite that masks the most common
* implementation: creating a reporter or logger first, then initializing
* any data or product settings the test needs setup first. It does this
* by separating this method into three methods:
* <code>
* preTestFileInit(); // Create/initialize Reporter
* doTestFileInit(); // User-defined: initialize product under test
* postTestFileInit() // Report out we've completed initialization
* </code>
* </p>
* @author Shane_Curcuru@lotus.com
* @see #preTestFileInit(java.util.Properties)
* @see #doTestFileInit(java.util.Properties)
* @see #postTestFileInit(java.util.Properties)
*
* NEEDSDOC @param p
*
* NEEDSDOC ($objectName$) @return
*/
public boolean testFileInit(Properties p)
{
// Note: we don't want to use shortcut operators here,
// since we want each method to get called
// Pass the Property block to each method, so that
// subclasses can do initialization whenever
// is best for their design
return preTestFileInit(p) & doTestFileInit(p) & postTestFileInit(p);
}
/**
* Initialize this test - called once before running testcases.
* <p>Create and initialize a Reporter here.</p>
* <p>This implementation simply creates a default Reporter
* and adds a ConsoleLogger. Most test groups will want to override
* this method to create custom Reporters or Loggers.</p>
* @author Shane_Curcuru@lotus.com
* @see #testFileInit(java.util.Properties)
*
* NEEDSDOC @param p
*
* NEEDSDOC ($objectName$) @return
*/
public boolean preTestFileInit(Properties p)
{
// Pass our properties block directly to the reporter
// so it can use the same values in initialization
setReporter(new Reporter(p));
reporter.addDefaultLogger();
reporter.testFileInit(testName, testComment);
return true;
}
/**
* Initialize this test - called once before running testcases.
* <p>Subclasses <b>must</b> override this to do whatever specific
* processing they need to initialize their product under test.</p>
* <p>If for any reason the test should not continue, it <b>must</b>
* return false from this method.</p>
* @author Shane_Curcuru@lotus.com
* @see #testFileInit(java.util.Properties)
*
* NEEDSDOC @param p
*
* NEEDSDOC ($objectName$) @return
*/
public boolean doTestFileInit(Properties p)
{
// @todo implement in your subclass
reporter.logTraceMsg(
"TestImpl.doTestFileInit() default implementation - please override");
return true;
}
/**
* Initialize this test - called once before running testcases.
* <p>Simply log out that our initialization has completed,
* so that structured-style logs will make it clear where startup
* code ends and testCase code begins.</p>
* @author Shane_Curcuru@lotus.com
* @see #testFileInit(java.util.Properties)
*
* NEEDSDOC @param p
*
* NEEDSDOC ($objectName$) @return
*/
public boolean postTestFileInit(Properties p)
{
reporter.logTraceMsg(
"TestImpl.postTestFileInit() initialization complete");
return true;
}
/**
* Run all of our testcases.
* Subclasses must override this method. It should cause each testCase
* in the test to be executed independently, and then return true if and
* only if all testCases passed successfully. If any testCase failed or
* caused any unexpected errors, exceptions, etc., it should return false.
* @author Shane_Curcuru@lotus.com
*
* NEEDSDOC @param p
* @return true if all testCases passed, false otherwise
*/
public boolean runTestCases(Properties p)
{
// @todo implement in your subclass
reporter.logTraceMsg(
"TestImpl.runTestCases() default implementation - please override");
return true;
}
/**
* Cleanup this test - called once after running testcases.
* @author Shane_Curcuru@lotus.com
*
* NEEDSDOC @param p
* @return true if cleanup successful, false otherwise
*/
public boolean testFileClose(Properties p)
{
// Note: we don't want to use shortcut operators here,
// since we want each method to get called
return preTestFileClose(p) & doTestFileClose(p)
& postTestFileClose(p);
}
/**
* Log a trace message - called once after running testcases.
* <p>Predefined behavior - subclasses should <B>not</B> override this method.</p>
* @todo currently is primarily here to mark that we're closing
* the test, in case doTestFileClose() blows up somehow. May not be needed.
* @author Shane_Curcuru@lotus.com
* @see #testFileClose()
*
* NEEDSDOC @param p
*
* NEEDSDOC ($objectName$) @return
*/
protected boolean preTestFileClose(Properties p)
{
// Have the reporter log a trace that the test is about to cleanup
reporter.logTraceMsg("TestImpl.preTestFileClose()");
return true;
}
/**
* Cleanup this test - called once after running testcases.
* <p>Subclasses <b>must</b> override this to do whatever specific
* processing they need to cleanup after all testcases are run.</p>
* @author Shane_Curcuru@lotus.com
*
* NEEDSDOC @param p
*
* NEEDSDOC ($objectName$) @return
*/
public boolean doTestFileClose(Properties p)
{
// @todo implement in your subclass
reporter.logTraceMsg(
"TestImpl.doTestFileClose() default implementation - please override");
return true;
}
/**
* Mark the test complete - called once after running testcases.
* <p>Predefined behavior - subclasses should <b>not</b> override
* this method. Currently just tells our reporter to log the
* testFileClose. This will calculate final results, and complete
* logging for any structured output logs (like XML files).</p>
* @author Shane_Curcuru@lotus.com
* @see #testFileClose()
*
* NEEDSDOC @param p
*
* NEEDSDOC ($objectName$) @return
*/
protected boolean postTestFileClose(Properties p)
{
// Have the reporter log out our completion
reporter.testFileClose();
return true;
}
/**
* Main method to run test from the command line.
* Test subclasses <B>must</B> override, obviously.
* @author Shane Curcuru
*
* NEEDSDOC @param args
*/
public static void main(String[] args)
{
TestImpl app = new TestImpl();
Properties p = new Properties();
p.put(MAIN_CMDLINE, args);
app.runTest(p);
}
} // end of class Test