verifyInternallyConsistentDefinition(testName, matrixSource, testDefinition, RuleEvaluator.FUNCTION_MAPPER, new ProvidedContext(ProvidedContext.EMPTY_CONTEXT,false));
}
public static void verifyInternallyConsistentDefinition(final String testName, final String matrixSource, @Nonnull final ConsumableTestDefinition testDefinition, final FunctionMapper functionMapper, final ProvidedContext providedContext) throws IncompatibleTestMatrixException {
final List<Allocation> allocations = testDefinition.getAllocations();
ExpressionFactory expressionFactory = new ExpressionFactoryImpl();
//verify test rule is valid EL
final String testRule = testDefinition.getRule();
final Map<String, ValueExpression> testConstants = ProctorUtils.convertToValueExpressionMap(expressionFactory, testDefinition.getConstants());
final VariableMapper variableMapper = new MulticontextReadOnlyVariableMapper(testConstants, providedContext.getContext());
final RuleEvaluator ruleEvaluator = new RuleEvaluator(expressionFactory, functionMapper, testDefinition.getConstants());
final ELContext elContext = ruleEvaluator.createELContext(variableMapper);
if(!isEmptyWhitespace(testRule)){
try {
final ValueExpression valueExpression = expressionFactory.createValueExpression(elContext, testRule, Boolean.class);
if (providedContext.isEvaluable()) { /*
* must have a context to test against, even if it's "Collections.emptyMap()", how to
* tell if this method is used for ProctorBuilder or during load of the testMatrix.
* also used to check to make sure any classes included in the EL have a
* default constructor for evaluation (unless they are an enum)
*/
valueExpression.getValue(elContext);
}
} catch (final ELException e) {
throw new IncompatibleTestMatrixException("Unable to evaluate rule ${" + testRule + "} in " + testName);
}
}
if (allocations.isEmpty()) {
throw new IncompatibleTestMatrixException("No allocations specified in test " + testName);
}
final List<TestBucket> buckets = testDefinition.getBuckets();
/*
* test the matrix for consistency with itself
*/
final Set<Integer> definedBuckets = Sets.newHashSet();
for (final TestBucket bucket : buckets) {
definedBuckets.add(bucket.getValue());
}
for(int i = 0; i < allocations.size(); i++ ) {
final Allocation allocation = allocations.get(i);
final List<Range> ranges = allocation.getRanges();
// ensure that each range refers to a known bucket
double bucketTotal = 0;
for (final Range range : ranges) {
bucketTotal += range.getLength();
// Internally consistent (within matrix itself)
if (!definedBuckets.contains(range.getBucketValue())) {
throw new IncompatibleTestMatrixException("Allocation range in " + testName + " from " + matrixSource + " refers to unknown bucket value " + range.getBucketValue());
}
}
// I hate floating points. TODO: extract a required precision constant/parameter?
if (bucketTotal < 0.9999 || bucketTotal > 1.0001) { // compensate for FP imprecision. TODO: determine what these bounds really should be by testing stuff
final StringBuilder sb = new StringBuilder(testName + " range with rule " + allocation.getRule() + " does not add up to 1 : ").append(ranges.get(0).getLength());
for (int r = 1; r < ranges.size(); r++) {
sb.append(" + ").append(ranges.get(r).getLength());
}
sb.append(" = ").append(bucketTotal);
throw new IncompatibleTestMatrixException(sb.toString());
}
final String rule = allocation.getRule();
final boolean lastAllocation = i == allocations.size() - 1;
final String bareRule = removeElExpressionBraces(rule);
if(lastAllocation) {
if (!isEmptyWhitespace(bareRule)) {
throw new IncompatibleTestMatrixException("Allocation[" + i + "] for test " + testName + " from " + matrixSource + " has non-empty rule: " + allocation.getRule());
}
} else {
if (isEmptyWhitespace(bareRule)) {
throw new IncompatibleTestMatrixException("Allocation[" + i + "] for test " + testName + " from " + matrixSource + " has empty rule: " + allocation.getRule());
}
}
//verify allocation rules are valid EL
if(!isEmptyWhitespace(bareRule)){
try {
final ValueExpression valueExpression = expressionFactory.createValueExpression(elContext, rule, Boolean.class);
if (providedContext.isEvaluable()) { /*
* must have a context to test against, even if it's "Collections.emptyMap()", how to
* tell if this method is used for ProctorBuilder or during load of the testMatrix.
* also used to check to make sure any classes included in the EL have a
* default constructor for evaluation (unless they are an enum)