Skip to content

Commit

Permalink
Interface for setting formatters
Browse files Browse the repository at this point in the history
  • Loading branch information
aaltat committed Oct 20, 2023
1 parent 8241598 commit 45f5731
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 109 deletions.
76 changes: 12 additions & 64 deletions assertionengine/formatter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import re
from typing import Dict, List

from robot.api.deco import keyword # type: ignore
from abc import ABC, abstractmethod
from typing import List


def _strip(value: str) -> str:
Expand All @@ -28,68 +27,17 @@ def _case_insensitive(value: str) -> str:
}


FormatterTypes = Dict[str, List[str]]


class Formatter:
def __init__(self, ctx):
self.ctx = ctx

@property
def keyword_formatters(self):
return self.ctx._keyword_formatters

@keyword_formatters.setter
def keyword_formatters(self, formatters: dict):
self.ctx._keyword_formatters = formatters

@property
def keywords(self) -> dict:
return self.ctx.keywords

@keyword
def set_assertion_formatters(self, formatters: FormatterTypes):
"""Set keywords formatters for assertions.
``formatters`` is dictionary, where key is the keyword name
where formatters are applied. Dictionary value is a list of
formatter which are applied. Using keywords always replaces
existing formatters for keywords.
Supported formatter are: `normalize space`, `strip` and
`apply to expected`.
Example:
| `Set Assertion Formatters` {"Keyword Name": ["strip", "normalize spaces"]}
"""
if not self._are_library_keywords(formatters):
raise AssertionError("Could not find keyword from library.")
formatters_with_methods = {}
for formatter in formatters:
formatters_with_methods[
self._get_library_keyword(formatter)
] = self._get_formatters(formatters[formatter])
self.keyword_formatters = formatters_with_methods

def _are_library_keywords(self, formatters: dict) -> bool:
return all(self._library_keyword(item) for item in formatters)

def _library_keyword(self, name: str) -> bool:
name = self._normalize_keyword(name)
if self._get_library_keyword(name):
return True
return False
class Formatter(ABC):
@abstractmethod
def get_formatter(self, keyword):
...

def _get_library_keyword(self, name: str):
name = self._normalize_keyword(name)
for kw in self.keywords:
kw_normalized = self._normalize_keyword(kw)
if kw_normalized == name:
return self.keywords[kw]
return None
@abstractmethod
def set_formatter(self, keyword, formatter):
...

def _normalize_keyword(self, name: str):
def normalize_keyword(self, name: str):
return name.lower().replace(" ", "_")

def _get_formatters(self, kw_formatter: List) -> List:
return [FormatRules[formatter] for formatter in kw_formatter]
def formatters_to_method(self, kw_formatter: List) -> List:
return [FormatRules[formatter.lower()] for formatter in kw_formatter]
35 changes: 29 additions & 6 deletions atest/TestLibrary.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
from typing import Optional, Any
import logging
from typing import Optional, Any, List, Callable

from robot.api.deco import keyword
from robotlibcore import DynamicCore

from assertionengine import verify_assertion, AssertionOperator, Formatter

LOG = logging.getLogger(__name__)


class TestLibrary(DynamicCore):
def __init__(self):
self._keyword_formatters = {}
DynamicCore.__init__(self, [Formatter(self)])
DynamicCore.__init__(self, [])
self.formatter = LibFormatter(self)

