diff --git a/yang-lsp/io.typefox.yang/src/main/java/io/typefox/yang/validation/YangValidator.xtend b/yang-lsp/io.typefox.yang/src/main/java/io/typefox/yang/validation/YangValidator.xtend index 4adf8df..0986f03 100644 --- a/yang-lsp/io.typefox.yang/src/main/java/io/typefox/yang/validation/YangValidator.xtend +++ b/yang-lsp/io.typefox.yang/src/main/java/io/typefox/yang/validation/YangValidator.xtend @@ -72,7 +72,7 @@ import io.typefox.yang.yang.BelongsTo import org.apache.xerces.impl.xpath.regex.ParseException /** - * This class contains custom validation rules for the YANG language. + * This class contains custom validation rules for the YANG language. */ @Singleton class YangValidator extends AbstractYangValidator { @@ -111,7 +111,7 @@ class YangValidator extends AbstractYangValidator { ]; validAugmentStatements.put(CHOICE, CASE); // https://tools.ietf.org/html/rfc7950#section-7.9.2 - // Shorthand "case" statement. + // Shorthand "case" statement. validShorthandStatements = ImmutableList.copyOf(#[ANYDATA, ANYXML, CHOICE, CONTAINER, LEAF, LIST, LEAF_LIST]); } @@ -152,14 +152,14 @@ class YangValidator extends AbstractYangValidator { ]; } } - + @Check def void checkVersionConsistency(Submodule subModule) { val submoduleVersion = subModule.yangVersion subModule .substatementsOfType(BelongsTo) .filter[module?.eResource !== null && !module.eIsProxy] - .forEach [ belongsTo | + .forEach [ belongsTo | val baseModuleVersion = belongsTo.module.yangVersion if(submoduleVersion != baseModuleVersion) { val message = '''A version «submoduleVersion» submodule cannot be included in a version «baseModuleVersion» module.'''; @@ -167,7 +167,7 @@ class YangValidator extends AbstractYangValidator { } ] } - + @Check def void checkSubstatements(Statement it) { @@ -215,7 +215,7 @@ class YangValidator extends AbstractYangValidator { @Check def checkIdentityrefType(Type it) { if (identityref) { - // The "base" statement, which is a sub-statement to the "type" statement, + // The "base" statement, which is a sub-statement to the "type" statement, // must be present at least once if the type is "identityref". // https://tools.ietf.org/html/rfc7950#section-9.10.2 if (substatementsOfType(Base).nullOrEmpty) { @@ -260,7 +260,7 @@ class YangValidator extends AbstractYangValidator { val fractionDigits = firstSubstatementsOfType(FractionDigits); val fractionDigitsExist = fractionDigits !== null; // Note, only the decimal type definition MUST have the `fraction-digits` statement. - // It is not mandatory for types that are derived from decimal built-ins. + // It is not mandatory for types that are derived from decimal built-ins. val decimalBuiltin = decimal; if (decimalBuiltin) { if (fractionDigitsExist) { @@ -287,6 +287,7 @@ class YangValidator extends AbstractYangValidator { @Check def checkPattern(Pattern it) { // https://tools.ietf.org/html/rfc7950#section-9.4.5 + // If 'it' is double quoted replace all '\\' with '\' if (eContainer instanceof Type) { val type = eContainer as Type; if (type.subtypeOfString) { @@ -348,8 +349,8 @@ class YangValidator extends AbstractYangValidator { try { revisionDateFormat.parse(revisionFile); val revisionStatement = substatementsOfType(Revision).head; - if(revisionStatement !== null - && revisionStatement.revision !== null + if(revisionStatement !== null + && revisionStatement.revision !== null && revisionStatement.revision != revisionFile) { val message = '''The revision date in the file name does not match.'''; warning(message, revisionStatement, REVISION__REVISION, REVISION_MISMATCH); @@ -364,7 +365,7 @@ class YangValidator extends AbstractYangValidator { @Check def checkTypedef(Typedef it) { // The [1..*] type cardinality is checked by other rules. - // Also, the type name uniqueness is checked in the scoping. + // Also, the type name uniqueness is checked in the scoping. // https://tools.ietf.org/html/rfc7950#section-7.3 if (name.builtinName) { val message = '''Illegal type name "«name»".'''; @@ -421,7 +422,7 @@ class YangValidator extends AbstractYangValidator { @Check def checkKey(Key key) { - // https://tools.ietf.org/html/rfc7950#section-7.8.2 + // https://tools.ietf.org/html/rfc7950#section-7.8.2 // A leaf identifier must not appear more than once in the key. key.references.filter[!node?.name.nullOrEmpty].toMultimap[node.name].asMap.forEach [ name, nodesWithSameName | if (nodesWithSameName.size > 1) { @@ -510,7 +511,7 @@ class YangValidator extends AbstractYangValidator { val message = '''The augment's target node must be either a «validTypes» node.'''; error(message, it, AUGMENT__PATH, INVALID_AUGMENTATION); } else { - // As a shorthand, the "case" statement can be omitted if the branch contains a single "anydata", "anyxml", + // As a shorthand, the "case" statement can be omitted if the branch contains a single "anydata", "anyxml", // "choice", "container", "leaf", "list", or "leaf-list" statement. val schemaNodes = substatements.filter(SchemaNode); if (target.eClass === CHOICE && schemaNodes.size === 1) { @@ -539,7 +540,7 @@ class YangValidator extends AbstractYangValidator { def void checkAction(Action it) { // https://tools.ietf.org/html/rfc7950#section-7.15 // An action must not have any ancestor node that is a list node without a "key" statement. - // An action must not be defined within an rpc, another action, or a notification, i.e., an action node must + // An action must not be defined within an rpc, another action, or a notification, i.e., an action node must // not have an rpc, action, or a notification node as one of its ancestors in the schema tree. checkAncestors(SCHEMA_NODE__NAME); } @@ -548,7 +549,7 @@ class YangValidator extends AbstractYangValidator { def void checkNotification(Notification it) { // https://tools.ietf.org/html/rfc7950#section-7.16 // A notification must not have any ancestor node that is a list node without a "key" statement. - // A notification must not be defined within an rpc, another action, or a notification, i.e., a notification node must + // A notification must not be defined within an rpc, another action, or a notification, i.e., a notification node must // not have an rpc, action, or a notification node as one of its ancestors in the schema tree. checkAncestors(SCHEMA_NODE__NAME); } diff --git a/yang-lsp/io.typefox.yang/src/test/java/io/typefox/yang/tests/YangValidatorTest.xtend b/yang-lsp/io.typefox.yang/src/test/java/io/typefox/yang/tests/YangValidatorTest.xtend index 7cfda14..be17565 100644 --- a/yang-lsp/io.typefox.yang/src/test/java/io/typefox/yang/tests/YangValidatorTest.xtend +++ b/yang-lsp/io.typefox.yang/src/test/java/io/typefox/yang/tests/YangValidatorTest.xtend @@ -31,7 +31,7 @@ import org.junit.Assert /** * Validation test for the YANG language. - * + * * @author akos.kitta */ class YangValidatorTest extends AbstractYangTest { @@ -581,6 +581,23 @@ class YangValidatorTest extends AbstractYangTest { assertError(EcoreUtil2.getAllContentsOfType(root, Type).head, TYPE_ERROR, 'int32'); } + @Test + def void checkPattern_04() { + val it = load(''' + module foo { + yang-version 1.1; + namespace "urn:yang:types"; + prefix "yang"; + typedef my-base-type { + type int32 { + pattern "[a-zA-Z0-9!$%\\^()\\[\\]_\\-~{}.+]*"; + } + } + } + '''); + assertNoErrors; + } + @Test def void checkEnumStatements() { val it = load(''' @@ -1168,7 +1185,7 @@ class YangValidatorTest extends AbstractYangTest { namespace "urn:example:my-crypto"; prefix mc; identity eth-if-speed { - description + description "Representing the configured or negotiated speed of an Ethernet interface. Definitions are only required for PHYs that can run at different speeds (e.g. BASE-T)."; } leaf crypto { @@ -1423,7 +1440,7 @@ class YangValidatorTest extends AbstractYangTest { } rpc run { input { uses g; } - output { + output { uses g { augment l { leaf xxx { @@ -1445,7 +1462,7 @@ class YangValidatorTest extends AbstractYangTest { module d { namespace urn:d; prefix d; - + container x { choice c { leaf d { @@ -1453,7 +1470,7 @@ class YangValidatorTest extends AbstractYangTest { } } } - + deviation /x/c/d { deviate «it»; } @@ -1469,7 +1486,7 @@ class YangValidatorTest extends AbstractYangTest { module d { namespace urn:d; prefix d; - + container x { choice c { leaf d { @@ -1477,7 +1494,7 @@ class YangValidatorTest extends AbstractYangTest { } } } - + deviation /x/c/d { deviate blabla; } @@ -1493,7 +1510,7 @@ class YangValidatorTest extends AbstractYangTest { module d { namespace urn:d; prefix d; - + leaf Num1 { type int32 { range min..max; @@ -1513,7 +1530,7 @@ class YangValidatorTest extends AbstractYangTest { module d { namespace urn:d; prefix d; - + leaf Num1 { type int32 { range min..max; @@ -1525,7 +1542,7 @@ class YangValidatorTest extends AbstractYangTest { '''); assertError(EcoreUtil2.getAllContentsOfType(root, Status).head, TYPE_ERROR); } - + @Test def void checkUriToProblem_01() { val model = loadWithSyntaxErrors(''' @@ -1534,7 +1551,7 @@ class YangValidatorTest extends AbstractYangTest { namespace bug196; leaf key-id { type string; - + when "/ctxsr6k:contexts/ctxr6k:context/ctxr6k:context-" + "name='local'" { description @@ -1544,7 +1561,7 @@ class YangValidatorTest extends AbstractYangTest { } } '''); - + val issues = validator.validate(model) val noUriIssues = issues.filter[it.uriToProblem === null].toList Assert.assertEquals("Some issues has no uriToProblem set", 0, noUriIssues.size) diff --git a/yang-lsp/io.typefox.yang/src/test/java/io/typefox/yang/tests/validation/RegexpTest.xtend b/yang-lsp/io.typefox.yang/src/test/java/io/typefox/yang/tests/validation/RegexpTest.xtend index 7a58303..7bb83dd 100644 --- a/yang-lsp/io.typefox.yang/src/test/java/io/typefox/yang/tests/validation/RegexpTest.xtend +++ b/yang-lsp/io.typefox.yang/src/test/java/io/typefox/yang/tests/validation/RegexpTest.xtend @@ -7,14 +7,14 @@ import org.junit.Test import static io.typefox.yang.validation.IssueCodes.* class RegexpTest extends AbstractYangTest { - + @Test def void testLegalPattern_0() { val foo = load(''' module foo { yang-version 1.1; namespace urn:ietf:params:xml:ns:yang:foo; prefix foo; - + typedef foo { type string { pattern [a-z0-9]; @@ -22,18 +22,18 @@ class RegexpTest extends AbstractYangTest { } } ''') - + validator.validate(foo) assertNoErrors(foo.allContents.filter(Pattern).head, TYPE_ERROR) } - + @Test def void testLegalPattern_1() { val foo = load(''' module foo { yang-version 1.1; namespace urn:ietf:params:xml:ns:yang:foo; prefix foo; - + typedef foo { type string { pattern [a-zA-_]; @@ -41,18 +41,37 @@ class RegexpTest extends AbstractYangTest { } } ''') - + + validator.validate(foo) + assertNoErrors(foo.allContents.filter(Pattern).head, TYPE_ERROR) + } + + @Test def void testLegalPattern_2() { + val foo = load(''' + module foo { + yang-version 1.1; + namespace urn:ietf:params:xml:ns:yang:foo; + prefix foo; + + typedef foo { + type string { + pattern "[a-zA-Z0-9!$%\\^()\\[\\]_\\-~{}.+]*"; + } + } + } + ''') + validator.validate(foo) assertNoErrors(foo.allContents.filter(Pattern).head, TYPE_ERROR) } - + @Test def void testIllegalPattern_0() { val foo = load(''' module foo { yang-version 1.1; namespace urn:ietf:params:xml:ns:yang:foo; prefix foo; - + typedef foo { type string { pattern [a-z-0]; @@ -60,18 +79,18 @@ class RegexpTest extends AbstractYangTest { } } ''') - + validator.validate(foo) assertError(foo.allContents.filter(Pattern).head, TYPE_ERROR) } - + @Test def void testIllegalPattern_1() { val foo = load(''' module foo { yang-version 1.1; namespace urn:ietf:params:xml:ns:yang:foo; prefix foo; - + typedef foo { type string { pattern [a-z-_]; @@ -79,9 +98,9 @@ class RegexpTest extends AbstractYangTest { } } ''') - + validator.validate(foo) assertError(foo.allContents.filter(Pattern).head, TYPE_ERROR) } - -} \ No newline at end of file + +}