/**
* Bean Validation TCK
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.beanvalidation.tck.util;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ElementKind;
import javax.validation.Path;
/**
* This class provides useful functions to assert correctness of constraint violations raised
* during tests.
*
* @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI
* @author Hardy Ferentschik
* @author Gunnar Morling
*/
public final class ConstraintViolationAssert {
/**
* Expected name for cross-parameter nodes.
*/
private static final String CROSS_PARAMETER_NODE_NAME = "<cross-parameter>";
/**
* Expected name for cross-parameter nodes.
*/
private static final String RETURN_VALUE_NODE_NAME = "<return value>";
/**
* Private constructor in order to avoid instantiation.
*/
private ConstraintViolationAssert() {
}
public static PathExpectation pathWith() {
return new PathExpectation();
}
public static class ConstraintViolationSetAssert {
public static void assertContainsOnlyPaths(Set<? extends ConstraintViolation<?>> actualViolations, PathExpectation... paths) {
assertNotNull( paths );
List<PathExpectation> actualPaths = new ArrayList<>();
for ( ConstraintViolation<?> violation : actualViolations ) {
actualPaths.add( new PathExpectation( violation.getPropertyPath() ) );
}
assertContainsOnly( actualPaths, paths );
}
public static void assertContainsPath(Set<? extends ConstraintViolation<?>> actualViolations, PathExpectation expectedPath) {
assertNotNull( expectedPath );
List<PathExpectation> actualPaths = new ArrayList<>();
for ( ConstraintViolation<?> violation : actualViolations ) {
PathExpectation actual = new PathExpectation( violation.getPropertyPath() );
if ( actual.equals( expectedPath ) ) {
return;
}
actualPaths.add( actual );
}
fail( String.format( "Didn't find path <%s> in actual paths <%s>.", expectedPath, actualPaths ) );
}
public static void assertContainsPaths(Set<? extends ConstraintViolation<?>> actualViolations, PathExpectation... expectedPaths) {
for ( PathExpectation pathExpectation : expectedPaths ) {
assertContainsPath( actualViolations, pathExpectation );
}
}
}
private static void assertContainsOnly(Collection<?> actual, Object[] expected) {
assertNotNull( actual );
assertNotNull( expected );
if ( expected.length == actual.size() ) {
boolean equal = true;
for ( Object expectedElement : expected ) {
if ( !actual.contains( expectedElement ) ) {
equal = false;
}
}
if ( equal ) {
return;
}
}
fail( String.format( "Should only contain the expected element: <%s>. Actual: <%s>.", Arrays.toString( expected ), actual ) );
}
/**
* A property path expected to be returned by a given {@link ConstraintViolation}.
*/
public static class PathExpectation {
private final List<NodeExpectation> nodes = new ArrayList<>();
private PathExpectation() {
}
private PathExpectation(Path propertyPath) {
for ( Path.Node node : propertyPath ) {
Integer parameterIndex = null;
if ( node.getKind() == ElementKind.PARAMETER ) {
parameterIndex = node.as( Path.ParameterNode.class ).getParameterIndex();
}
Class<?> containerClass = getContainerClass( node );
Integer typeArgumentIndex = getTypeArgumentIndex( node );
nodes.add(
new NodeExpectation(
node.getName(),
node.getKind(),
node.isInIterable(),
node.getKey(),
node.getIndex(),
parameterIndex,
containerClass,
typeArgumentIndex
)
);
}
}
public PathExpectation property(String name) {
nodes.add( new NodeExpectation( name, ElementKind.PROPERTY ) );
return this;
}
public PathExpectation property(String name, Class<?> containerClass, Integer typeArgumentIndex) {
nodes.add( new NodeExpectation( name, ElementKind.PROPERTY, false, null, null, null, containerClass, typeArgumentIndex ) );
return this;
}
public PathExpectation property(String name, boolean inIterable, Object key, Integer index) {
nodes.add( new NodeExpectation( name, ElementKind.PROPERTY, inIterable, key, index, null, null, null ) );
return this;
}
public PathExpectation property(String name, boolean inIterable, Object key, Integer index, Class<?> containerClass, Integer typeArgumentIndex) {
nodes.add( new NodeExpectation( name, ElementKind.PROPERTY, inIterable, key, index, null, containerClass, typeArgumentIndex ) );
return this;
}
public PathExpectation bean() {
nodes.add( new NodeExpectation( null, ElementKind.BEAN ) );
return this;
}
public PathExpectation bean(boolean inIterable, Object key, Integer index) {
nodes.add( new NodeExpectation( null, ElementKind.BEAN, inIterable, key, index, null, null, null ) );
return this;
}
public PathExpectation bean(boolean inIterable, Object key, Integer index, Class<?> containerClass, Integer typeArgumentIndex) {
nodes.add( new NodeExpectation( null, ElementKind.BEAN, inIterable, key, index, null, containerClass, typeArgumentIndex ) );
return this;
}
public PathExpectation method(String name) {
nodes.add( new NodeExpectation( name, ElementKind.METHOD ) );
return this;
}
public PathExpectation parameter(String name, int index) {
nodes.add( new NodeExpectation( name, ElementKind.PARAMETER, false, null, null, index, null, null ) );
return this;
}
public PathExpectation crossParameter() {
nodes.add( new NodeExpectation( CROSS_PARAMETER_NODE_NAME, ElementKind.CROSS_PARAMETER ) );
return this;
}
public PathExpectation returnValue() {
nodes.add( new NodeExpectation( RETURN_VALUE_NODE_NAME, ElementKind.RETURN_VALUE ) );
return this;
}
public PathExpectation containerElement(String name, boolean inIterable, Object key, Integer index, Class<?> containerClass, Integer typeArgumentIndex) {
nodes.add( new NodeExpectation( name, ElementKind.CONTAINER_ELEMENT, inIterable, key, index, null, containerClass, typeArgumentIndex ) );
return this;
}
@Override
public String toString() {
String lineBreak = System.getProperty( "line.separator" );
StringBuilder asString = new StringBuilder( lineBreak + "PathExpectation(" + lineBreak );
for ( NodeExpectation node : nodes ) {
asString.append( " " ).append( node ).append( lineBreak );
}
return asString.append( ")" ).toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ( ( nodes == null ) ? 0 : nodes.hashCode() );
return result;
}
@Override
public boolean equals(Object obj) {
if ( this == obj ) {
return true;
}
if ( obj == null ) {
return false;
}
if ( getClass() != obj.getClass() ) {
return false;
}
PathExpectation other = (PathExpectation) obj;
if ( nodes == null ) {
if ( other.nodes != null ) {
return false;
}
}
else if ( !nodes.equals( other.nodes ) ) {
return false;
}
return true;
}
}
/**
* A node expected to be contained in the property path returned by a given {@link ConstraintViolation}.
*/
private static class NodeExpectation {
private final String name;
private final ElementKind kind;
private final boolean inIterable;
private final Object key;
private final Integer index;
private final Integer parameterIndex;
private final Class<?> containerClass;
private final Integer typeArgumentIndex;
private NodeExpectation(String name, ElementKind kind) {
this( name, kind, false, null, null, null, null, null );
}
private NodeExpectation(String name, ElementKind kind, boolean inIterable, Object key, Integer index,
Integer parameterIndex, Class<?> containerClass, Integer typeArgumentIndex) {
this.name = name;
this.kind = kind;
this.inIterable = inIterable;
this.key = key;
this.index = index;
this.parameterIndex = parameterIndex;
this.containerClass = containerClass;
this.typeArgumentIndex = typeArgumentIndex;
}
@Override
public String toString() {
return "NodeExpectation(" + name + ", " + kind + ", " + inIterable
+ ", " + key + ", " + index + ", " + parameterIndex + ", " + containerClass + ", " + typeArgumentIndex + ")";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ( inIterable ? 1231 : 1237 );
result = prime * result + ( ( index == null ) ? 0 : index.hashCode() );
result = prime * result + ( ( key == null ) ? 0 : key.hashCode() );
result = prime * result + ( ( kind == null ) ? 0 : kind.hashCode() );
result = prime * result + ( ( name == null ) ? 0 : name.hashCode() );
result = prime * result + ( ( parameterIndex == null ) ? 0 : parameterIndex.hashCode() );
result = prime * result + ( ( containerClass == null ) ? 0 : containerClass.hashCode() );
result = prime * result + ( ( typeArgumentIndex == null ) ? 0 : typeArgumentIndex.hashCode() );
return result;
}
@Override
public boolean equals(Object obj) {
if ( this == obj ) {
return true;
}
if ( obj == null ) {
return false;
}
if ( getClass() != obj.getClass() ) {
return false;
}
NodeExpectation other = (NodeExpectation) obj;
if ( inIterable != other.inIterable ) {
return false;
}
if ( index == null ) {
if ( other.index != null ) {
return false;
}
}
else if ( !index.equals( other.index ) ) {
return false;
}
if ( key == null ) {
if ( other.key != null ) {
return false;
}
}
else if ( !key.equals( other.key ) ) {
return false;
}
if ( kind != other.kind ) {
return false;
}
if ( name == null ) {
if ( other.name != null ) {
return false;
}
}
else if ( !name.equals( other.name ) ) {
return false;
}
if ( parameterIndex == null ) {
if ( other.parameterIndex != null ) {
return false;
}
}
else if ( !parameterIndex.equals( other.parameterIndex ) ) {
return false;
}
if ( containerClass == null ) {
if ( other.containerClass != null ) {
return false;
}
}
else if ( !containerClass.equals( other.containerClass ) ) {
return false;
}
if ( typeArgumentIndex == null ) {
if ( other.typeArgumentIndex != null ) {
return false;
}
}
else if ( !typeArgumentIndex.equals( other.typeArgumentIndex ) ) {
return false;
}
return true;
}
}
private static Class<?> getContainerClass(Path.Node node) {
Class<?> containerClass = null;
if ( node.getKind() == ElementKind.PROPERTY ) {
containerClass = node.as( Path.PropertyNode.class ).getContainerClass();
}
if ( node.getKind() == ElementKind.BEAN ) {
containerClass = node.as( Path.BeanNode.class ).getContainerClass();
}
if ( node.getKind() == ElementKind.CONTAINER_ELEMENT ) {
containerClass = node.as( Path.ContainerElementNode.class ).getContainerClass();
}
return containerClass;
}
private static Integer getTypeArgumentIndex(Path.Node node) {
Integer typeArgumentIndex = null;
if ( node.getKind() == ElementKind.PROPERTY ) {
typeArgumentIndex = node.as( Path.PropertyNode.class ).getTypeArgumentIndex();
}
if ( node.getKind() == ElementKind.BEAN ) {
typeArgumentIndex = node.as( Path.BeanNode.class ).getTypeArgumentIndex();
}
if ( node.getKind() == ElementKind.CONTAINER_ELEMENT ) {
typeArgumentIndex = node.as( Path.ContainerElementNode.class ).getTypeArgumentIndex();
}
return typeArgumentIndex;
}
}