@keyword
def is_equal(
Expand All @@ -19,7 +23,8 @@ def is_equal(
assertion_expected: Any = None,
message: str = None,
):
formatter = self._keyword_formatters.get(self.is_equal)
formatter = self.formatter.get_formatter(self.is_equal)
LOG.info(formatter)
return verify_assertion(
value,
assertion_operator,
Expand All @@ -37,8 +42,8 @@ def is_equal_as_number(
assertion_expected: Any = None,
message: str = None,
):
print(f"integer: '{integer}' and type: {type(integer)}")
formatter = self._keyword_formatters.get(self.is_equal_as_number)
LOG.info(f"integer: '{integer}' and type: {type(integer)}")
formatter = self.formatter.get_formatter(self.is_equal_as_number)
return verify_assertion(
integer,
assertion_operator,
Expand All @@ -49,5 +54,23 @@ def is_equal_as_number(
)

@keyword
def Get_keyword_Formatters(self) -> dict:
def get_keyword_formatters(self) -> dict:
return self._keyword_formatters

@keyword
def set_assertion_formatter(self, keyword: str, *formatters: str):
kw_method = self.keywords.get(self.formatter.normalize_keyword(keyword))
self.formatter.set_formatter(kw_method, *formatters)


class LibFormatter(Formatter):
def __init__(self, library: TestLibrary):
self.library = library

def set_formatter(self, keyword: Callable, *formatter: str):
formatter = self.formatters_to_method(list(formatter))
self.library._keyword_formatters[keyword] = list(formatter)

def get_formatter(self, keyword: Callable):
LOG.info(self.library._keyword_formatters.get(keyword))
return self.library._keyword_formatters.get(keyword)
37 changes: 23 additions & 14 deletions atest/assertion_equal.robot
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,31 @@ Library TestLibrary.py
Set Assertion Formatters
${formatters} = Get Keyword Formatters
Should Be Empty ${formatters}
Set Assertion Formatters
... {"Is Equal": ["strip", "apply to expected"], "get Keyword formatters": ["strip", "normalize spaces"]}
Set Assertion Formatter
... is EQual
... strip apply to expected
Set Assertion Formatter
... is_equAl
... strip apply to expected
Set Assertion Formatter
... get Keyword formatters
... strip normalize spaces
${formatters} = Get Keyword Formatters
Length Should Be ${formatters} 2
FOR ${formatter} IN @{formatters}
Length Should Be ${formatters}[${formatter}] 2
END

Test With Assertion Formatter Apply To Expected
Set Assertion Formatters
... {"Is Equal": ["normalize spaces", "apply to expected"]}
Set Assertion Formatter
... Is Equal
... normalize spaces apply to expected
Is Equal A${SPACE*2}B equal A${SPACE*4}B

Test Fail With Assertion Formatter Apply To Expected
Set Assertion Formatters
... {"Is Equal": ["normalize spaces", "apply to expected"]}
Set Assertion Formatter
... Is Equal
... normalize spaces apply to expected
TRY
Is Equal A${SPACE*2}B equal A${SPACE*4}C
EXCEPT Prefix message 'A B' (str) should be 'A C' (str)
Expand All @@ -30,14 +39,14 @@ Test Fail With Assertion Formatter Apply To Expected

Setting Assertion Formatters For Not Existing Keyword Should Fail
TRY
Set Assertion Formatters {"Not Here": ["strip", "apply to expected"]}
Set Assertion Formatter Not Here strip apply to expected
EXCEPT Could not find keyword from library.
Log Error Message Correct
END

Setting Assertion Formatters For Not Existing Formatter Should Fail
TRY
Set Assertion Formatters {"Is Equal": ["strip", "not here"]}
Set Assertion Formatter Is Equal strip not here
EXCEPT KeyError: 'not here'
Log Error Message Correct
END
Expand All @@ -48,28 +57,28 @@ Values Are Equal
Is Equal One Equals One

Values Are Equal With Formatter
Set Assertion Formatters {"Is Equal": ["strip", "normalize spaces"]}
Set Assertion Formatter Is Equal strip normalize spaces
Is Equal ${SPACE}${SPACE}1${SPACE}${SPACE}1${SPACE}1${SPACE}${SPACE} equal
... 1${SPACE}1${SPACE}1

Values Are Equal With Case Insensitive Formatter
Set Assertion Formatters {"Is Equal": ["case insensitive"]}
Set Assertion Formatter Is Equal case insensitive
Is Equal FOOBAR equal foobar
TRY
Is Equal FOOBAR equal foobaR
EXCEPT Prefix message 'foobar' (str) should be 'foobaR' (str)
Log Error Message Correct
END
Set Assertion Formatters {"Is Equal": ["case insensitive", "apply to expected"]}
Set Assertion Formatter Is Equal case insensitive apply to expected
Is Equal FOOBAR equal foobaR

Values Are Equal With Formatter For Expected
Set Assertion Formatters {"Is Equal": ["strip", "apply to expected"]}
Set Assertion Formatter Is Equal strip apply to expected
Is Equal 1${SPACE}1${SPACE}1 equal
... ${SPACE * 2}1${SPACE}1${SPACE}1${SPACE * 2}

Formatter Fails When Value Is Not Corrent Type
Set Assertion Formatters {"Is Equal As Number": ["strip", "normalize spaces"]}
Set Assertion Formatter Is Equal As Number strip normalize spaces
TRY
Is Equal As Number ${SPACE}1${SPACE} == ${1}
EXCEPT AttributeError: 'int' object has no attribute 'strip'
Expand All @@ -91,7 +100,7 @@ Values Are Equal Fails Witn Different Spaces
END

Values Are Equal Fails With Formatter
Set Assertion Formatters {"Is Equal": ["strip", "normalize spaces"]}
Set Assertion Formatter Is Equal strip normalize spaces
TRY
Is Equal ${SPACE}1${SPACE}1 == ${SPACE}1${SPACE}2
EXCEPT Prefix message '1 1' (str) should be ' 1 2' (str)
Expand Down
4 changes: 3 additions & 1 deletion tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ def lint_robot(ctx):
def lint(ctx, error=False):
"""Lint Robot Framework test data and Python code."""
print("Lint python")
black_command = "black --config ./pyproject.toml assertionengine/ tasks.py atest/"
black_command = (
"black --config ./pyproject.toml assertionengine/ tasks.py atest/ utest/"
)
ruff_command = "ruff assertionengine"

if error:
Expand Down
55 changes: 40 additions & 15 deletions utest/test_assertion_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,7 @@ def test_equals():
"1 😀a for 🥳❤️",
),
_validate_operator(
AssertionOperator["=="],
"1​👽  🥳〿❤️ÄÖÅ",
"1 😀a for 🥳❤️",
message="Other"
AssertionOperator["=="], "1​👽  🥳〿❤️ÄÖÅ", "1 😀a for 🥳❤️", message="Other"
),
]
verify_all("Test equals", results)
Expand All @@ -68,8 +65,15 @@ def test_not_equals():
_validate_operator(
AssertionOperator["!="], "actual", "actual", "partial error message"
),
_validate_operator(AssertionOperator["!="], " actual ", "expected", formatters=[_strip]),
_validate_operator(AssertionOperator["!="], " actual ", " actual ", formatters=[_strip, _apply_to_expected]),
_validate_operator(
AssertionOperator["!="], " actual ", "expected", formatters=[_strip]
),
_validate_operator(
AssertionOperator["!="],
" actual ",
" actual ",
formatters=[_strip, _apply_to_expected],
),
]
verify_all("Not equal", results)

