You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2020/11/07 14:29:16 UTC

[sling-org-apache-sling-feature-extension-apiregions] branch SLING-9867 updated: Add validator tests

This is an automated email from the ASF dual-hosted git repository.

cziegeler pushed a commit to branch SLING-9867
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-extension-apiregions.git


The following commit(s) were added to refs/heads/SLING-9867 by this push:
     new 522ca6c  Add validator tests
522ca6c is described below

commit 522ca6c8ec9a857f003af39c26e831fafb7b0c52
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Sat Nov 7 15:28:50 2020 +0100

    Add validator tests
---
 .../apiregions/api/config/PropertyType.java        |   4 +-
 .../extension/apiregions/api/config/Validator.java | 193 ++++++-----
 .../apiregions/api/config/ValidatorTest.java       | 379 +++++++++++++++++++++
 3 files changed, 487 insertions(+), 89 deletions(-)

diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyType.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyType.java
index e0d2889..fd8e971 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyType.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyType.java
@@ -23,9 +23,9 @@ public enum PropertyType {
  
     STRING,
     LONG,
-    INT,
+    INTEGER,
     SHORT,
-    CHAR,
+    CHARACTER,
     BYTE,
     DOUBLE,
     FLOAT,
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Validator.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Validator.java
index 6a3eda6..1a21b21 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Validator.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Validator.java
@@ -65,41 +65,11 @@ public class Validator {
 			}
 
 			if ( values != null ) {
-				// array or collection
-				if ( prop.getCardinality() > 0 && values.size() > prop.getCardinality() ) {
-                    messages.add("Array/collection contains too many elements, only " + prop.getCardinality() + " allowed");
-				}
-				for(final Object val : values) {
-					validateValue(prop, val, messages);
-				}
-				if ( prop.getIncludes() != null ) {
-					for(final String inc : prop.getIncludes()) {
-						boolean found = false;
-						for(final Object val : values) {
-                            if ( inc.equals(val.toString())) {
-								found = true;
-								break;
-							}
-						}
-						if ( !found ) {
-                            messages.add("Required included value " + inc + " not found");
-						}
-					}
-				}
-				if ( prop.getExcludes() != null ) {
-					for(final String exc : prop.getExcludes()) {
-						boolean found = false;
-						for(final Object val : values) {
-                            if ( exc.equals(val.toString())) {
-								found = true;
-								break;
-							}
-						}
-						if ( found ) {
-                            messages.add("Required excluded value " + exc + " found");
-						}
-					}
-				}
+                // array or collection
+                for(final Object val : values) {
+                    validateValue(prop, val, messages);
+                }
+                validateList(prop, values, messages);
 			}
 		}
 		return new ValidationResult(){
@@ -114,6 +84,40 @@ public class Validator {
 		};
 	}
 
