Skip to content
Merged
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
54 changes: 15 additions & 39 deletions doc/commenting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -288,45 +288,21 @@ The rule definition must follow the syntax which is defined by a regex search wh

.. code-block:: python

'is_greater_than': r'\b(is_greater_than)\b: \d+(\.\d+)?' # is_greater_than: 1
'is_greater_than_or_equal_to': r'\b(is_greater_than_or_equal_to)\b: \d+(\.\d+)?' # is_greater_than_or_equal_to: 1
'is_less_than_or_equal_to': r'\b(is_less_than_or_equal_to)\b: \d+(\.\d+)?' # is_less_than_or_equal_to: 10
'is_less_than': r'\b(is_less_than)\b: \d+(\.\d+)?' # is_less_than: 2
'is_equal': r'\b(is_equal)\b: \d+(\.\d+)?' # is_equal: 1
'is_different': r'\b(is_different)\b: \d+(\.\d+)?' # is_different: 2
'is_global_unique': r'\b(is_global_unique)\b' # is_global_unique
'refers': r'\b(refers)\b' # refers
'is_iso_country_code': r'\b(is_iso_country_code)\b' # is_iso_country_code
'first_element': r'\b(first_element)\b: \{.*: \d+\.\d+\}' # first_element: {is_equal: 0.13, is_greater_than: 0.13}
'last_element': r'\b(last_element)\b: \{.*: \d+\.\d+\}' # last_element: {is_equal: 0.13, is_greater_than: 0.13}
'is_optional': r'\b(is_optional)\b' # is_optional
'check_if': r'\b(check_if)\b: \[\{.*: \d+(\.\d+)?, target: .*}, \{do_check: \{.*: \d+(\.\d+)?}}]' # check_if: [{is_equal: 2, is_greater_than: 3, target: this.y}, {do_check: {is_equal: 1, is_less_than: 3}}]

You can check the correctness of these regular expression on `regex101 <https://regex101.com/r/6tomm6/16>`_.


.. is_greater_than: 2
.. is_greater_than: 2.23
.. is_greater_than_or_equal_to: 1
.. is_greater_than_or_equal_to: 1.12
.. is_less_than_or_equal_to: 10
.. is_less_than_or_equal_to: 10.123
.. is_less_than: 2
.. is_less_than: 2.321
.. is_equal: 1
.. is_equal: 1.312
.. is_different: 2
.. is_different: 2.2122
.. is_global_unique
.. refers
.. is_iso_country_code
.. first_element: {is_equal: 3, is_greater: 2}
.. first_element: {is_equal: 0.13, is_greater: 0.13}
.. last_element: {is_equal: 3, is_greater: 2}
.. last_element: {is_equal: 0.13, is_greater: 0.13}
.. check_if: [{is_equal: 2, is_greater_than: 3, target: this.y}, {do_check: {is_equal: 1, is_less_than: 3}}]
.. is_set

'is_greater_than': r'^[ ]\b(is_greater_than)\b: ([\s\d]+)$' # is_greater_than: 1
'is_greater_than_or_equal_to': r'^[ ]\b(is_greater_than_or_equal_to)\b: ([\s\d]+)$' # is_greater_than_or_equal_to: 1
'is_less_than_or_equal_to': r'^[ ]\b(is_less_than_or_equal_to)\b: ([\s\d]+)$' # is_less_than_or_equal_to: 10
'is_less_than': r'^[ ]\b(is_less_than)\b: ([\s\d]+)$' # is_less_than: 2
'is_equal': r'^[ ]\b(is_equal_to)\b: ([\s\d]+)$' # is_equal_to: 1
'is_different': r'^[ ]\b(is_different_to)\b: ([\s\d]+)$' # is_different_to: 2
'is_global_unique': r'^[ ]\b(is_globally_unique)\b' # is_globally_unique
'refers': r'^[ ]\b(refers_to)\b' # refers_to: DetectedObject
'is_iso_country_code': r'^[ ]\b(is_iso_country_code)\b' # is_iso_country_code
'first_element': r'^[ ]\b(first_element)\b' # first_element height is_equal_to 0.13
'last_element': r'^[ ]\b(last_element)\b' # last_element width is_equal_to 0.13
'check_if': r'^[ ](\bcheck_if\b)(.*\belse do_check\b)' # check_if this.type is_equal_to 2 else do_check is_set

You can check the correctness of these regular expression on `regex101 <https://regex101.com/r/P4KeuO/1>`_.


Commenting with doxygen references
------------------------------------
Expand Down
24 changes: 12 additions & 12 deletions rules.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
is_greater_than: '\b(is_greater_than)\b: '
is_greater_than_or_equal_to: '\b(is_greater_than_or_equal_to)\b: '
is_less_than_or_equal_to: '\b(is_less_than_or_equal_to)\b: '
is_less_than: '\b(is_less_than)\b: '
is_equal_to: '\b(is_equal_to)\b: '
is_different_to: '\b(is_different_to)\b: '
is_globally_unique: '\b(is_globally_unique)\b'
refers_to: '\b(refers_to)\b'
is_iso_country_code: '\b(is_iso_country_code)\b'
first_element: '\b(first_element)\b'
last_element: '\b(last_element)\b'
check_if: '(\bcheck_if\b)(.*\belse do_check\b)'
is_greater_than: '^[ ]\b(is_greater_than)\b: ([\s\d]+)$'
is_greater_than_or_equal_to: '^[ ]\b(is_greater_than_or_equal_to)\b: ([\s\d]+)$'
is_less_than_or_equal_to: '^[ ]\b(is_less_than_or_equal_to)\b: ([\s\d]+)$'
is_less_than: '^[ ]\b(is_less_than)\b: ([\s\d]+)$'
is_equal_to: '^[ ]\b(is_equal_to)\b: ([\s\d]+)$'
is_different_to: '^[ ]\b(is_different_to)\b: ([\s\d]+)$'
is_globally_unique: '^[ ]\b(is_globally_unique)\b'
refers_to: '^[ ]\b(refers_to)\b'
is_iso_country_code: '^[ ]\b(is_iso_country_code)\b'
first_element: '^[ ]\b(first_element)\b'
last_element: '^[ ]\b(last_element)\b'
check_if: '^[ ](\bcheck_if\b)(.*\belse do_check\b)'
133 changes: 133 additions & 0 deletions tests/test_rules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import re
import glob
import unittest
import yaml