Expand All @@ -80,15 +84,19 @@ def test_contains():
_validate_operator(AssertionOperator["contains"], "actual", "nope", "custom"),
_validate_operator(AssertionOperator["*="], "actual", "tual"),
_validate_operator(AssertionOperator["*="], "actual", "nope"),
_validate_operator(AssertionOperator["*="], "actual ", "nope", formatters=[_strip]),
_validate_operator(
AssertionOperator["*="], "actual ", "nope", formatters=[_strip]
),
]
verify_all("Contains", results)


def test_not_contains():
results = [
_validate_operator(AssertionOperator["not contains"], "actual", "xxx"),
_validate_operator(AssertionOperator["not contains"], " actual", "xxx", formatters=[_strip]),
_validate_operator(
AssertionOperator["not contains"], " actual", "xxx", formatters=[_strip]
),
_validate_operator(AssertionOperator["not contains"], "actual", "t", "custom"),
]
verify_all("Not contains", results)
Expand Down Expand Up @@ -143,7 +151,9 @@ def test_match():
_validate_operator(
AssertionOperator["matches"], "Actual\nmultiline", "/(\\d)+/"
),
_validate_operator(AssertionOperator["matches"], "Actual ", "ual$", formatters=[_strip]),
_validate_operator(
AssertionOperator["matches"], "Actual ", "ual$", formatters=[_strip]
),
]
verify_all("match", results)

Expand All @@ -165,7 +175,9 @@ def test_validate(with_suite):
results = [
_validate_operator(AssertionOperator("validate"), 1, "0 < value < 2"),
_validate_operator(AssertionOperator("validate"), 1, "value == 'hello'"),
_validate_operator(AssertionOperator("validate"), 1, "value == 'hello'", formatters=[_strip]),
_validate_operator(
AssertionOperator("validate"), 1, "value == 'hello'", formatters=[_strip]
),
]
verify_all("validate", results)

Expand All @@ -176,7 +188,9 @@ def test_then(with_suite):
verify_assertion(8, then_op, "value + 3") == 11,
verify_assertion(2, then_op, "value + 3") == 5,
verify_assertion("René", then_op, "'Hello ' + value + '!'") == "Hello René!",
verify_assertion("René ", then_op, "'Hello ' + value + '!'", formatters=[_strip]),
verify_assertion(
"René ", then_op, "'Hello ' + value + '!'", formatters=[_strip]
),
]
verify_all("then", results)

Expand All @@ -195,7 +209,9 @@ def test_start_with():
AssertionOperator["^="], "Hel[4,5]?[1-9]+ Robots", "Hel[4,5]?[1-"
),
_validate_operator(AssertionOperator["^="], "Hel[4,5]?[1-9]+ Robots", ".*"),
_validate_operator(AssertionOperator["^="], " Hello Robots ", "Hello", formatters=[_strip]),
_validate_operator(
AssertionOperator["^="], " Hello Robots ", "Hello", formatters=[_strip]
),
]
verify_all("start with", results)

Expand All @@ -208,7 +224,9 @@ def test_ends_with():
AssertionOperator["$="], "Hel[4,5]?[1-9]+ Robots", "[1-9]+ Robots"
),
_validate_operator(AssertionOperator["$="], "Hel[4,5]?[1-9]+ Robots", ".*"),
_validate_operator(AssertionOperator["$="], " Hello Robots ", "Robots", formatters=[_strip]),
_validate_operator(
AssertionOperator["$="], " Hello Robots ", "Robots", formatters=[_strip]
),
]
verify_all("ends with", results)

Expand All @@ -223,10 +241,17 @@ def test_invalid_operator():


def _validate_operator(
operator: AssertionOperator, actual, expected, message="", custom_message="", formatters=None
operator: AssertionOperator,
actual,
expected,
message="",
custom_message="",
formatters=None,
):
try:
return verify_assertion(actual, operator, expected, message, custom_message, formatters)
return verify_assertion(
actual, operator, expected, message, custom_message, formatters
)
except Exception as error:
return error

Expand Down
Loading

0 comments on commit 45f5731

Please sign in to comment.