blob: 0d92ceccda828e2fa74e2f791ccee6a722053e10 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2007 Boeing.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Boeing - initial API and implementation
*******************************************************************************/
package org.eclipse.osee.ote.core;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import org.codehaus.jackson.annotate.JsonProperty;
import org.eclipse.osee.framework.jdk.core.persistence.XmlizableStream;
import org.eclipse.osee.framework.logging.ILoggerFilter;
import org.eclipse.osee.framework.logging.ILoggerListener;
import org.eclipse.osee.framework.logging.OseeLog;
import org.eclipse.osee.ote.core.enums.PromptResponseType;
import org.eclipse.osee.ote.core.enums.ScriptTypeEnum;
import org.eclipse.osee.ote.core.environment.TestEnvironment;
import org.eclipse.osee.ote.core.environment.command.CommandDescription;
import org.eclipse.osee.ote.core.environment.interfaces.IScriptCompleteListener;
import org.eclipse.osee.ote.core.environment.interfaces.IScriptInitializer;
import org.eclipse.osee.ote.core.environment.interfaces.ITestEnvironmentAccessor;
import org.eclipse.osee.ote.core.environment.interfaces.ITestLogger;
import org.eclipse.osee.ote.core.environment.interfaces.ITestStation;
import org.eclipse.osee.ote.core.environment.interfaces.ITimeout;
import org.eclipse.osee.ote.core.environment.status.CommandEndedStatusEnum;
import org.eclipse.osee.ote.core.framework.testrun.ITestRunListener;
import org.eclipse.osee.ote.core.framework.testrun.ITestRunListenerProvider;
import org.eclipse.osee.ote.core.log.ITestPointTally;
import org.eclipse.osee.ote.core.log.record.ScriptResultRecord;
/**
* TestScript is the abstract base class for all test scripts. This class provides the interfaces necessary to allow a
* script to be run against remote testing environment servers.
* <p>
* The TestScript class contains the following information:
* <ul>
* <li><b>crew</b> - Defines if the script is for pilot or copilot.
* <li><b>isBatchable</b> - Defines whether or not the script is batchable. This typically indicates whether or not
* there are any prompts in the script.
* <li><b>isMpLevel</b> - Defines if the script tests at the Mission Processor level or if it tests at the node level.
* <li>Selective run list of TestCase's for test case skipping
* <li>ScriptInitializer object
* <li>TestCase objects that make up the runtime of the script
* </ul>
* <p>
* When building a new TestScript object it is important to understand that the object does not only contain the runtime
* code for performing the test, but also contains information about the test being performed that can be retrieved by
* instantiating the object with out necessarily running it.
* <p>
* The following sample should be followed closely when building TestScript objects. <br>
* <br>
* <hr>
* <br>
* <code>
* public class SampleScript extends TestCase {
* <ul style="list-style: none">
* <li> </code><i>Place declarations for <b>LRU Models</b>,<b>Messages</b>, and <b>Support</b> classes here.</i><code>
* <li>
* <li> </code><i>This constructor is necessary for instantiating the TestScript object to get data!</i><code>
* <li> SampleScript () {
* <ul style="list-style: none">
* <li> this (null,null);
* </ul>
* <li> }
* <li>
* <li> </code><i>This constructor is used at runtime.</i><code>
* <li> SampleScript (TestEnvironment environment, TestEnvironmentController connection) {
* <ul style="list-style: none">
* <li> super (environment, connection, [batchability, true or false]);
* <li>
* <li> </code><i>Place construction for <b>LRU Models</b>,<b>Messages</b>, and <b>Support</b> classes here.</i><code>
* <li>
* <li> </code><i>Construct all test cases here. The base TestCase constructor will automatically add
* <li>itself to the run list of the TestScript.</i><code>
* <li> new OipCase(this);
* <li> </code><i>...</i><code>
* </ul>
* <li> }
* <li>
* <li> </code><i>This inner class defines the setup for the script and must be present.</i><code>
* <li> private class LocalSetupTestCase extends SetupTestCase {
* <ul style="list-style: none">
* <li>
* <li> </code><i>Allows the setup to add itself to the runlist.</i><code>
* <li> protected LocalSetupTestCase(TestScript parent) {
* <ul style="list-style: none">
* <li> super(parent);
* </ul>
* <li> }
* <li>
* <li> </code><i>Provides the runtime code for initialization.</i><code>
* <li> public void doTestCase(ITestEnvironmentAccessor environment, ITestLogger logger) {
* <ul style="list-style: none">
* <li> </code><i>Place any necessary setup code here and it will be run prior to any of the test cases.</i><code>
* </ul>
* <li> }
* </ul>
* <li> }
* <li> <br>
* <li> </code><i>Place all of the test cases here. These will be inner classes that extend </i>
* {@link org.eclipse.osee.ote.core.TestCase TestCase}<i>.
* <li><b>NOTE:</b>All of these inner classes must be instantiated in the constructor for the TestScript object. If an
* inner class is not instantiated then it will <b>not</b> be run, and information within the class will not be
* available when the TestScript is instantiated for data retrieval.</i><code>
* <li> public class OipCase extends TestCase {
* <ul style="list-style: none">
* <li> public OipCase(TestScript parent) {
* <ul style="list-style: none">
* <li> </code><i>Use <b>one</b> of the following constructors based on if this TestCase is standalone</i><code>
* <li> super(parent);</code> <i>Standalone defaulted to <b>false</b></i><code>
* <li> super(parent, true);</code><i>Standalone explicitly set to <b>true</b></i><code>
* <li>
* <li> </code><i>All requirements tested in the test case should be noted here with the </i>
* <code>{@link org.eclipse.osee.ote.core.TestCase#addTracability(String) addTracability}</code><i> method.</i> <code>
* </ul>
* <li> }
* <li>
* <li> </code><i><b>Note:</b>It is very important that </i><code>doTestCase</code><i> always has the </i>
* <code>throws InterrupedException</code><i> in the method declaration. The ability to abort a script relies on this
* statement, and without it many calls would have to be wrapped with a try/catch block.</i><code>
* <li> public void doTestCase(ITestEnvironmentAccessor environment, ITestLogger logger) throws InterruptedException {
* <ul style="list-style: none">
* <li> </code><i>Place all of the runtime code for the test case here.</i><code>
* </ul>
* <li> }
* </ul>
* <li> }
* </ul>
* }
* </code> <br>
* <hr>
*
* @author Ryan D. Brooks
* @author Robert A. Fisher
* @see org.eclipse.osee.ote.core.TestCase
*/
public abstract class TestScript implements ITimeout {
private static final Set<Class<? extends TestScript>> instances =
Collections.synchronizedSet(new HashSet<Class<? extends TestScript>>());
private static final AtomicLong constructed = new AtomicLong(0);
private static final AtomicLong finalized = new AtomicLong(0);
private final boolean isBatchable;
private final ITestStation testStation;
private CommandDescription cmdDescription;
protected TestCase currentTestCase;
protected final TestEnvironment environment;
private final boolean isMpLevel;
private IScriptInitializer scriptInitializer;
private final ScriptTypeEnum scriptType;
private final ArrayList<TestCase> selectiveRunList = new ArrayList<>(32);
private Date startTime;
private final ArrayList<TestCase> testCases = new ArrayList<>(32);
private final ITestPointTally testPointTally;
protected CommandEndedStatusEnum status;
private Throwable rootCause;
private volatile boolean timedOut;
private volatile boolean aborted = false;
private final ArrayList<IScriptCompleteListener> scriptCompleteListeners =
new ArrayList<IScriptCompleteListener>(32);
private final ScriptResultRecord scriptResultRecord;
private int pass;
private int fail;
private ScriptLoggingListener loggingListener;
private final TestPromptImpl promptImpl;
private ITestRunListenerProvider listenerProvider;
private boolean shouldPauseOnFail;
private boolean printFailToConsole;
public TestScript(TestEnvironment environment, IUserSession callback, ScriptTypeEnum scriptType, boolean isBatchable) {
constructed.incrementAndGet();
this.scriptType = scriptType;
this.isBatchable = isBatchable;
this.isMpLevel = false;
promptImpl = new TestPromptImpl();
scriptResultRecord = new ScriptResultRecord(this);
if (environment != null) {
this.environment = environment;
this.startTime = new Date(0);
testStation = this.environment.getTestStation();
GCHelper.getGCHelper().addRefWatch(this);
instances.add(getClass());
} else {
throw new TestException("No environment found: Can not run script ", Level.SEVERE);
}
this.testPointTally = new TestPointTally(this.getClass().getName());
shouldPauseOnFail = OteProperties.isPauseOnFailEnabled();
printFailToConsole = OteProperties.isPrintFailToConsoleEnabled();
}
public void abort() {
OseeLog.log(TestEnvironment.class, Level.SEVERE, "Aborting script", new Exception());
aborted = true;
}
public void abortDueToThrowable(Throwable cause) {
this.rootCause = cause;
abort();
}
/**
* Removes all of the test cases from the selective run list. The list will be empty after this call returns.
*/
public void clearRunList() {
selectiveRunList.clear();
}
public void endTest() {
// provided as a hook for scripts to override
}
/**
* @return Returns the startTime.
*/
public Date getStartTime() {
return startTime;
}
public TestCase getTestCase() {
return currentTestCase;
}
/**
* Get list of test cases.
*
* @return reference to arrayList testCases.
*/
public/* TestCase */List<TestCase> getTestCases() {
ArrayList<TestCase> testCaseList = new ArrayList<>();
testCaseList.add(getSetupTestCase());
testCaseList.addAll(testCases);
testCaseList.add(getTearDownTestCase());
return testCaseList;
}
public ITestEnvironmentAccessor getTestEnvironment() {
return environment;
}
public IUserSession getUserSession() {
return ServiceUtility.getService(OTESessionManager.class).getActiveUser();
}
public TestScript getTestScript() {
return this;
}
public ITestLogger getLogger() {
return environment.getLogger();
}
public ScriptTypeEnum getType() {
return this.scriptType;
}
public final boolean isBatchable() {
return isBatchable;
}
public final boolean isMpLevel() {
return isMpLevel;
}
public String prompt(final TestPrompt prompt) throws InterruptedException {
return promptImpl.prompt(prompt, environment, this);
}
/**
* This method will display a null prompt to the console.
*/
public void prompt() throws InterruptedException {
getTestScript().prompt(new TestPrompt(null, PromptResponseType.NONE));
}
/**
* This method will display the message input to the console.
*/
public void prompt(String message) throws InterruptedException {
getTestScript().prompt(new TestPrompt(message, PromptResponseType.NONE));
}
/**
* This method will display the message input to the console. It will also prompt the user with a dialog box to input
* whether the condition passed or failed.
*/
public void promptPassFail(String message) throws InterruptedException {
getTestScript().prompt(new TestPrompt(message, PromptResponseType.PASS_FAIL));
}
/**
* This method will display the message input to the console. It also pauses the script running, and will prompt the
* user with a dialog box to continue on with the running of the script.
*/
public void promptPause(String message) throws InterruptedException {
prompt(new TestPrompt(message, PromptResponseType.SCRIPT_PAUSE));
}
public void pauseScriptOnFail(int testPoint) throws InterruptedException{
if (shouldPauseOnFail){
promptPause("Test point " + testPoint + " failed.\n");
}
}
public void pauseScriptOnFail(int testPoint, String name, String expected, String actual, String stackTrace) throws InterruptedException{
if (shouldPauseOnFail){
promptPause("TP " + testPoint + ": " + name + "\nExpected: " + expected + "\nActual: " + actual + "\n\n" + stackTrace );
}
}
public void printFailure(int testPoint) throws InterruptedException{
if (printFailToConsole){
prompt("Test point " + testPoint + " failed.\n");
}
}
public void printFailure(int testPoint, String name, String expected, String actual, String stackTrace) throws InterruptedException{
if (printFailToConsole){
prompt("TP " + testPoint + ": " + name + "\nExpected: " + expected + "\nActual: " + actual + "\n\n" + stackTrace );
}
}
/**
* Do not use this method from tests instead use {@link #logTestPoint(boolean, String, String, String)}. Takes result of test point and updates the total test point tally.
*
* @return total number of test points completed as an int.
*/
public int __recordTestPoint(boolean passed) {
return testPointTally.tallyTestPoint(passed);
}
public int getCurrentPointNumber() {
return testPointTally.getTestPointTotal();
}
public void addScriptSummary(XmlizableStream xml) {
scriptResultRecord.addChildElement((XmlizableStream) xml);
}
/**
* Add a single test case to selective run list.
*/
public void selectTestCase(int testCaseNumber) {
TestCase testCase = testCases.get(testCaseNumber - 1);
if (!testCase.isStandAlone()) {
throw new IllegalArgumentException("Test case " + testCaseNumber + " is not stand alone.");
}
selectiveRunList.add(testCase);
}
/**
* Add multiple test cases to the selective run list.
*/
public void selectTestCases(int testCaseNumberStart, int testCaseNumberEnd) {
for (int i = testCaseNumberStart; i <= testCaseNumberEnd; i++) {
selectTestCase(i);
}
}
/**
* Sets the script initializer.
*/
public void setScriptInitializer(IScriptInitializer scriptInitializer) {
this.scriptInitializer = scriptInitializer;
}
public IScriptInitializer getScriptInitializer() {
return scriptInitializer;
}
/**
* Causes current thread to wait until another thread invokes the {@link java.lang.Object#notify()}method or the
* {@link java.lang.Object#notifyAll()}method for this object.
*/
public synchronized void testWait(int milliseconds) throws InterruptedException {
if (milliseconds < 2) {
return;
}
environment.getLogger().methodCalled(this.environment, new MethodFormatter().add(milliseconds));
environment.setTimerFor(this, milliseconds);
wait();
this.getTestEnvironment().getScriptCtrl().lock();
environment.getLogger().methodEnded(this.environment);
}
public synchronized void testWaitNoLog(int milliseconds) throws InterruptedException {
if (milliseconds < 2) {
return;
}
environment.setTimerFor(this, milliseconds);
wait();
this.getTestEnvironment().getScriptCtrl().lock();
}
/**
* Prints text to the console informing the user of how much time is left in the wait. Any wait time can be passed
* in, but this is intended for long waits; usually > 20 seconds. Will not print any info is the time is less than
* 1000 ms.
*
* @param ms Milliseconds to wait for.
*/
public synchronized void testWaitWithInfo(int ms) throws InterruptedException {
int mult = 0;
if (ms > 999) {
prompt(new TestPrompt("\tWaiting for " + ms / 1000.0 + " seconds."));
while (ms >= 30000) {
mult++;
ms -= 20000;
testWait(20000);
prompt(new TestPrompt("\t" + 20 * mult + " seconds elapsed."));
}
prompt(new TestPrompt("\tFinishing up; " + ms / 1000.0 + " more seconds."));
}
testWait(ms);
prompt(new TestPrompt("\ttestWait done."));
}
// public String toString() {
// String description = "Test Script:\n";
// for (Iterator iter = testCases.iterator(); iter.hasNext();) {
// TestCase testCase = (TestCase) iter.next();
// description += "\t" + testCase + "\n";
// }
// return description;
// }
/**
* Add test case to the test scripts list of test cases.
*
* @return the number of test cases in the arrayList testCases as an int.
*/
protected int addTestCase(TestCase testCase) {
testCases.add(testCase);
return testCases.size();
}
@Deprecated
public CommandDescription getCommandDescription() {
return this.cmdDescription;
}
protected TestCase getSetupTestCase() {
return null;
}
protected TestCase getTearDownTestCase() {
return null;
}
/**
* Log a test point to the test.
*
* @param isPassed
* @param testPointName
* @param expected
* @param actual
*/
public void logTestPoint(boolean isPassed, String testPointName, String expected, String actual) {
this.getLogger().testpoint(this.getTestEnvironment(), this, this.getTestCase(), isPassed, testPointName,
expected, actual);
}
@Override
public boolean isTimedOut() {
return this.timedOut;
}
@Override
public void setTimeout(boolean timeout) {
this.timedOut = timeout;
}
/**
* Use addTestRunListener(ITestRunListener listener) instead.
*
* @see #addTestRunListener(ITestRunListener listener)
*/
@Deprecated
public void addScriptCompleteListener(IScriptCompleteListener listener) {
scriptCompleteListeners.add(listener);
}
public ITestStation getTestStation() {
return testStation;
}
protected void dispose() {
OseeLog.log(TestScript.class, Level.FINEST, "calling dispose on the TestScript.class");
}
/**
* Do not use this method from tests instead use {@link #logTestPoint(boolean, String, String, String)}.
*
*/
public void __addTestPoint(boolean pass) {
if (pass) {
this.pass++;
} else {
this.fail++;
}
}
private class ScriptLoggingListener implements ILoggerListener {
ILoggerFilter filter = new TestScriptLogFilter();
public ILoggerFilter getFilter() {
return filter;
}
@Override
public void log(String loggerName, Level level, String message, Throwable th) {
if (environment.getLogger() != null) {
environment.getLogger().log(level, message, th);
}
}
}
@Override
protected void finalize() throws Throwable {
instances.remove(getClass());
finalized.incrementAndGet();
super.finalize();
}
public static long getConstructed() {
return constructed.get();
}
public static long getFinalized() {
return finalized.get();
}
public static Collection<Class<? extends TestScript>> getInstances() {
return new HashSet<Class<? extends TestScript>>(instances);
}
public void disposeTest() {
dispose();
}
public boolean isAborted() {
return aborted;
}
@Deprecated
public void processScriptcompleteListeners() {
for (IScriptCompleteListener listener : scriptCompleteListeners) {
try {
listener.onScriptComplete();
} catch (Throwable t) {
OseeLog.log(TestEnvironment.class, Level.SEVERE,
"exception while notifying script complete listener " + listener.getClass().getName(), t);
}
}
}
public void setListenerProvider(ITestRunListenerProvider listenerProvider) {
this.listenerProvider = listenerProvider;
}
public boolean addTestRunListener(ITestRunListener listener) {
if (listenerProvider == null) {
String message =
String.format("Could not add run listener %s since listener provider is null.",
listener.getClass().getName());
OseeLog.log(getClass(), Level.WARNING, message, new Exception());
return false;
}
return this.listenerProvider.addTestRunListener(listener);
}
public boolean removeTestRunListener(ITestRunListener listener) {
if (listenerProvider == null) {
String message =
String.format("Could not remove run listener %s since listener provider is null.",
listener.getClass().getName());
OseeLog.log(getClass(), Level.WARNING, message, new Exception());
return false;
}
return this.listenerProvider.removeTestRunListener(listener);
}
public int getPasses() {
return pass;
}
public int getFails() {
return fail;
}
@JsonProperty
public ScriptResultRecord getScriptResultRecord() {
return scriptResultRecord;
}
public void setAborted(boolean aborted) {
this.aborted = aborted;
}
void setTestCase(TestCase currentTestCase) {
this.currentTestCase = currentTestCase;
}
public String getOutfileComment() {
return "\nNO_OUTFILE_COMMENT\n";
}
}