PROTO_FILES = glob.glob("*.proto")

class TestRules(unittest.TestCase):
""" Test class for units documentation. """

def test_rules_compliance(self):
''' Test rule compliance syntax of proto files. '''

with open(r'rules.yml') as rules_file:
RULES_DICT = yaml.load(rules_file, Loader=yaml.BaseLoader)

for file in PROTO_FILES:
with open(file, "rt") as fin, self.subTest(file=file):
line_number = 0
numMessage = 0
lineruleCount = 0
foundruleCount = 0
saveStatement = ""
isEnum = False

for line in fin:
line_number += 1

# Divide statement and comment. Concatenate multi line statements.
# Search for comment ("//").
matchComment = re.search("//", line)
if matchComment is not None:
statement = line[:matchComment.start()]
comment = line[matchComment.end():]
else:
statement = line
comment = ""

# Add part of the statement from last line.
statement = saveStatement + " " + statement
saveStatement = ""

# New line is not necessary. Remove for a better output.
statement = statement.replace("\n", "")
comment = comment.replace("\n", "")

# Is statement complete
matchSep = re.search(r"[{};]", statement)
if matchSep is None:
saveStatement = statement
statement = ""
else:
saveStatement = statement[matchSep.end():]
statement = statement[:matchSep.end()]

# Search for "message".
matchMessage = re.search(r"\bmessage\b", statement)
if matchMessage is not None:
# a new message or a new nested message
numMessage += 1
self.assertFalse(foundruleCount > 0 or lineruleCount > 0, file + f" in line {str(line_number-1)}: message should not have rules for '{statement.strip()}'")
endOfLine = statement[matchMessage.end():]
matchName = re.search(r"\b\w[\S]*\b", endOfLine)

elif re.search(r"\bextend\b", statement) is not None:
# treat extend as message
numMessage += 1
else:
# Check field names
if numMessage > 0:
matchName = re.search(r"\b\w[\S]*\b\s*=", statement)
if matchName is not None:
checkName = statement[matchName.start():matchName.end()-1]
# Check field message type (remove field name)
type = statement.replace(checkName, "")
matchName = re.search(r"\b\w[\S\.]*\s*=", type)

if isEnum:
matchName = re.search(r"\b\w[\S:]+\b", statement)
if matchName is not None:
checkName = statement[matchName.start():matchName.end()]
self.assertFalse(foundruleCount > 0 or lineruleCount > 0, file + f" in line {str(line_number-1)}: enum field should not have rules for '{statement.strip()}'")

# Search for "enum".
matchEnum = re.search(r"\benum\b", statement)
if matchEnum is not None:
isEnum = True
endOfLine = statement[matchEnum.end():]
matchName = re.search(r"\b\w[\S]*\b", endOfLine)
if matchName is not None:
self.assertFalse(foundruleCount > 0 or lineruleCount > 0, file + f" in line {str(line_number-1)}: enum should not have rules for '{statement.strip()}'")

# Search for a closing brace.
matchClosingBrace = re.search("}", statement)
if numMessage > 0 and matchClosingBrace is not None:
numMessage -= 1

if isEnum is True and matchClosingBrace is not None:
isEnum = False

if matchComment is not None:
if re.search(r"^[ ]\\\bendrules\b$", comment) is not None:
endRule = True

if re.search(r"^[ ]\\\brules\b$", comment) is not None:
hasRule = True
lineruleCount = 0
foundruleCount = 0

if not endRule and comment != '':
for rulename, ruleregex in RULES_DICT.items():
if re.search(ruleregex, comment):
foundruleCount += 1

elif len(saveStatement) == 0:
if numMessage > 0:
if statement.find(";") != -1:
statement = statement.strip()
self.assertFalse(hasRule and lineruleCount != foundruleCount and endRule and lineruleCount-foundruleCount-1>0, file + " in line " + str(line_number) + ": "+str(lineruleCount-foundruleCount-1)+" defined rule(s) does not exists for: '"+statement+"'")
self.assertFalse(hasRule and not endRule, file + " in line " + str(line_number) + ": \\endrules statement does not exists for: '"+statement+"'. Check spacing and rule syntax.")
self.assertFalse(not hasRule and endRule, file + " in line " + str(line_number) + ": \\rules statement does not exists for: '"+statement+"'. Check spacing and rule syntax.")
self.assertFalse(not hasRule and not endRule and lineruleCount < foundruleCount, file + " in line " + str(line_number) + ": rules found but no statements (\\rules and \\endrules) around it for: '"+statement+"'")
lineruleCount = 0
foundruleCount = 0

hasRule = False
endRule = False

if hasRule and not endRule or not hasRule and endRule:
lineruleCount += 1

if __name__ == '__main__':
unittest.main()