From e6dcbd95fbb6e41622463ffb6043df7706a4dadd Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 25 Aug 2017 20:08:58 +0800 Subject: [PATCH] fix #33: invoke functions in url --- ate/__init__.py | 2 +- ate/testcase.py | 103 ++++++++++++++++++++------------- tests/test_testcase.py | 125 ++++++++++++++++++++++++++--------------- 3 files changed, 144 insertions(+), 86 deletions(-) diff --git a/ate/__init__.py b/ate/__init__.py index b703f5c9..9bdd4d27 100644 --- a/ate/__init__.py +++ b/ate/__init__.py @@ -1 +1 @@ -__version__ = '0.4.1' \ No newline at end of file +__version__ = '0.5.0' \ No newline at end of file diff --git a/ate/testcase.py b/ate/testcase.py index b1e00cce..428b489f 100644 --- a/ate/testcase.py +++ b/ate/testcase.py @@ -5,10 +5,11 @@ from ate import utils from ate.exception import ParamsError variable_regexp = r"\$([\w_]+)" -function_regexp = re.compile(r"^\$\{([\w_]+)\(([\$\w_ =,]*)\)\}$") +function_regexp = r"\$\{[\w_]+\([\$\w_ =,]*\)\}" +function_regexp_compile = re.compile(r"^\$\{([\w_]+)\(([\$\w_ =,]*)\)\}$") -def get_contain_variables(content): +def extract_variables(content): """ extract all variable names from content, which is in format $variable @param (str) content @return (list) variable name list @@ -18,9 +19,12 @@ def get_contain_variables(content): /$var1/$var2 => ["var1", "var2"] abc => [] """ - return re.findall(variable_regexp, content) + try: + return re.findall(variable_regexp, content) + except TypeError: + return [] -def parse_variables(content, variable_mapping): +def eval_content_variables(content, variable_mapping): """ replace all variables of string content with mapping value. @param (str) content @return (str) parsed content @@ -35,8 +39,8 @@ def parse_variables(content, variable_mapping): /$var_1/$var_2/var3 => "/abc/def/var3" ${func($var_1, $var_2, xyz)} => "${func(abc, def, xyz)}" """ - variable_name_list = get_contain_variables(content) - for variable_name in variable_name_list: + variables_list = extract_variables(content) + for variable_name in variables_list: if variable_name not in variable_mapping: raise ParamsError( "%s is not defined in bind variables!" % variable_name) @@ -54,20 +58,52 @@ def parse_variables(content, variable_mapping): return content -def is_functon(content): - """ check if content is a function, which is in format ${func()} +def extract_functions(content): + """ extract all functions from string content, which are in format ${fun()} + Notice: extract_functions should be called after eval_content_variables, thus + there will not be any variables in given content @param (str) content - @return (bool) True or False + @return (list) functions list - e.g. ${func()} => True - ${func(5)} => True - ${func(1, 2)} => True - ${func(a=1, b=2)} => True - $abc => False - abc => False + e.g. ${func(5)} => ["${func(5)}"] + ${func(a=1, b=2)} => ["${func(a=1, b=2)}"] + /api/1000?_t=${get_timestamp()} => ["get_timestamp()"] + /api/${add(1, 2)} => ["add(1, 2)"] + "/api/${add(1, 2)}?_t=${get_timestamp()}" => ["${add(1, 2)}", "${get_timestamp()}"] """ - matched = function_regexp.match(content) - return True if matched else False + try: + return re.findall(function_regexp, content) + except TypeError: + return [] + +def eval_content_functions(content, variables_binds, functions_binds): + functions_list = extract_functions(content) + for func_content in functions_list: + function_meta = parse_function(func_content) + func_name = function_meta['func_name'] + + func = functions_binds.get(func_name) + if func is None: + raise ParamsError( + "%s is not defined in bind functions!" % func_name) + + args = function_meta.get('args', []) + kwargs = function_meta.get('kwargs', {}) + args = parse_content_with_bindings(args, variables_binds, functions_binds) + kwargs = parse_content_with_bindings(kwargs, variables_binds, functions_binds) + eval_value = func(*args, **kwargs) + + if func_content == content: + # content is a variable + content = eval_value + else: + # content contains one or many variables + content = content.replace( + func_content, + str(eval_value), 1 + ) + + return content def parse_string_value(str_value): """ parse string to number if possible @@ -99,7 +135,7 @@ def parse_function(content): "args": [], "kwargs": {} } - matched = function_regexp.match(content) + matched = function_regexp_compile.match(content) function_meta["func_name"] = matched.group(1) args_str = matched.group(2).replace(" ", "") @@ -167,8 +203,11 @@ def parse_content_with_bindings(content, variables_binds, functions_binds): if isinstance(content, dict): evaluated_data = {} for key, value in content.items(): - evaluated_data[key] = parse_content_with_bindings( + eval_key = parse_content_with_bindings( + key, variables_binds, functions_binds) + eval_value = parse_content_with_bindings( value, variables_binds, functions_binds) + evaluated_data[eval_key] = eval_value return evaluated_data @@ -178,25 +217,11 @@ def parse_content_with_bindings(content, variables_binds, functions_binds): # content is in string format here content = "" if content is None else content.strip() - if is_functon(content): - # function marker: ${func(1, 2, a=3, b=4)} - fuction_meta = parse_function(content) - func_name = fuction_meta['func_name'] + # replace functions with evaluated value + # Notice: eval_content_functions must be called before eval_content_variables + content = eval_content_functions(content, variables_binds, functions_binds) - func = functions_binds.get(func_name) - if func is None: - raise ParamsError( - "%s is not defined in bind functions!" % func_name) + # replace variables with binding value + content = eval_content_variables(content, variables_binds) - args = fuction_meta.get('args', []) - kwargs = fuction_meta.get('kwargs', {}) - args = parse_content_with_bindings(args, variables_binds, functions_binds) - kwargs = parse_content_with_bindings(kwargs, variables_binds, functions_binds) - return func(*args, **kwargs) - - elif get_contain_variables(content): - parsed_data = parse_variables(content, variables_binds) - return parsed_data - - else: - return content + return content diff --git a/tests/test_testcase.py b/tests/test_testcase.py index faef8659..3fb07a9a 100644 --- a/tests/test_testcase.py +++ b/tests/test_testcase.py @@ -1,58 +1,59 @@ +import time import unittest -from ate.exception import ParamsError from ate import testcase +from ate.exception import ParamsError class TestcaseParserUnittest(unittest.TestCase): - def test_get_contain_variables(self): + def test_extract_variables(self): self.assertEqual( - testcase.get_contain_variables("$var"), + testcase.extract_variables("$var"), ["var"] ) self.assertEqual( - testcase.get_contain_variables("$var123"), + testcase.extract_variables("$var123"), ["var123"] ) self.assertEqual( - testcase.get_contain_variables("$var_name"), + testcase.extract_variables("$var_name"), ["var_name"] ) self.assertEqual( - testcase.get_contain_variables("var"), + testcase.extract_variables("var"), [] ) self.assertEqual( - testcase.get_contain_variables("a$var"), + testcase.extract_variables("a$var"), ["var"] ) self.assertEqual( - testcase.get_contain_variables("$v ar"), + testcase.extract_variables("$v ar"), ["v"] ) self.assertEqual( - testcase.get_contain_variables(" "), + testcase.extract_variables(" "), [] ) self.assertEqual( - testcase.get_contain_variables("$abc*"), + testcase.extract_variables("$abc*"), ["abc"] ) self.assertEqual( - testcase.get_contain_variables("${func()}"), + testcase.extract_variables("${func()}"), [] ) self.assertEqual( - testcase.get_contain_variables("${func(1,2)}"), + testcase.extract_variables("${func(1,2)}"), [] ) self.assertEqual( - testcase.get_contain_variables("${gen_md5($TOKEN, $data, $random)}"), + testcase.extract_variables("${gen_md5($TOKEN, $data, $random)}"), ["TOKEN", "data", "random"] ) - def test_parse_variables(self): + def test_eval_content_variables(self): variable_mapping = { "var_1": "abc", "var_2": "def", @@ -62,67 +63,54 @@ class TestcaseParserUnittest(unittest.TestCase): "var_6": None } self.assertEqual( - testcase.parse_variables("$var_1", variable_mapping), + testcase.eval_content_variables("$var_1", variable_mapping), "abc" ) self.assertEqual( - testcase.parse_variables("var_1", variable_mapping), + testcase.eval_content_variables("var_1", variable_mapping), "var_1" ) self.assertEqual( - testcase.parse_variables("$var_1#XYZ", variable_mapping), + testcase.eval_content_variables("$var_1#XYZ", variable_mapping), "abc#XYZ" ) self.assertEqual( - testcase.parse_variables("/$var_1/$var_2/var3", variable_mapping), + testcase.eval_content_variables("/$var_1/$var_2/var3", variable_mapping), "/abc/def/var3" ) self.assertEqual( - testcase.parse_variables("/$var_1/$var_2/$var_1", variable_mapping), + testcase.eval_content_variables("/$var_1/$var_2/$var_1", variable_mapping), "/abc/def/abc" ) self.assertEqual( - testcase.parse_variables("${func($var_1, $var_2, xyz)}", variable_mapping), + testcase.eval_content_variables("${func($var_1, $var_2, xyz)}", variable_mapping), "${func(abc, def, xyz)}" ) self.assertEqual( - testcase.parse_variables("$var_3", variable_mapping), + testcase.eval_content_variables("$var_3", variable_mapping), 123 ) self.assertEqual( - testcase.parse_variables("$var_4", variable_mapping), + testcase.eval_content_variables("$var_4", variable_mapping), {"a": 1} ) self.assertEqual( - testcase.parse_variables("$var_5", variable_mapping), + testcase.eval_content_variables("$var_5", variable_mapping), True ) self.assertEqual( - testcase.parse_variables("abc$var_5", variable_mapping), + testcase.eval_content_variables("abc$var_5", variable_mapping), "abcTrue" ) self.assertEqual( - testcase.parse_variables("abc$var_4", variable_mapping), + testcase.eval_content_variables("abc$var_4", variable_mapping), "abc{'a': 1}" ) self.assertEqual( - testcase.parse_variables("$var_6", variable_mapping), + testcase.eval_content_variables("$var_6", variable_mapping), None ) - def test_is_functon(self): - self.assertTrue(testcase.is_functon("${func()}")) - self.assertTrue(testcase.is_functon("${func(5)}")) - self.assertTrue(testcase.is_functon("${func(1, 2)}")) - self.assertTrue(testcase.is_functon("${func($a, $b)}")) - self.assertTrue(testcase.is_functon("${func(a=1, b=2)}")) - self.assertTrue(testcase.is_functon("${func(1, 2, a=3, b=4)}")) - self.assertTrue(testcase.is_functon("${func(1, $b, c=$x, d=4)}")) - self.assertFalse(testcase.is_functon("${func}")) - self.assertFalse(testcase.is_functon("$abc")) - self.assertFalse(testcase.is_functon("abc")) - self.assertFalse(testcase.is_functon("${}")) - def test_parse_string_value(self): self.assertEqual(testcase.parse_string_value("123"), 123) self.assertEqual(testcase.parse_string_value("12.3"), 12.3) @@ -205,7 +193,6 @@ class TestcaseParserUnittest(unittest.TestCase): "/users/100/1000/1498?userId=1000&data=1498" ) - def test_parse_content_with_bindings_functions(self): import random, string functions_binds = { @@ -227,20 +214,66 @@ class TestcaseParserUnittest(unittest.TestCase): 3 ) + def test_extract_functions(self): + self.assertEqual( + testcase.extract_functions("${func()}"), + ["${func()}"] + ) + self.assertEqual( + testcase.extract_functions("${func(5)}"), + ["${func(5)}"] + ) + self.assertEqual( + testcase.extract_functions("${func(a=1, b=2)}"), + ["${func(a=1, b=2)}"] + ) + self.assertEqual( + testcase.extract_functions("${func(1, $b, c=$x, d=4)}"), + ["${func(1, $b, c=$x, d=4)}"] + ) + self.assertEqual( + testcase.extract_functions("/api/1000?_t=${get_timestamp()}"), + ["${get_timestamp()}"] + ) + self.assertEqual( + testcase.extract_functions("/api/${add(1, 2)}"), + ["${add(1, 2)}"] + ) + self.assertEqual( + testcase.extract_functions("/api/${add(1, 2)}?_t=${get_timestamp()}"), + ["${add(1, 2)}", "${get_timestamp()}"] + ) + self.assertEqual( + testcase.extract_functions("abc${func(1, 2, a=3, b=4)}def"), + ["${func(1, 2, a=3, b=4)}"] + ) + + def test_eval_content_functions(self): + functions_binds = { + "add_two_nums": lambda a, b=1: a + b + } + self.assertEqual( + testcase.eval_content_functions("${add_two_nums(1, 2)}", {}, functions_binds), + 3 + ) + self.assertEqual( + testcase.eval_content_functions("/api/${add_two_nums(1, 2)}", {}, functions_binds), + "/api/3" + ) + def test_parse_content_with_bindings_testcase(self): variables_binds = { "uid": "1000", "random": "A2dEx", "authorization": "a83de0ff8d2e896dbd8efb81ba14e17d", - "data": {"name": "user", "password": "123456"}, - "expected_status": 201, - "expected_success": True + "data": {"name": "user", "password": "123456"} } functions_binds = { - "add_two_nums": lambda a, b=1: a + b + "add_two_nums": lambda a, b=1: a + b, + "get_timestamp": lambda: int(time.time() * 1000) } testcase_template = { - "url": "http://127.0.0.1:5000/api/users/$uid", + "url": "http://127.0.0.1:5000/api/users/$uid/${add_two_nums(1,2)}", "method": "POST", "headers": { "Content-Type": "application/json", @@ -255,7 +288,7 @@ class TestcaseParserUnittest(unittest.TestCase): self.assertEqual( parsed_testcase["url"], - "http://127.0.0.1:5000/api/users/%s" % variables_binds["uid"] + "http://127.0.0.1:5000/api/users/1000/3" ) self.assertEqual( parsed_testcase["headers"]["authorization"],