Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle double quoted patterns #247

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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]);
}

Expand Down Expand Up @@ -152,22 +152,22 @@ 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.''';
error(message, belongsTo, BELONGS_TO__MODULE, BAD_INCLUDE_YANG_VERSION);
}
]
}


@Check
def void checkSubstatements(Statement it) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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 '\'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dhuebner could you please help how to achieve unescaping backslashes in case the pattern is double quoted

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bump

if (eContainer instanceof Type) {
val type = eContainer as Type;
if (type.subtypeOfString) {
Expand Down Expand Up @@ -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);
Expand All @@ -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»".''';
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import org.junit.Assert

/**
* Validation test for the YANG language.
*
*
* @author akos.kitta
*/
class YangValidatorTest extends AbstractYangTest {
Expand Down Expand Up @@ -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('''
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -1423,7 +1440,7 @@ class YangValidatorTest extends AbstractYangTest {
}
rpc run {
input { uses g; }
output {
output {
uses g {
augment l {
leaf xxx {
Expand All @@ -1445,15 +1462,15 @@ class YangValidatorTest extends AbstractYangTest {
module d {
namespace urn:d;
prefix d;

container x {
choice c {
leaf d {
type string;
}
}
}

deviation /x/c/d {
deviate «it»;
}
Expand All @@ -1469,15 +1486,15 @@ class YangValidatorTest extends AbstractYangTest {
module d {
namespace urn:d;
prefix d;

container x {
choice c {
leaf d {
type string;
}
}
}

deviation /x/c/d {
deviate blabla;
}
Expand All @@ -1493,7 +1510,7 @@ class YangValidatorTest extends AbstractYangTest {
module d {
namespace urn:d;
prefix d;

leaf Num1 {
type int32 {
range min..max;
Expand All @@ -1513,7 +1530,7 @@ class YangValidatorTest extends AbstractYangTest {
module d {
namespace urn:d;
prefix d;

leaf Num1 {
type int32 {
range min..max;
Expand All @@ -1525,7 +1542,7 @@ class YangValidatorTest extends AbstractYangTest {
''');
assertError(EcoreUtil2.getAllContentsOfType(root, Status).head, TYPE_ERROR);
}

@Test
def void checkUriToProblem_01() {
val model = loadWithSyntaxErrors('''
Expand All @@ -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
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,81 +7,100 @@ 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];
}
}
}
''')

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-_];
}
}
}
''')


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];
}
}
}
''')

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-_];
}
}
}
''')

validator.validate(foo)
assertError(foo.allContents.filter(Pattern).head, TYPE_ERROR)
}
}

}
Loading