package nl.uva.softwcons.ql.eval;
import static nl.uva.softwcons.helper.TestHelper.DUMMY_LINE_INFO;
import static nl.uva.softwcons.helper.TestHelper.QUESTION;
import static nl.uva.softwcons.helper.TestHelper.QUESTION2;
import static nl.uva.softwcons.helper.TestHelper.QUESTION3;
import static nl.uva.softwcons.helper.TestHelper.UNUSED;
import static nl.uva.softwcons.ql.eval.value.UndefinedValue.UNDEFINED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import nl.uva.softwcons.helper.TestHelper;
import nl.uva.softwcons.ql.FormBuilder;
import nl.uva.softwcons.ql.ast.expression.identifier.Identifier;
import nl.uva.softwcons.ql.ast.form.Form;
import nl.uva.softwcons.ql.ast.statement.Computable;
import nl.uva.softwcons.ql.ast.statement.ComputedQuestion;
import nl.uva.softwcons.ql.ast.statement.Conditional;
import nl.uva.softwcons.ql.eval.value.BooleanValue;
import nl.uva.softwcons.ql.eval.value.NumberValue;
import nl.uva.softwcons.ql.eval.value.Value;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
public class EvaluatorTest {
private static final Identifier QUESTION4 = new Identifier("question4", DUMMY_LINE_INFO);
private static final class BooleanValueMatcher extends ArgumentMatcher<Value> {
private final boolean expectedValue;
public BooleanValueMatcher(boolean expectedValue) {
this.expectedValue = expectedValue;
}
@Override
public boolean matches(Object argument) {
return ((Value) argument).getBoolean() == expectedValue;
}
}
@Test
public void testGettingANonexistentValue() {
String questionText = "question: \"Label\" boolean";
Form form = FormBuilder.build(TestHelper.buildForm("form1", questionText));
assertThat(new Evaluator(form).getValue(UNUSED)).isEqualTo(UNDEFINED);
}
@Test
public void testGettingANonfiledQuestionValue() {
String questionText = "question: \"Label\" boolean";
Form form = FormBuilder.build(TestHelper.buildForm("form1", questionText));
assertThat(new Evaluator(form).getValue(QUESTION)).isEqualTo(UNDEFINED);
}
@Test
public void testGettingAYetNoncomputableValue() {
String question1Text = "question: \"Label\" boolean";
String question2Text = "question2: \"Label\" boolean(question != true)";
Form form = FormBuilder.build(TestHelper.buildForm("form1", question1Text, question2Text));
assertThat(new Evaluator(form).getValue(QUESTION2)).isEqualTo(UNDEFINED);
}
@Test
public void testGettingComputableQuestionValues() {
String question1Text = "question: \"Label\" boolean (true != false)";
String question2Text = "question2: \"Label\" number (6 * 5)";
String question3Text = "question3: \"Label\" number (\"lazy\")";
Form form = FormBuilder.build(TestHelper.buildForm("form1", question1Text, question2Text, question3Text));
assertThat(new Evaluator(form).getValue(QUESTION).getBoolean()).isEqualTo(true);
assertThat(new Evaluator(form).getValue(QUESTION2).getNumber()).isEqualTo("30");
assertThat(new Evaluator(form).getValue(QUESTION3).getString()).isEqualTo("lazy");
}
@Test
public void testGettingAValueAfterManuallySettingIt() {
String questionText = "question: \"Label\" boolean";
Form form = FormBuilder.build(TestHelper.buildForm("form1", questionText));
Evaluator e = new Evaluator(form);
e.updateValue(QUESTION, new BooleanValue(true));
assertThat(e.getValue(QUESTION).getBoolean()).isEqualTo(true);
}
@Test
public void testThatUpdatingValuesIsPropagatedToReferences() {
String questionText = "question: \"Label\" number";
String question2Text = "question2: \"Label\" boolean (question > 0)";
Form form = FormBuilder.build(TestHelper.buildForm("form1", questionText, question2Text));
Evaluator e = new Evaluator(form);
assertThat(e.getValue(QUESTION2)).isEqualTo(UNDEFINED);
e.updateValue(QUESTION, new NumberValue(1));
assertThat(e.getValue(QUESTION2).getBoolean()).isEqualTo(true);
}
@Test
public void testThatUpdatingValuesIsPropagatedToReferencesInConditionals() {
String questionText = "question: \"Label\" number";
String question2Text = "if (true) { question2: \"Label\" number (question * 10) }";
Form form = FormBuilder.build(TestHelper.buildForm("form1", questionText, question2Text));
Evaluator e = new Evaluator(form);
assertThat(e.getValue(QUESTION2)).isEqualTo(UNDEFINED);
e.updateValue(QUESTION, new NumberValue(1));
assertThat(e.getValue(QUESTION2).getNumber()).isEqualTo("10");
}
@Test
public void testThatUpdatingValuesIsPropagatedAllTheWay() {
String questionText = "question: \"Label\" number";
String question2Text = "question2: \"Label\" boolean (question > 0)";
String question3Text = "question3: \"Label\" boolean (question2 == true)";
String question4Text = "question4: \"Label\" boolean (question3 == false)";
Form form = FormBuilder.build(TestHelper.buildForm("form1", questionText, question2Text, question3Text,
question4Text));
Evaluator e = new Evaluator(form);
assertThat(e.getValue(QUESTION2)).isEqualTo(UNDEFINED);
assertThat(e.getValue(QUESTION3)).isEqualTo(UNDEFINED);
assertThat(e.getValue(QUESTION4)).isEqualTo(UNDEFINED);
e.updateValue(QUESTION, new NumberValue(1));
assertThat(e.getValue(QUESTION2).getBoolean()).isEqualTo(true);
assertThat(e.getValue(QUESTION3).getBoolean()).isEqualTo(true);
assertThat(e.getValue(QUESTION4).getBoolean()).isEqualTo(false);
}
@SuppressWarnings("unchecked")
@Test
public void testThatCallbacksForQuestionsAreCalledAfterValueUpdate() {
String questionText = "question: \"Label\" number";
String question2Text = "question2: \"Label\" boolean (question > 0)";
Form form = FormBuilder.build(TestHelper.buildForm("form1", questionText, question2Text));
Evaluator e = new Evaluator(form);
// add listener to "question2"
ValueChangeListener<Value> listener = mock(ValueChangeListener.class);
e.addListener((Computable) form.getStatements().get(1), listener);
// verify it is not called before the update of "question"
verify(listener, never()).processValueChange(null);
e.updateValue(QUESTION, new NumberValue(1));
// verify callback is called with the correct value
verify(listener).processValueChange(argThat(new BooleanValueMatcher(true)));
}
@SuppressWarnings("unchecked")
@Test
public void testThatCallbacksForConditionalsAreCalledAfterValueUpdate() {
String questionText = "question: \"Label\" number";
String question2Text = "if (question > 0) { question2: \"Label\" boolean }";
Form form = FormBuilder.build(TestHelper.buildForm("form1", questionText, question2Text));
Evaluator e = new Evaluator(form);
// add listener to the conditional
ValueChangeListener<Value> listener = mock(ValueChangeListener.class);
e.addListener((Computable) form.getStatements().get(1), listener);
// verify it is not called before the update of "question"
verify(listener, never()).processValueChange(null);
e.updateValue(QUESTION, new NumberValue(1));
// verify callback is called with the correct value
verify(listener).processValueChange(argThat(new BooleanValueMatcher(true)));
}
@SuppressWarnings("unchecked")
@Test
public void testThatCallbacksForInsideConditionalsAreCalledAfterValueUpdate() {
String questionText = "question: \"Label\" number";
String conditionalText = "if (true) { question2: \"Label\" boolean (question > 0) }";
Form form = FormBuilder.build(TestHelper.buildForm("form1", questionText, conditionalText));
Conditional conditional = (Conditional) form.getStatements().get(1);
Evaluator e = new Evaluator(form);
// add listener to "question2"
ValueChangeListener<Value> listener = mock(ValueChangeListener.class);
e.addListener((ComputedQuestion) conditional.getQuestions().get(0), listener);
// verify it is not called before the update of "question"
verify(listener, never()).processValueChange(null);
e.updateValue(QUESTION, new NumberValue(1));
// verify callback is called with the correct value
verify(listener).processValueChange(argThat(new BooleanValueMatcher(true)));
}
@SuppressWarnings("unchecked")
@Test
public void testThatCallbacksAreCalledAfterValueUpdateAllTheWay() {
String questionText = "question: \"Label\" number";
String question2Text = "question2: \"Label\" boolean (question > 0)";
String question3Text = "question3: \"Label\" boolean (question2 == true)";
String question4Text = "question4: \"Label\" boolean (question3 == false)";
Form form = FormBuilder.build(TestHelper.buildForm("form1", questionText, question2Text, question3Text,
question4Text));
Evaluator e = new Evaluator(form);
// add listeners to all computed questions
ValueChangeListener<Value> question2listener = mock(ValueChangeListener.class);
ValueChangeListener<Value> question3listener = mock(ValueChangeListener.class);
ValueChangeListener<Value> question4listener = mock(ValueChangeListener.class);
e.addListener((Computable) form.getStatements().get(1), question2listener);
e.addListener((Computable) form.getStatements().get(2), question3listener);
e.addListener((Computable) form.getStatements().get(3), question4listener);
// verify nothing is called before the update of "question"
verify(question2listener, never()).processValueChange(null);
verify(question3listener, never()).processValueChange(null);
verify(question4listener, never()).processValueChange(null);
e.updateValue(QUESTION, new NumberValue(1));
// verify callbacks are called with the correct values
verify(question2listener).processValueChange(argThat(new BooleanValueMatcher(true)));
verify(question3listener).processValueChange(argThat(new BooleanValueMatcher(true)));
verify(question4listener).processValueChange(argThat(new BooleanValueMatcher(false)));
}
}