package org.whole.lang.tests.visitors;
import static org.whole.lang.tests.reflect.TestsEntityDescriptorEnum.*;
import static org.whole.lang.tests.reflect.TestsFeatureDescriptorEnum.*;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;
import org.whole.lang.bindings.BindingManagerFactory;
import org.whole.lang.bindings.ITransactionScope;
import org.whole.lang.commons.factories.CommonsEntityAdapterFactory;
import org.whole.lang.exceptions.IWholeRuntimeException;
import org.whole.lang.exceptions.WholeIllegalArgumentException;
import org.whole.lang.iterators.IEntityIterator;
import org.whole.lang.iterators.IteratorFactory;
import org.whole.lang.matchers.GenericMatcherFactory;
import org.whole.lang.matchers.Matcher;
import org.whole.lang.model.IEntity;
import org.whole.lang.model.NullEntity;
import org.whole.lang.operations.InterpreterOperation;
import org.whole.lang.operations.OperationCanceledException;
import org.whole.lang.queries.factories.QueriesEntityFactory;
import org.whole.lang.queries.model.Path;
import org.whole.lang.reflect.EntityKinds;
import org.whole.lang.tests.factories.TestsEntityFactory;
import org.whole.lang.tests.matchers.TestsMatcherFactory;
import org.whole.lang.tests.model.AfterTest;
import org.whole.lang.tests.model.AfterTestCase;
import org.whole.lang.tests.model.AllOf;
import org.whole.lang.tests.model.AnyOf;
import org.whole.lang.tests.model.AssertThat;
import org.whole.lang.tests.model.AssumeThat;
import org.whole.lang.tests.model.BeforeTest;
import org.whole.lang.tests.model.BeforeTestCase;
import org.whole.lang.tests.model.BooleanLiteral;
import org.whole.lang.tests.model.Comment;
import org.whole.lang.tests.model.Constraint;
import org.whole.lang.tests.model.DataName;
import org.whole.lang.tests.model.Equals;
import org.whole.lang.tests.model.Expression;
import org.whole.lang.tests.model.Filter;
import org.whole.lang.tests.model.FilterRule;
import org.whole.lang.tests.model.HasKind;
import org.whole.lang.tests.model.HasType;
import org.whole.lang.tests.model.IntLiteral;
import org.whole.lang.tests.model.IsAssignableTo;
import org.whole.lang.tests.model.IsDef;
import org.whole.lang.tests.model.IsFalse;
import org.whole.lang.tests.model.IsNull;
import org.whole.lang.tests.model.IsTrue;
import org.whole.lang.tests.model.IsUndef;
import org.whole.lang.tests.model.Matches;
import org.whole.lang.tests.model.Not;
import org.whole.lang.tests.model.Outcome;
import org.whole.lang.tests.model.OutcomeEnum;
import org.whole.lang.tests.model.Result;
import org.whole.lang.tests.model.Results;
import org.whole.lang.tests.model.Same;
import org.whole.lang.tests.model.Sequence;
import org.whole.lang.tests.model.StringLiteral;
import org.whole.lang.tests.model.SubjectStatement;
import org.whole.lang.tests.model.Test;
import org.whole.lang.tests.model.TestCase;
import org.whole.lang.tests.model.TestCases;
import org.whole.lang.tests.model.TestStatement;
import org.whole.lang.tests.model.TestStatements;
import org.whole.lang.tests.model.TestSuite;
import org.whole.lang.tests.model.Tests;
import org.whole.lang.tests.model.Throws;
import org.whole.lang.tests.model.UsingFilter;
import org.whole.lang.tests.util.TestsHelpers;
import org.whole.lang.util.BehaviorUtils;
import org.whole.lang.util.EntityUtils;
import org.whole.lang.visitors.GenericTraversalFactory;
import org.whole.lang.visitors.IVisitor;
import org.whole.lang.visitors.MissingVariableException;
/**
* @author Enrico Persiani
*/
public class TestsInterpreterVisitor extends TestsTraverseAllVisitor {
@Override
public InterpreterOperation getOperation() {
return (InterpreterOperation) super.getOperation();
}
protected final PrintWriter printWriter() {
return getOperation().getPrintWriter();
}
protected Object getResultValue() {
return getResult().wGetValue();
}
protected void setResultValue(Object value) {
setResult(BindingManagerFactory.instance.createSpecificValue(value));
}
protected void setResultVisitor(IVisitor visitor) {
setResult(BindingManagerFactory.instance.createValue(visitor));
}
protected IVisitor getResultVisitor() {
return (IVisitor) getResultValue();
}
protected void resetIterator(IEntityIterator<?> iterator) {
IEntity selfEntity = getBindings().wGet("self");
iterator.reset(selfEntity != null ? selfEntity : NullEntity.instance);
}
@SuppressWarnings("unchecked")
protected FilterRule getFilterRule(UsingFilter usingFilter) {
Filter filter = usingFilter.getFilter();
Map<String, FilterRule> filterRulesMap = (Map<String, FilterRule>) getBindings().wGetValue("filterRulesMap");
return filterRulesMap.get(filter.getName().getValue());
}
protected IEntity applyFilterRule(IEntity entity) {
FilterRule filterRule = (FilterRule) getBindings().wGet("activeFilterRule");
if (filterRule == null)
return entity;
IEntity filteredEntity = EntityUtils.clone(entity);
getBindings().wDef("self", filteredEntity);
stagedVisit(filterRule.getBody());
getResult();
return filteredEntity;
}
@Override
public void visit(TestSuite entity) {
Results results = TestsEntityFactory.instance.createResults();
printWriter().printf("=== %s test suite ===\n\n", entity.getName().getValue());
getBindings().wDefValue("filterRulesMap", TestsHelpers.createFilterRulesMap(entity));
TestCases testCases = entity.getTestCases();
for (int i = 0; i < testCases.wSize(); i++) {
TestCase testCase = testCases.get(i);
if (getBindings().wIsSet("runSingleTest") && getBindings().wGet("runSingleTest").wGetParent().wGetParent() != testCase)
continue;
testCase.accept(this);
Results testCaseResults = (Results) getResult();
results.getErrors().setValue(results.getErrors().getValue() + testCaseResults.getErrors().getValue());
results.getFailures().setValue(results.getFailures().getValue() + testCaseResults.getFailures().getValue());
results.getSuccesses().setValue(results.getSuccesses().getValue() + testCaseResults.getSuccesses().getValue());
}
if (EntityUtils.isResolver(entity.getExpectedResults()))
entity.setExpectedResults(EntityUtils.clone(results));
if (!Matcher.match(results, entity.getActualResults()))
entity.setActualResults(results);
setResult(results);
}
@Override
public void visit(TestCase entity) {
String name = entity.getName().getValue();
Results results = TestsEntityFactory.instance.createResults();
printWriter().printf("* %s test case running:\n\n", name);
try {
IEntityIterator<BeforeTestCase> beforeIterator = IteratorFactory.<BeforeTestCase>childMatcherIterator().withPattern(BeforeTestCase);
beforeIterator.reset(entity.getAspects());
for (BeforeTestCase beforeTestCase : beforeIterator) {
beforeTestCase.accept(this);
getResult();
}
Tests tests = entity.getTests();
for (int i = 0; i < tests.wSize(); i++) {
Test test = tests.get(i);
if (getBindings().wIsSet("runSingleTest") && getBindings().wGet("runSingleTest") != test)
continue;
test.accept(this);
IntLiteral result = null;
switch (getResult().wGet(outcome).wEnumValue().getOrdinal()) {
case OutcomeEnum.ERROR_ord:
result = results.getErrors();
break;
case OutcomeEnum.FAILURE_ord:
result = results.getErrors();
break;
case OutcomeEnum.SUCCESS_ord:
result = results.getSuccesses();
break;
default:
throw new WholeIllegalArgumentException("unsupported outcome");
}
result.setValue(result.getValue() + 1);
}
IEntityIterator<AfterTestCase> afterIterator = IteratorFactory.<AfterTestCase>childMatcherIterator().withPattern(AfterTestCase);
afterIterator.reset(entity.getAspects());
for (AfterTestCase afterTestCase : afterIterator) {
afterTestCase.accept(this);
getResult();
}
boolean testCaseSuccess = results.getErrors().getValue() + results.getFailures().getValue() == 0;
printWriter().printf("\n* %s test case %s\n", name, testCaseSuccess ? "OK" : "FAILED");
} catch (OperationCanceledException e) {
throw e;
} catch (RuntimeException e) {
reportError(name, e);
results.getErrors().setValue(entity.getTests().wSize());
results.getFailures().setValue(0);
results.getSuccesses().setValue(0);
}
if (EntityUtils.isResolver(entity.getExpectedResults()))
entity.setExpectedResults(EntityUtils.clone(results));
if (!Matcher.match(results, entity.getActualResults()))
entity.setActualResults(results);
setResult(results);
}
@Override
public void visit(Test entity) {
ITransactionScope ts = BindingManagerFactory.instance.createTransactionScope();
getBindings().wEnterScope(ts);
String name = entity.getName().getValue();
Outcome outcome = TestsEntityFactory.instance.createOutcome(OutcomeEnum.SUCCESS);
StringLiteral location = CommonsEntityAdapterFactory.createResolver(StringLiteral);
StringLiteral cause = CommonsEntityAdapterFactory.createResolver(StringLiteral);
Result result = TestsEntityFactory.instance.createResult(outcome, location, cause);
try {
for (BeforeTest beforeTest : BehaviorUtils.<BeforeTest>compileAndLazyEvaluate(createAspectPath("BeforeTest"), entity)) {
beforeTest.accept(this);
getResult();
}
entity.getBody().accept(this);
getResult();
for (AfterTest afterTest : BehaviorUtils.<AfterTest>compileAndLazyEvaluate(createAspectPath("AfterTest"), entity)) {
afterTest.accept(this);
getResult();
}
printWriter().printf(" %32s(...) OK\n", name);
} catch (OperationCanceledException e) {
throw e;
} catch (TestsException e) {
outcome.wSetValue(OutcomeEnum.FAILURE);
location.setValue(EntityUtils.getLocation(e.getSubjectStatement()));
cause.setValue(e.getMessage());
reportFailure(name, e);
} catch (RuntimeException e) {
outcome.wSetValue(OutcomeEnum.ERROR);
IEntity sourceEntity = null;
if (e instanceof IWholeRuntimeException) {
sourceEntity = ((IWholeRuntimeException) e).getSourceEntity();
if (EntityUtils.getCompoundRoot(sourceEntity) != EntityUtils.getCompoundRoot(entity))
sourceEntity = null; //FIXME replace with outer aspect or statement
}
if (sourceEntity == null)
sourceEntity = entity;
location.setValue(EntityUtils.getLocation(sourceEntity));
cause.setValue(e.getMessage());
reportError(name, e);
} finally {
ts.rollback();
getBindings().wExitScope();
}
if (EntityUtils.isResolver(entity.getExpectedResult()))
entity.setExpectedResult(EntityUtils.clone(result));
if (!Matcher.match(result, entity.getActualResult()))
entity.setActualResult(result);
setResult(result);
}
protected void reportFailure(String name, TestsException e) {
String error = Matcher.matchImpl(AssertThat, e.getSubjectStatement()) ? "AssertionError" : "AssupmtionError";
printWriter().printf(" %32s(...) FAILED: %s: %s", name, error, e.getMessage());
printWriter().printf(" [at %s]\n", EntityUtils.getLocation(e.getSubjectStatement()));
}
protected void reportError(String name, RuntimeException e) {
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
printWriter().printf(" %32s(...) ERRORS: %s", name, writer.toString());
IWholeRuntimeException wre = (IWholeRuntimeException) e;
if (wre.getSourceEntity() != null)
printWriter().printf(" [at %s]\n\n", EntityUtils.getLocation(wre.getSourceEntity()));
}
@Override
public void visit(TestStatements entity) {
for (int i = 0; i < entity.wSize(); i++) {
handleCancelRequest();
TestStatement statement = entity.get(i);
statement.accept(this);
getResult();
}
}
protected static Path createAspectPath(String aspectTypeName) {
QueriesEntityFactory qef = QueriesEntityFactory.instance;
return qef.createPath(
qef.createFilter(qef.createAncestorStep(), qef.createTypeTest("TestCase")),
qef.createFeatureStep("aspects"),
qef.createFilter(qef.createChildStep(), qef.createTypeTest(aspectTypeName))
);
}
@Override
public void visit(AssumeThat entity) {
evaluate(entity);
}
@Override
public void visit(AssertThat entity) {
evaluate(entity);
}
@Override
public void visit(UsingFilter entity) {
getBindings().wDef("activeFilterRule", getFilterRule(entity));
super.visit(entity);
getBindings().wUnset("activeFilterRule");
}
protected IEntity evaluate(Expression subject, boolean catchExceptions) {
IEntity entity = NullEntity.instance; //null;
try {
subject.accept(this);
entity = getResult();
} catch (OperationCanceledException e) {
throw e;
} catch (RuntimeException e) {
if (catchExceptions)
// save exception for later evaluation
getBindings().wDefValue("thrownException", e);
else
throw e;
}
return entity == null ? NullEntity.instance : entity;
}
protected IVisitor evaluate(Constraint constraint) {
constraint.accept(this);
return getResultVisitor();
}
protected void evaluate(SubjectStatement entity) {
ITransactionScope ts = BindingManagerFactory.instance.createTransactionScope();
getBindings().wEnterScope(ts);
try {
IEntity subject = evaluate(entity.getSubject(), true);
if (!EntityUtils.isNull(subject))
subject = applyFilterRule(subject);
IVisitor constraint = evaluate(entity.getConstraint());
boolean result = Matcher.match(constraint, subject);
if (getBindings().wIsSet("thrownException"))
throw (RuntimeException) getBindings().wGetValue("thrownException");
if (!result)
throw new TestsException(entity, subject, constraint, getBindings());
} finally {
ts.rollback();
getBindings().wExitScope();
}
}
@Override
public void visit(AllOf entity) {
int size = entity.wSize();
IVisitor[] visitors = new IVisitor[size];
for (int i = 0; i < size; i++) {
entity.get(i).accept(this);
visitors[i] = getResultVisitor();
}
setResultVisitor(GenericTraversalFactory.instance.all(visitors).withSourceEntity(entity));
}
@Override
public void visit(AnyOf entity) {
int size = entity.wSize();
IVisitor[] visitors = new IVisitor[size];
for (int i = 0; i < size; i++) {
entity.get(i).accept(this);
visitors[i] = getResultVisitor();
}
setResultVisitor(GenericTraversalFactory.instance.one(visitors).withSourceEntity(entity));
}
@Override
public void visit(Not entity) {
entity.getConstraint().accept(this);
setResultVisitor(GenericTraversalFactory.instance.not(getResultVisitor()).withSourceEntity(entity));
}
@Override
public void visit(Equals entity) {
setResultVisitor(TestsMatcherFactory.instance.equals(applyFilterRule(evaluate(entity.getObject(), false))).withSourceEntity(entity));
}
@Override
public void visit(Matches entity) {
setResultVisitor(GenericMatcherFactory.instance.match(applyFilterRule(evaluate(entity.getObject(), false))).withSourceEntity(entity));
}
@Override
public void visit(Same entity) {
// entity.getObject().accept(this);
setResultVisitor(TestsMatcherFactory.instance.same(evaluate(entity.getObject(), false)).withSourceEntity(entity));
}
@Override
public void visit(HasKind entity) {
EntityKinds kind = EntityKinds.valueOf(entity.getKind().getValue().getName());
setResultVisitor(GenericMatcherFactory.instance.hasKindMatcher(kind).withSourceEntity(entity));
}
@Override
public void visit(HasType entity) {
setResultVisitor(GenericMatcherFactory.instance.hasTypeMatcher(entity.getDescriptorName().getValue()).withSourceEntity(entity));
}
@Override
public void visit(IsAssignableTo entity) {
String edName = entity.getDescriptorName().getValue();
setResultVisitor(GenericMatcherFactory.instance.isPlatformSubtypeOfMatcher(edName).withSourceEntity(entity));
}
@Override
public void visit(IsDef entity) {
IVisitor v = TestsMatcherFactory.instance.defined();
v.withSourceEntity(entity).setBindings(getBindings());
setResultVisitor(v);
}
@Override
public void visit(IsUndef entity) {
IVisitor v = GenericTraversalFactory.instance.not(TestsMatcherFactory.instance.defined());
v.withSourceEntity(entity).setBindings(getBindings());
setResultVisitor(v);
}
@Override
public void visit(IsFalse entity) {
setResultVisitor(TestsMatcherFactory.instance.equalsValue(false).withSourceEntity(entity));
}
@Override
public void visit(IsTrue entity) {
setResultVisitor(TestsMatcherFactory.instance.equalsValue(true).withSourceEntity(entity));
}
@Override
public void visit(Throws entity) {
String className = entity.getThrowableType().getValue();
IVisitor v = TestsMatcherFactory.instance.hasThrown(className);
v.withSourceEntity(entity).setBindings(getBindings());
setResultVisitor(v);
}
@Override
public void visit(IsNull entity) {
setResultVisitor(TestsMatcherFactory.instance.isNull().withSourceEntity(entity));
}
@Override
public void visit(Comment entity) {
}
@Override
public void visit(BooleanLiteral entity) {
setResult(entity);
}
@Override
public void visit(IntLiteral entity) {
setResult(entity);
}
@Override
public void visit(StringLiteral entity) {
setResult(entity);
}
@Override
public void visit(Sequence entity) {
setResult(entity);
}
@Override
public void visit(DataName entity) {
String name = entity.getValue();
IEntity result = getBindings().wGet(name);
if (result == null)
throw new MissingVariableException(name).withSourceEntity(entity).withBindings(getBindings());
else
setResult(result);
}
}