package org.ovirt.engine.core.bll.validator;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.both;
import static org.hamcrest.CoreMatchers.not;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.ovirt.engine.core.bll.ValidationResult;
import org.ovirt.engine.core.common.errors.EngineMessage;
/**
* Useful matchers to use when testing {@link ValidationResult}s, using the
* {@link org.junit.Assert#assertThat(Object, Matcher)} (or equivalent) method.<br>
* These matchers are best used when imported statically.<br>
* <br>
* A couple of examples:<br>
* * To check that <i>validationMethod()</i> returned a <b>valid</b> result:
*
* <pre>
* assertThat(<i>validationMethod()</i>,<b>isValid()</b>);
* </pre>
*
*
* * To check that <i>validationMethod()</i> <b>fails</b> with an error message
* <i>EngineMessage.EXPECTED_ERROR_MESSAGE</i> (no check for replacements will be done):
*
* <pre>
* assertThat(<i>validationMethod()</i>, <b>failsWith</b>(<i>EngineMessage.EXPECTED_ERROR_MESSAGE</i>));
* </pre>
*
* * To check that <i>validationMethod()</i> <b>fails</b> with an error message
* <i>EngineMessage.EXPECTED_ERROR_MESSAGE</i> and the <b>replacements</b> contain a string
* <i>REPLACEMENT_CONSTANT</i>:
*
* <pre>
* assertThat(<i>validationMethod()</i>,
* both(<b>replacements</b>(hasItem(containsString(<i>REPLACEMENT_CONSTANT</i>))))
* .and(<b>failsWith</b>(<i>EngineMessage.EXPECTED_ERROR_MESSAGE</i>)));
* </pre>
*
* @see org.junit.Assert#assertThat(Object, Matcher)
* @see org.hamcrest.CoreMatchers
*/
public class ValidationResultMatchers {
//do not instantiate me.
private ValidationResultMatchers() {
}
/**
* @return A matcher matching any {@link ValidationResult} that returns true for {@link ValidationResult#isValid()}.
*/
public static Matcher<ValidationResult> isValid() {
return new IsValid();
}
/**
* @param expectedError
* The error message expected in {@link ValidationResult#getMessages()}
* @return A matcher matching any {@link ValidationResult} that is not valid and fails with the given error.
*/
public static Matcher<ValidationResult> failsWith(EngineMessage expectedError) {
return new Fails(expectedError);
}
public static Matcher<ValidationResult> failsWith(EngineMessage expectedError, String... variableReplacements) {
return new Fails(expectedError, variableReplacements);
}
public static Matcher<ValidationResult> failsWith(EngineMessage expectedError,
Collection<String> variableReplacements) {
return new Fails(expectedError, variableReplacements.toArray(new String[variableReplacements.size()]));
}
public static Matcher<ValidationResult> failsWith(List<EngineMessage> expectedErrors,
Collection<String> variableReplacements) {
return new Fails(expectedErrors, variableReplacements.toArray(new String[variableReplacements.size()]));
}
public static Matcher<ValidationResult> failsWith(ValidationResult validationResult) {
if (ValidationResult.VALID.equals(validationResult)) {
throw new IllegalArgumentException("Illegal matcher usage: you cannot pass ValidationResult.VALID here.");
}
return failsWith(validationResult.getMessages(), validationResult.getVariableReplacements());
}
/**
* @param matcher
* The matcher to match against {@link ValidationResult#getVariableReplacements()}
* @return A matcher matching any {@link ValidationResult} that it's variable replacements match the given matcher.
*/
public static Matcher<ValidationResult> replacements(Matcher<Iterable<? super String>> matcher) {
return new Replacements(matcher);
}
public static Matcher<ValidationResult> hasVariableReplacements(String... variableReplacements) {
return new HasVariableReplacements(variableReplacements);
}
private static class IsValid extends TypeSafeMatcher<ValidationResult> {
@Override
public void describeTo(Description description) {
description.appendText("valid ValidationResult");
}
@Override
public boolean matchesSafely(ValidationResult item) {
return item.isValid();
}
}
private static class WithMessages extends TypeSafeMatcher<ValidationResult> {
private List<EngineMessage> expected;
public WithMessages(List<EngineMessage> expected) {
this.expected = expected;
}
@Override
public void describeTo(Description description) {
description.appendText("messages \"" +
expected.stream().map(m -> m.name()).collect(Collectors.joining()) + "\"");
}
@Override
public boolean matchesSafely(ValidationResult item) {
return CollectionUtils.isEqualCollection(expected, item.getMessages());
}
}
private static class Replacements extends TypeSafeMatcher<ValidationResult> {
private Matcher<Iterable<? super String>> matcher;
public Replacements(Matcher<Iterable<? super String>> matcher) {
this.matcher = matcher;
}
@Override
public void describeTo(Description description) {
description.appendText("the variable replacements is ").appendDescriptionOf(matcher);
}
@Override
public boolean matchesSafely(ValidationResult item) {
return matcher.matches(item.getVariableReplacements());
}
}
private static class HasVariableReplacements extends TypeSafeMatcher<ValidationResult> {
private final String[] variableReplacements;
public HasVariableReplacements(String... variableReplacements) {
this.variableReplacements = variableReplacements;
}
@Override
protected boolean matchesSafely(ValidationResult item) {
final List<String> vr = item.getVariableReplacements();
return variableReplacements.length == vr.size() && vr.containsAll(Arrays.asList(variableReplacements));
}
@Override
public void describeTo(Description description) {
description.appendText("ValidationResult containing exactly these variable replacements: " + Arrays.toString(variableReplacements));
}
}
private static class Fails extends TypeSafeMatcher<ValidationResult> {
private Matcher<ValidationResult> matcher;
public Fails(EngineMessage expected) {
matcher = both(not(isValid())).and(new WithMessages(Collections.singletonList(expected)));
}
public Fails(EngineMessage expected, String... variableReplacements) {
this(Collections.singletonList(expected), variableReplacements);
}
public Fails(List<EngineMessage> expected, String... variableReplacements) {
matcher = allOf(
not(isValid()),
new WithMessages(expected),
hasVariableReplacements(variableReplacements));
}
@Override
public void describeTo(Description description) {
description.appendText("a failure ").appendDescriptionOf(matcher);
}
@Override
public boolean matchesSafely(ValidationResult actual) {
return matcher.matches(actual);
}
}
}