/* * 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$ */ /* * * TestImpl.java * */ package org.apache.qetest; import java.util.Properties; /** * Minimal class defining a test implementation, using a Reporter. *

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.

*

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.

* @author Shane_Curcuru@lotus.com * @version $Id$ */ public class TestImpl implements Test { /** * Name (and description) of the current test. *

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.

*/ 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. *

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.

* *

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.

*

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.

*

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: * * test.setReporter(); // optional, depending on the test * test.testFileInit(); * test.runTestCases(); * test.testFileClose(); * 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 all * 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 not override this method. *

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: * * preTestFileInit(); // Create/initialize Reporter * doTestFileInit(); // User-defined: initialize product under test * postTestFileInit() // Report out we've completed initialization * *

* @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. *

Create and initialize a Reporter here.

*

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.

* @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. *

Subclasses must override this to do whatever specific * processing they need to initialize their product under test.

*

If for any reason the test should not continue, it must * return false from this method.

* @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. *

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.

* @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. *

Predefined behavior - subclasses should not override this method.

* @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. *

Subclasses must override this to do whatever specific * processing they need to cleanup after all testcases are run.

* @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. *

Predefined behavior - subclasses should not 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).

* @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 must 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