From 8836c828058d9b7d08408d133a741ed99244d9d4 Mon Sep 17 00:00:00 2001 From: httprunner Date: Tue, 16 Apr 2019 19:20:49 +0800 Subject: [PATCH 01/15] fix: match duplicate variable/function in raw string e.g. "ABC$var_1/123$var_1/456" "ABC${func1($var_1, $var_3)}--${func1($var_1, $var_3)}" --- httprunner/__about__.py | 2 +- httprunner/parser.py | 2 ++ tests/test_parser.py | 26 ++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/httprunner/__about__.py b/httprunner/__about__.py index cc08e19be..2a95ad4ca 100644 --- a/httprunner/__about__.py +++ b/httprunner/__about__.py @@ -1,7 +1,7 @@ __title__ = 'HttpRunner' __description__ = 'One-stop solution for HTTP(S) testing.' __url__ = 'https://github.com/HttpRunner/HttpRunner' -__version__ = '2.1.1' +__version__ = '2.1.2' __author__ = 'debugtalk' __author_email__ = 'mail@debugtalk.com' __license__ = 'Apache-2.0' diff --git a/httprunner/parser.py b/httprunner/parser.py index b9c3a36c8..5d5740345 100644 --- a/httprunner/parser.py +++ b/httprunner/parser.py @@ -458,6 +458,7 @@ def __parse(self, raw_string): self.check_variables_set ) args_mapping[match_start_position] = lazy_func + match_start_position += len(func_str) # search variable like $var var_match_list = regex_findall_variables(self._string) @@ -474,6 +475,7 @@ def __parse(self, raw_string): # self._string = self._string.replace("}", "}}") self._string = self._string.replace(var, "{}", 1) args_mapping[match_start_position] = var_name + match_start_position += len(var) self._args = [args_mapping[key] for key in sorted(args_mapping.keys())] diff --git a/tests/test_parser.py b/tests/test_parser.py index fe5ad205d..00fe4358a 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -320,6 +320,11 @@ def test_lazy_string(self): self.assertEqual(var._args, ["var_1"]) self.assertEqual(var.to_value(variables_mapping), "ABCabc$") + var = parser.LazyString("ABC$var_1/123$var_1/456", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}/123{}/456") + self.assertEqual(var._args, ["var_1", "var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc/123abc/456") + var = parser.LazyString("ABC$var_1{", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC{}{") self.assertEqual(var._args, ["var_1"]) @@ -361,6 +366,27 @@ def test_lazy_string(self): self.assertEqual(var._string, "ABC{}{}") self.assertEqual(var.to_value(variables_mapping), "ABCabc123True") + var = parser.LazyString( + "ABC${func1($var_1, $var_3)}--${func1($var_1, $var_3)}", + functions_mapping, + check_variables_set + ) + self.assertEqual(var._string, "ABC{}--{}") + self.assertEqual(var.to_value(variables_mapping), "ABCabc123--abc123") + + var = parser.LazyString("ABC${func1($var_1, $var_3)}$var_1", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}{}") + self.assertEqual(var.to_value(variables_mapping), "ABCabc123abc") + + # TODO: fix + # var = parser.LazyString( + # "ABC${func1($var_1, $var_3)}$var_1--${func1($var_1, $var_3)}$var_1", + # functions_mapping, + # check_variables_set + # ) + # self.assertEqual(var._string, "ABC{}{}--{}{}") + # self.assertEqual(var.to_value(variables_mapping), "ABCabc123abc--abc123abc") + var = parser.LazyString("ABC${func1($var_1, $var_3)}DE$var_4", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC{}DE{}") self.assertEqual(var.to_value(variables_mapping), "ABCabc123DE{'a': 1}") From 57e447bdca55baab52fac6eae7efcea761949edb Mon Sep 17 00:00:00 2001 From: httprunner Date: Wed, 17 Apr 2019 15:17:36 +0800 Subject: [PATCH 02/15] fix: match duplicate variable/function in raw string e.g. "ABC${func1($var_1, $var_3)}$var_1--${func1($var_1, $var_3)}$var_1" --- httprunner/parser.py | 89 ++++++++++++++++++++++++-------------------- tests/test_parser.py | 15 ++++---- 2 files changed, 56 insertions(+), 48 deletions(-) diff --git a/httprunner/parser.py b/httprunner/parser.py index 5d5740345..f37dab2bb 100644 --- a/httprunner/parser.py +++ b/httprunner/parser.py @@ -436,48 +436,57 @@ def __parse(self, raw_string): args: ["${func2($a, $b)}", "$c"] """ - self._string = raw_string - args_mapping = {} - - # Notice: functions must be handled before variables - # search function like ${func($a, $b)} - func_match_list = regex_findall_functions(self._string) - match_start_position = 0 - for func_match in func_match_list: - func_str = "${%s(%s)}" % (func_match[0], func_match[1]) - match_start_position = raw_string.index(func_str, match_start_position) - self._string = self._string.replace(func_str, "{}", 1) - function_meta = parse_function_params(func_match[1]) - function_meta = { - "func_name": func_match[0] - } - function_meta.update(parse_function_params(func_match[1])) - lazy_func = LazyFunction( - function_meta, - self.functions_mapping, - self.check_variables_set - ) - args_mapping[match_start_position] = lazy_func - match_start_position += len(func_str) - - # search variable like $var - var_match_list = regex_findall_variables(self._string) - match_start_position = 0 - for var_name in var_match_list: - # check if any variable undefined in check_variables_set - if var_name not in self.check_variables_set: - raise exceptions.VariableNotFound(var_name) + self._args = [] + + try: + match_start_position = raw_string.index("$", 0) + self._string = raw_string[0:match_start_position] + except ValueError: + self._string = raw_string + return + + while match_start_position < len(raw_string): + + # Notice: functions must be handled before variables + # search function like ${func($a, $b)} + func_match = function_regex_compile.match(raw_string, match_start_position) + if func_match: + function_meta = parse_function_params(func_match[1]) + function_meta = { + "func_name": func_match[1] + } + function_meta.update(parse_function_params(func_match[2])) + lazy_func = LazyFunction( + function_meta, + self.functions_mapping, + self.check_variables_set + ) + self._args.append(lazy_func) + match_start_position = func_match.end() + self._string += "{}" + continue - var = "${}".format(var_name) - match_start_position = raw_string.index(var, match_start_position) - # TODO: escape '{' and '}' - # self._string = self._string.replace("{", "{{") - # self._string = self._string.replace("}", "}}") - self._string = self._string.replace(var, "{}", 1) - args_mapping[match_start_position] = var_name - match_start_position += len(var) + # search variable like $var + var_match = variable_regex_compile.match(raw_string, match_start_position) + if var_match: + var_name = var_match.group(1) + # check if any variable undefined in check_variables_set + if var_name not in self.check_variables_set: + raise exceptions.VariableNotFound(var_name) + + self._args.append(var_name) + match_start_position = var_match.end() + self._string += "{}" + continue - self._args = [args_mapping[key] for key in sorted(args_mapping.keys())] + curr_position = match_start_position + try: + # find next $ location + match_start_position = raw_string.index("$", curr_position+1) + self._string += raw_string[curr_position:match_start_position] + except ValueError: + self._string += raw_string[curr_position:] + break def __repr__(self): return "LazyString({})".format(self.raw_string) diff --git a/tests/test_parser.py b/tests/test_parser.py index 00fe4358a..e387f4028 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -378,14 +378,13 @@ def test_lazy_string(self): self.assertEqual(var._string, "ABC{}{}") self.assertEqual(var.to_value(variables_mapping), "ABCabc123abc") - # TODO: fix - # var = parser.LazyString( - # "ABC${func1($var_1, $var_3)}$var_1--${func1($var_1, $var_3)}$var_1", - # functions_mapping, - # check_variables_set - # ) - # self.assertEqual(var._string, "ABC{}{}--{}{}") - # self.assertEqual(var.to_value(variables_mapping), "ABCabc123abc--abc123abc") + var = parser.LazyString( + "ABC${func1($var_1, $var_3)}$var_1--${func1($var_1, $var_3)}$var_1", + functions_mapping, + check_variables_set + ) + self.assertEqual(var._string, "ABC{}{}--{}{}") + self.assertEqual(var.to_value(variables_mapping), "ABCabc123abc--abc123abc") var = parser.LazyString("ABC${func1($var_1, $var_3)}DE$var_4", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC{}DE{}") From c47061c612f8db7ba6eee6315008589d10fa7faf Mon Sep 17 00:00:00 2001 From: httprunner Date: Wed, 17 Apr 2019 15:29:13 +0800 Subject: [PATCH 03/15] fix: escape '{' and '}' --- httprunner/parser.py | 11 ++++++++--- tests/test_parser.py | 33 ++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/httprunner/parser.py b/httprunner/parser.py index f37dab2bb..0162da301 100644 --- a/httprunner/parser.py +++ b/httprunner/parser.py @@ -483,10 +483,15 @@ def __parse(self, raw_string): try: # find next $ location match_start_position = raw_string.index("$", curr_position+1) - self._string += raw_string[curr_position:match_start_position] + remain_string = raw_string[curr_position:match_start_position] except ValueError: - self._string += raw_string[curr_position:] - break + remain_string = raw_string[curr_position:] + # break while loop + match_start_position = len(raw_string) + + remain_string = remain_string.replace("{", "{{") + remain_string = remain_string.replace("}", "}}") + self._string += remain_string def __repr__(self): return "LazyString({})".format(self.raw_string) diff --git a/tests/test_parser.py b/tests/test_parser.py index e387f4028..e87dc1eba 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -326,21 +326,44 @@ def test_lazy_string(self): self.assertEqual(var.to_value(variables_mapping), "ABCabc/123abc/456") var = parser.LazyString("ABC$var_1{", functions_mapping, check_variables_set) - self.assertEqual(var._string, "ABC{}{") + self.assertEqual(var._string, "ABC{}{{") self.assertEqual(var._args, ["var_1"]) - # self.assertEqual(var.to_value(variables_mapping), "ABCabc{") + self.assertEqual(var.to_value(variables_mapping), "ABCabc{") + + var = parser.LazyString("ABC$var_1}", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}}}") + self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc}") var = parser.LazyString("ABC$$var_1{", functions_mapping, check_variables_set) - self.assertEqual(var._string, "ABC${}{") + self.assertEqual(var._string, "ABC${}{{") self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABC$abc{") var = parser.LazyString("ABC$var_1${", functions_mapping, check_variables_set) - self.assertEqual(var._string, "ABC{}${") + self.assertEqual(var._string, "ABC{}${{") self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc${") var = parser.LazyString("ABC$var_1${a", functions_mapping, check_variables_set) - self.assertEqual(var._string, "ABC{}${a") + self.assertEqual(var._string, "ABC{}${{a") + self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc${a") + + var = parser.LazyString("ABC$var_1$}a", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}$}}a") + self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc$}a") + + var = parser.LazyString("ABC$var_1}{a", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}}}{{a") + self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc}{a") + + var = parser.LazyString("ABC$var_1{}a", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}{{}}a") self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc{}a") var = parser.LazyString("ABC$var_1/$var_2/$var_1", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC{}/{}/{}") From a539384120a350927b7eabd6bd5537121e6b394f Mon Sep 17 00:00:00 2001 From: httprunner Date: Wed, 17 Apr 2019 15:41:53 +0800 Subject: [PATCH 04/15] fix: regex match in Python 2.7/3.3/3.4/3.5 --- httprunner/parser.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/httprunner/parser.py b/httprunner/parser.py index 0162da301..924816a37 100644 --- a/httprunner/parser.py +++ b/httprunner/parser.py @@ -451,11 +451,11 @@ def __parse(self, raw_string): # search function like ${func($a, $b)} func_match = function_regex_compile.match(raw_string, match_start_position) if func_match: - function_meta = parse_function_params(func_match[1]) + function_meta = parse_function_params(func_match.group(1)) function_meta = { - "func_name": func_match[1] + "func_name": func_match.group(1) } - function_meta.update(parse_function_params(func_match[2])) + function_meta.update(parse_function_params(func_match.group(2))) lazy_func = LazyFunction( function_meta, self.functions_mapping, From a6b94b8e37701f4d8af0c112bfc48142e4d7a92f Mon Sep 17 00:00:00 2001 From: httprunner Date: Wed, 17 Apr 2019 16:22:32 +0800 Subject: [PATCH 05/15] use $$ to escape $ notation --- httprunner/parser.py | 13 ++++++++++++- tests/test_parser.py | 10 ++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/httprunner/parser.py b/httprunner/parser.py index 924816a37..e74919acc 100644 --- a/httprunner/parser.py +++ b/httprunner/parser.py @@ -7,6 +7,8 @@ from httprunner import exceptions, utils, validator from httprunner.compat import basestring, builtin_str, numeric_types, str +# use $$ to escape $ notation +dolloar_regex_compile = re.compile(r"\$\$") # TODO: change variable notation from $var to {{var}} # $var_1 variable_regex_compile = re.compile(r"\$(\w+)") @@ -447,7 +449,16 @@ def __parse(self, raw_string): while match_start_position < len(raw_string): - # Notice: functions must be handled before variables + # Notice: notation priority + # $$ > ${func($a, $b)} > $var + + # search $$ + dollar_match = dolloar_regex_compile.match(raw_string, match_start_position) + if dollar_match: + match_start_position = dollar_match.end() + self._string += "$" + continue + # search function like ${func($a, $b)} func_match = function_regex_compile.match(raw_string, match_start_position) if func_match: diff --git a/tests/test_parser.py b/tests/test_parser.py index e87dc1eba..3c2a04552 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -336,10 +336,20 @@ def test_lazy_string(self): self.assertEqual(var.to_value(variables_mapping), "ABCabc}") var = parser.LazyString("ABC$$var_1{", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC$var_1{{") + self.assertEqual(var._args, []) + self.assertEqual(var.to_value(variables_mapping), "ABC$var_1{") + + var = parser.LazyString("ABC$$$var_1{", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC${}{{") self.assertEqual(var._args, ["var_1"]) self.assertEqual(var.to_value(variables_mapping), "ABC$abc{") + var = parser.LazyString("ABC$$$$var_1{", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC$$var_1{{") + self.assertEqual(var._args, []) + self.assertEqual(var.to_value(variables_mapping), "ABC$$var_1{") + var = parser.LazyString("ABC$var_1${", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC{}${{") self.assertEqual(var._args, ["var_1"]) From 396179aeb925eee9d52e84d6a412c8508e713f0e Mon Sep 17 00:00:00 2001 From: httprunner Date: Wed, 17 Apr 2019 16:23:38 +0800 Subject: [PATCH 06/15] update changelog for 2.1.2 --- HISTORY.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 4cd557f72..072ddac01 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,16 @@ # Release History +## 2.1.2 (2019-04-17) + +**Features** + +- use \$\$ to escape \$ notation + +**Bugfixes** + +- match duplicate variable/function in single raw string +- escape '{' and '}' notation in raw string + ## 2.1.1 (2019-04-11) **Features** From 19f2198b7ba997609b347bfd1a486f4869be8bb3 Mon Sep 17 00:00:00 2001 From: httprunner Date: Wed, 17 Apr 2019 18:20:05 +0800 Subject: [PATCH 07/15] support new variable notation: ${var} --- HISTORY.md | 1 + httprunner/parser.py | 18 ++-- tests/test_parser.py | 207 +++++++++++++++++++++++++++++++------------ 3 files changed, 164 insertions(+), 62 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 072ddac01..bb49b1c62 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -4,6 +4,7 @@ **Features** +- support new variable notation ${var} - use \$\$ to escape \$ notation **Bugfixes** diff --git a/httprunner/parser.py b/httprunner/parser.py index e74919acc..f80af2d01 100644 --- a/httprunner/parser.py +++ b/httprunner/parser.py @@ -9,10 +9,9 @@ # use $$ to escape $ notation dolloar_regex_compile = re.compile(r"\$\$") -# TODO: change variable notation from $var to {{var}} -# $var_1 -variable_regex_compile = re.compile(r"\$(\w+)") -# ${func1($var_1, $var_3)} +# variable notation, e.g. ${var} or $var +variable_regex_compile = re.compile(r"\$\{(\w+)\}|\$(\w+)") +# function notation, e.g. ${func1($var_1, $var_3)} function_regex_compile = re.compile(r"\$\{(\w+)\(([\$\w\.\-/\s=,]*)\)\}") @@ -70,7 +69,12 @@ def regex_findall_variables(content): """ try: - return variable_regex_compile.findall(content) + vars_list = [] + for var_tuple in variable_regex_compile.findall(content): + vars_list.append( + var_tuple[0] or var_tuple[1] + ) + return vars_list except TypeError: return [] @@ -477,10 +481,10 @@ def __parse(self, raw_string): self._string += "{}" continue - # search variable like $var + # search variable like ${var} or $var var_match = variable_regex_compile.match(raw_string, match_start_position) if var_match: - var_name = var_match.group(1) + var_name = var_match.group(1) or var_match.group(2) # check if any variable undefined in check_variables_set if var_name not in self.check_variables_set: raise exceptions.VariableNotFound(var_name) diff --git a/tests/test_parser.py b/tests/test_parser.py index 3c2a04552..57945547a 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -37,6 +37,10 @@ def test_regex_findall_variables(self): parser.regex_findall_variables("a$var"), ["var"] ) + self.assertEqual( + parser.regex_findall_variables("a$var${var2}$var3${var4}"), + ["var", "var2", "var3", "var4"] + ) self.assertEqual( parser.regex_findall_variables("$v ar"), ["v"] @@ -281,7 +285,7 @@ def test_eval_lazy_data(self): {"abc": "def"} ) - def test_lazy_string(self): + def test_parse_func_var_abnormal(self): variables_mapping = { "var_1": "abc", "var_2": "def", @@ -295,61 +299,37 @@ def test_lazy_string(self): "func1": lambda x,y: str(x) + str(y) } - var = parser.LazyString("ABC$var_1", functions_mapping, check_variables_set) - self.assertEqual(var._string, "ABC{}") - self.assertEqual(var._args, ["var_1"]) - self.assertEqual(var.to_value(variables_mapping), "ABCabc") - - var = parser.LazyString("ABC$var_1$var_3", functions_mapping, check_variables_set) - self.assertEqual(var._string, "ABC{}{}") - self.assertEqual(var._args, ["var_1", "var_3"]) - self.assertEqual(var.to_value(variables_mapping), "ABCabc123") - - var = parser.LazyString("ABC$var_1/$var_3", functions_mapping, check_variables_set) - self.assertEqual(var._string, "ABC{}/{}") - self.assertEqual(var._args, ["var_1", "var_3"]) - self.assertEqual(var.to_value(variables_mapping), "ABCabc/123") - - var = parser.LazyString("ABC$var_1/", functions_mapping, check_variables_set) - self.assertEqual(var._string, "ABC{}/") - self.assertEqual(var._args, ["var_1"]) - self.assertEqual(var.to_value(variables_mapping), "ABCabc/") - - var = parser.LazyString("ABC$var_1$", functions_mapping, check_variables_set) - self.assertEqual(var._string, "ABC{}$") - self.assertEqual(var._args, ["var_1"]) - self.assertEqual(var.to_value(variables_mapping), "ABCabc$") - - var = parser.LazyString("ABC$var_1/123$var_1/456", functions_mapping, check_variables_set) - self.assertEqual(var._string, "ABC{}/123{}/456") - self.assertEqual(var._args, ["var_1", "var_1"]) - self.assertEqual(var.to_value(variables_mapping), "ABCabc/123abc/456") - + # { var = parser.LazyString("ABC$var_1{", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC{}{{") self.assertEqual(var._args, ["var_1"]) self.assertEqual(var.to_value(variables_mapping), "ABCabc{") + # } var = parser.LazyString("ABC$var_1}", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC{}}}") self.assertEqual(var._args, ["var_1"]) self.assertEqual(var.to_value(variables_mapping), "ABCabc}") + # $$ var = parser.LazyString("ABC$$var_1{", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC$var_1{{") self.assertEqual(var._args, []) self.assertEqual(var.to_value(variables_mapping), "ABC$var_1{") + # $$$ var = parser.LazyString("ABC$$$var_1{", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC${}{{") self.assertEqual(var._args, ["var_1"]) self.assertEqual(var.to_value(variables_mapping), "ABC$abc{") + # $$$$ var = parser.LazyString("ABC$$$$var_1{", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC$$var_1{{") self.assertEqual(var._args, []) self.assertEqual(var.to_value(variables_mapping), "ABC$$var_1{") + # ${ var = parser.LazyString("ABC$var_1${", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC{}${{") self.assertEqual(var._args, ["var_1"]) @@ -360,45 +340,37 @@ def test_lazy_string(self): self.assertEqual(var._args, ["var_1"]) self.assertEqual(var.to_value(variables_mapping), "ABCabc${a") + # $} var = parser.LazyString("ABC$var_1$}a", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC{}$}}a") self.assertEqual(var._args, ["var_1"]) self.assertEqual(var.to_value(variables_mapping), "ABCabc$}a") + # }{ var = parser.LazyString("ABC$var_1}{a", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC{}}}{{a") self.assertEqual(var._args, ["var_1"]) self.assertEqual(var.to_value(variables_mapping), "ABCabc}{a") + # {} var = parser.LazyString("ABC$var_1{}a", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC{}{{}}a") self.assertEqual(var._args, ["var_1"]) self.assertEqual(var.to_value(variables_mapping), "ABCabc{}a") - var = parser.LazyString("ABC$var_1/$var_2/$var_1", functions_mapping, check_variables_set) - self.assertEqual(var._string, "ABC{}/{}/{}") - self.assertEqual(var._args, ["var_1", "var_2", "var_1"]) - self.assertEqual(var.to_value(variables_mapping), "ABCabc/def/abc") - - var = parser.LazyString("func1($var_1, $var_3)", functions_mapping, check_variables_set) - self.assertEqual(var._string, "func1({}, {})") - self.assertEqual(var._args, ["var_1", "var_3"]) - self.assertEqual(var.to_value(variables_mapping), "func1(abc, 123)") - - var = parser.LazyString("${func1($var_1, $var_3)}", functions_mapping, check_variables_set) - self.assertEqual(var._string, "{}") - self.assertIsInstance(var._args[0], parser.LazyFunction) - self.assertEqual(var.to_value(variables_mapping), "abc123") - - var = parser.LazyString("ABC${func1($var_1, $var_3)}DE", functions_mapping, check_variables_set) - self.assertEqual(var._string, "ABC{}DE") - self.assertIsInstance(var._args[0], parser.LazyFunction) - self.assertEqual(var.to_value(variables_mapping), "ABCabc123DE") - - var = parser.LazyString("ABC${func1($var_1, $var_3)}$var_5", functions_mapping, check_variables_set) - self.assertEqual(var._string, "ABC{}{}") - self.assertEqual(var.to_value(variables_mapping), "ABCabc123True") - + def test_parse_func_var_duplicate(self): + variables_mapping = { + "var_1": "abc", + "var_2": "def", + "var_3": 123, + "var_4": {"a": 1}, + "var_5": True, + "var_6": None + } + check_variables_set = variables_mapping.keys() + functions_mapping = { + "func1": lambda x,y: str(x) + str(y) + } var = parser.LazyString( "ABC${func1($var_1, $var_3)}--${func1($var_1, $var_3)}", functions_mapping, @@ -419,6 +391,34 @@ def test_lazy_string(self): self.assertEqual(var._string, "ABC{}{}--{}{}") self.assertEqual(var.to_value(variables_mapping), "ABCabc123abc--abc123abc") + def test_parse_function(self): + variables_mapping = { + "var_1": "abc", + "var_2": "def", + "var_3": 123, + "var_4": {"a": 1}, + "var_5": True, + "var_6": None + } + check_variables_set = variables_mapping.keys() + functions_mapping = { + "func1": lambda x,y: str(x) + str(y) + } + + var = parser.LazyString("${func1($var_1, $var_3)}", functions_mapping, check_variables_set) + self.assertEqual(var._string, "{}") + self.assertIsInstance(var._args[0], parser.LazyFunction) + self.assertEqual(var.to_value(variables_mapping), "abc123") + + var = parser.LazyString("ABC${func1($var_1, $var_3)}DE", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}DE") + self.assertIsInstance(var._args[0], parser.LazyFunction) + self.assertEqual(var.to_value(variables_mapping), "ABCabc123DE") + + var = parser.LazyString("ABC${func1($var_1, $var_3)}$var_5", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}{}") + self.assertEqual(var.to_value(variables_mapping), "ABCabc123True") + var = parser.LazyString("ABC${func1($var_1, $var_3)}DE$var_4", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC{}DE{}") self.assertEqual(var.to_value(variables_mapping), "ABCabc123DE{'a': 1}") @@ -427,6 +427,103 @@ def test_lazy_string(self): self.assertEqual(var._string, "ABC{}{}") self.assertEqual(var.to_value(variables_mapping), "ABCTrueabc123") + + def test_parse_variable(self): + """ variable format ${var} and $var + """ + variables_mapping = { + "var_1": "abc", + "var_2": "def", + "var_3": 123, + "var_4": {"a": 1}, + "var_5": True, + "var_6": None + } + check_variables_set = variables_mapping.keys() + functions_mapping = {} + + # format: $var + var = parser.LazyString("ABC$var_1", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}") + self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc") + + var = parser.LazyString("ABC$var_1$var_3", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}{}") + self.assertEqual(var._args, ["var_1", "var_3"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc123") + + var = parser.LazyString("ABC$var_1/$var_3", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}/{}") + self.assertEqual(var._args, ["var_1", "var_3"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc/123") + + var = parser.LazyString("ABC$var_1/", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}/") + self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc/") + + var = parser.LazyString("ABC$var_1$", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}$") + self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc$") + + var = parser.LazyString("ABC$var_1/123$var_1/456", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}/123{}/456") + self.assertEqual(var._args, ["var_1", "var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc/123abc/456") + + var = parser.LazyString("ABC$var_1/$var_2/$var_1", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}/{}/{}") + self.assertEqual(var._args, ["var_1", "var_2", "var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc/def/abc") + + var = parser.LazyString("func1($var_1, $var_3)", functions_mapping, check_variables_set) + self.assertEqual(var._string, "func1({}, {})") + self.assertEqual(var._args, ["var_1", "var_3"]) + self.assertEqual(var.to_value(variables_mapping), "func1(abc, 123)") + + # format: ${var} + var = parser.LazyString("ABC${var_1}", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}") + self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc") + + var = parser.LazyString("ABC${var_1}${var_3}", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}{}") + self.assertEqual(var._args, ["var_1", "var_3"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc123") + + var = parser.LazyString("ABC${var_1}/${var_3}", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}/{}") + self.assertEqual(var._args, ["var_1", "var_3"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc/123") + + var = parser.LazyString("ABC${var_1}/", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}/") + self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc/") + + var = parser.LazyString("ABC${var_1}123", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}123") + self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc123") + + var = parser.LazyString("ABC${var_1}/123${var_1}/456", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}/123{}/456") + self.assertEqual(var._args, ["var_1", "var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc/123abc/456") + + var = parser.LazyString("ABC${var_1}/${var_2}/${var_1}", functions_mapping, check_variables_set) + self.assertEqual(var._string, "ABC{}/{}/{}") + self.assertEqual(var._args, ["var_1", "var_2", "var_1"]) + self.assertEqual(var.to_value(variables_mapping), "ABCabc/def/abc") + + var = parser.LazyString("func1(${var_1}, ${var_3})", functions_mapping, check_variables_set) + self.assertEqual(var._string, "func1({}, {})") + self.assertEqual(var._args, ["var_1", "var_3"]) + self.assertEqual(var.to_value(variables_mapping), "func1(abc, 123)") + def test_parse_data_multiple_identical_variables(self): variables_mapping = { "userid": 100, From 81448db31808de80525f2d40dbd3d9c122558975 Mon Sep 17 00:00:00 2001 From: httprunner Date: Wed, 17 Apr 2019 19:01:11 +0800 Subject: [PATCH 08/15] fix: check if variable exist with $$ notation --- httprunner/parser.py | 28 +++++++++++++++++++++------- tests/test_parser.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/httprunner/parser.py b/httprunner/parser.py index f80af2d01..7485127d3 100644 --- a/httprunner/parser.py +++ b/httprunner/parser.py @@ -31,18 +31,32 @@ def parse_string_value(str_value): return str_value -def is_variable_exist(content): +def is_var_or_func_exist(content): + """ check if variable or function exist + """ if not isinstance(content, basestring): return False - return True if variable_regex_compile.search(content) else False + try: + match_start_position = content.index("$", 0) + except ValueError: + return False + + while match_start_position < len(content): + dollar_match = dolloar_regex_compile.match(content, match_start_position) + if dollar_match: + match_start_position = dollar_match.end() + continue + func_match = function_regex_compile.match(content, match_start_position) + if func_match: + return True -def is_function_exist(content): - if not isinstance(content, basestring): - return False + var_match = variable_regex_compile.match(content, match_start_position) + if var_match: + return True - return True if function_regex_compile.search(content) else False + return False def regex_findall_variables(content): @@ -580,7 +594,7 @@ def prepare_lazy_data(content, functions_mapping=None, check_variables_set=None, elif isinstance(content, basestring): # content is in string format here - if not (is_variable_exist(content) or is_function_exist(content)): + if not is_var_or_func_exist(content): # content is neither variable nor function return content diff --git a/tests/test_parser.py b/tests/test_parser.py index 57945547a..3dfb78470 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -703,6 +703,37 @@ def test_parse_variables_mapping_2(self): self.assertEqual(parsed_testcase["num2"], 6) self.assertEqual(parsed_testcase["num1"], 3) + def test_is_var_or_func_exist(self): + self.assertTrue(parser.is_var_or_func_exist("$var")) + self.assertTrue(parser.is_var_or_func_exist("${var}")) + self.assertTrue(parser.is_var_or_func_exist("$var${var}")) + self.assertFalse(parser.is_var_or_func_exist("${var")) + self.assertFalse(parser.is_var_or_func_exist("$$var")) + self.assertFalse(parser.is_var_or_func_exist("var$$0")) + self.assertTrue(parser.is_var_or_func_exist("var$$$0")) + self.assertFalse(parser.is_var_or_func_exist("var$$$$0")) + self.assertTrue(parser.is_var_or_func_exist("${func()}")) + self.assertTrue(parser.is_var_or_func_exist("${func($a)}")) + self.assertTrue(parser.is_var_or_func_exist("${func($a)}$b")) + + def test_parse_variables_mapping_dollar_notation(self): + variables = { + "varA": "123$varB", + "varB": "456$$0", + "varC": "${sum_two($a, $b)}", + "a": 1, + "b": 2, + "c": "abc" + } + functions = { + "sum_two": sum_two + } + prepared_variables = parser.prepare_lazy_data(variables, functions, variables.keys()) + parsed_testcase = parser.parse_variables_mapping(prepared_variables) + self.assertEqual(parsed_testcase["varB"], "456$$0") + self.assertEqual(parsed_testcase["varA"], "123456$$0") + self.assertEqual(parsed_testcase["varC"], 3) + def test_prepare_lazy_data(self): variables = { "host": "https://httprunner.org", From 3339ebfef5726d8be36879137c1285f06b0f5ac6 Mon Sep 17 00:00:00 2001 From: httprunner Date: Wed, 17 Apr 2019 19:20:45 +0800 Subject: [PATCH 09/15] try Python 3.7 for travis CI --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4c1d86b98..a99d0121a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,11 @@ python: - 3.4 - 3.5 - 3.6 - - 3.7-dev +matrix: + include: + - python: 3.7 + dist: xenial + sudo: true install: - pip install pipenv --upgrade-strategy=only-if-needed - pipenv install --dev --skip-lock From 9d1b0d37b6157b922ef98b78b39762d8618a6f3d Mon Sep 17 00:00:00 2001 From: httprunner Date: Wed, 17 Apr 2019 20:36:20 +0800 Subject: [PATCH 10/15] fix: replace $$ notation with $ and consider it as normal char. --- httprunner/parser.py | 4 +++- tests/test_parser.py | 27 +++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/httprunner/parser.py b/httprunner/parser.py index 7485127d3..8faf1b5e1 100644 --- a/httprunner/parser.py +++ b/httprunner/parser.py @@ -596,7 +596,9 @@ def prepare_lazy_data(content, functions_mapping=None, check_variables_set=None, # content is in string format here if not is_var_or_func_exist(content): # content is neither variable nor function - return content + # replace $$ notation with $ and consider it as normal char. + # e.g. abc => abc, abc$$def => abc$def, abc$$$$def$$h => abc$$def$h + return content.replace("$$", "$") functions_mapping = functions_mapping or {} check_variables_set = check_variables_set or set() diff --git a/tests/test_parser.py b/tests/test_parser.py index 3dfb78470..6ad569ce9 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -730,8 +730,8 @@ def test_parse_variables_mapping_dollar_notation(self): } prepared_variables = parser.prepare_lazy_data(variables, functions, variables.keys()) parsed_testcase = parser.parse_variables_mapping(prepared_variables) - self.assertEqual(parsed_testcase["varB"], "456$$0") - self.assertEqual(parsed_testcase["varA"], "123456$$0") + self.assertEqual(parsed_testcase["varA"], "123456$0") + self.assertEqual(parsed_testcase["varB"], "456$0") self.assertEqual(parsed_testcase["varC"], 3) def test_prepare_lazy_data(self): @@ -770,6 +770,29 @@ def test_prepare_lazy_data_not_found(self): variables.keys() ) + def test_prepare_lazy_data_dual_dollar(self): + variables = { + "num0": 123, + "var1": "abc$$num0", + "var2": "abc$$$num0", + "var3": "abc$$$$num0", + } + functions = { + "sum_two": sum_two + } + prepared_variables = parser.prepare_lazy_data( + variables, + functions, + variables.keys() + ) + self.assertEqual(prepared_variables["var1"], "abc$num0") + self.assertIsInstance(prepared_variables["var2"], parser.LazyString) + self.assertEqual(prepared_variables["var3"], "abc$$num0") + + parsed_variables = parser.parse_variables_mapping(prepared_variables) + self.assertEqual(parsed_variables["var1"], "abc$num0") + self.assertEqual(parsed_variables["var2"], "abc$123") + self.assertEqual(parsed_variables["var3"], "abc$$num0") class TestParser(unittest.TestCase): From 1ef324b7d0d948118e06ee56bd83e4f6d056f8e1 Mon Sep 17 00:00:00 2001 From: httprunner Date: Wed, 17 Apr 2019 21:06:42 +0800 Subject: [PATCH 11/15] fix: print_info value is None TypeError: unsupported format string passed to NoneType.__format__ --- httprunner/utils.py | 2 ++ tests/test_utils.py | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/httprunner/utils.py b/httprunner/utils.py index 89e728b5e..e8e907472 100644 --- a/httprunner/utils.py +++ b/httprunner/utils.py @@ -385,6 +385,8 @@ def print_info(info_mapping): continue elif isinstance(value, (dict, list)): value = json.dumps(value) + elif value is None: + value = "None" if is_py2: if isinstance(key, unicode): diff --git a/tests/test_utils.py b/tests/test_utils.py index 9a804b2af..581384249 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -263,3 +263,15 @@ def test_cartesian_product_empty(self): parameters_content_list = [] product_list = utils.gen_cartesian_product(*parameters_content_list) self.assertEqual(product_list, []) + + def test_print_info(self): + info_mapping = { + "a": 1, + "t": (1, 2), + "b": { + "b1": 123 + }, + "c": None, + "d": [4, 5] + } + utils.print_info(info_mapping) From b07cae6b2c2497d5847ebc6c1d31d80a34b92e46 Mon Sep 17 00:00:00 2001 From: httprunner Date: Thu, 18 Apr 2019 15:37:55 +0800 Subject: [PATCH 12/15] fix: escape braces --- httprunner/parser.py | 12 +++++++----- tests/test_parser.py | 10 ++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/httprunner/parser.py b/httprunner/parser.py index 8faf1b5e1..9dabd4e2b 100644 --- a/httprunner/parser.py +++ b/httprunner/parser.py @@ -458,11 +458,15 @@ def __parse(self, raw_string): """ self._args = [] + def escape_braces(origin_string): + return origin_string.replace("{", "{{").replace("}", "}}") + try: match_start_position = raw_string.index("$", 0) - self._string = raw_string[0:match_start_position] + begin_string = raw_string[0:match_start_position] + self._string = escape_braces(begin_string) except ValueError: - self._string = raw_string + self._string = escape_braces(raw_string) return while match_start_position < len(raw_string): @@ -518,9 +522,7 @@ def __parse(self, raw_string): # break while loop match_start_position = len(raw_string) - remain_string = remain_string.replace("{", "{{") - remain_string = remain_string.replace("}", "}}") - self._string += remain_string + self._string += escape_braces(remain_string) def __repr__(self): return "LazyString({})".format(self.raw_string) diff --git a/tests/test_parser.py b/tests/test_parser.py index 6ad569ce9..0675f198d 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -305,6 +305,16 @@ def test_parse_func_var_abnormal(self): self.assertEqual(var._args, ["var_1"]) self.assertEqual(var.to_value(variables_mapping), "ABCabc{") + var = parser.LazyString("{ABC$var_1{}a}", functions_mapping, check_variables_set) + self.assertEqual(var._string, "{{ABC{}{{}}a}}") + self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "{ABCabc{}a}") + + var = parser.LazyString("AB{C$var_1{}a}", functions_mapping, check_variables_set) + self.assertEqual(var._string, "AB{{C{}{{}}a}}") + self.assertEqual(var._args, ["var_1"]) + self.assertEqual(var.to_value(variables_mapping), "AB{Cabc{}a}") + # } var = parser.LazyString("ABC$var_1}", functions_mapping, check_variables_set) self.assertEqual(var._string, "ABC{}}}") From a0a54cfc85b18ab35ebad6d178dfb667e2a5c929 Mon Sep 17 00:00:00 2001 From: httprunner Date: Thu, 18 Apr 2019 16:28:11 +0800 Subject: [PATCH 13/15] fix: display api name when running api as testcase --- httprunner/parser.py | 3 +++ tests/httpbin/api/get_headers.yml | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/httprunner/parser.py b/httprunner/parser.py index 9dabd4e2b..08c0bdbdf 100644 --- a/httprunner/parser.py +++ b/httprunner/parser.py @@ -1254,6 +1254,9 @@ def parse_tests(tests_mapping): # encapsulate api as a testcase for api_content in tests_mapping["apis"]: testcase = { + "config": { + "name": api_content.get("name") + }, "teststeps": [api_content] } parsed_testcase = _parse_testcase(testcase, project_mapping) diff --git a/tests/httpbin/api/get_headers.yml b/tests/httpbin/api/get_headers.yml index 9871035f2..a05e5bd5b 100644 --- a/tests/httpbin/api/get_headers.yml +++ b/tests/httpbin/api/get_headers.yml @@ -1,9 +1,11 @@ -name: headers +name: get headers base_url: http://httpbin.org +variables: + expected_status_code: 200 request: url: /headers method: GET validate: - - eq: ["status_code", 200] + - eq: ["status_code", $expected_status_code] - eq: [content.headers.Host, "httpbin.org"] From 81488a9455d46154c61dec60bb14b1461b204e30 Mon Sep 17 00:00:00 2001 From: httprunner Date: Fri, 19 Apr 2019 11:05:52 +0800 Subject: [PATCH 14/15] update unittest --- tests/testsuites/create_users_with_parameters.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testsuites/create_users_with_parameters.yml b/tests/testsuites/create_users_with_parameters.yml index cd608af7b..a134e9566 100644 --- a/tests/testsuites/create_users_with_parameters.yml +++ b/tests/testsuites/create_users_with_parameters.yml @@ -1,5 +1,5 @@ config: - name: create users with uid + name: create users with parameters variables: device_sn: ${gen_random_string(15)} base_url: "http://127.0.0.1:5000" From 0f1925ecd38c89d2826977c97cc90036633c3be0 Mon Sep 17 00:00:00 2001 From: httprunner Date: Fri, 19 Apr 2019 11:13:26 +0800 Subject: [PATCH 15/15] rename HISTORY to CHANGELOG --- HISTORY.md => CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) rename HISTORY.md => CHANGELOG.md (94%) diff --git a/HISTORY.md b/CHANGELOG.md similarity index 94% rename from HISTORY.md rename to CHANGELOG.md index bb49b1c62..dfcfbf061 100644 --- a/HISTORY.md +++ b/CHANGELOG.md @@ -6,11 +6,14 @@ - support new variable notation ${var} - use \$\$ to escape \$ notation +- add Python 3.7 for travis CI **Bugfixes** - match duplicate variable/function in single raw string - escape '{' and '}' notation in raw string +- print_info: TypeError when value is None +- display api name when running api as testcase ## 2.1.1 (2019-04-11)