Skip to content

Commit

Permalink
Merge pull request MarketSquare#51 from WamanAvadhani/master
Browse files Browse the repository at this point in the history
Adding support for regular expression matching for values of type Chars
  • Loading branch information
jussimalinen committed Dec 4, 2015
2 parents e0477cc + edea7f0 commit 71092a3
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 3 deletions.
60 changes: 57 additions & 3 deletions atest/header_filter.robot
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -17,18 +18,66 @@ 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:.*invalid

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:.*invalid

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

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
Setup UDP server and client protocol=StringInHeader

Define a protocol with string field in header
New protocol StringInHeader
Chars * string_field
Uint 1 integer_field
Chars * string_field terminator=0x00
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

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:second string message header:integer_field:10
Client sends message

Receive example message matching filter
Expand All @@ -38,4 +87,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
3 changes: 3 additions & 0 deletions src/Rammbock/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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! |
Expand All @@ -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))
Expand Down
7 changes: 7 additions & 0 deletions src/Rammbock/templates/message_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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])
Expand Down
17 changes: 17 additions & 0 deletions src/Rammbock/templates/primitives.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 []
Expand Down Expand Up @@ -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):

Expand Down
7 changes: 7 additions & 0 deletions utest/test_templates/test_primitives.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'))
Expand All @@ -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, [])
Expand Down

0 comments on commit 71092a3

Please sign in to comment.