+    static void validateList(final Property prop, final List<Object> values, final List<String> messages) {
+        if ( prop.getCardinality() > 0 && values.size() > prop.getCardinality() ) {
+            messages.add("Array/collection contains too many elements, only " + prop.getCardinality() + " allowed");
+        }
+        if ( prop.getIncludes() != null ) {
+            for(final String inc : prop.getIncludes()) {
+                boolean found = false;
+                for(final Object val : values) {
+                    if ( inc.equals(val.toString())) {
+                        found = true;
+                        break;
+                    }
+                }
+                if ( !found ) {
+                    messages.add("Required included value " + inc + " not found");
+                }
+            }
+        }
+        if ( prop.getExcludes() != null ) {
+            for(final String exc : prop.getExcludes()) {
+                boolean found = false;
+                for(final Object val : values) {
+                    if ( exc.equals(val.toString())) {
+                        found = true;
+                        break;
+                    }
+                }
+                if ( found ) {
+                    messages.add("Required excluded value " + exc + " found");
+                }
+            }
+        }
+    }
+
 	static void validateValue(final Property prop, final Object value, final List<String> messages) {
 		if ( value != null ) {
 			switch ( prop.getType() ) {
@@ -121,13 +125,13 @@ public class Validator {
 							   break;
 				case BYTE : validateByte(prop, value, messages);
 							break;
-				case CHAR : validateChar(prop, value, messages);
+				case CHARACTER : validateCharacter(prop, value, messages);
 							break;
 				case DOUBLE : validateDouble(prop, value, messages); 
 							break;
 				case FLOAT : validateFloat(prop, value, messages); 
 							break;
-				case INT : validateInt(prop, value, messages);
+				case INTEGER : validateInteger(prop, value, messages);
 							break;
 				case LONG : validateLong(prop, value, messages);
 							break;
@@ -142,23 +146,9 @@ public class Validator {
 				case URL : validateURL(prop, value, messages);
 							break;
 				default : messages.add("Unable to validate value - unknown property type : " + prop.getType());
-			}
-			if ( prop.getRegexPattern() != null ) {
-				if ( !prop.getRegexPattern().matcher(value.toString()).matches() ) {
-                    messages.add("Value " + value + " does not match regex " + prop.getRegex());
-				}
-			}
-			if ( prop.getOptions() != null ) {
-				boolean found = false;
-				for(final Option opt : prop.getOptions()) {
-					if ( opt.getValue().equals(value.toString() ) ) {
-						found = true; 
-					}
-				}
-				if ( !found ) {
-					messages.add("Value " + value + " does not match provided options");
-				}
-			}
+            }
+            validateRegex(prop, value, messages);
+            validateOptions(prop, value, messages);
 		} else {
 			messages.add("Null value provided for validation");
 		}
@@ -185,7 +175,9 @@ public class Validator {
 					validateRange(prop, Byte.valueOf(v), messages);
 				} catch ( final NumberFormatException nfe ) {
                     messages.add("Value is not a valid Byte : " + value);
-				}
+                }
+            } else if ( value instanceof Number ) {
+                validateRange(prop, ((Number)value).byteValue(), messages);            
 			} else {
 				messages.add("Byte value must either be of type Byte or String : " + value);
 			}
@@ -203,6 +195,8 @@ public class Validator {
 				} catch ( final NumberFormatException nfe ) {
                     messages.add("Value is not a valid Short : " + value);
 				}
+            } else if ( value instanceof Number ) {
+                validateRange(prop, ((Number)value).shortValue(), messages);            
 			} else {
 				messages.add("Short value must either be of type Short or String : " + value);
 			}
@@ -211,7 +205,7 @@ public class Validator {
 		}
 	}
 
-	static void validateInt(final Property prop, final Object value, final List<String> messages) {
+	static void validateInteger(final Property prop, final Object value, final List<String> messages) {
         if ( ! (value instanceof Integer) ) {
 			if ( value instanceof String ) {
 				final String v = (String)value;
@@ -220,6 +214,8 @@ public class Validator {
 				} catch ( final NumberFormatException nfe ) {
                     messages.add("Value is not a valid Integer : " + value);
 				}
+            } else if ( value instanceof Number ) {
+                validateRange(prop, ((Number)value).intValue(), messages);            
 			} else {
 				messages.add("Integer value must either be of type Integer or String : " + value);
 			}
@@ -237,6 +233,8 @@ public class Validator {
 				} catch ( final NumberFormatException nfe ) {
                     messages.add("Value is not a valid Long : " + value);
 				}
+            } else if ( value instanceof Number ) {
+                validateRange(prop, ((Number)value).longValue(), messages);            
 			} else {
 				messages.add("Long value must either be of type Long or String : " + value);
 			}
@@ -254,6 +252,8 @@ public class Validator {
 				} catch ( final NumberFormatException nfe ) {
                     messages.add("Value is not a valid Float : " + value);
 				}
+            } else if ( value instanceof Number ) {
+                validateRange(prop, ((Number)value).floatValue(), messages);            
 			} else {
 				messages.add("Float value must either be of type Float or String : " + value);
 			}
@@ -271,6 +271,8 @@ public class Validator {
 				} catch ( final NumberFormatException nfe ) {
                     messages.add("Value is not a valid Double : " + value);
 				}
+            } else if ( value instanceof Number ) {
+                validateRange(prop, ((Number)value).doubleValue(), messages);            
 			} else {
 				messages.add("Double value must either be of type Double or String : " + value);
 			}
@@ -279,7 +281,7 @@ public class Validator {
 		}
 	}
 
