package org.aitools.programd.test.aiml;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import org.aitools.programd.Core;
import org.aitools.util.Text;
import org.aitools.util.runtime.DeveloperError;
import org.aitools.util.xml.XHTML;
import org.jdom.Element;
/**
* A TestCase contains an input and a set of checkers that test the response to that input.
*
* @author Albertas Mickensas
* @author <a href="noel@aitools.org">Noel Bush</a>
*/
public class TestCase {
/** The string "{@value} ". */
public static String TAG_TESTCASE = "TestCase";
/** The string "{@value} ". */
public static String TAG_DESCRIPTION = "Description";
/** The string "{@value} ". */
public static String TAG_INPUT = "Input";
/** The name of this test case. */
protected String _name;
/** The suite to which this test case belongs. */
protected TestSuite _suite;
/** The inputs that this test case should send. */
protected String _input;
/** The checker(s) contained in this test case. */
protected List<Checker> _checkers = new ArrayList<Checker>();
/** The last response received by this test case. */
protected String _lastResponse;
/**
* A private constructor, for use in persistence.
*/
@SuppressWarnings("unused")
private TestCase() {
// Do nothing.
}
/**
* Creates a new TestCase from the given XML element.
*
* @param element the TestCase element
* @param encoding the encoding of the document from which this element comes
* @param index a default index to use for automatically naming this case
* @param suite the test suite to which this case belongs
*/
@SuppressWarnings("unchecked")
public TestCase(Element element, String encoding, int index, TestSuite suite) {
this._suite = suite;
if (element.getAttribute("name") != null) {
try {
this._name = new String(element.getAttributeValue("name").getBytes(encoding)).intern();
}
catch (UnsupportedEncodingException e) {
throw new DeveloperError(String.format("Platform does not support encoding \"%s\"!", encoding), e);
}
}
else {
this._name = "case-" + index;
}
List<Element> children = element.getChildren();
int checkersStart = 0;
// Might be a description here.
Element child = children.get(0);
if (child.getName().equals(TAG_DESCRIPTION)) {
checkersStart = 2;
}
else {
checkersStart = 1;
}
try {
this._input = new String(children.get(checkersStart - 1).getText().getBytes(encoding)).intern();
}
catch (UnsupportedEncodingException e) {
throw new DeveloperError(String.format("Platform does not support encoding \"%s\"!", encoding), e);
}
for (Element checker : children.subList(checkersStart, children.size())) {
this._checkers.add(Checker.create(checker, encoding));
}
}
/**
* Constructs a basic TestCase with just an input.
*
* @param testInput the input to use
*/
public TestCase(String testInput) {
this._name = "testcase-" + System.currentTimeMillis();
this._input = testInput;
}
/**
* Constructs a basic TestCase with an input and an expected answer (utility constructor).
*
* @param testInput the input to use
* @param expectedAnswer the answer to expect
*/
public TestCase(String testInput, String expectedAnswer) {
this._name = "testcase-" + System.currentTimeMillis();
this._input = testInput;
this.addChecker(new AnswerChecker(expectedAnswer));
}
/**
* Adds a given checker.
*
* @param checker the checker to add
*/
public void addChecker(Checker checker) {
this._checkers.add(checker);
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof TestCase)) {
return false;
}
TestCase other = (TestCase) obj;
return other._name.equals(this._name) && other._input.equals(this._input) && other._checkers.equals(this._checkers)
&& other._lastResponse.equals(this._lastResponse);
}
/**
* Produces a map of checker names to contents that can be used to describe the test case textually.
*
* @return a map of checker names to contents that can be used to describe the test case textually
*/
public List<String[]> getDescription() {
List<String[]> result = new ArrayList<String[]>();
for (Checker checker : this._checkers) {
result.add(new String[] { checker.getTagName(), checker.getContent() });
}
return result;
}
/**
* Returns the expected response, which may or may not be a literal string.
*
* @return the expected response, or a description of it
*/
public String getExpected() {
return "Expected" + Text.mergeStringArrays(this.getDescription());
}
/**
* @return the input to be sent by this test case
*/
public String getInput() {
return this._input;
}
/**
* @return the last response received by this test case
*/
public String getLastResponse() {
return this._lastResponse;
}
/**
* @return the name of this test case
*/
public String getName() {
return this._name;
}
/**
* @return the suite to which this case belongs
*/
public TestSuite getSuite() {
return this._suite;
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return (this._name + this._input + this._checkers.toString() + this._lastResponse).hashCode();
}
/**
* Removes all checkers.
*/
public void removeCheckers() {
this._checkers = new ArrayList<Checker>();
}
/**
* Response is valid if at least one of the checkers returns a positive result.
*
* @param response the response to check
* @return whether or not the response is valud
*/
private boolean responseIsValid(String response) {
boolean result = false;
for (Checker checker : this._checkers) {
result |= checker.test(response);
}
return result;
}
/**
* Runs this test case for the given botid.
*
* @param core the Core to use for testing
* @param userid the userid to use when testing
* @param botid the bot for whom to run this test case
* @return whether the test passed
*/
public boolean run(Core core, String userid, String botid) {
this._lastResponse = org.jdom.Text.normalizeString(core.getResponse(this._input, userid, botid));
return this.responseIsValid(Text.renderAsLines(XHTML.breakLines(this._lastResponse)));
}
}