BVAL-167: don't be fooled by inclusion of constrained interface at multiple hierarchy levels
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Liskov.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Liskov.java
index 18dfadc..d286898 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Liskov.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Liskov.java
@@ -52,13 +52,19 @@
 
             @Override
             public boolean test(Map<Meta<?>, Set<ValidationElement>> detectedValidationElements) {
-                boolean lowestFound = false;
+                Class<?> declaringType = null;
 
-                for (Set<ValidationElement> validated : detectedValidationElements.values()) {
-                    if (lowestFound) {
+                for (Map.Entry<Meta<?>, Set<ValidationElement>> e : detectedValidationElements.entrySet()){
+                    final Class<?> t = e.getKey().getDeclaringClass();
+                    if (declaringType != null) {
+                        if (declaringType.isAssignableFrom(t)) {
+                            continue;
+                        }
                         return false;
                     }
-                    lowestFound = !validated.isEmpty();
+                    if (!e.getValue().isEmpty()){
+                        declaringType = t;
+                    }
                 }
                 return true;
             }
@@ -67,16 +73,34 @@
 
             @Override
             public boolean test(Map<Meta<?>, Set<ValidationElement>> detectedValidationElements) {
-                final Set<Class<?>> interfaces = detectedValidationElements.keySet().stream().map(Meta::getDeclaringClass)
-                        .filter(Class::isInterface).collect(Collectors.toSet());
-                if (interfaces.isEmpty()) {
+                if (detectedValidationElements.size() < 2) {
+                    // no unrelated hierarchy possible
                     return true;
                 }
-                final boolean allRelated =
-                    detectedValidationElements.keySet().stream().map(Meta::getDeclaringClass).allMatch(ifc -> interfaces
-                        .stream().filter(Predicate.isEqual(ifc).negate()).allMatch(ifc2 -> related(ifc, ifc2)));
-
-                return allRelated;
+                final Map<Class<?>, Set<ValidationElement>> interfaceValidation = new LinkedHashMap<>();
+                detectedValidationElements.forEach((k,v)->{
+                    final Class<?> t = k.getDeclaringClass();
+                    if (t.isInterface()){
+                        interfaceValidation.put(t, v);
+                    }
+                });
+                if (interfaceValidation.isEmpty()) {
+                    // if all are classes, there can be no unrelated types in the hierarchy:
+                    return true;
+                }
+                // verify that all types can be assigned to the constrained interfaces:
+                for (Meta<?> meta : detectedValidationElements.keySet()) {
+                    final Class<?> t = meta.getDeclaringClass();
+                    for (Map.Entry<Class<?>, Set<ValidationElement>> e : interfaceValidation.entrySet()) {
+                        if (t.equals(e.getKey()) || e.getValue().isEmpty()) {
+                            continue;
+                        }
+                        if (!e.getKey().isAssignableFrom(t)) {
+                            return false;
+                        }
+                    }
+                }
+                return true;
             }
         };
         //@formatter:on
@@ -122,7 +146,7 @@
 
             // pre-check return value overridden hierarchy:
             Stream.of(StrengtheningIssue.values())
-                .filter((Predicate<? super StrengtheningIssue>) si -> !(si == StrengtheningIssue.overriddenHierarchy
+                .filter(si -> !(si == StrengtheningIssue.overriddenHierarchy
                     && detectedValidationElements.values().stream().filter(s -> !s.isEmpty()).count() < 2))
                 .forEach(si -> si.check(detectedValidationElements));
 
diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/LiskovTest.java b/bval-jsr/src/test/java/org/apache/bval/jsr/LiskovTest.java
index 9f40d0c..9f52f51 100644
--- a/bval-jsr/src/test/java/org/apache/bval/jsr/LiskovTest.java
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/LiskovTest.java
@@ -18,25 +18,17 @@
  */
 package org.apache.bval.jsr;
 
-import java.lang.reflect.Method;
-
 import javax.validation.Validation;
 import javax.validation.ValidatorFactory;
 import javax.validation.constraints.NotNull;
-import javax.validation.executable.ExecutableValidator;
 
-import org.junit.Ignore;
 import org.junit.Test;
 
-@Ignore("requires some revisiting of Liskov impl - discuss in progress")
 public class LiskovTest {
-    @Test // this test was throwing a Liskov exception, here to ensure it is not the case
-    public void validateParams() throws NoSuchMethodException {
-        final Api service = new Impl();
+    @Test
+    public void testBVal167() {
         try (final ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
-            final ExecutableValidator validator = factory.getValidator().forExecutables();
-            final Method method = Api.class.getMethod("read", String.class);
-            validator.validateParameters(service, method, new Object[]{""});
+            factory.getValidator().getConstraintsForClass(Impl.class);
         }
     }
 
@@ -44,16 +36,18 @@
         String read(@NotNull String key);
     }
 
-    public static abstract class Base {
+    public interface Api2 extends Api {
+        @Override
+        String read(String key);
+    }
+
+    public static abstract class Base implements Api {
+        @Override
         public String read(final String key) {
             return null;
         }
     }
 
-    public static class Impl extends Base implements Api {
-        @Override
-        public String read(final String key) {
-            return super.read(key);
-        }
+    public static class Impl extends Base implements Api2 {
     }
 }