-	static void validateChar(final Property prop, final Object value, final List<String> messages) {
+	static void validateCharacter(final Property prop, final Object value, final List<String> messages) {
         if ( ! (value instanceof Character) ) {
 			if ( value instanceof String ) {
 				final String v = (String)value;
@@ -317,37 +319,54 @@ public class Validator {
 
 	static void validateRange(final Property prop, final Number value, final List<String> messages) {
 	    if ( prop.getRange() != null ) {
-			try {
-                if ( prop.getRange().getMin() != null ) {
-                    if ( value instanceof Float || value instanceof Double ) {
-                        final double min = prop.getRange().getMin().doubleValue();
-                        if ( value.doubleValue() < min ) {
-                             messages.add("Value " + value + " is too low; should not be lower than " + prop.getRange().getMin());
-                        }    
-                    } else {
-                        final long min = prop.getRange().getMin().longValue();
-                        if ( value.longValue() < min ) {
-                             messages.add("Value " + value + " is too low; should not be lower than " + prop.getRange().getMin());
-                        }    
-                    }
-				}
-                if ( prop.getRange().getMax() != null ) {
-                    if ( value instanceof Float || value instanceof Double ) {
-                        final double max = prop.getRange().getMax().doubleValue();
-                        if ( value.doubleValue() > max ) {
-                            messages.add("Value " + value + " is too high; should not be higher than " + prop.getRange().getMax());
-                        }    
-                    } else {
-                        final long max = prop.getRange().getMax().longValue();
-                        if ( value.longValue() > max ) {
-                            messages.add("Value " + value + " is too high; should not be higher than " + prop.getRange().getMax());
-                        }    
-                    }
-				}
-			} catch ( final NumberFormatException nfe) {
-				messages.add("Invalid range specified in " + prop.getRange());
-			}
+            if ( prop.getRange().getMin() != null ) {
+                if ( value instanceof Float || value instanceof Double ) {
+                    final double min = prop.getRange().getMin().doubleValue();
+                    if ( value.doubleValue() < min ) {
+                            messages.add("Value " + value + " is too low; should not be lower than " + prop.getRange().getMin());
+                    }    
+                } else {
+                    final long min = prop.getRange().getMin().longValue();
+                    if ( value.longValue() < min ) {
+                            messages.add("Value " + value + " is too low; should not be lower than " + prop.getRange().getMin());
+                    }    
+                }
+            }
+            if ( prop.getRange().getMax() != null ) {
+                if ( value instanceof Float || value instanceof Double ) {
+                    final double max = prop.getRange().getMax().doubleValue();
+                    if ( value.doubleValue() > max ) {
+                        messages.add("Value " + value + " is too high; should not be higher than " + prop.getRange().getMax());
+                    }    
+                } else {
+                    final long max = prop.getRange().getMax().longValue();
+                    if ( value.longValue() > max ) {
+                        messages.add("Value " + value + " is too high; should not be higher than " + prop.getRange().getMax());
+                    }    
+                }
+            }
 		}
 	}
 
+    static void validateRegex(final Property prop, final Object value, final List<String> messages) {
+        if ( prop.getRegexPattern() != null ) {
+            if ( !prop.getRegexPattern().matcher(value.toString()).matches() ) {
+                messages.add("Value " + value + " does not match regex " + prop.getRegex());
+            }
+        }
+    }
+
+    static void validateOptions(final Property prop, final Object value, final List<String> messages) {
+        if ( prop.getOptions() != null ) {
+            boolean found = false;
+            for(final Option opt : prop.getOptions()) {
+                if ( opt.getValue().equals(value.toString() ) ) {
+                    found = true; 
+                }
+            }
+            if ( !found ) {
+                messages.add("Value " + value + " does not match provided options");
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ValidatorTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ValidatorTest.java
new file mode 100644
index 0000000..1bfff16
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ValidatorTest.java
@@ -0,0 +1,379 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sling.feature.extension.apiregions.api.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+public class ValidatorTest {
+    
+    @Test public void testValidateValueWithNull() {
+        final List<String> messages = new ArrayList<>();
+        Validator.validateValue(null, null, messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+    }
+
+    @Test public void testValidateBoolean() {
+        final Property prop = new Property();
+        prop.setType(PropertyType.BOOLEAN);
+
+        final List<String> messages = new ArrayList<>();
+
+        Validator.validateBoolean(prop, Boolean.TRUE, messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateBoolean(prop, Boolean.FALSE, messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateBoolean(prop, "TRUE", messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateBoolean(prop, "FALSE", messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateBoolean(prop, "yes", messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+
+        Validator.validateBoolean(prop, 1, messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+    }
+
+    @Test public void testValidateByte() {
+        final Property prop = new Property();
+        prop.setType(PropertyType.BYTE);
+
+        final List<String> messages = new ArrayList<>();
+
+        Validator.validateByte(prop, (byte)1, messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateByte(prop, "1", messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateByte(prop, "yes", messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+
+        Validator.validateByte(prop, 1, messages);
+        assertTrue(messages.isEmpty());
+    }
+
+    @Test public void testValidateShort() {
+        final Property prop = new Property();
+        prop.setType(PropertyType.SHORT);
+
+        final List<String> messages = new ArrayList<>();
+
+        Validator.validateShort(prop, (short)1, messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateShort(prop, "1", messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateShort(prop, "yes", messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+
+        Validator.validateShort(prop, 1, messages);
+        assertTrue(messages.isEmpty());
+    }
+
+    @Test public void testValidateInteger() {
+        final Property prop = new Property();
+        prop.setType(PropertyType.INTEGER);
+
+        final List<String> messages = new ArrayList<>();
+
+        Validator.validateInteger(prop, 1, messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateInteger(prop, "1", messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateInteger(prop, "yes", messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+
+        Validator.validateInteger(prop, 1, messages);
+        assertTrue(messages.isEmpty());
+    }
+
+    @Test public void testValidateLong() {
+        final Property prop = new Property();
+        prop.setType(PropertyType.LONG);
+
+        final List<String> messages = new ArrayList<>();
+
+        Validator.validateLong(prop, 1L, messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateLong(prop, "1", messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateLong(prop, "yes", messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+
+        Validator.validateLong(prop, 1, messages);
+        assertTrue(messages.isEmpty());
+    }
+
+    @Test public void testValidateFloat() {
+        final Property prop = new Property();
+        prop.setType(PropertyType.FLOAT);
+
+        final List<String> messages = new ArrayList<>();
+
+        Validator.validateFloat(prop, 1.1, messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateFloat(prop, "1.1", messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateFloat(prop, "yes", messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+
+        Validator.validateFloat(prop, 1, messages);
+        assertTrue(messages.isEmpty());
+    }
+
+    @Test public void testValidateDouble() {
+        final Property prop = new Property();
+        prop.setType(PropertyType.DOUBLE);
+
+        final List<String> messages = new ArrayList<>();
+
+        Validator.validateDouble(prop, 1.1d, messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateDouble(prop, "1.1", messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateDouble(prop, "yes", messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+
+        Validator.validateDouble(prop, 1, messages);
+        assertTrue(messages.isEmpty());
+    }
+
+    @Test public void testValidateChar() {
+        final Property prop = new Property();
+        prop.setType(PropertyType.CHARACTER);
+
+        final List<String> messages = new ArrayList<>();
+
+        Validator.validateCharacter(prop, 'x', messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateCharacter(prop, "y", messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateCharacter(prop, "yes", messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+    }
+
+    @Test public void testValidateUrl() {
+        final List<String> messages = new ArrayList<>();
+
+        Validator.validateURL(null, "https://sling.apache.org/documentation", messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateURL(null, "hello world", messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+    }
+
+    @Test public void testValidateEmail() {
+        final List<String> messages = new ArrayList<>();
+
+        Validator.validateEmail(null, "a@b.com", messages);
+        assertTrue(messages.isEmpty());
+
+        Validator.validateEmail(null, "hello world", messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+    }
+
+    @Test public void testValidatePassword() {
+        final Property prop = new Property();
+        final List<String> messages = new ArrayList<>();
+
+        Validator.validatePassword(prop, null, messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+
+        prop.setVariable("secret");
+        Validator.validatePassword(prop, null, messages);
+        assertTrue(messages.isEmpty());
+    }
+
+    @Test public void testValidateRange() {
+        final List<String> messages = new ArrayList<>();
+        final Property prop = new Property();
+
+        // no range set
+        Validator.validateRange(prop, 2, messages);
+        assertTrue(messages.isEmpty());
+
+        // empty range set
+        prop.setRange(new Range());
+        Validator.validateRange(prop, 2, messages);
+        assertTrue(messages.isEmpty());
+
+        // min set
+        prop.getRange().setMin(5);
+        Validator.validateRange(prop, 5, messages);
+        assertTrue(messages.isEmpty());
+        Validator.validateRange(prop, 6, messages);
+        assertTrue(messages.isEmpty());
+        Validator.validateRange(prop, 4, messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+
+        Validator.validateRange(prop, 5.0, messages);
+        assertTrue(messages.isEmpty());
+        Validator.validateRange(prop, 6.0, messages);
+        assertTrue(messages.isEmpty());
+        Validator.validateRange(prop, 4.0, messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+
+        // max set
+        prop.getRange().setMax(6);
+        Validator.validateRange(prop, 5, messages);
+        assertTrue(messages.isEmpty());
+        Validator.validateRange(prop, 6, messages);
+        assertTrue(messages.isEmpty());
+        Validator.validateRange(prop, 7, messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+
+        Validator.validateRange(prop, 5.0, messages);
+        assertTrue(messages.isEmpty());
+        Validator.validateRange(prop, 6.0, messages);
+        assertTrue(messages.isEmpty());
+        Validator.validateRange(prop, 7.0, messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+    }   
+    
+    @Test public void testValidateRegex() {
+        final List<String> messages = new ArrayList<>();
+        final Property prop = new Property();
+
+        // no regex
+        Validator.validateRegex(prop, "hello world", messages);
+        Validator.validateRegex(prop, "world", messages);
+        assertTrue(messages.isEmpty());
+
+        // regex
+        prop.setRegex("h(.*)");
+        Validator.validateRegex(prop, "hello world", messages);
+        assertTrue(messages.isEmpty());
+        Validator.validateRegex(prop, "world", messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+    }
+
+    @Test public void testValidateOptions() {
+        final List<String> messages = new ArrayList<>();
+        final Property prop = new Property();
+
+        // no options
+        Validator.validateOptions(prop, "foo", messages);
+        Validator.validateOptions(prop, "bar", messages);
+        assertTrue(messages.isEmpty());
+
+        // options
+        final List<Option> options = new ArrayList<>();
+        final Option o1 = new Option();
+        o1.setValue("foo");
+        final Option o2 = new Option();
+        o2.setValue("7");
+        options.add(o1);
+        options.add(o2);
+        prop.setOptions(options);
+        Validator.validateOptions(prop, "foo", messages);
+        assertTrue(messages.isEmpty());
+        Validator.validateOptions(prop, "bar", messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+        Validator.validateOptions(prop, 7, messages);
+        assertTrue(messages.isEmpty());
+    }
+    
+    @Test public void testValidateList() {
+        final List<String> messages = new ArrayList<>();
+        final Property prop = new Property();
+
+        final List<Object> values = new ArrayList<>();
+        values.add("a");
+        values.add("b");
+        values.add("c");
+
+        // default cardinality - no excludes/includes
+        Validator.validateList(prop, values, messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+
+        // cardinality 3 - no excludes/includes
+        prop.setCardinality(3);
+        Validator.validateList(prop, values, messages);
+        assertTrue(messages.isEmpty());
+
+        values.add("d");
+        Validator.validateList(prop, values, messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+
+        // excludes
+        prop.setExcludes(new String[] {"d", "e"});
+        Validator.validateList(prop, values, messages);
+        assertEquals(2, messages.size()); // cardinality and exclude
+        messages.clear();
+
+        values.remove("d");
+        Validator.validateList(prop, values, messages);
+        assertTrue(messages.isEmpty());
+
+        // includes
+        prop.setIncludes(new String[] {"b"});
+        Validator.validateList(prop, values, messages);
+        assertTrue(messages.isEmpty());
+
+        prop.setIncludes(new String[] {"x"});
+        Validator.validateList(prop, values, messages);
+        assertEquals(1, messages.size());
+        messages.clear();
+
+        values.add("x");
+        values.remove("a");
+        Validator.validateList(prop, values, messages);
+        assertTrue(messages.isEmpty());
+    }
+}