From 6b118c5cc4ae5a34b1846a5596b418c26a38016b Mon Sep 17 00:00:00 2001 From: WamanAvadhani Date: Thu, 3 Dec 2015 17:37:40 +0530 Subject: [PATCH 1/3] Adding support for regular expression matching for values of type Chars. #50 --- atest/header_filter.robot | 50 ++++++++++++++++++++++-- src/Rammbock/core.py | 3 ++ src/Rammbock/templates/message_stream.py | 7 ++++ src/Rammbock/templates/primitives.py | 17 ++++++++ utest/test_templates/test_primitives.py | 7 ++++ 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/atest/header_filter.robot b/atest/header_filter.robot index 9970687..c7e4386 100644 --- a/atest/header_filter.robot +++ b/atest/header_filter.robot @@ -3,6 +3,7 @@ Test Setup Setup up server for test Test Teardown Reset Rammbock Library Rammbock Resource Protocols.robot +Default Tags regression *** Test Cases *** String message field matching the header filter @@ -17,6 +18,43 @@ String message field not matching the header filter with unicode value Define and send example message Run keyword and expect error * timed out Receive example message with filter value देवनागरी +String message field matching the header filter with regexp + Define and send example message + Receive example message with filter value REGEXP:.*message + +String message field not matching the header filter with regexp + Define and send example message + Run keyword and expect error * timed out Receive example message with filter value REGEXP:.*message. + +String message field matching the header filter with invalid regexp + Define and send example message + Run keyword and expect error Invalid RegEx * Receive example message with filter value REGEXP:** + +String message field matching the regexp + Define and send example message + Receive example message with regexp value REGEXP:.*message + +String message field not matching the regexp + Define and send example message + Run keyword and expect error * does not match the RegEx * Receive example message with regexp value REGEXP:.*message. + +Comparing string message field with an invalid regexp + Define and send example message + Run keyword and expect error Invalid RegEx * Receive example message with regexp value REGEXP:[0-9]++ + +Comparing string message field with a blank regexp + Define and send example message + Receive example message with regexp value REGEXP: + +Comparing string message field with a invalid ending regexp + Define and send example message + Run keyword and expect error Invalid RegEx * Receive example message with regexp value REGEXP:[] + +Comparing integer message field with a regexp + Define and send example message + New message exMessage StringInHeader header:integer_field:REGEXP:.* + Run keyword and expect error * can not be matched to regular expression pattern * Server receives message + *** Keywords *** Setup up server for test Define a protocol with string field in header @@ -24,11 +62,12 @@ Setup up server for test Define a protocol with string field in header New protocol StringInHeader - Chars * string_field + Uint 1 integer_field + Chars * string_field End protocol Define and send example message - New message exMessage StringInHeader header:string_field:match string message + New message exMessage StringInHeader header:string_field:match string message header:integer_field:10 Client sends message Receive example message matching filter @@ -38,4 +77,9 @@ Receive example message matching filter Receive example message with filter value [arguments] ${value} New message exMessage StringInHeader header:string_field:${value} - Server receives message header_filter=string_field + Server receives message header_filter=string_field + +Receive example message with regexp value + [arguments] ${value} + New message exMessage StringInHeader header:string_field:${value} + Server receives message diff --git a/src/Rammbock/core.py b/src/Rammbock/core.py index 8f51ec2..5c4a987 100644 --- a/src/Rammbock/core.py +++ b/src/Rammbock/core.py @@ -700,6 +700,8 @@ def chars(self, length, name, value=None, terminator=None): length of value and decoded as all available bytes. `value` is optional. + `value` could be either a "String" or a "Regular Expression" and + if it is a Regular Expression it must be prefixed by 'REGEXP:'. Examples: | chars | 16 | field | Hello World! | @@ -708,6 +710,7 @@ def chars(self, length, name, value=None, terminator=None): | chars | charLength | field | | chars | * | field | Hello World! | + | chars | * | field | REGEXP:^{[a-zA-Z ]+}$ | """ self._add_field(Char(length, name, value, terminator)) diff --git a/src/Rammbock/templates/message_stream.py b/src/Rammbock/templates/message_stream.py index 6e1914e..2a40d29 100644 --- a/src/Rammbock/templates/message_stream.py +++ b/src/Rammbock/templates/message_stream.py @@ -14,6 +14,7 @@ import time import threading import traceback +import re from Rammbock.logger import logger from Rammbock.binary_tools import to_bin, to_int @@ -100,6 +101,12 @@ def _matches(self, header, fields, header_filter): (header_filter, header_filter)) field = header[header_filter] if field._type == 'chars': + if fields[header_filter].startswith('REGEXP:'): + try: + regexp = fields[header_filter].split(':')[1].strip() + return bool(re.match(regexp, field.ascii)) + except re.error as e: + raise Exception("Invalid RegEx Error : " + str(e)) return field.ascii == fields[header_filter] if field._type == 'uint': return field.uint == to_int(fields[header_filter]) diff --git a/src/Rammbock/templates/primitives.py b/src/Rammbock/templates/primitives.py index dc5ed66..3b4bb6f 100644 --- a/src/Rammbock/templates/primitives.py +++ b/src/Rammbock/templates/primitives.py @@ -83,8 +83,14 @@ def validate(self, parent, paramdict, name=None): e.args = ('Validating {}:{} failed. {}.\n Did you set default value as numeric object instead of string?' .format(name, forced_value, e.args[0]),) raise e + if forced_value.startswith('REGEXP'): + return self._validate_regexp(forced_value, value, field) return self._validate_exact_match(forced_value, value, field) + def _validate_regexp(self, forced_pattern, value, field): + return ["Value of field '%s' can not be matched to regular expression pattern '%s'" % + (field._get_recursive_name(), forced_pattern)] + def _validate_pattern(self, forced_pattern, value, field): if self._validate_or(forced_pattern, value, field): return [] @@ -215,6 +221,17 @@ def _prepare_data(self, data): return data[0:data.index(self._terminator) + len(self._terminator)] return data + def _validate_regexp(self, forced_pattern, value, field): + try: + regexp = forced_pattern.split(':')[1].strip() + if bool(re.match(regexp, field.ascii)): + return [] + else: + return ['Value of field %s does not match the RegEx %s!=%s' % + (field._get_recursive_name(), self._default_presentation_format(value), forced_pattern)] + except re.error as e: + raise Exception("Invalid RegEx Error : " + str(e)) + class Binary(_TemplateField): diff --git a/utest/test_templates/test_primitives.py b/utest/test_templates/test_primitives.py index e4d3c18..fe62014 100644 --- a/utest/test_templates/test_primitives.py +++ b/utest/test_templates/test_primitives.py @@ -146,6 +146,7 @@ def test_validate_uint(self): self._should_pass(UInt(2, 'field', '0x04').validate({'field': field}, {})) self._should_pass(UInt(2, 'field', '0x0004').validate({'field': field}, {})) self._should_pass(UInt(2, 'field', '(0|4)').validate({'field': field}, {})) + self._should_fail(UInt(2, 'field', 'REGEXP:.*').validate({'field': field}, {}), 1) def test_validate_int(self): field = Field('int', 'field', to_bin('0xffb8')) @@ -154,11 +155,17 @@ def test_validate_int(self): self._should_pass(Int(2, 'field', '-0x48').validate({'field': field}, {})) self._should_pass(Int(2, 'field', '-0x0048').validate({'field': field}, {})) self._should_pass(Int(2, 'field', '(0|-72)').validate({'field': field}, {})) + self._should_fail(Int(2, 'field', 'REGEXP:.*').validate({'field': field}, {}), 1) def test_validate_chars(self): field = Field('chars', 'field', 'foo\x00\x00') + field_regEx = Field('chars', 'field', '{ Message In Braces }') self._should_pass(Char(5, 'field', 'foo').validate({'field': field}, {})) self._should_pass(Char(5, 'field', '(what|foo|bar)').validate({'field': field}, {})) + self._should_pass(Char(5, 'field', 'REGEXP:^{[a-zA-Z ]+}$').validate({'field': field_regEx}, {})) + self._should_pass(Char(5, 'field', 'REGEXP:^foo').validate({'field': field}, {})) + self._should_pass(Char(5, 'field', 'REGEXP:').validate({'field': field}, {})) + self._should_fail(Char(5, 'field', 'REGEXP:^abc').validate({'field': field}, {}), 1) def _should_pass(self, validation): self.assertEquals(validation, []) From 3fa1a929dc6204038bb12109e55c69c7b657ec33 Mon Sep 17 00:00:00 2001 From: WamanAvadhani Date: Fri, 4 Dec 2015 10:52:29 +0530 Subject: [PATCH 2/3] atest: Added RegEx header_filter test with multiple messages in stream. --- atest/header_filter.robot | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/atest/header_filter.robot b/atest/header_filter.robot index c7e4386..b7699a7 100644 --- a/atest/header_filter.robot +++ b/atest/header_filter.robot @@ -24,7 +24,7 @@ String message field matching the header filter with regexp String message field not matching the header filter with regexp Define and send example message - Run keyword and expect error * timed out Receive example message with filter value REGEXP:.*message. + Run keyword and expect error * timed out Receive example message with filter value REGEXP:.*invalid String message field matching the header filter with invalid regexp Define and send example message @@ -36,7 +36,7 @@ String message field matching the regexp String message field not matching the regexp Define and send example message - Run keyword and expect error * does not match the RegEx * Receive example message with regexp value REGEXP:.*message. + Run keyword and expect error * does not match the RegEx * Receive example message with regexp value REGEXP:.*invalid Comparing string message field with an invalid regexp Define and send example message @@ -55,6 +55,10 @@ Comparing integer message field with a regexp New message exMessage StringInHeader header:integer_field:REGEXP:.* Run keyword and expect error * can not be matched to regular expression pattern * Server receives message +String message field matching the header filter with multiple messages in stream + Define and send multiple messages + Receive example message with filter value REGEXP:^first + *** Keywords *** Setup up server for test Define a protocol with string field in header @@ -63,15 +67,21 @@ Setup up server for test Define a protocol with string field in header New protocol StringInHeader Uint 1 integer_field - Chars * string_field + Chars * string_field terminator=0x0e End protocol Define and send example message - New message exMessage StringInHeader header:string_field:match string message header:integer_field:10 + New message exMessage StringInHeader header:string_field:match string message. header:integer_field:10 + Client sends message + +Define and send multiple messages + New message exMessage1 StringInHeader header:string_field:first string message. header:integer_field:10 + Client sends message + New message exMessage2 StringInHeader header:string_field:sencond string message. header:integer_field:10 Client sends message Receive example message matching filter - New message exMessage StringInHeader header:string_field:match string message + New message exMessage StringInHeader header:string_field:match string message. Server receives message header_filter=string_field Receive example message with filter value From 641614226aeb2132099ef40286d09843b8f6aaa3 Mon Sep 17 00:00:00 2001 From: WamanAvadhani Date: Fri, 4 Dec 2015 11:11:04 +0530 Subject: [PATCH 3/3] atest: Modified the terminator to a more meaningful value --- atest/header_filter.robot | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/atest/header_filter.robot b/atest/header_filter.robot index b7699a7..6c4e1e7 100644 --- a/atest/header_filter.robot +++ b/atest/header_filter.robot @@ -67,21 +67,21 @@ Setup up server for test Define a protocol with string field in header New protocol StringInHeader Uint 1 integer_field - Chars * string_field terminator=0x0e + Chars * string_field terminator=0x00 End protocol Define and send example message - New message exMessage StringInHeader header:string_field:match string message. header:integer_field:10 + New message exMessage StringInHeader header:string_field:match string message header:integer_field:10 Client sends message Define and send multiple messages - New message exMessage1 StringInHeader header:string_field:first string message. header:integer_field:10 + New message exMessage1 StringInHeader header:string_field:first string message header:integer_field:10 Client sends message - New message exMessage2 StringInHeader header:string_field:sencond string message. header:integer_field:10 + New message exMessage2 StringInHeader header:string_field:second string message header:integer_field:10 Client sends message Receive example message matching filter - New message exMessage StringInHeader header:string_field:match string message. + New message exMessage StringInHeader header:string_field:match string message Server receives message header_filter=string_field Receive example message with filter value