From: Maros Marsalek Date: Thu, 18 Apr 2013 10:32:54 +0000 (+0200) Subject: Yang validation moved to validator package and validation listener refactored. X-Git-Tag: releasepom-0.1.0~523^2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=c073bcd7e39c1b393d2c2612bbd57334d10294f7 Yang validation moved to validator package and validation listener refactored. Added validations for yang statements according to RFC-6020 (section 6 and 7). Added new tests for statement validation and utility classes created. This is only initial validation performed on every yang file without any context from other yang files. Signed-off-by: Maros Marsalek --- diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/pom.xml b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/pom.xml index add138e8b9..ce0543dafd 100644 --- a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/pom.xml +++ b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/pom.xml @@ -54,7 +54,13 @@ org.mockito mockito-all 1.8.4 - + + + com.google.guava + guava + 14.0.1 + + diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelParserImpl.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelParserImpl.java index e2ebea7f88..af972f08a5 100644 --- a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelParserImpl.java +++ b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelParserImpl.java @@ -78,6 +78,7 @@ import org.opendaylight.controller.yang.model.parser.util.YangParseException; import org.opendaylight.controller.yang.model.util.ExtendedType; import org.opendaylight.controller.yang.model.util.IdentityrefType; import org.opendaylight.controller.yang.model.util.UnknownType; +import org.opendaylight.controller.yang.model.validator.YangModelBasicValidator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -161,19 +162,8 @@ public class YangModelParserImpl implements YangModelParser { final List trees = parseStreams(yangFiles); final ModuleBuilder[] builders = new ModuleBuilder[trees.size()]; - // validation - // if validation fails with any file, do not continue and throw - // exception - for (int i = 0; i < trees.size(); i++) { - try { - final YangModelValidationListener yangModelParser = new YangModelValidationListener(); - walker.walk(yangModelParser, trees.get(i)); - } catch (IllegalStateException e) { - // wrap exception to add information about which file failed - throw new YangValidationException( - "Yang validation failed for file" + yangFiles[i], e); - } - } + // validate yang + new YangModelBasicValidator(walker).validate(trees); YangModelParserListenerImpl yangModelParser = null; for (int i = 0; i < trees.size(); i++) { diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelParserListenerImpl.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelParserListenerImpl.java index 76eafaa26d..eacc5460b3 100644 --- a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelParserListenerImpl.java +++ b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelParserListenerImpl.java @@ -78,8 +78,8 @@ import org.opendaylight.controller.yang.model.util.YangTypesConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -final class YangModelParserListenerImpl extends YangParserBaseListener { - +public final class YangModelParserListenerImpl extends YangParserBaseListener { + private static final Logger logger = LoggerFactory .getLogger(YangModelParserListenerImpl.class); @@ -90,7 +90,7 @@ final class YangModelParserListenerImpl extends YangParserBaseListener { private String yangModelPrefix; private Date revision = new Date(0L); - final static DateFormat simpleDateFormat = new SimpleDateFormat( + public final static DateFormat simpleDateFormat = new SimpleDateFormat( "yyyy-MM-dd"); private final Stack actualPath = new Stack(); diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListener.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListener.java deleted file mode 100644 index 68887a1915..0000000000 --- a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListener.java +++ /dev/null @@ -1,414 +0,0 @@ -/* - * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.controller.yang.model.parser.impl; - -import java.net.URI; -import java.net.URISyntaxException; -import java.text.DateFormat; -import java.text.ParseException; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Pattern; - -import org.antlr.v4.runtime.tree.ParseTree; -import org.opendaylight.controller.antlrv4.code.gen.YangParser; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Belongs_to_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Import_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Include_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_header_stmtsContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Namespace_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Prefix_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_date_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtsContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_header_stmtsContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Yang_version_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParserBaseListener; -import org.opendaylight.controller.yang.model.parser.util.YangModelBuilderUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Validation listener that validates yang statements according to RFC-6020. - * This validator expects only one module or submodule per file. - */ - -/* - * TODO is this assumption(module per file) correct ? if so, should a check be - * performed ? - * - * TODO break into smaller classes e.g. class for header statements, body - * statements... - */ -final class YangModelValidationListener extends YangParserBaseListener { - - private static final Logger logger = LoggerFactory - .getLogger(YangModelValidationListener.class); - - private final Set uniquePrefixes; - private final Set uniqueImports; - private final Set uniqueIncludes; - - public YangModelValidationListener() { - super(); - uniquePrefixes = new HashSet(); - uniqueImports = new HashSet(); - uniqueIncludes = new HashSet(); - } - - /** - * Rules: - *
    - *
  1. Identifier contains only permitted characters
  2. - *
  3. One revision statements present
  4. - *
  5. One header statements present
  6. - *
- */ - @Override - public void enterModule_stmt(YangParser.Module_stmtContext ctx) { - String moduleName = getName(ctx); - - checkIdentifier(moduleName, "Module"); - - checkPresentChildOfType(ctx, Revision_stmtsContext.class, - f("Missing revision statements in module:%s", moduleName), true); - - checkPresentChildOfType(ctx, Module_header_stmtsContext.class, - f("Missing header statements in module:%s", moduleName), true); - } - - /** - * Rules: - *
    - *
  1. Identifier contains only permitted characters
  2. - *
  3. One revision statements present
  4. - *
  5. One header statements present
  6. - *
- */ - @Override - public void enterSubmodule_stmt(Submodule_stmtContext ctx) { - String submoduleName = getName(ctx); - - checkIdentifier(submoduleName, "Submodule"); - - checkPresentChildOfType( - ctx, - Revision_stmtsContext.class, - f("Missing revision statements in submodule:%s", submoduleName), - true); - - checkPresentChildOfType(ctx, Submodule_header_stmtsContext.class, - f("Missing header statements in submodule:%s", submoduleName), - true); - } - - /** - * Rules: - *
    - *
  1. One Belongs-to statement present
  2. - *
- */ - @Override - public void enterSubmodule_header_stmts(Submodule_header_stmtsContext ctx) { - String submoduleName = getRootParentName(ctx); - - checkPresentChildOfType( - ctx, - Belongs_to_stmtContext.class, - f("Missing belongs-to statement in submodule:%s", submoduleName), - true); - - // check Yang version present, if not issue warning - checkYangVersion(ctx, submoduleName); - } - - /** - * Rules: - *
    - *
  1. Identifier contains only permitted characters
  2. - *
  3. One Prefix statement child
  4. - *
- */ - @Override - public void enterBelongs_to_stmt(Belongs_to_stmtContext ctx) { - String belongToName = getName(ctx); - String rootParentName = getRootParentName(ctx); - - checkIdentifier(belongToName, - f("In (sub)module:%s , Belongs-to statement", rootParentName)); - - checkPresentChildOfType( - ctx, - Prefix_stmtContext.class, - f("Missing prefix statement in belongs-to:%s, in (sub)module:%s", - belongToName, rootParentName), true); - } - - /** - * Rules: - *
    - *
  1. At least one Revision statement present
  2. - *
- */ - @Override - public void enterRevision_stmts(Revision_stmtsContext ctx) { - String rootParentName = getRootParentName(ctx); - - checkPresentChildOfType( - ctx, - Revision_stmtContext.class, - f("Missing at least one revision statement in (sub)module:%s", - rootParentName), false); - } - - /** - * Rules: - *
    - *
  1. One Namespace statement present
  2. - *
- */ - @Override - public void enterModule_header_stmts(Module_header_stmtsContext ctx) { - String moduleName = getRootParentName(ctx); - - checkPresentChildOfType(ctx, Namespace_stmtContext.class, - f("Missing namespace statement in module:%s", moduleName), true); - - // check Yang version present, if not issue warning - checkYangVersion(ctx, moduleName); - } - - /** - * Rules: - *
    - *
  1. Namespace string can be parsed as URI
  2. - *
- */ - @Override - public void enterNamespace_stmt(Namespace_stmtContext ctx) { - String namespaceName = getName(ctx); - String rootParentName = getRootParentName(ctx); - - try { - new URI(namespaceName); - } catch (URISyntaxException e) { - throw new YangValidationException(f( - "Namespace:%s in module:%s cannot be parsed as URI", - namespaceName, rootParentName)); - } - } - - /** - * Rules: - *
    - *
  1. Identifier contains only permitted characters
  2. - *
  3. Every import(identified by identifier) within a module/submodule is - * present only once
  4. - *
  5. One prefix statement child
  6. - *
  7. One revision-date statement child
  8. - *
- */ - @Override - public void enterImport_stmt(Import_stmtContext ctx) { - String importName = getName(ctx); - String rootParentName = getRootParentName(ctx); - - checkIdentifier(importName, - f("In (sub)module:%s , Import statement", rootParentName)); - - if (uniqueImports.contains(importName)) - throw new YangValidationException(f( - "Module:%s imported twice in (sub)module:%s", importName, - rootParentName)); - uniqueImports.add(importName); - - checkPresentChildOfType( - ctx, - Prefix_stmtContext.class, - f("Missing prefix statement in import:%s, in (sub)module:%s", - importName, rootParentName), true); - //checkPresentChildOfType( - // ctx, - // Revision_date_stmtContext.class, - // f("Missing revision-date statement in import:%s, in (sub)module:%s", - // importName, rootParentName), true); - } - - /** - * Rules: - *
    - *
  1. Date is in valid format
  2. - *
- */ - @Override - public void enterRevision_date_stmt(Revision_date_stmtContext ctx) { - String rootParentName = getRootParentName(ctx); - String exceptionMessage = f( - "Invalid date format for revision-date:%s in import/include statement:%s, in (sub)module:%s , expected date format is:%s", - getName(ctx), getRootParentName(ctx), rootParentName, - YangModelParserListenerImpl.simpleDateFormat.format(new Date())); - - validateDateFormat(getName(ctx), - YangModelParserListenerImpl.simpleDateFormat, exceptionMessage); - } - - /** - * Rules: - *
    - *
  1. Identifier contains only permitted characters
  2. - *
  3. Every include(identified by identifier) within a module/submodule is - * present only once
  4. - *
  5. One Revision-date statement child
  6. - *
- */ - @Override - public void enterInclude_stmt(Include_stmtContext ctx) { - String includeName = getName(ctx); - String rootParentName = getRootParentName(ctx); - - checkIdentifier(includeName, - f("In (sub)module:%s , Include statement", rootParentName)); - - if (uniqueIncludes.contains(includeName)) - throw new YangValidationException(f( - "Submodule:%s included twice in (sub)module:%s", - includeName, rootParentName)); - uniqueIncludes.add(includeName); - - checkPresentChildOfType( - ctx, - Revision_date_stmtContext.class, - f("Missing revision-date statement in include:%s, in (sub)module:%s", - includeName, rootParentName), true); - } - - static final String SUPPORTED_YANG_VERSION = "1"; - - /** - * Rules: - *
    - *
  1. Yang-version is specified as 1
  2. - *
- */ - @Override - public void enterYang_version_stmt(YangParser.Yang_version_stmtContext ctx) { - String version = getName(ctx); - String rootParentName = getRootParentName(ctx); - if (!version.equals(SUPPORTED_YANG_VERSION)) { - throw new YangValidationException( - f("Unsupported yang version:%s, in (sub)module:%s, supported version:%s", - version, rootParentName, SUPPORTED_YANG_VERSION)); - } - } - - /** - * Rules: - *
    - *
  1. Date is in valid format
  2. - *
- */ - @Override - public void enterRevision_stmt(YangParser.Revision_stmtContext ctx) { - String parentName = getRootParentName(ctx); - String exceptionMessage = f( - "Invalid date format for revision:%s in (sub)module:%s, expected date format is:%s", - getName(ctx), parentName, - YangModelParserListenerImpl.simpleDateFormat.format(new Date())); - - validateDateFormat(getName(ctx), - YangModelParserListenerImpl.simpleDateFormat, exceptionMessage); - } - - /** - * Rules: - *
    - *
  1. Identifier contains only permitted characters
  2. - *
  3. Every prefix(identified by identifier) within a module/submodule is - * presented only once
  4. - *
- */ - @Override - public void enterPrefix_stmt(Prefix_stmtContext ctx) { - String name = getName(ctx); - checkIdentifier( - name, - f("In module or import statement:%s , Prefix", - getRootParentName(ctx))); - - if (uniquePrefixes.contains(name)) - throw new YangValidationException(f( - "Not a unique prefix:%s, in (sub)module:%s", name, - getRootParentName(ctx))); - uniquePrefixes.add(name); - } - - private String getRootParentName(ParseTree ctx) { - ParseTree root = ctx; - while (root.getParent() != null) { - root = root.getParent(); - } - return getName(root); - } - - private static String getName(ParseTree child) { - return YangModelBuilderUtil.stringFromNode(child); - } - - private static String f(String base, Object... args) { - return String.format(base, args); - } - - private static void checkYangVersion(ParseTree ctx, String moduleName) { - if (!checkPresentChildOfType(ctx, Yang_version_stmtContext.class, true)) - logger.warn(f( - "Yang version statement not present in module:%s, Validating as yang version:%s", - moduleName, SUPPORTED_YANG_VERSION)); - } - - private static void validateDateFormat(String string, DateFormat format, - String message) { - try { - format.parse(string); - } catch (ParseException e) { - throw new YangValidationException(message); - } - } - - private static Pattern identifierPattern = Pattern - .compile("[a-zA-Z_][a-zA-Z0-9_.-]*"); - - static void checkIdentifier(String name, String messagePrefix) { - if (!identifierPattern.matcher(name).matches()) - throw new YangValidationException(f( - "%s identifier:%s is not in required format:%s", - messagePrefix, name, identifierPattern.toString())); - } - - private static void checkPresentChildOfType(ParseTree ctx, - Class expectedChildType, String message, boolean atMostOne) { - if (!checkPresentChildOfType(ctx, expectedChildType, atMostOne)) - throw new YangValidationException(message); - } - - private static boolean checkPresentChildOfType(ParseTree ctx, - Class expectedChildType, boolean atMostOne) { - - int count = 0; - - for (int i = 0; i < ctx.getChildCount(); i++) { - ParseTree child = ctx.getChild(i); - if (expectedChildType.isInstance(child)) - count++; - } - - return atMostOne ? count == 1 ? true : false : count != 0 ? true - : false; - } -} diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangValidationException.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/util/YangValidationException.java similarity index 78% rename from opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangValidationException.java rename to opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/util/YangValidationException.java index 43d39406de..25d317da12 100644 --- a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangValidationException.java +++ b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/util/YangValidationException.java @@ -5,13 +5,13 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.yang.model.parser.impl; +package org.opendaylight.controller.yang.model.parser.util; /** * Unchecked exception thrown if yang definition is not valid according to - * {@link YangModelValidationListener} + * {@link YangModelBasicValidationListener} */ -public class YangValidationException extends RuntimeException { +public final class YangValidationException extends RuntimeException { private static final long serialVersionUID = 7414330400390825381L; diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/BasicValidations.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/BasicValidations.java new file mode 100644 index 0000000000..f9e3fc2770 --- /dev/null +++ b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/BasicValidations.java @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/eplv10.html + */ +package org.opendaylight.controller.yang.model.validator; + +import java.text.DateFormat; +import java.text.ParseException; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.antlr.v4.runtime.tree.ParseTree; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Yang_version_stmtContext; +import org.opendaylight.controller.yang.model.parser.impl.YangModelParserListenerImpl; +import org.opendaylight.controller.yang.model.parser.util.YangValidationException; + +import com.google.common.collect.Sets; + +/** + * Reusable checks of basic constraints on yang statements + */ +final class BasicValidations { + + static final String SUPPORTED_YANG_VERSION = "1"; + + static void checkNotPresentBoth(ParseTree parent, + Class childType1, + Class childType2) { + if (BasicValidations.checkPresentChildOfTypeSafe(parent, childType1, + true) + && BasicValidations.checkPresentChildOfTypeSafe(parent, + childType2, false)) + ValidationUtil + .ex(ValidationUtil + .f("(In (sub)module:%s) Both %s and %s statement present in %s:%s", + ValidationUtil.getRootParentName(parent), + ValidationUtil + .getSimpleStatementName(childType1), + ValidationUtil + .getSimpleStatementName(childType2), + ValidationUtil + .getSimpleStatementName(parent + .getClass()), + ValidationUtil.getName(parent))); + } + + static void checkOnlyPermittedValues(ParseTree ctx, + Set permittedValues) { + String mandatory = ValidationUtil.getName(ctx); + String rootParentName = ValidationUtil.getRootParentName(ctx); + + if (!permittedValues.contains(mandatory)) + ValidationUtil + .ex(ValidationUtil + .f("(In (sub)module:%s) %s:%s, illegal value for %s statement, only permitted:%s", + rootParentName, ValidationUtil + .getSimpleStatementName(ctx + .getClass()), mandatory, + ValidationUtil.getSimpleStatementName(ctx + .getClass()), permittedValues)); + } + + static void checkUniquenessInNamespace(ParseTree stmt, Set uniques) { + String name = ValidationUtil.getName(stmt); + String rootParentName = ValidationUtil.getRootParentName(stmt); + + if (uniques.contains(name)) + ValidationUtil.ex(ValidationUtil.f( + "(In (sub)module:%s) %s:%s not unique in (sub)module", + rootParentName, + ValidationUtil.getSimpleStatementName(stmt.getClass()), + name)); + uniques.add(name); + } + + /** + * Check if only one module or submodule is present in session(one yang + * file) + */ + static void checkOnlyOneModulePresent(String moduleName, String globalId) { + if (globalId != null) + ValidationUtil.ex(ValidationUtil + .f("Multiple (sub)modules per file")); + } + + static void checkPresentYangVersion(ParseTree ctx, String moduleName) { + if (!checkPresentChildOfTypeSafe(ctx, Yang_version_stmtContext.class, + true)) + ValidationUtil + .ex(ValidationUtil + .f("Yang version statement not present in module:%s, Validating as yang version:%s", + moduleName, SUPPORTED_YANG_VERSION)); + } + + static void checkDateFormat(ParseTree stmt, DateFormat format) { + try { + format.parse(ValidationUtil.getName(stmt)); + } catch (ParseException e) { + String exceptionMessage = ValidationUtil + .f("(In (sub)module:%s) %s:%s, invalid date format expected date format is:%s", + ValidationUtil.getRootParentName(stmt), + ValidationUtil.getSimpleStatementName(stmt + .getClass()), ValidationUtil.getName(stmt), + YangModelParserListenerImpl.simpleDateFormat + .format(new Date())); + ValidationUtil.ex(exceptionMessage); + } + } + + static Pattern identifierPattern = Pattern + .compile("[a-zA-Z_][a-zA-Z0-9_.-]*"); + + static void checkIdentifier(ParseTree statement) { + checkIdentifierInternal(statement, ValidationUtil.getName(statement)); + } + + static void checkIdentifierInternal(ParseTree statement, String name) { + if (!identifierPattern.matcher(name).matches()) { + + String message = ValidationUtil + .f("%s statement identifier:%s is not in required format:%s", + ValidationUtil.getSimpleStatementName(statement + .getClass()), name, identifierPattern + .toString()); + String parent = ValidationUtil.getRootParentName(statement); + message = parent.equals(name) ? message : ValidationUtil.f( + "(In (sub)module:%s) %s", parent, message); + + ValidationUtil.ex(message); + } + } + + static Pattern prefixedIdentifierPattern = Pattern.compile("(.+):(.+)"); + + static void checkPrefixedIdentifier(ParseTree statement) { + checkPrefixedIdentifierInternal(statement, + ValidationUtil.getName(statement)); + } + + private static void checkPrefixedIdentifierInternal(ParseTree statement, + String id) { + Matcher matcher = prefixedIdentifierPattern.matcher(id); + + if (matcher.matches()) { + try { + // check prefix + checkIdentifierInternal(statement, matcher.group(1)); + // check ID + checkIdentifierInternal(statement, matcher.group(2)); + } catch (YangValidationException e) { + ValidationUtil.ex(ValidationUtil.f( + "Prefixed id:%s not in required format, details:%s", + id, e.getMessage())); + } + } else + checkIdentifierInternal(statement, id); + } + + static void checkSchemaNodeIdentifier(ParseTree statement) { + String id = ValidationUtil.getName(statement); + + try { + for (String oneOfId : id.split("/")) { + if (oneOfId.isEmpty()) + continue; + checkPrefixedIdentifierInternal(statement, oneOfId); + } + } catch (YangValidationException e) { + ValidationUtil.ex(ValidationUtil.f( + "Schema node id:%s not in required format, details:%s", id, + e.getMessage())); + } + } + + private static interface MessageProvider { + String getMessage(); + } + + static void checkPresentChildOfTypeInternal(ParseTree parent, + Set> expectedChildType, + MessageProvider message, boolean atMostOne) { + if (!checkPresentChildOfTypeSafe(parent, expectedChildType, atMostOne)) { + String str = atMostOne ? "(Expected exactly one statement) " + + message.getMessage() : message.getMessage(); + ValidationUtil.ex(str); + } + } + + static void checkPresentChildOfType(final ParseTree parent, + final Class expectedChildType, + boolean atMostOne) { + + // Construct message in checkPresentChildOfTypeInternal only if + // validaiton fails, not in advance + MessageProvider message = new MessageProvider() { + + @Override + public String getMessage() { + String message = ValidationUtil + .f("Missing %s statement in %s:%s", ValidationUtil + .getSimpleStatementName(expectedChildType), + ValidationUtil.getSimpleStatementName(parent + .getClass()), ValidationUtil + .getName(parent)); + + String root = ValidationUtil.getRootParentName(parent); + message = parent.equals(ValidationUtil + .getRootParentName(parent)) ? message : ValidationUtil + .f("(In (sub)module:%s) %s", root, message); + return message; + } + }; + + Set> expectedChildTypeSet = Sets + .newHashSet(); + expectedChildTypeSet.add(expectedChildType); + + checkPresentChildOfTypeInternal(parent, expectedChildTypeSet, message, + atMostOne); + } + + static void checkPresentChildOfTypes(final ParseTree parent, + final Set> expectedChildTypes, + boolean atMostOne) { + + // Construct message in checkPresentChildOfTypeInternal only if + // validaiton fails, not in advance + MessageProvider message = new MessageProvider() { + + @Override + public String getMessage() { + StringBuilder childTypes = new StringBuilder(); + String orStr = " OR "; + for (Class type : expectedChildTypes) { + childTypes.append(ValidationUtil + .getSimpleStatementName(type)); + childTypes.append(orStr); + } + + String message = ValidationUtil + .f("Missing %s statement in %s:%s", childTypes + .toString(), ValidationUtil + .getSimpleStatementName(parent.getClass()), + ValidationUtil.getName(parent)); + + String root = ValidationUtil.getRootParentName(parent); + message = parent.equals(ValidationUtil + .getRootParentName(parent)) ? message : ValidationUtil + .f("(In (sub)module:%s) %s", root, message); + + return message; + } + }; + + checkPresentChildOfTypeInternal(parent, expectedChildTypes, message, + atMostOne); + } + + static boolean checkPresentChildOfTypeSafe(ParseTree parent, + Set> expectedChildType, boolean atMostOne) { + + int foundChildrenOfType = ValidationUtil.countPresentChildrenOfType( + parent, expectedChildType); + + return atMostOne ? foundChildrenOfType == 1 ? true : false + : foundChildrenOfType != 0 ? true : false; + } + + static boolean checkPresentChildOfTypeSafe(ParseTree parent, + Class expectedChildType, boolean atMostOne) { + + int foundChildrenOfType = ValidationUtil.countPresentChildrenOfType( + parent, expectedChildType); + + return atMostOne ? foundChildrenOfType == 1 ? true : false + : foundChildrenOfType != 0 ? true : false; + } + + static List getAndCheckUniqueKeys(ParseTree ctx) { + String key = ValidationUtil.getName(ctx); + ParseTree parent = ctx.getParent(); + String rootParentName = ValidationUtil.getRootParentName(ctx); + + List keyList = ValidationUtil.listKeysFromId(key); + Set duplicates = ValidationUtil.getDuplicates(keyList); + + if (duplicates.size() != 0) + ValidationUtil.ex(ValidationUtil.f( + "(In (sub)module:%s) %s:%s, %s:%s contains duplicates:%s", + rootParentName, + ValidationUtil.getSimpleStatementName(parent.getClass()), + ValidationUtil.getName(parent), + ValidationUtil.getSimpleStatementName(ctx.getClass()), key, + duplicates)); + return keyList; + } +} diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/ValidationUtil.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/ValidationUtil.java new file mode 100644 index 0000000000..31c4b8a34b --- /dev/null +++ b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/ValidationUtil.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/eplv10.html + */ +package org.opendaylight.controller.yang.model.validator; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.antlr.v4.runtime.tree.ParseTree; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_stmtContext; +import org.opendaylight.controller.yang.model.parser.util.YangModelBuilderUtil; +import org.opendaylight.controller.yang.model.parser.util.YangValidationException; + +/** + * Validation utilities + */ +final class ValidationUtil { + + static void ex(String message) { + throw new YangValidationException(message); + } + + static Set getDuplicates(Collection keyList) { + Set all = new HashSet(); + Set duplicates = new HashSet(); + + for (String key : keyList) { + if (!all.add(key)) + duplicates.add(key); + } + return duplicates; + } + + static List listKeysFromId(String keys) { + return Arrays.asList(keys.split(" ")); + } + + static String getRootParentName(ParseTree ctx) { + ParseTree root = getRootParent(ctx); + return ValidationUtil.getName(root); + } + + private static ParseTree getRootParent(ParseTree ctx) { + ParseTree root = ctx; + while (root.getParent() != null) { + if (root.getClass().equals(Module_stmtContext.class) + || root.getClass().equals(Submodule_stmtContext.class)) + break; + root = root.getParent(); + } + return root; + } + + static String getName(ParseTree child) { + return YangModelBuilderUtil.stringFromNode(child); + } + + static String f(String base, Object... args) { + return String.format(base, args); + } + + /** + * Get simple name from statement class e.g. Module from Module_stmt_context + */ + static String getSimpleStatementName( + Class typeOfStatement) { + + String className = typeOfStatement.getSimpleName(); + int lastIndexOf = className.indexOf('$'); + className = lastIndexOf == -1 ? className : className + .substring(lastIndexOf + 1); + int indexOfStmt = className.indexOf("_stmt"); + int index = indexOfStmt == -1 ? className.indexOf("_arg") : indexOfStmt; + return className.substring(0, index).replace('_', '-'); + } + + static int countPresentChildrenOfType(ParseTree parent, + Set> expectedChildTypes) { + int foundChildrenOfType = 0; + + for (Class type : expectedChildTypes) { + foundChildrenOfType += countPresentChildrenOfType(parent, type); + } + return foundChildrenOfType; + } + + static int countPresentChildrenOfType(ParseTree parent, + Class expectedChildType) { + int foundChildrenOfType = 0; + + for (int i = 0; i < parent.getChildCount(); i++) { + ParseTree child = parent.getChild(i); + if (expectedChildType.isInstance(child)) + foundChildrenOfType++; + } + return foundChildrenOfType; + } + +} diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/YangModelBasicValidationListener.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/YangModelBasicValidationListener.java new file mode 100644 index 0000000000..37e2a215b3 --- /dev/null +++ b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/YangModelBasicValidationListener.java @@ -0,0 +1,672 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.yang.model.validator; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Set; + +import org.antlr.v4.runtime.tree.ParseTree; +import org.opendaylight.controller.antlrv4.code.gen.YangParser; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Anyxml_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Argument_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Augment_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Base_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Belongs_to_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Case_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Choice_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Config_argContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Container_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Default_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Deviate_add_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Deviation_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Extension_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Feature_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Grouping_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Identity_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.If_feature_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Import_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Include_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Key_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Leaf_list_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Leaf_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.List_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Mandatory_argContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Mandatory_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_header_stmtsContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Namespace_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Notification_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Ordered_by_argContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Prefix_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Refine_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_date_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Rpc_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Status_argContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_header_stmtsContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Type_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Typedef_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Unique_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Uses_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Yin_element_argContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParserBaseListener; +import org.opendaylight.controller.yang.model.parser.impl.YangModelParserListenerImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Sets; + +/** + * Validation listener that validates yang statements according to RFC-6020. + * This validator expects only one module or submodule per file and performs + * only basic validation where context from all yang models is not present. + */ +final class YangModelBasicValidationListener extends YangParserBaseListener { + + private static final Logger logger = LoggerFactory + .getLogger(YangModelBasicValidationListener.class); + + private final Set uniquePrefixes; + private final Set uniqueImports; + private final Set uniqueIncludes; + + private String globalModuleId; + + YangModelBasicValidationListener() { + super(); + uniquePrefixes = Sets.newHashSet(); + uniqueImports = Sets.newHashSet(); + uniqueIncludes = Sets.newHashSet(); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
  3. Header statements present(mandatory prefix and namespace statements + * are in header)
  4. + *
  5. Only one module or submodule per file
  6. + *
+ */ + @Override + public void enterModule_stmt(Module_stmtContext ctx) { + + BasicValidations.checkIdentifier(ctx); + + BasicValidations.checkPresentChildOfType(ctx, + Module_header_stmtsContext.class, true); + + String moduleName = ValidationUtil.getName(ctx); + BasicValidations.checkOnlyOneModulePresent(moduleName, globalModuleId); + globalModuleId = moduleName; + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
  3. Header statements present(mandatory belongs-to statement is in + * header)
  4. + *
  5. Only one module or submodule per file
  6. + *
+ */ + @Override + public void enterSubmodule_stmt(Submodule_stmtContext ctx) { + + BasicValidations.checkIdentifier(ctx); + + BasicValidations.checkPresentChildOfType(ctx, + Submodule_header_stmtsContext.class, true); + + String submoduleName = ValidationUtil.getName(ctx); + BasicValidations.checkOnlyOneModulePresent(submoduleName, + globalModuleId); + globalModuleId = submoduleName; + + } + + /** + * Constraints: + *
    + *
  1. One Belongs-to statement present
  2. + *
+ */ + @Override + public void enterSubmodule_header_stmts(Submodule_header_stmtsContext ctx) { + BasicValidations.checkPresentChildOfType(ctx, + Belongs_to_stmtContext.class, true); + + // check Yang version present, if not log + try { + BasicValidations.checkPresentYangVersion(ctx, + ValidationUtil.getRootParentName(ctx)); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + } + + /** + * Constraints: + *
    + *
  1. One Namespace statement present
  2. + *
  3. One Prefix statement present
  4. + *
+ */ + @Override + public void enterModule_header_stmts(Module_header_stmtsContext ctx) { + String moduleName = ValidationUtil.getRootParentName(ctx); + + BasicValidations.checkPresentChildOfType(ctx, + Namespace_stmtContext.class, true); + BasicValidations.checkPresentChildOfType(ctx, Prefix_stmtContext.class, + true); + + // check Yang version present, if not log + try { + BasicValidations.checkPresentYangVersion(ctx, moduleName); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + } + + /** + * Constraints: + *
    + *
  1. Date is in valid format
  2. + *
+ */ + @Override + public void enterRevision_stmt(Revision_stmtContext ctx) { + BasicValidations.checkDateFormat(ctx, + YangModelParserListenerImpl.simpleDateFormat); + + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
  3. One Prefix statement child
  4. + *
+ */ + @Override + public void enterBelongs_to_stmt(Belongs_to_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + + BasicValidations.checkPresentChildOfType(ctx, Prefix_stmtContext.class, + true); + } + + /** + * Constraints: + *
    + *
  1. Namespace string can be parsed as URI
  2. + *
+ */ + @Override + public void enterNamespace_stmt(Namespace_stmtContext ctx) { + String namespaceName = ValidationUtil.getName(ctx); + String rootParentName = ValidationUtil.getRootParentName(ctx); + + try { + new URI(namespaceName); + } catch (URISyntaxException e) { + ValidationUtil.ex(ValidationUtil.f( + "(In module:%s) Namespace:%s cannot be parsed as URI", + rootParentName, namespaceName)); + } + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
  3. Every import(identified by identifier) within a module/submodule is + * present only once
  4. + *
  5. One prefix statement child
  6. + *
+ */ + @Override + public void enterImport_stmt(Import_stmtContext ctx) { + + BasicValidations.checkIdentifier(ctx); + + BasicValidations.checkUniquenessInNamespace(ctx, uniqueImports); + + BasicValidations.checkPresentChildOfType(ctx, Prefix_stmtContext.class, + true); + + } + + /** + * Constraints: + *
    + *
  1. Date is in valid format
  2. + *
+ */ + @Override + public void enterRevision_date_stmt(Revision_date_stmtContext ctx) { + BasicValidations.checkDateFormat(ctx, + YangModelParserListenerImpl.simpleDateFormat); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
  3. Every include(identified by identifier) within a module/submodule is + * present only once
  4. + *
+ */ + @Override + public void enterInclude_stmt(Include_stmtContext ctx) { + + BasicValidations.checkIdentifier(ctx); + + BasicValidations.checkUniquenessInNamespace(ctx, uniqueIncludes); + } + + /** + * Constraints: + *
    + *
  1. Yang-version is specified as 1
  2. + *
+ */ + @Override + public void enterYang_version_stmt(YangParser.Yang_version_stmtContext ctx) { + String version = ValidationUtil.getName(ctx); + String rootParentName = ValidationUtil.getRootParentName(ctx); + if (!version.equals(BasicValidations.SUPPORTED_YANG_VERSION)) { + ValidationUtil + .ex(ValidationUtil + .f("(In (sub)module:%s) Unsupported yang version:%s, supported version:%s", + rootParentName, version, + BasicValidations.SUPPORTED_YANG_VERSION)); + } + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
  3. Every prefix(identified by identifier) within a module/submodule is + * presented only once
  4. + *
+ */ + @Override + public void enterPrefix_stmt(Prefix_stmtContext ctx) { + + BasicValidations.checkIdentifier(ctx); + + BasicValidations.checkUniquenessInNamespace(ctx, uniquePrefixes); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
  3. One type statement child
  4. + *
+ */ + @Override + public void enterTypedef_stmt(Typedef_stmtContext ctx) { + + BasicValidations.checkIdentifier(ctx); + + BasicValidations.checkPresentChildOfType(ctx, Type_stmtContext.class, + true); + } + + /** + * Constraints: + *
    + *
  1. (Prefix):Identifier is in required format
  2. + *
+ */ + @Override + public void enterType_stmt(Type_stmtContext ctx) { + BasicValidations.checkPrefixedIdentifier(ctx); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
+ */ + @Override + public void enterContainer_stmt(Container_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
  3. One type statement child
  4. + *
  5. Default statement must not be present if mandatory statement is
  6. + *
+ */ + @Override + public void enterLeaf_stmt(Leaf_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + + BasicValidations.checkPresentChildOfType(ctx, Type_stmtContext.class, + true); + + BasicValidations.checkNotPresentBoth(ctx, Mandatory_stmtContext.class, + Default_stmtContext.class); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
  3. One type statement child
  4. + *
+ */ + @Override + public void enterLeaf_list_stmt(Leaf_list_stmtContext ctx) { + + BasicValidations.checkIdentifier(ctx); + + BasicValidations.checkPresentChildOfType(ctx, Type_stmtContext.class, + true); + } + + private static final Set permittedOrderByArgs = Sets.newHashSet( + "system", "user"); + + /** + * Constraints: + *
    + *
  1. Value must be one of: system, user
  2. + *
+ */ + @Override + public void enterOrdered_by_arg(Ordered_by_argContext ctx) { + BasicValidations.checkOnlyPermittedValues(ctx, permittedOrderByArgs); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
+ */ + @Override + public void enterList_stmt(List_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + // TODO check: "if config==true then key must be present" could be + // performed + } + + /** + * Constraints: + *
    + *
  1. No duplicate keys
  2. + *
+ */ + @Override + public void enterKey_stmt(Key_stmtContext ctx) { + BasicValidations.getAndCheckUniqueKeys(ctx); + } + + /** + * Constraints: + *
    + * + *
+ */ + @Override + public void enterUnique_stmt(Unique_stmtContext ctx) { + BasicValidations.getAndCheckUniqueKeys(ctx); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
  3. Default statement must not be present if mandatory statement is
  4. + *
+ */ + @Override + public void enterChoice_stmt(Choice_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + + BasicValidations.checkNotPresentBoth(ctx, Mandatory_stmtContext.class, + Default_stmtContext.class); + + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
+ */ + @Override + public void enterCase_stmt(Case_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + } + + private static final Set permittedBooleanArgs = Sets.newHashSet( + "true", "false"); + + /** + * Constraints: + *
    + *
  1. Value must be one of: true, false
  2. + *
+ */ + @Override + public void enterMandatory_arg(Mandatory_argContext ctx) { + BasicValidations.checkOnlyPermittedValues(ctx, permittedBooleanArgs); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
+ */ + @Override + public void enterAnyxml_stmt(Anyxml_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
+ */ + @Override + public void enterGrouping_stmt(Grouping_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + } + + /** + * Constraints: + *
    + *
  1. (Prefix):Identifier is in required format
  2. + *
+ */ + @Override + public void enterUses_stmt(Uses_stmtContext ctx) { + BasicValidations.checkPrefixedIdentifier(ctx); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
+ */ + @Override + public void enterRefine_stmt(Refine_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
+ */ + @Override + public void enterRpc_stmt(Rpc_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
+ */ + @Override + public void enterNotification_stmt(Notification_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + } + + /** + * Constraints: + *
    + *
  1. Schema Node Identifier is in required format
  2. + *
+ */ + @Override + public void enterAugment_stmt(Augment_stmtContext ctx) { + BasicValidations.checkSchemaNodeIdentifier(ctx); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
+ */ + @Override + public void enterIdentity_stmt(Identity_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + } + + /** + * Constraints: + *
    + *
  1. (Prefix):Identifier is in required format
  2. + *
+ */ + @Override + public void enterBase_stmt(Base_stmtContext ctx) { + BasicValidations.checkPrefixedIdentifier(ctx); + + } + + /** + * Constraints: + *
    + *
  1. Value must be one of: true, false
  2. + *
+ */ + @Override + public void enterYin_element_arg(Yin_element_argContext ctx) { + BasicValidations.checkOnlyPermittedValues(ctx, permittedBooleanArgs); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
+ */ + @Override + public void enterExtension_stmt(Extension_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
+ */ + @Override + public void enterArgument_stmt(Argument_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + } + + /** + * Constraints: + *
    + *
  1. Identifier is in required format
  2. + *
+ */ + @Override + public void enterFeature_stmt(Feature_stmtContext ctx) { + BasicValidations.checkIdentifier(ctx); + + } + + /** + * Constraints: + *
    + *
  1. (Prefix):Identifier is in required format
  2. + *
+ */ + @Override + public void enterIf_feature_stmt(If_feature_stmtContext ctx) { + BasicValidations.checkPrefixedIdentifier(ctx); + } + + /** + * Constraints: + *
    + *
  1. Schema Node Identifier is in required format
  2. + *
  3. At least one deviate-* statement child
  4. + *
+ */ + @Override + public void enterDeviation_stmt(Deviation_stmtContext ctx) { + BasicValidations.checkSchemaNodeIdentifier(ctx); + + Set> types = Sets.newHashSet(); + types.add(Deviate_add_stmtContext.class); + types.add(Deviate_add_stmtContext.class); + BasicValidations.checkPresentChildOfTypes(ctx, types, false); + } + + /** + * Constraints: + *
    + *
  1. Value must be one of: true, false
  2. + *
+ */ + @Override + public void enterConfig_arg(Config_argContext ctx) { + BasicValidations.checkOnlyPermittedValues(ctx, permittedBooleanArgs); + } + + private static final Set permittedStatusArgs = Sets.newHashSet( + "current", "deprecated", "obsolete"); + + /** + * Constraints: + *
    + *
  1. Value must be one of: "current", "deprecated", "obsolete"
  2. + *
+ */ + @Override + public void enterStatus_arg(Status_argContext ctx) { + BasicValidations.checkOnlyPermittedValues(ctx, permittedStatusArgs); + } + +} diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/YangModelBasicValidator.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/YangModelBasicValidator.java new file mode 100644 index 0000000000..dc80dba014 --- /dev/null +++ b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/YangModelBasicValidator.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.yang.model.validator; + +import java.util.List; + +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.ParseTreeWalker; +import org.opendaylight.controller.yang.model.parser.util.YangValidationException; + +/** + * Exposed basic yang validation. + * + * Every file is validated using {@link YangModelBasicValidationListener}. + */ +public final class YangModelBasicValidator { + + private final ParseTreeWalker walker; + + public YangModelBasicValidator(ParseTreeWalker walker) { + this.walker = walker; + } + + public YangModelBasicValidator() { + this.walker = new ParseTreeWalker(); + } + + public void validate(List trees) { + for (int i = 0; i < trees.size(); i++) { + try { + final YangModelBasicValidationListener yangModelParser = new YangModelBasicValidationListener(); + walker.walk(yangModelParser, trees.get(i)); + } catch (YangValidationException e) { + // wrap exception to add information about which file failed + throw new YangValidationException( + "Yang validation failed for file" + e); + } + } + } + +} diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListenerTest_Module.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListenerTest_Module.java deleted file mode 100644 index 76715e864d..0000000000 --- a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListenerTest_Module.java +++ /dev/null @@ -1,315 +0,0 @@ -package org.opendaylight.controller.yang.model.parser.impl; - -import static org.hamcrest.core.Is.*; -import static org.junit.Assert.*; -import static org.junit.matchers.JUnitMatchers.*; -import static org.mockito.Mockito.*; - -import java.util.Date; - -import org.antlr.v4.runtime.tree.ParseTree; -import org.junit.Before; -import org.junit.Test; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Import_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Include_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_header_stmtsContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Namespace_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Prefix_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_date_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtsContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.StringContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Yang_version_stmtContext; - -public class YangModelValidationListenerTest_Module { - - private YangModelValidationListener valid; - private Module_stmtContext ctx; - - @Before - public void setUp() { - valid = new YangModelValidationListener(); - } - - @Test(expected = YangValidationException.class) - public void testRevisionInvalidDateFormat() { - Revision_stmtContext mockedRev = mockModuleWithRevision(2, "badFormat"); - - try { - valid.enterRevision_stmt(mockedRev); - } catch (YangValidationException e) { - assertThat( - e.getMessage(), - containsString("Invalid date format for revision:badFormat in (sub)module:module1, expected date format is:")); - throw e; - } - } - - private Revision_stmtContext mockModuleWithRevision(int moduleChildren, - String date) { - Revision_stmtContext mockedRev = mock(Revision_stmtContext.class); - doReturn(1).when(mockedRev).getChildCount(); - mockName(mockedRev, date); - - Revision_stmtsContext revs = mockRevisionsParent(2, mockedRev); - - mockModuleParent(moduleChildren, revs, "module1"); - return mockedRev; - } - - @Test - public void testRevisionValidDateFormat() { - Revision_stmtContext mockedRev = mockModuleWithRevision(2, - getFormattedDate()); - - valid.enterRevision_stmt(mockedRev); - } - - private String getFormattedDate() { - return YangModelParserListenerImpl.simpleDateFormat.format(new Date()); - } - - @Test(expected = YangValidationException.class) - public void testNoRevision() { - - Module_stmtContext ctx = mock(Module_stmtContext.class); - doReturn(1).when(ctx).getChildCount(); - mockName(ctx, "module1"); - - try { - valid.enterModule_stmt(ctx); - } catch (YangValidationException e) { - assertThat( - e.getMessage(), - containsString("Missing revision statements in module:module1")); - throw e; - } - } - - @Test(expected = YangValidationException.class) - public void testNoHeaderStmts() { - mockModuleWithRevision(2, "1999-4-5"); - - try { - valid.enterModule_stmt(ctx); - } catch (YangValidationException e) { - assertThat( - e.getMessage(), - containsString("Missing header statements in module:module1")); - throw e; - } - } - - @Test(expected = YangValidationException.class) - public void testNoNamespace() { - Module_header_stmtsContext header = mock(Module_header_stmtsContext.class); - mockModuleParent(2, header, "module1"); - - try { - valid.enterModule_header_stmts(header); - } catch (YangValidationException e) { - assertThat( - e.getMessage(), - containsString("Missing namespace statement in module:module1")); - throw e; - } - } - - @Test - public void testPrefixes() { - Prefix_stmtContext pref = mock(Prefix_stmtContext.class); - doReturn(1).when(pref).getChildCount(); - mockName(pref, "unique1"); - mockModuleParent(2, pref, "module1"); - valid.enterPrefix_stmt(pref); - - pref = mock(Prefix_stmtContext.class); - doReturn(1).when(pref).getChildCount(); - mockName(pref, "unique1"); - mockModuleParent(2, pref, "module1"); - - try { - valid.enterPrefix_stmt(pref); - } catch (Exception e) { - return; - } - - fail("Validation Exception should have occured"); - } - - @Test - public void testNamespace() { - Namespace_stmtContext namespace = mock(Namespace_stmtContext.class); - doReturn(1).when(namespace).getChildCount(); - mockName(namespace, "http://test.parsing.uri.com"); - mockModuleParent(2, namespace, "module1"); - valid.enterNamespace_stmt(namespace); - - namespace = mock(Namespace_stmtContext.class); - doReturn(1).when(namespace).getChildCount(); - mockName(namespace, "invalid uri"); - mockModuleParent(2, namespace, "module1"); - try { - valid.enterNamespace_stmt(namespace); - } catch (YangValidationException e) { - assertThat( - e.getMessage(), - containsString("Namespace:invalid uri in module:module1 cannot be parsed as URI")); - return; - } - - fail("Validation Exception should have occured"); - } - - @Test - public void testImports() { - Import_stmtContext impor = mockImport("unique1", "p1"); - mockModuleParent(2, impor, "module1"); - valid.enterImport_stmt(impor); - - impor = mockImport("unique1", "p2"); - mockModuleParent(2, impor, "module1"); - mockName(impor, "unique1"); - - try { - valid.enterImport_stmt(impor); - } catch (YangValidationException e) { - assertThat( - e.getMessage(), - containsString("Module:unique1 imported twice in (sub)module:module1")); - return; - } - - fail("Validation Exception should have occured"); - } - - @Test - public void testIncludes() { - Include_stmtContext impor = mockInclude("unique1"); - mockModuleParent(2, impor, "module1"); - valid.enterInclude_stmt(impor); - - impor = mockInclude("unique1"); - mockModuleParent(2, impor, "module1"); - mockName(impor, "unique1"); - - try { - valid.enterInclude_stmt(impor); - } catch (YangValidationException e) { - assertThat( - e.getMessage(), - containsString("Submodule:unique1 included twice in (sub)module:module1")); - return; - } - - fail("Validation Exception should have occured"); - } - - private Import_stmtContext mockImport(String name, String prefixName) { - Import_stmtContext impor = mock(Import_stmtContext.class); - doReturn(3).when(impor).getChildCount(); - Prefix_stmtContext prefix = mock(Prefix_stmtContext.class); - mockName(prefix, prefixName); - doReturn(prefix).when(impor).getChild(1); - Revision_date_stmtContext revDate = mock(Revision_date_stmtContext.class); - mockName(revDate, getFormattedDate()); - doReturn(revDate).when(impor).getChild(2); - mockName(impor, name); - return impor; - } - - private Include_stmtContext mockInclude(String name) { - Include_stmtContext impor = mock(Include_stmtContext.class); - doReturn(2).when(impor).getChildCount(); - Revision_date_stmtContext revDate = mock(Revision_date_stmtContext.class); - mockName(revDate, getFormattedDate()); - doReturn(revDate).when(impor).getChild(1); - mockName(impor, name); - return impor; - } - - @Test(expected = YangValidationException.class) - public void testInvalidYangVersion() { - - Yang_version_stmtContext yangVersion = mock(Yang_version_stmtContext.class); - doReturn(1).when(yangVersion).getChildCount(); - mockName(yangVersion, "55Unsup"); - - mockModuleParent(2, yangVersion, "module1"); - - try { - valid.enterYang_version_stmt(yangVersion); - } catch (YangValidationException e) { - assertThat( - e.getMessage(), - containsString("Unsupported yang version:55Unsup, in (sub)module:module1, supported version:" - + YangModelValidationListener.SUPPORTED_YANG_VERSION)); - throw e; - } - } - - private void mockModuleParent(int moduleChildren, ParseTree child, - String moduleName) { - ctx = mock(Module_stmtContext.class); - doReturn(moduleChildren).when(ctx).getChildCount(); - mockName(ctx, moduleName); - doReturn(child).when(ctx).getChild(1); - doReturn(ctx).when(child).getParent(); - } - - static Revision_stmtsContext mockRevisionsParent(int moduleChildren, - Revision_stmtContext mockedRev) { - Revision_stmtsContext revs = mock(Revision_stmtsContext.class); - doReturn(moduleChildren).when(revs).getChildCount(); - doReturn(mockedRev).when(revs).getChild(1); - doReturn(revs).when(mockedRev).getParent(); - return revs; - } - - @Test - public void testValidYangVersion() { - - Yang_version_stmtContext ctx = mock(Yang_version_stmtContext.class); - doReturn(1).when(ctx).getChildCount(); - mockName(ctx, "1"); - - valid.enterYang_version_stmt(ctx); - } - - @Test - public void testIdentifierMatching() { - YangModelValidationListener.checkIdentifier("_ok98-.87.-.8...88-asdAD", - null); - YangModelValidationListener.checkIdentifier("AA.bcd", null); - YangModelValidationListener.checkIdentifier("a", null); - - int thrown = 0; - - try { - YangModelValidationListener.checkIdentifier("9aa", null); - } catch (YangValidationException e) { - thrown++; - } - try { - YangModelValidationListener.checkIdentifier("-", null); - } catch (YangValidationException e) { - thrown++; - } - try { - YangModelValidationListener.checkIdentifier(".", null); - } catch (YangValidationException e) { - thrown++; - } - - assertThat(thrown, is(3)); - } - - static void mockName(ParseTree mockedRev, String name) { - StringContext nameCtx = mock(StringContext.class); - ParseTree internalName = mock(ParseTree.class); - doReturn(name).when(internalName).getText(); - doReturn(internalName).when(nameCtx).getChild(0); - doReturn(nameCtx).when(mockedRev).getChild(0); - } -} diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListenerTest_SubModule.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListenerTest_SubModule.java deleted file mode 100644 index 056e869db8..0000000000 --- a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListenerTest_SubModule.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.opendaylight.controller.yang.model.parser.impl; - -import static org.junit.Assert.*; -import static org.junit.matchers.JUnitMatchers.*; -import static org.mockito.Mockito.*; - -import org.antlr.v4.runtime.tree.ParseTree; -import org.junit.Before; -import org.junit.Test; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Belongs_to_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Prefix_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtsContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_header_stmtsContext; -import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_stmtContext; - -public class YangModelValidationListenerTest_SubModule { - - private YangModelValidationListener valid; - private Submodule_stmtContext ctx; - - @Before - public void setUp() { - valid = new YangModelValidationListener(); - } - - @Test(expected = YangValidationException.class) - public void testNoRevision() { - - Submodule_stmtContext ctx = mock(Submodule_stmtContext.class); - doReturn(1).when(ctx).getChildCount(); - YangModelValidationListenerTest_Module.mockName(ctx, "submodule1"); - - try { - valid.enterSubmodule_stmt(ctx); - } catch (YangValidationException e) { - assertThat( - e.getMessage(), - containsString("Missing revision statements in submodule:submodule1")); - throw e; - } - } - - @Test(expected = YangValidationException.class) - public void testNoHeaderStmts() { - mockSubmoduleWithRevision(2, "1999-4-5", "submodule"); - - try { - valid.enterSubmodule_stmt(ctx); - } catch (YangValidationException e) { - assertThat( - e.getMessage(), - containsString("Missing header statements in submodule:submodule")); - throw e; - } - } - - @Test(expected = YangValidationException.class) - public void testNoBelongsTo() { - Submodule_header_stmtsContext header = mock(Submodule_header_stmtsContext.class); - mockSubmoduleParent(2, header, "submodule"); - - try { - valid.enterSubmodule_header_stmts(header); - } catch (YangValidationException e) { - assertThat( - e.getMessage(), - containsString("Missing belongs-to statement in submodule:submodule")); - throw e; - } - } - - @Test(expected = YangValidationException.class) - public void testBelongsToNoPrefix() { - Belongs_to_stmtContext belongsTo = mock(Belongs_to_stmtContext.class); - doReturn(1).when(belongsTo).getChildCount(); - YangModelValidationListenerTest_Module.mockName(belongsTo, - "supermodule"); - - mockSubmoduleParent(2, belongsTo, "submodule"); - - try { - valid.enterBelongs_to_stmt(belongsTo); - } catch (YangValidationException e) { - assertThat( - e.getMessage(), - containsString("Missing prefix statement in belongs-to:supermodule, in (sub)module:submodule")); - throw e; - } - } - - @Test - public void testBelongsTo() { - Belongs_to_stmtContext belongsTo = mock(Belongs_to_stmtContext.class); - doReturn(2).when(belongsTo).getChildCount(); - YangModelValidationListenerTest_Module.mockName(belongsTo, - "supermodule"); - - Prefix_stmtContext prefix = mock(Prefix_stmtContext.class); - doReturn(prefix).when(belongsTo).getChild(1); - doReturn(belongsTo).when(prefix).getParent(); - - mockSubmoduleParent(2, belongsTo, "submodule"); - valid.enterBelongs_to_stmt(belongsTo); - - } - - private Revision_stmtContext mockSubmoduleWithRevision(int moduleChildren, - String date, String nameOfSubmodule) { - Revision_stmtContext mockedRev = mock(Revision_stmtContext.class); - doReturn(1).when(mockedRev).getChildCount(); - YangModelValidationListenerTest_Module.mockName(mockedRev, date); - - Revision_stmtsContext revs = YangModelValidationListenerTest_Module - .mockRevisionsParent(2, mockedRev); - - mockSubmoduleParent(moduleChildren, revs, nameOfSubmodule); - return mockedRev; - } - - private void mockSubmoduleParent(int moduleChildren, ParseTree child, - String moduleName) { - ctx = mock(Submodule_stmtContext.class); - doReturn(moduleChildren).when(ctx).getChildCount(); - YangModelValidationListenerTest_Module.mockName(ctx, moduleName); - doReturn(child).when(ctx).getChild(1); - doReturn(ctx).when(child).getParent(); - } -} diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationListTest.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationListTest.java new file mode 100644 index 0000000000..4e05f48a77 --- /dev/null +++ b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationListTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.yang.model.validator; + +import static org.junit.Assert.*; +import static org.junit.matchers.JUnitMatchers.*; + +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Default_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Key_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Leaf_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.List_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Mandatory_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Ordered_by_argContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Type_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Unique_stmtContext; +import org.opendaylight.controller.yang.model.parser.util.YangValidationException; + +public class YangModelValidationListTest { + + private YangModelBasicValidationListener valid; + + @Before + public void setUp() { + valid = new YangModelBasicValidationListener(); + } + + @Test(expected = YangValidationException.class) + public void testKeyValidationDuplicates() { + + List_stmtContext list = YangModelValidationTest.mockStatement( + List_stmtContext.class, "list"); + Key_stmtContext key = YangModelValidationTest.mockStatement( + Key_stmtContext.class, "leaf1 leaf2 leaf1 leaf1"); + YangModelValidationTest.addChild(list, key); + + try { + valid.enterKey_stmt(key); + } catch (YangValidationException e) { + assertThat(e.getMessage(), + containsString("contains duplicates:[leaf1]")); + throw e; + } + } + + @Test(expected = YangValidationException.class) + public void testUniqueValidationDuplicates() { + List_stmtContext list = YangModelValidationTest.mockStatement( + List_stmtContext.class, "list"); + Unique_stmtContext unique = YangModelValidationTest.mockStatement( + Unique_stmtContext.class, "leaf1/a leaf2/n leaf1/a leaf1"); + YangModelValidationTest.addChild(list, unique); + + try { + valid.enterUnique_stmt(unique); + } catch (YangValidationException e) { + assertThat(e.getMessage(), + containsString("contains duplicates:[leaf1/a]")); + throw e; + } + } + + @Test(expected = YangValidationException.class) + public void testOrderBy() { + Ordered_by_argContext ctx = YangModelValidationTest.mockStatement( + Ordered_by_argContext.class, "unknown"); + + try { + valid.enterOrdered_by_arg(ctx); + } catch (YangValidationException e) { + assertThat( + e.getMessage(), + containsString("Ordered-by:unknown, illegal value for Ordered-by statement, only permitted:")); + throw e; + } + } + + @Test(expected = YangValidationException.class) + public void testLeaf() { + Leaf_stmtContext ctx = YangModelValidationTest.mockStatement( + Leaf_stmtContext.class, "leaf1"); + Default_stmtContext def = YangModelValidationTest.mockStatement( + Default_stmtContext.class, "default"); + YangModelValidationTest.addChild(ctx, def); + Type_stmtContext typ = YangModelValidationTest.mockStatement( + Type_stmtContext.class, "type"); + YangModelValidationTest.addChild(ctx, def); + YangModelValidationTest.addChild(ctx, typ); + + Mandatory_stmtContext mand = YangModelValidationTest.mockStatement( + Mandatory_stmtContext.class, null); + YangModelValidationTest.addChild(ctx, mand); + + try { + valid.enterLeaf_stmt(ctx); + } catch (YangValidationException e) { + assertThat( + e.getMessage(), + containsString("Both Mandatory and Default statement present")); + throw e; + } + } + +} diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationModuleTest.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationModuleTest.java new file mode 100644 index 0000000000..2bdd3e5074 --- /dev/null +++ b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationModuleTest.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.yang.model.validator; + +import static org.junit.Assert.*; +import static org.junit.matchers.JUnitMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_header_stmtsContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Namespace_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtsContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Yang_version_stmtContext; +import org.opendaylight.controller.yang.model.parser.util.YangValidationException; + +public class YangModelValidationModuleTest { + + private YangModelBasicValidationListener valid; + + @Before + public void setUp() { + valid = new YangModelBasicValidationListener(); + } + + @Test(expected = YangValidationException.class) + public void testRevisionInvalidDateFormat() { + Revision_stmtContext mockedRev = mockModuleWithRevision("badFormat", + "module1"); + + try { + valid.enterRevision_stmt(mockedRev); + } catch (YangValidationException e) { + assertThat( + e.getMessage(), + containsString("Revision:badFormat, invalid date format expected date format is:")); + throw e; + } + } + + @Test + public void testRevisionValidDateFormat() { + Revision_stmtContext mockedRev = mockModuleWithRevision( + YangModelValidationTest.getFormattedDate(), "module1"); + + valid.enterRevision_stmt(mockedRev); + } + + @Test(expected = YangValidationException.class) + public void testNoHeaderStmts() { + Revision_stmtContext rev = mockModuleWithRevision("1999-4-5", "module1"); + + try { + valid.enterModule_stmt((Module_stmtContext) rev.getParent() + .getParent()); + } catch (YangValidationException e) { + assertThat( + e.getMessage(), + containsString("Missing Module-header statement in Module:module1")); + throw e; + } + } + + @Test(expected = YangValidationException.class) + public void testMultipleModulesPerSession() { + Module_stmtContext module1 = (Module_stmtContext) mockModuleWithRevision( + "1999-09-10", "m1").getParent().getParent(); + YangModelValidationTest.addChild(module1, YangModelValidationTest + .mockStatement(Namespace_stmtContext.class, "")); + + Module_stmtContext module2 = (Module_stmtContext) mockModuleWithRevision( + "1999-09-10", "m2").getParent().getParent(); + YangModelValidationTest.addChild(module1, YangModelValidationTest + .mockStatement(Namespace_stmtContext.class, "")); + valid.enterModule_stmt(module1); + + try { + valid.enterModule_stmt(module2); + } catch (YangValidationException e) { + assertThat(e.getMessage(), + containsString("Multiple (sub)modules per file")); + throw e; + } + } + + @Test(expected = YangValidationException.class) + public void testNoNamespace() { + Module_header_stmtsContext header = YangModelValidationTest + .mockStatement(Module_header_stmtsContext.class, null); + Module_stmtContext mod = YangModelValidationTest.mockStatement( + Module_stmtContext.class, "module1"); + YangModelValidationTest.addChild(mod, header); + + try { + valid.enterModule_header_stmts(header); + } catch (YangValidationException e) { + assertThat( + e.getMessage(), + containsString("Missing Namespace statement in Module-header:")); + throw e; + } + } + + @Test(expected = YangValidationException.class) + public void testNoPrefix() { + Module_header_stmtsContext header = YangModelValidationTest + .mockStatement(Module_header_stmtsContext.class, null); + Namespace_stmtContext nmspc = YangModelValidationTest.mockStatement( + Namespace_stmtContext.class, "http://test"); + Module_stmtContext mod = YangModelValidationTest.mockStatement( + Module_stmtContext.class, "module1"); + YangModelValidationTest.addChild(mod, header); + YangModelValidationTest.addChild(header, nmspc); + + try { + valid.enterModule_header_stmts(header); + } catch (YangValidationException e) { + assertThat( + e.getMessage(), + containsString("Missing Prefix statement in Module-header:")); + throw e; + } + } + + @Test(expected = YangValidationException.class) + public void testInvalidYangVersion() { + + Yang_version_stmtContext yangVersion = YangModelValidationTest + .mockStatement(Yang_version_stmtContext.class, "55Unsup"); + + Module_stmtContext mod = YangModelValidationTest.mockStatement( + Module_stmtContext.class, "module1"); + YangModelValidationTest.addChild(mod, yangVersion); + + try { + valid.enterYang_version_stmt(yangVersion); + } catch (YangValidationException e) { + assertThat( + e.getMessage(), + containsString("Unsupported yang version:55Unsup, supported version:" + + BasicValidations.SUPPORTED_YANG_VERSION)); + throw e; + } + } + + @Test + public void testValidYangVersion() { + + Yang_version_stmtContext ctx = mock(Yang_version_stmtContext.class); + doReturn(1).when(ctx).getChildCount(); + YangModelValidationTest.mockName(ctx, "1"); + + valid.enterYang_version_stmt(ctx); + } + + private static Revision_stmtContext mockModuleWithRevision(String date, + String moduleName) { + Revision_stmtContext mockedRev = YangModelValidationTest.mockStatement( + Revision_stmtContext.class, date); + Revision_stmtsContext revs = YangModelValidationTest.mockStatement( + Revision_stmtsContext.class, null); + Module_stmtContext mod = YangModelValidationTest.mockStatement( + Module_stmtContext.class, moduleName); + + YangModelValidationTest.addChild(revs, mockedRev); + YangModelValidationTest.addChild(mod, revs); + return mockedRev; + } +} diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationSubModuleTest.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationSubModuleTest.java new file mode 100644 index 0000000000..fe4f0a2452 --- /dev/null +++ b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationSubModuleTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.yang.model.validator; + +import static org.junit.Assert.*; +import static org.junit.matchers.JUnitMatchers.*; +import static org.mockito.Mockito.*; + +import org.antlr.v4.runtime.tree.ParseTree; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Belongs_to_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_header_stmtsContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_stmtContext; +import org.opendaylight.controller.yang.model.parser.util.YangValidationException; + +public class YangModelValidationSubModuleTest { + + private YangModelBasicValidationListener valid; + + @Before + public void setUp() { + valid = new YangModelBasicValidationListener(); + } + + @Test(expected = YangValidationException.class) + public void testNoRevision() { + + Submodule_stmtContext ctx = YangModelValidationTest.mockStatement( + Submodule_stmtContext.class, "submodule1"); + + try { + valid.enterSubmodule_stmt(ctx); + } catch (YangValidationException e) { + assertThat( + e.getMessage(), + containsString("Missing Submodule-header statement in Submodule:submodule")); + throw e; + } + } + + @Test(expected = YangValidationException.class) + public void testNoBelongsTo() { + Submodule_header_stmtsContext header = mock(Submodule_header_stmtsContext.class); + mockSubmoduleParent(header, "submodule"); + + try { + valid.enterSubmodule_header_stmts(header); + } catch (YangValidationException e) { + assertThat( + e.getMessage(), + containsString("Missing Belongs-to statement in Submodule-header:")); + throw e; + } + } + + @Test(expected = YangValidationException.class) + public void testBelongsToNoPrefix() { + Belongs_to_stmtContext belongsTo = YangModelValidationTest + .mockStatement(Belongs_to_stmtContext.class, "supermodule"); + + mockSubmoduleParent(belongsTo, "submodule"); + + try { + valid.enterBelongs_to_stmt(belongsTo); + } catch (YangValidationException e) { + assertThat( + e.getMessage(), + containsString("Missing Prefix statement in Belongs-to:supermodule")); + throw e; + } + } + + private Submodule_stmtContext mockSubmoduleParent(ParseTree child, + String moduleName) { + Submodule_stmtContext ctx = YangModelValidationTest.mockStatement( + Submodule_stmtContext.class, moduleName); + YangModelValidationTest.addChild(ctx, child); + return ctx; + } +} diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationTest.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationTest.java new file mode 100644 index 0000000000..ffcadf6397 --- /dev/null +++ b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationTest.java @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.yang.model.validator; + +import static org.hamcrest.core.Is.*; +import static org.junit.Assert.*; +import static org.junit.matchers.JUnitMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.List; + +import org.antlr.v4.runtime.tree.ParseTree; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Augment_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Deviate_add_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Deviate_delete_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Deviation_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Import_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Include_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Namespace_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Prefix_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_date_stmtContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.Status_argContext; +import org.opendaylight.controller.antlrv4.code.gen.YangParser.StringContext; +import org.opendaylight.controller.yang.model.parser.impl.YangModelParserListenerImpl; +import org.opendaylight.controller.yang.model.parser.util.YangValidationException; + +import com.google.common.collect.Sets; + +public class YangModelValidationTest { + + private YangModelBasicValidationListener valid; + + @Before + public void setUp() { + + valid = new YangModelBasicValidationListener(); + } + + @Test + public void testPrefixes() { + Prefix_stmtContext pref = mockStatement(Prefix_stmtContext.class, + "unique1"); + Module_stmtContext module = mockStatement(Module_stmtContext.class, + "module1"); + addChild(module, pref); + + valid.enterPrefix_stmt(pref); + + pref = mockStatement(Prefix_stmtContext.class, "unique1"); + module = mockStatement(Module_stmtContext.class, "module1"); + addChild(module, pref); + + try { + valid.enterPrefix_stmt(pref); + } catch (Exception e) { + return; + } + + fail("Validation Exception should have occured"); + } + + @Test + public void testNamespace() { + + Namespace_stmtContext namespace = mockStatement( + Namespace_stmtContext.class, "http://test.parsing.uri.com"); + Module_stmtContext module = mockStatement(Module_stmtContext.class, + "module1"); + addChild(module, namespace); + + valid.enterNamespace_stmt(namespace); + + namespace = mockStatement(Namespace_stmtContext.class, "invalid uri"); + module = mockStatement(Module_stmtContext.class, "module1"); + addChild(module, namespace); + + try { + valid.enterNamespace_stmt(namespace); + } catch (YangValidationException e) { + assertThat( + e.getMessage(), + containsString("Namespace:invalid uri cannot be parsed as URI")); + return; + } + + fail("Validation Exception should have occured"); + } + + @Test + public void testImports() { + Import_stmtContext impor = mockImport("unique1", "p1"); + Module_stmtContext mod = mockStatement(Module_stmtContext.class, + "module1"); + addChild(mod, impor); + + valid.enterImport_stmt(impor); + + impor = mockImport("unique1", "p2"); + mod = mockStatement(Module_stmtContext.class, "module1"); + addChild(mod, impor); + + try { + valid.enterImport_stmt(impor); + } catch (YangValidationException e) { + assertThat(e.getMessage(), + containsString("Import:unique1 not unique")); + return; + } + + fail("Validation Exception should have occured"); + } + + @Test + public void testIncludes() { + Include_stmtContext incl = mockInclude("unique1"); + Module_stmtContext mod = mockStatement(Module_stmtContext.class, + "module1"); + addChild(mod, incl); + valid.enterInclude_stmt(incl); + + incl = mockInclude("unique1"); + mod = mockStatement(Module_stmtContext.class, "module1"); + addChild(mod, incl); + + try { + valid.enterInclude_stmt(incl); + } catch (YangValidationException e) { + assertThat(e.getMessage(), + containsString("Include:unique1 not unique in (sub)module")); + return; + } + + fail("Validation Exception should have occured"); + } + + @Test + public void testIdentifierMatching() { + List ids = new ArrayList(); + // valid + ids.add("_ok98-.87.-.8...88-asdAD"); + ids.add("AA.bcd"); + ids.add("a"); + // invalid + ids.add("9aa"); + ids.add("-"); + ids.add("."); + + int thrown = 0; + for (String id : ids) { + try { + BasicValidations.checkIdentifierInternal( + mock(Module_stmtContext.class), id); + } catch (YangValidationException e) { + thrown++; + } + } + + assertThat(thrown, is(3)); + } + + @Test(expected = YangValidationException.class) + public void testAugument() { + Augment_stmtContext augument = mockStatement(Augment_stmtContext.class, + "/a:*abc/a:augument1"); + Module_stmtContext mod1 = mockStatement(Module_stmtContext.class, + "mod1"); + addChild(mod1, augument); + + try { + valid.enterAugment_stmt(augument); + } catch (YangValidationException e) { + assertThat( + e.getMessage(), + containsString("Schema node id:/a:*abc/a:augument1 not in required format, details:Prefixed id:a:*abc not in required format")); + throw e; + } + } + + @Test + public void testDeviate() { + Deviation_stmtContext ctx = mockStatement(Deviation_stmtContext.class, + "deviations"); + Deviate_add_stmtContext add = mockStatement( + Deviate_add_stmtContext.class, "add"); + Deviate_delete_stmtContext del = mockStatement( + Deviate_delete_stmtContext.class, "delete"); + + addChild(ctx, add); + addChild(ctx, del); + + valid.enterDeviation_stmt(ctx); + + HashSet> types = Sets.newHashSet(); + types.add(Deviate_add_stmtContext.class); + types.add(Deviate_delete_stmtContext.class); + + int count = ValidationUtil.countPresentChildrenOfType(ctx, types); + assertThat(count, is(2)); + } + + @Test(expected = YangValidationException.class) + public void testStatus() throws Exception { + Status_argContext status = mockStatement(Status_argContext.class, + "unknown"); + try { + valid.enterStatus_arg(status); + } catch (YangValidationException e) { + assertThat( + e.getMessage(), + containsString("illegal value for Status statement, only permitted:")); + throw e; + } + } + + private Import_stmtContext mockImport(String name, String prefixName) { + Import_stmtContext impor = mockStatement(Import_stmtContext.class, name); + + Prefix_stmtContext prefix = mockStatement(Prefix_stmtContext.class, + prefixName); + Revision_date_stmtContext revDate = mockStatement( + Revision_date_stmtContext.class, getFormattedDate()); + + addChild(impor, prefix); + addChild(impor, revDate); + return impor; + } + + static String getFormattedDate() { + return YangModelParserListenerImpl.simpleDateFormat.format(new Date()); + } + + private Include_stmtContext mockInclude(String name) { + Include_stmtContext incl = mockStatement(Include_stmtContext.class, + name); + + Revision_date_stmtContext revDate = mockStatement( + Revision_date_stmtContext.class, getFormattedDate()); + + addChild(incl, revDate); + return incl; + } + + static void mockName(ParseTree stmt, String name) { + StringContext nameCtx = mock(StringContext.class); + ParseTree internalName = mock(ParseTree.class); + doReturn(1).when(stmt).getChildCount(); + doReturn(name).when(internalName).getText(); + doReturn(internalName).when(nameCtx).getChild(0); + doReturn(nameCtx).when(stmt).getChild(0); + } + + static T mockStatement(Class stmtType, String name) { + T stmt = stmtType.cast(mock(stmtType)); + + doReturn(0).when(stmt).getChildCount(); + + if (name != null) + mockName(stmt, name); + return stmt; + } + + static void addChild(ParseTree parent, ParseTree child) { + int childCount = parent.getChildCount() + 1; + doReturn(childCount).when(parent).getChildCount(); + doReturn(child).when(parent).getChild(childCount - 1); + doReturn(parent).when(child).getParent(); + } + +}