Improve network config validation errors to show which fields are invalid.
Previously, uploading invalid config results in a generic error message
which makes it difficult to figure out what is wrong with the config
Change-Id: I307d2fc0669679b067389c722556eef3aae098b9
diff --git a/core/api/src/main/java/org/onosproject/net/config/Config.java b/core/api/src/main/java/org/onosproject/net/config/Config.java
index b36b0e2..4aa3d8d 100644
--- a/core/api/src/main/java/org/onosproject/net/config/Config.java
+++ b/core/api/src/main/java/org/onosproject/net/config/Config.java
@@ -21,7 +21,6 @@
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
@@ -103,7 +102,7 @@
* Default implementation returns true.
* Subclasses are expected to override this with their own validation.
* Implementations are free to throw a RuntimeException if data is invalid.
- * * </p>
+ * </p>
*
* @return true if the data is valid; false otherwise
* @throws RuntimeException if configuration is invalid or completely foobar
@@ -114,11 +113,13 @@
// isString(path)
// isBoolean(path)
// isNumber(path, [min, max])
+ // isIntegralNumber(path, [min, max])
// isDecimal(path, [min, max])
// isMacAddress(path)
// isIpAddress(path)
// isIpPrefix(path)
// isConnectPoint(path)
+ // isTpPort(path)
return true;
}
@@ -153,8 +154,9 @@
/**
* Applies any configuration changes made via this configuration.
- *
+ * <p>
* Not effective for detached configs.
+ * </p>
*/
public void apply() {
checkState(delegate != null, "Cannot apply detached config");
@@ -414,7 +416,12 @@
*/
protected boolean hasOnlyFields(ObjectNode node, String... allowedFields) {
Set<String> fields = ImmutableSet.copyOf(allowedFields);
- return !Iterators.any(node.fieldNames(), f -> !fields.contains(f));
+ node.fieldNames().forEachRemaining(f -> {
+ if (!fields.contains(f)) {
+ throw new InvalidFieldException(f, "Field is not allowed");
+ }
+ });
+ return true;
}
/**
@@ -437,7 +444,12 @@
*/
protected boolean hasFields(ObjectNode node, String... mandatoryFields) {
Set<String> fields = ImmutableSet.copyOf(mandatoryFields);
- return Iterators.all(fields.iterator(), f -> !node.path(f).isMissingNode());
+ fields.forEach(f -> {
+ if (node.path(f).isMissingNode()) {
+ throw new InvalidFieldException(f, "Mandatory field is not present");
+ }
+ });
+ return true;
}
/**
@@ -446,12 +458,10 @@
* @param field JSON field name
* @param presence specifies if field is optional or mandatory
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid MAC
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isMacAddress(String field, FieldPresence presence) {
- JsonNode node = object.path(field);
- return isValid(node, presence, node.isTextual() &&
- MacAddress.valueOf(node.asText()) != null);
+ return isMacAddress(object, field, presence);
}
/**
@@ -462,12 +472,13 @@
* @param field JSON field name
* @param presence specifies if field is optional or mandatory
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid MAC
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isMacAddress(ObjectNode objectNode, String field, FieldPresence presence) {
- JsonNode node = objectNode.path(field);
- return isValid(node, presence, node.isTextual() &&
- MacAddress.valueOf(node.asText()) != null);
+ return isValid(objectNode, field, presence, n -> {
+ MacAddress.valueOf(n.asText());
+ return true;
+ });
}
/**
@@ -476,7 +487,7 @@
* @param field JSON field name
* @param presence specifies if field is optional or mandatory
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid IP
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isIpAddress(String field, FieldPresence presence) {
return isIpAddress(object, field, presence);
@@ -490,12 +501,13 @@
* @param field JSON field name
* @param presence specifies if field is optional or mandatory
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid IP
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isIpAddress(ObjectNode objectNode, String field, FieldPresence presence) {
- JsonNode node = objectNode.path(field);
- return isValid(node, presence, node.isTextual() &&
- IpAddress.valueOf(node.asText()) != null);
+ return isValid(objectNode, field, presence, n -> {
+ IpAddress.valueOf(n.asText());
+ return true;
+ });
}
/**
@@ -504,8 +516,7 @@
* @param field JSON field name
* @param presence specifies if field is optional or mandatory
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid IP
- * prefix
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isIpPrefix(String field, FieldPresence presence) {
return isIpPrefix(object, field, presence);
@@ -519,13 +530,13 @@
* @param field JSON field name
* @param presence specifies if field is optional or mandatory
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid IP
- * prefix
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isIpPrefix(ObjectNode objectNode, String field, FieldPresence presence) {
- JsonNode node = objectNode.path(field);
- return isValid(node, presence, node.isTextual() &&
- IpPrefix.valueOf(node.asText()) != null);
+ return isValid(objectNode, field, presence, n -> {
+ IpPrefix.valueOf(n.asText());
+ return true;
+ });
}
/**
@@ -534,7 +545,7 @@
* @param field JSON field name
* @param presence specifies if field is optional or mandatory
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid value
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isTpPort(String field, FieldPresence presence) {
return isTpPort(object, field, presence);
@@ -548,12 +559,13 @@
* @param field JSON field name
* @param presence specifies if field is optional or mandatory
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid value
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isTpPort(ObjectNode objectNode, String field, FieldPresence presence) {
- JsonNode node = objectNode.path(field);
- return isValid(node, presence, node.isNumber() &&
- TpPort.tpPort(node.asInt()) != null);
+ return isValid(objectNode, field, presence, n -> {
+ TpPort.tpPort(n.asInt());
+ return true;
+ });
}
/**
@@ -562,8 +574,7 @@
* @param field JSON field name
* @param presence specifies if field is optional or mandatory
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid
- * connect point string representation
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isConnectPoint(String field, FieldPresence presence) {
return isConnectPoint(object, field, presence);
@@ -577,13 +588,13 @@
* @param field JSON field name
* @param presence specifies if field is optional or mandatory
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid
- * connect point string representation
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isConnectPoint(ObjectNode objectNode, String field, FieldPresence presence) {
- JsonNode node = objectNode.path(field);
- return isValid(node, presence, node.isTextual() &&
- ConnectPoint.deviceConnectPoint(node.asText()) != null);
+ return isValid(objectNode, field, presence, n -> {
+ ConnectPoint.deviceConnectPoint(n.asText());
+ return true;
+ });
}
/**
@@ -593,7 +604,7 @@
* @param presence specifies if field is optional or mandatory
* @param pattern optional regex pattern
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid string
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isString(String field, FieldPresence presence, String... pattern) {
return isString(object, field, presence, pattern);
@@ -608,13 +619,17 @@
* @param presence specifies if field is optional or mandatory
* @param pattern optional regex pattern
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid string
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isString(ObjectNode objectNode, String field,
FieldPresence presence, String... pattern) {
- JsonNode node = objectNode.path(field);
- return isValid(node, presence, node.isTextual() &&
- (pattern.length > 0 && node.asText().matches(pattern[0]) || pattern.length < 1));
+ return isValid(objectNode, field, presence, (node) -> {
+ if (!(node.isTextual() &&
+ (pattern.length > 0 && node.asText().matches(pattern[0]) || pattern.length < 1))) {
+ fail("Invalid string value");
+ }
+ return true;
+ });
}
/**
@@ -624,28 +639,34 @@
* @param presence specifies if field is optional or mandatory
* @param minMax optional min/max values
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isNumber(String field, FieldPresence presence, long... minMax) {
- JsonNode node = object.path(field);
- return isValid(node, presence, node.isNumber() &&
- (minMax.length > 0 && minMax[0] <= node.asLong() || minMax.length < 1) &&
- (minMax.length > 1 && minMax[1] > node.asLong() || minMax.length < 2));
+ return isNumber(object, field, presence, minMax);
}
+
/**
- * Indicates whether the specified field holds a valid number.
+ * Indicates whether the specified field of a particular node holds a
+ * valid number.
*
+ * @param objectNode JSON object
* @param field JSON field name
* @param presence specifies if field is optional or mandatory
* @param minMax optional min/max values
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid
+ * @throws InvalidFieldException if the field is present but not valid
*/
- protected boolean isNumber(String field, FieldPresence presence, double... minMax) {
- JsonNode node = object.path(field);
- return isValid(node, presence, node.isNumber() &&
- (minMax.length > 0 && minMax[0] <= node.asDouble() || minMax.length < 1) &&
- (minMax.length > 1 && minMax[1] > node.asDouble() || minMax.length < 2));
+ protected boolean isNumber(ObjectNode objectNode, String field,
+ FieldPresence presence, long... minMax) {
+ return isValid(objectNode, field, presence, n -> {
+ long number = (n.isNumber()) ? n.asLong() : Long.parseLong(n.asText());
+ if (minMax.length > 1) {
+ verifyRange(number, minMax[0], minMax[1]);
+ } else if (minMax.length > 0) {
+ verifyRange(number, minMax[0]);
+ }
+ return true;
+ });
}
/**
@@ -655,7 +676,7 @@
* @param presence specifies if field is optional or mandatory
* @param minMax optional min/max values
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isIntegralNumber(String field, FieldPresence presence, long... minMax) {
return isIntegralNumber(object, field, presence, minMax);
@@ -670,16 +691,18 @@
* @param presence specifies if field is optional or mandatory
* @param minMax optional min/max values
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isIntegralNumber(ObjectNode objectNode, String field,
FieldPresence presence, long... minMax) {
- JsonNode node = objectNode.path(field);
-
- return isValid(node, presence, n -> {
- long number = (node.isIntegralNumber()) ? n.asLong() : Long.parseLong(n.asText());
- return (minMax.length > 0 && minMax[0] <= number || minMax.length < 1) &&
- (minMax.length > 1 && minMax[1] > number || minMax.length < 2);
+ return isValid(objectNode, field, presence, n -> {
+ long number = (n.isIntegralNumber()) ? n.asLong() : Long.parseLong(n.asText());
+ if (minMax.length > 1) {
+ verifyRange(number, minMax[0], minMax[1]);
+ } else if (minMax.length > 0) {
+ verifyRange(number, minMax[0]);
+ }
+ return true;
});
}
@@ -690,13 +713,34 @@
* @param presence specifies if field is optional or mandatory
* @param minMax optional min/max values
* @return true if valid; false otherwise
- * @throws IllegalArgumentException if field is present, but not valid
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isDecimal(String field, FieldPresence presence, double... minMax) {
- JsonNode node = object.path(field);
- return isValid(node, presence, (node.isDouble() || node.isFloat()) &&
- (minMax.length > 0 && minMax[0] <= node.asDouble() || minMax.length < 1) &&
- (minMax.length > 1 && minMax[1] > node.asDouble() || minMax.length < 2));
+ return isDecimal(object, field, presence, minMax);
+ }
+
+ /**
+ * Indicates whether the specified field of a particular node holds a valid
+ * decimal number.
+ *
+ * @param objectNode JSON node
+ * @param field JSON field name
+ * @param presence specifies if field is optional or mandatory
+ * @param minMax optional min/max values
+ * @return true if valid; false otherwise
+ * @throws InvalidFieldException if the field is present but not valid
+ */
+ protected boolean isDecimal(ObjectNode objectNode, String field,
+ FieldPresence presence, double... minMax) {
+ return isValid(objectNode, field, presence, n -> {
+ double number = (n.isDouble()) ? n.asDouble() : Double.parseDouble(n.asText());
+ if (minMax.length > 1) {
+ verifyRange(number, minMax[0], minMax[1]);
+ } else if (minMax.length > 0) {
+ verifyRange(number, minMax[0]);
+ }
+ return true;
+ });
}
/**
@@ -705,6 +749,7 @@
* @param field JSON field name
* @param presence specifies if field is optional or mandatory
* @return true if valid; false otherwise
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isBoolean(String field, FieldPresence presence) {
return isBoolean(object, field, presence);
@@ -718,11 +763,15 @@
* @param field JSON field name
* @param presence specifies if field is optional or mandatory
* @return true if valid; false otherwise
+ * @throws InvalidFieldException if the field is present but not valid
*/
protected boolean isBoolean(ObjectNode objectNode, String field, FieldPresence presence) {
- JsonNode node = objectNode.path(field);
- return isValid(node, presence, node.isBoolean() ||
- (node.isTextual() && isBooleanString(node.asText())));
+ return isValid(objectNode, field, presence, n -> {
+ if (!(n.isBoolean() || (n.isTextual() && isBooleanString(n.asText())))) {
+ fail("Field is not a boolean value");
+ }
+ return true;
+ });
}
/**
@@ -737,39 +786,56 @@
}
/**
- * Indicates whether the node is present and of correct value or not
- * mandatory and absent.
+ * Indicates whether a field in the node is present and of correct value or
+ * not mandatory and absent.
*
- * @param node JSON node
- * @param presence specifies if field is optional or mandatory
- * @param correctValue true if the value is correct
- * @return true if the field is as expected
- */
- private boolean isValid(JsonNode node, FieldPresence presence, boolean correctValue) {
- return isValid(node, presence, n -> correctValue);
- }
-
- /**
- * Indicates whether the node is present and of correct value or not
- * mandatory and absent.
- *
- * @param node JSON node
+ * @param objectNode JSON object node containing field to validate
+ * @param field name of field to validate
* @param presence specified if field is optional or mandatory
* @param validationFunction function which can be used to verify if the
* node has the correct value
* @return true if the field is as expected
+ * @throws InvalidFieldException if the field is present but not valid
*/
- private boolean isValid(JsonNode node, FieldPresence presence,
+ private boolean isValid(ObjectNode objectNode, String field, FieldPresence presence,
Function<JsonNode, Boolean> validationFunction) {
+ JsonNode node = objectNode.path(field);
boolean isMandatory = presence == FieldPresence.MANDATORY;
- if (isMandatory && validationFunction.apply(node)) {
- return true;
+ if (isMandatory && node.isMissingNode()) {
+ throw new InvalidFieldException(field, "Mandatory field not present");
}
if (!isMandatory && (node.isNull() || node.isMissingNode())) {
return true;
}
- return validationFunction.apply(node);
+ try {
+ if (validationFunction.apply(node)) {
+ return true;
+ } else {
+ throw new InvalidFieldException(field, "Validation error");
+ }
+ } catch (IllegalArgumentException e) {
+ throw new InvalidFieldException(field, e);
+ }
}
+
+ private static void fail(String message) {
+ throw new IllegalArgumentException(message);
+ }
+
+ private static <N extends Comparable> void verifyRange(N num, N min) {
+ if (num.compareTo(min) < 0) {
+ fail("Field must be greater than " + min);
+ }
+ }
+
+ private static <N extends Comparable> void verifyRange(N num, N min, N max) {
+ verifyRange(num, min);
+
+ if (num.compareTo(max) > 0) {
+ fail("Field must be less than " + max);
+ }
+ }
+
}
diff --git a/core/api/src/main/java/org/onosproject/net/config/InvalidConfigException.java b/core/api/src/main/java/org/onosproject/net/config/InvalidConfigException.java
new file mode 100644
index 0000000..c46ef65
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/config/InvalidConfigException.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed 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.onosproject.net.config;
+
+/**
+ * Indicates an invalid configuration was supplied by the user.
+ */
+public class InvalidConfigException extends RuntimeException {
+
+ private final String subjectKey;
+ private final String subject;
+ private final String configKey;
+
+ /**
+ * Creates a new invalid config exception about a specific config.
+ *
+ * @param subjectKey config's subject key
+ * @param subject config's subject
+ * @param configKey config's config key
+ */
+ public InvalidConfigException(String subjectKey, String subject, String configKey) {
+ this(subjectKey, subject, configKey, null);
+ }
+
+ /**
+ * Creates a new invalid config exception about a specific config with an
+ * exception regarding the cause of the invalidity.
+ *
+ * @param subjectKey config's subject key
+ * @param subject config's subject
+ * @param configKey config's config key
+ * @param cause cause of the invalidity
+ */
+ public InvalidConfigException(String subjectKey, String subject, String configKey, Throwable cause) {
+ super(message(subjectKey, subject, configKey, cause), cause);
+ this.subjectKey = subjectKey;
+ this.subject = subject;
+ this.configKey = configKey;
+ }
+
+ /**
+ * Returns the subject key of the config.
+ *
+ * @return subject key
+ */
+ public String subjectKey() {
+ return subjectKey;
+ }
+
+ /**
+ * Returns the string representation of the subject of the config.
+ *
+ * @return subject
+ */
+ public String subject() {
+ return subject;
+ }
+
+ /**
+ * Returns the config key of the config.
+ *
+ * @return config key
+ */
+ public String configKey() {
+ return configKey;
+ }
+
+ private static String message(String subjectKey, String subject, String configKey, Throwable cause) {
+ String error = "Error parsing config " + subjectKey + "/" + subject + "/" + configKey;
+ if (cause != null) {
+ error = error + ": " + cause.getMessage();
+ }
+ return error;
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/config/InvalidFieldException.java b/core/api/src/main/java/org/onosproject/net/config/InvalidFieldException.java
new file mode 100644
index 0000000..f542dae
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/config/InvalidFieldException.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed 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.onosproject.net.config;
+
+/**
+ * Indicates a field of a configuration was invalid.
+ */
+public class InvalidFieldException extends RuntimeException {
+
+ private final String field;
+ private final String reason;
+
+ /**
+ * Creates a new invalid field exception about a given field.
+ *
+ * @param field field name
+ * @param reason reason the field is invalid
+ */
+ public InvalidFieldException(String field, String reason) {
+ super(message(field, reason));
+ this.field = field;
+ this.reason = reason;
+ }
+
+ /**
+ * Creates a new invalid field exception about a given field.
+ *
+ * @param field field name
+ * @param cause throwable that occurred while trying to validate field
+ */
+ public InvalidFieldException(String field, Throwable cause) {
+ super(message(field, cause.getMessage()));
+ this.field = field;
+ this.reason = cause.getMessage();
+ }
+
+ /**
+ * Returns the field name.
+ *
+ * @return field name
+ */
+ public String field() {
+ return field;
+ }
+
+ /**
+ * Returns the reason the field failed to validate.
+ *
+ * @return reason
+ */
+ public String reason() {
+ return reason;
+ }
+
+ private static String message(String field, String reason) {
+ return "Field \"" + field + "\" is invalid: " + reason;
+ }
+
+}
diff --git a/core/api/src/test/java/org/onosproject/net/config/ConfigTest.java b/core/api/src/test/java/org/onosproject/net/config/ConfigTest.java
index 84dbca7..956befe 100644
--- a/core/api/src/test/java/org/onosproject/net/config/ConfigTest.java
+++ b/core/api/src/test/java/org/onosproject/net/config/ConfigTest.java
@@ -21,7 +21,6 @@
import org.junit.Before;
import org.junit.Test;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.onosproject.net.config.Config.FieldPresence.MANDATORY;
import static org.onosproject.net.config.Config.FieldPresence.OPTIONAL;
@@ -37,8 +36,15 @@
private static final String TEXT = "text";
private static final String LONG = "long";
private static final String DOUBLE = "double";
+ private static final String BOOLEAN = "boolean";
private static final String MAC = "mac";
+ private static final String BAD_MAC = "badMac";
private static final String IP = "ip";
+ private static final String BAD_IP = "badIp";
+ private static final String PREFIX = "prefix";
+ private static final String BAD_PREFIX = "badPrefix";
+ private static final String CONNECT_POINT = "connectPoint";
+ private static final String BAD_CONNECT_POINT = "badConnectPoint";
private static final String TP_PORT = "tpPort";
private static final String BAD_TP_PORT = "badTpPort";
@@ -52,7 +58,12 @@
public void setUp() {
json = new ObjectMapper().createObjectNode()
.put(TEXT, "foo").put(LONG, 5).put(DOUBLE, 0.5)
- .put(MAC, "ab:cd:ef:ca:fe:ed").put(IP, "12.34.56.78")
+ .put(BOOLEAN, "true")
+ .put(MAC, "ab:cd:ef:ca:fe:ed").put(BAD_MAC, "ab:cd:ef:ca:fe.ed")
+ .put(IP, "12.34.56.78").put(BAD_IP, "12.34-56.78")
+ .put(PREFIX, "12.34.56.78/18").put(BAD_PREFIX, "12.34.56.78-18")
+ .put(CONNECT_POINT, "of:0000000000000001/1")
+ .put(BAD_CONNECT_POINT, "of:0000000000000001-1")
.put(TP_PORT, 65535).put(BAD_TP_PORT, 65536);
cfg = new TestConfig();
cfg.init(SUBJECT, KEY, json, mapper, delegate);
@@ -60,16 +71,20 @@
@Test
public void hasOnlyFields() {
- assertTrue("has unexpected fields", cfg.hasOnlyFields(TEXT, LONG, DOUBLE, MAC, IP,
- TP_PORT, BAD_TP_PORT));
- assertFalse("did not detect unexpected fields", cfg.hasOnlyFields(TEXT, LONG, DOUBLE, MAC));
- assertTrue("is not proper text", cfg.isString(TEXT, MANDATORY));
+ assertTrue("has unexpected fields",
+ cfg.hasOnlyFields(TEXT, LONG, DOUBLE, BOOLEAN, MAC, BAD_MAC,
+ IP, BAD_IP, PREFIX, BAD_PREFIX,
+ CONNECT_POINT, BAD_CONNECT_POINT, TP_PORT, BAD_TP_PORT));
+ assertTrue("did not detect unexpected fields",
+ expectInvalidField(() -> cfg.hasOnlyFields(TEXT, LONG, DOUBLE, MAC)));
}
@Test
public void hasFields() {
- assertTrue("does not have mandatory field", cfg.hasFields(TEXT, LONG, DOUBLE, MAC));
- assertFalse("did not detect missing field", cfg.hasFields("none"));
+ assertTrue("does not have mandatory field",
+ cfg.hasFields(TEXT, LONG, DOUBLE, MAC));
+ assertTrue("did not detect missing field",
+ expectInvalidField(() -> cfg.hasFields("none")));
}
@Test
@@ -79,7 +94,10 @@
assertTrue("is not proper text", cfg.isString(TEXT, OPTIONAL, "^f.*"));
assertTrue("is not proper text", cfg.isString(TEXT, OPTIONAL));
assertTrue("is not proper text", cfg.isString("none", OPTIONAL));
- assertFalse("did not detect missing field", cfg.isString("none", MANDATORY));
+ assertTrue("did not detect missing field",
+ expectInvalidField(() -> cfg.isString("none", MANDATORY)));
+ assertTrue("did not detect bad text",
+ expectInvalidField(() -> cfg.isString(TEXT, OPTIONAL, "^b.*")));
}
@Test
@@ -88,12 +106,41 @@
assertTrue("is not proper number", cfg.isNumber(LONG, MANDATORY, 0));
assertTrue("is not proper number", cfg.isNumber(LONG, MANDATORY, 0, 10));
assertTrue("is not proper number", cfg.isNumber(LONG, MANDATORY, 5, 6));
- assertFalse("is not in range", cfg.isNumber(LONG, MANDATORY, 6, 10));
- assertFalse("is not in range", cfg.isNumber(LONG, MANDATORY, 4, 5));
+ assertTrue("is not in range",
+ expectInvalidField(() -> cfg.isNumber(LONG, MANDATORY, 6, 10)));
+ assertTrue("is not in range", cfg.isNumber(LONG, MANDATORY, 4, 5));
assertTrue("is not proper number", cfg.isNumber(LONG, OPTIONAL, 0, 10));
assertTrue("is not proper number", cfg.isNumber(LONG, OPTIONAL));
assertTrue("is not proper number", cfg.isNumber("none", OPTIONAL));
- assertFalse("did not detect missing field", cfg.isNumber("none", MANDATORY));
+ assertTrue("did not detect missing field",
+ expectInvalidField(() -> cfg.isNumber("none", MANDATORY)));
+ assertTrue("is not proper number",
+ expectInvalidField(() -> cfg.isNumber(TEXT, MANDATORY)));
+
+ assertTrue("is not proper number", cfg.isNumber(DOUBLE, MANDATORY, 0, 1));
+ assertTrue("is not in range",
+ expectInvalidField(() -> cfg.isNumber(DOUBLE, MANDATORY, 1, 2)));
+ }
+
+ @Test
+ public void isIntegralNumber() {
+ assertTrue("is not proper number", cfg.isIntegralNumber(LONG, MANDATORY));
+ assertTrue("is not proper number", cfg.isIntegralNumber(LONG, MANDATORY, 0));
+ assertTrue("is not proper number", cfg.isIntegralNumber(LONG, MANDATORY, 0, 10));
+ assertTrue("is not proper number", cfg.isIntegralNumber(LONG, MANDATORY, 5, 6));
+ assertTrue("is not in range",
+ expectInvalidField(() -> cfg.isIntegralNumber(LONG, MANDATORY, 6, 10)));
+ assertTrue("is not in range", cfg.isIntegralNumber(LONG, MANDATORY, 4, 5));
+ assertTrue("is not proper number", cfg.isIntegralNumber(LONG, OPTIONAL, 0, 10));
+ assertTrue("is not proper number", cfg.isIntegralNumber(LONG, OPTIONAL));
+ assertTrue("is not proper number", cfg.isIntegralNumber("none", OPTIONAL));
+ assertTrue("did not detect missing field",
+ expectInvalidField(() -> cfg.isIntegralNumber("none", MANDATORY)));
+ assertTrue("is not proper number",
+ expectInvalidField(() -> cfg.isIntegralNumber(TEXT, MANDATORY)));
+
+ assertTrue("is not in range",
+ expectInvalidField(() -> cfg.isIntegralNumber(DOUBLE, MANDATORY, 0, 10)));
}
@Test
@@ -102,12 +149,26 @@
assertTrue("is not proper decimal", cfg.isDecimal(DOUBLE, MANDATORY, 0.0));
assertTrue("is not proper decimal", cfg.isDecimal(DOUBLE, MANDATORY, 0.0, 1.0));
assertTrue("is not proper decimal", cfg.isDecimal(DOUBLE, MANDATORY, 0.5, 0.6));
- assertFalse("is not in range", cfg.isDecimal(DOUBLE, MANDATORY, 0.6, 1.0));
- assertFalse("is not in range", cfg.isDecimal(DOUBLE, MANDATORY, 0.4, 0.5));
+ assertTrue("is not proper decimal", cfg.isDecimal(DOUBLE, MANDATORY, 0.4, 0.5));
+ assertTrue("is not in range",
+ expectInvalidField(() -> cfg.isDecimal(DOUBLE, MANDATORY, 0.6, 1.0)));
+ assertTrue("is not in range",
+ expectInvalidField(() -> cfg.isDecimal(DOUBLE, MANDATORY, 0.3, 0.4)));
assertTrue("is not proper decimal", cfg.isDecimal(DOUBLE, OPTIONAL, 0.0, 1.0));
assertTrue("is not proper decimal", cfg.isDecimal(DOUBLE, OPTIONAL));
assertTrue("is not proper decimal", cfg.isDecimal("none", OPTIONAL));
- assertFalse("did not detect missing field", cfg.isDecimal("none", MANDATORY));
+ assertTrue("did not detect missing field",
+ expectInvalidField(() -> cfg.isDecimal("none", MANDATORY)));
+ }
+
+ @Test
+ public void isBoolean() {
+ assertTrue("is not proper boolean", cfg.isBoolean(BOOLEAN, MANDATORY));
+ assertTrue("did not detect missing field",
+ expectInvalidField(() -> cfg.isBoolean("none", MANDATORY)));
+ assertTrue("is not proper boolean", cfg.isBoolean("none", OPTIONAL));
+ assertTrue("did not detect bad boolean",
+ expectInvalidField(() -> cfg.isBoolean(TEXT, MANDATORY)));
}
@Test
@@ -115,26 +176,43 @@
assertTrue("is not proper mac", cfg.isMacAddress(MAC, MANDATORY));
assertTrue("is not proper mac", cfg.isMacAddress(MAC, OPTIONAL));
assertTrue("is not proper mac", cfg.isMacAddress("none", OPTIONAL));
- assertFalse("did not detect missing field", cfg.isMacAddress("none", MANDATORY));
+ assertTrue("did not detect missing field",
+ expectInvalidField(() -> cfg.isMacAddress("none", MANDATORY)));
+ assertTrue("did not detect bad ip",
+ expectInvalidField(() -> cfg.isMacAddress(BAD_MAC, MANDATORY)));
}
- @Test(expected = IllegalArgumentException.class)
- public void badMacAddress() {
- assertTrue("is not proper mac", cfg.isMacAddress(TEXT, MANDATORY));
- }
-
-
@Test
public void isIpAddress() {
assertTrue("is not proper ip", cfg.isIpAddress(IP, MANDATORY));
assertTrue("is not proper ip", cfg.isIpAddress(IP, OPTIONAL));
assertTrue("is not proper ip", cfg.isIpAddress("none", OPTIONAL));
- assertFalse("did not detect missing field", cfg.isMacAddress("none", MANDATORY));
+ assertTrue("did not detect missing ip",
+ expectInvalidField(() -> cfg.isIpAddress("none", MANDATORY)));
+ assertTrue("did not detect bad ip",
+ expectInvalidField(() -> cfg.isIpAddress(BAD_IP, MANDATORY)));
}
- @Test(expected = IllegalArgumentException.class)
- public void badIpAddress() {
- assertTrue("is not proper ip", cfg.isIpAddress(TEXT, MANDATORY));
+ @Test
+ public void isIpPrefix() {
+ assertTrue("is not proper prefix", cfg.isIpPrefix(PREFIX, MANDATORY));
+ assertTrue("is not proper prefix", cfg.isIpPrefix(PREFIX, OPTIONAL));
+ assertTrue("is not proper prefix", cfg.isIpPrefix("none", OPTIONAL));
+ assertTrue("did not detect missing prefix",
+ expectInvalidField(() -> cfg.isIpPrefix("none", MANDATORY)));
+ assertTrue("did not detect bad prefix",
+ expectInvalidField(() -> cfg.isIpPrefix(BAD_PREFIX, MANDATORY)));
+ }
+
+ @Test
+ public void isConnectPoint() {
+ assertTrue("is not proper connectPoint", cfg.isConnectPoint(CONNECT_POINT, MANDATORY));
+ assertTrue("is not proper connectPoint", cfg.isConnectPoint(CONNECT_POINT, OPTIONAL));
+ assertTrue("is not proper connectPoint", cfg.isConnectPoint("none", OPTIONAL));
+ assertTrue("did not detect missing connectPoint",
+ expectInvalidField(() -> cfg.isConnectPoint("none", MANDATORY)));
+ assertTrue("did not detect bad connectPoint",
+ expectInvalidField(() -> cfg.isConnectPoint(BAD_CONNECT_POINT, MANDATORY)));
}
@Test
@@ -142,16 +220,28 @@
assertTrue("is not proper transport port", cfg.isTpPort(TP_PORT, MANDATORY));
assertTrue("is not proper transport port", cfg.isTpPort(TP_PORT, OPTIONAL));
assertTrue("is not proper transport port", cfg.isTpPort("none", OPTIONAL));
- assertFalse("did not detect missing field", cfg.isTpPort("none", MANDATORY));
+ assertTrue("did not detect missing field",
+ expectInvalidField(() -> cfg.isTpPort("none", MANDATORY)));
+ assertTrue("is not proper transport port",
+ expectInvalidField(() -> cfg.isTpPort(BAD_TP_PORT, MANDATORY)));
}
- @Test(expected = IllegalArgumentException.class)
- public void badTpPort() {
- assertTrue("is not proper transport port", cfg.isTpPort(BAD_TP_PORT, MANDATORY));
+ /**
+ * Expects an InvalidFieldException to be thrown when the given runnable is
+ * run.
+ *
+ * @param runnable runnable to run
+ * @return true if an InvalidFieldException was thrown, otherwise false
+ */
+ private boolean expectInvalidField(Runnable runnable) {
+ try {
+ runnable.run();
+ return false;
+ } catch (InvalidFieldException e) {
+ return true;
+ }
}
- // TODO: Add tests for other helper methods
-
private class TestConfig extends Config<String> {
}
@@ -160,4 +250,4 @@
public void onApply(Config config) {
}
}
-}
\ No newline at end of file
+}