Skip to content

Commit

Permalink
DROOLS-5876: Display actual test results instead of a generic message (
Browse files Browse the repository at this point in the history
  • Loading branch information
yesamer committed Mar 4, 2021
1 parent 1045d4c commit ffa8123
Show file tree
Hide file tree
Showing 14 changed files with 393 additions and 180 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@
*/
package org.drools.scenariosimulation.api.model;

import java.util.List;
import java.util.Objects;

import com.thoughtworks.xstream.annotations.XStreamOmitField;

/**
* FactMappingValue contains the identifier of a fact mapping + the raw value
* - collectionPathToValue: In case of an error occurred in a collection (List or Map), it contains the path which
* describes how to reach the wrong field (eg. Item #1 | field | ... ).
*/
public class FactMappingValue {

Expand All @@ -32,6 +35,8 @@ public class FactMappingValue {
@XStreamOmitField
private Object errorValue;
@XStreamOmitField
private List<String> collectionPathToValue;
@XStreamOmitField
private String exceptionMessage;

public FactMappingValue() {
Expand Down Expand Up @@ -89,9 +94,19 @@ public void setExceptionMessage(String exceptionMessage) {
this.status = FactMappingValueStatus.FAILED_WITH_EXCEPTION;
}

public List<String> getCollectionPathToValue() {
return collectionPathToValue;
}

public void setCollectionPathToValue(List<String> collectionPathToValue) {
this.collectionPathToValue = collectionPathToValue;
this.status = FactMappingValueStatus.FAILED_WITH_ERROR;
}

public void resetStatus() {
this.status = FactMappingValueStatus.SUCCESS;
this.exceptionMessage = null;
this.errorValue = null;
this.collectionPathToValue = null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

package org.drools.scenariosimulation.api.model;

import java.util.Arrays;
import java.util.List;

import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -42,16 +45,17 @@ public void resetStatus() {
assertThat(value.getStatus()).isEqualTo(FactMappingValueStatus.SUCCESS);
assertThat(value.getExceptionMessage()).isNull();
assertThat(value.getErrorValue()).isNull();
assertThat(value.getCollectionPathToValue()).isNull();
}

@Test
public void setErrorValue() {
String errorValue = VALUE;
FactMappingValue value = new FactMappingValue();
value.setErrorValue(errorValue);
value.setErrorValue(VALUE);
assertThat(value.getStatus()).isEqualTo(FactMappingValueStatus.FAILED_WITH_ERROR);
assertThat(value.getExceptionMessage()).isNull();
assertThat(value.getErrorValue()).isEqualTo(errorValue);
assertThat(value.getCollectionPathToValue()).isNull();
assertThat(value.getErrorValue()).isEqualTo(VALUE);
}

@Test
Expand All @@ -62,5 +66,17 @@ public void setExceptionMessage() {
assertThat(value.getStatus()).isEqualTo(FactMappingValueStatus.FAILED_WITH_EXCEPTION);
assertThat(value.getExceptionMessage()).isEqualTo(exceptionValue);
assertThat(value.getErrorValue()).isNull();
assertThat(value.getCollectionPathToValue()).isNull();
}

@Test
public void setPathToValue() {
List<String> path = Arrays.asList("Step1", "Step2");
FactMappingValue value = new FactMappingValue();
value.setCollectionPathToValue(path);
assertThat(value.getStatus()).isEqualTo(FactMappingValueStatus.FAILED_WITH_ERROR);
assertThat(value.getExceptionMessage()).isNull();
assertThat(value.getErrorValue()).isNull();
assertThat(value.getCollectionPathToValue()).isSameAs(path);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ public Object evaluateLiteralExpression(String rawExpression, String className,
}

@Override
public boolean evaluateUnaryExpression(String rawExpression, Object resultValue, Class<?> resultClass) {
public ExpressionEvaluatorResult evaluateUnaryExpression(String rawExpression, Object resultValue, Class<?> resultClass) {
if (isStructuredResult(resultClass)) {
return verifyResult(rawExpression, resultValue, resultClass);
} else {
return internalUnaryEvaluation(rawExpression, resultValue, resultClass, false);
return ExpressionEvaluatorResult.of(internalUnaryEvaluation(rawExpression, resultValue, resultClass, false));
}
}

Expand Down Expand Up @@ -143,9 +143,9 @@ protected Object createAndFillObject(ObjectNode json, Object toReturn, String cl
return toReturn;
}

protected boolean verifyResult(String rawExpression, Object resultRaw, Class<?> resultClass) {
protected ExpressionEvaluatorResult verifyResult(String rawExpression, Object resultRaw, Class<?> resultClass) {
if (rawExpression == null) {
return resultRaw == null;
return ExpressionEvaluatorResult.of(resultRaw == null);
}
if (resultRaw != null && !(resultRaw instanceof List) && !(resultRaw instanceof Map)) {
throw new IllegalArgumentException("A list or map was expected");
Expand All @@ -155,41 +155,54 @@ protected boolean verifyResult(String rawExpression, Object resultRaw, Class<?>

if (jsonNode.isTextual()) {
/* JSON Text: expression manually written by the user to build a list/map */
return internalUnaryEvaluation(jsonNode.asText(), resultRaw, resultClass, false);
return ExpressionEvaluatorResult.of(internalUnaryEvaluation(jsonNode.asText(), resultRaw, resultClass, false));
} else if (jsonNode.isArray()) {
/* JSON Array: list of expressions created using List collection editor */
return verifyList((ArrayNode) jsonNode, (List) resultRaw);
return verifyList((ArrayNode) jsonNode, (List<Object>) resultRaw);
} else if (jsonNode.isObject()) {
/* JSON Map: map of expressions created using Map collection editor */
return verifyObject((ObjectNode) jsonNode, resultRaw);
}
throw new IllegalArgumentException(ConstantsHolder.MALFORMED_RAW_DATA_MESSAGE);
}

protected boolean verifyList(ArrayNode json, List resultRaw) {
protected ExpressionEvaluatorResult verifyList(ArrayNode json, List<Object> resultRaw) {
if (resultRaw == null) {
return isListEmpty(json);
return ExpressionEvaluatorResult.of(isListEmpty(json));
}
int elementNumber = 0;
for (JsonNode node : json) {
elementNumber++;
boolean success = false;
boolean simpleTypeNode = isSimpleTypeNode(node);

for (Object result : resultRaw) {
boolean simpleTypeNode = isSimpleTypeNode(node);
if (simpleTypeNode && internalUnaryEvaluation(getSimpleTypeNodeTextValue(node), result, result.getClass(), true)) {
success = true;
} else if (!simpleTypeNode && verifyObject((ObjectNode) node, result)) {
} else if (!simpleTypeNode && verifyObject((ObjectNode) node, result).isSuccessful()) {
success = true;
}

if (success) {
break;
}
}

if (!success) {
return false;
ExpressionEvaluatorResult evaluatorResult = ExpressionEvaluatorResult.ofFailed();
if (simpleTypeNode) {
evaluatorResult.setWrongValue(getSimpleTypeNodeTextValue(node));
}
evaluatorResult.addListItemStepToPath(elementNumber);
return evaluatorResult;
}
}
return true;
return ExpressionEvaluatorResult.ofSuccessful();
}

protected boolean verifyObject(ObjectNode json, Object resultRaw) {
protected ExpressionEvaluatorResult verifyObject(ObjectNode json, Object resultRaw) {
if (resultRaw == null) {
return isObjectEmpty(json);
return ExpressionEvaluatorResult.of(isObjectEmpty(json));
}
Iterator<Map.Entry<String, JsonNode>> fields = json.fields();
while (fields.hasNext()) {
Expand All @@ -198,26 +211,38 @@ protected boolean verifyObject(ObjectNode json, Object resultRaw) {
JsonNode jsonNode = element.getValue();
Object fieldValue = extractFieldValue(resultRaw, key);
Class<?> fieldClass = fieldValue != null ? fieldValue.getClass() : null;
ExpressionEvaluatorResult evaluatorResult = ExpressionEvaluatorResult.ofFailed();

if (isSimpleTypeNode(jsonNode)) {
if (!internalUnaryEvaluation(getSimpleTypeNodeTextValue(jsonNode), fieldValue, fieldClass, true)) {
return false;
String nodeValue = getSimpleTypeNodeTextValue(jsonNode);
if (!internalUnaryEvaluation(nodeValue, fieldValue, fieldClass, true)) {
evaluatorResult.setWrongValue(nodeValue);
evaluatorResult.addMapItemStepToPath(key);
return evaluatorResult;
}
} else if (jsonNode.isArray()) {
if (!verifyList((ArrayNode) jsonNode, (List) fieldValue)) {
return false;
evaluatorResult = verifyList((ArrayNode) jsonNode, (List) fieldValue);
if (!evaluatorResult.isSuccessful()) {
evaluatorResult.addMapItemStepToPath(key);
return evaluatorResult;
}
} else if (jsonNode.isObject()) {
if (!verifyObject((ObjectNode) jsonNode, fieldValue)) {
return false;
evaluatorResult = verifyObject((ObjectNode) jsonNode, fieldValue);
if (!evaluatorResult.isSuccessful()) {
if (resultRaw instanceof Map) {
evaluatorResult.addMapItemStepToPath(key);
} else {
evaluatorResult.addFieldItemStepToPath(key);
}
return evaluatorResult;
}
} else {
if (!internalUnaryEvaluation(jsonNode.textValue(), fieldValue, fieldClass, true)) {
return false;
return ExpressionEvaluatorResult.ofFailed(jsonNode.textValue(), key);
}
}
}
return true;
return ExpressionEvaluatorResult.ofSuccessful();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

public interface ExpressionEvaluator {

boolean evaluateUnaryExpression(String rawExpression, Object resultValue, Class<?> resultClass);
ExpressionEvaluatorResult evaluateUnaryExpression(String rawExpression, Object resultValue, Class<?> resultClass);

Object evaluateLiteralExpression(String rawExpression, String className, List<String> genericClasses);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates.
*
* Licensed 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.
*/
package org.drools.scenariosimulation.backend.expression;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* This DTO holds info related to an expression evaluation.
* - successful: field represents status of involved evaluation (successful or failed);
* - pathToWrongValue: a list which contains the steps to describe the wrong value. In case of nested object or collections,
* can require multiple steps (eg. Author.books). In case of a list, conversion is to report the Item
* number (eg. Author.books.Item(2).isAvailable)
* - wrongValue: The actual wrong value
* Instantiated objects can be accessed only to retrieve the success status and to generate an error message, if evaluation
* failed, based on wrongValue and its path.
*/
public class ExpressionEvaluatorResult {

private boolean successful;
private String wrongValue;
private List<String> pathToWrongValue;

public static ExpressionEvaluatorResult of(boolean successful) {
return new ExpressionEvaluatorResult(successful);
}

public static ExpressionEvaluatorResult ofSuccessful() {
return new ExpressionEvaluatorResult(true);
}

public static ExpressionEvaluatorResult ofFailed() {
return new ExpressionEvaluatorResult(false);
}

public static ExpressionEvaluatorResult ofFailed(String wrongValue, String wrongValueField) {
return new ExpressionEvaluatorResult(false, wrongValue, wrongValueField);
}

private ExpressionEvaluatorResult(boolean successful, String wrongValue, String wrongValueField) {
this.successful = successful;
this.pathToWrongValue = new ArrayList<>();
this.addFieldItemStepToPath(wrongValueField);
this.wrongValue = wrongValue;
}

private ExpressionEvaluatorResult(boolean successful) {
this.successful = successful;
this.pathToWrongValue = new ArrayList<>();
this.wrongValue = null;
}

public boolean isSuccessful() {
return successful;
}

public void addFieldItemStepToPath(String fieldName) {
pathToWrongValue.add(0, fieldName);
}

public void addListItemStepToPath(int elementNumber) {
pathToWrongValue.add(0, "Item #: " + elementNumber);
}

public void addMapItemStepToPath(String key) {
pathToWrongValue.add(0, "Item key \"" + key + "\"");
}

public void setWrongValue(String wrongValue) {
this.wrongValue = wrongValue;
}

public String getWrongValue() {
return wrongValue;
}

public List<String> getPathToWrongValue() {
return Collections.unmodifiableList(pathToWrongValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,16 @@ public MVELExpressionEvaluator(ClassLoader classLoader) {
}

@Override
public boolean evaluateUnaryExpression(String rawExpression, Object resultValue, Class<?> resultClass) {
public ExpressionEvaluatorResult evaluateUnaryExpression(String rawExpression, Object resultValue, Class<?> resultClass) {
Map<String, Object> params = new HashMap<>();
params.put(ACTUAL_VALUE_IDENTIFIER, resultValue);

Object expressionResult = compileAndExecute(rawExpression, params);
if (!(expressionResult instanceof Boolean)) {
// try to compare via compare/equals operators
return compareValues(expressionResult, resultValue);
return ExpressionEvaluatorResult.of(compareValues(expressionResult, resultValue));
}
return (boolean) expressionResult;
return ExpressionEvaluatorResult.of((boolean) expressionResult);
}

@Override
Expand All @@ -79,7 +79,7 @@ public String fromObjectToExpression(Object value) {
protected Object compileAndExecute(String rawExpression, Map<String, Object> params) {
ParserContext ctx = new ParserContext(this.config);
for (Map.Entry<String, Object> entry : params.entrySet()) {
Class type = entry.getValue() != null ? entry.getValue().getClass() : null;
Class<?> type = entry.getValue() != null ? entry.getValue().getClass() : null;
ctx.addVariable(entry.getKey(), type);
}

Expand Down
Loading

0 comments on commit ffa8123

Please sign in to comment.