mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-12 02:21:29 +08:00
253 lines
6.7 KiB
Python
253 lines
6.7 KiB
Python
# encoding: utf-8
|
|
|
|
import ast
|
|
import os
|
|
import re
|
|
|
|
from httprunner import exceptions
|
|
from httprunner.compat import builtin_str, numeric_types, str
|
|
|
|
variable_regexp = r"\$([\w_]+)"
|
|
function_regexp = r"\$\{([\w_]+\([\$\w\.\-_ =,]*\))\}"
|
|
function_regexp_compile = re.compile(r"^([\w_]+)\(([\$\w\.\-_ =,]*)\)$")
|
|
|
|
|
|
def parse_string_value(str_value):
|
|
""" parse string to number if possible
|
|
e.g. "123" => 123
|
|
"12.2" => 12.3
|
|
"abc" => "abc"
|
|
"$var" => "$var"
|
|
"""
|
|
try:
|
|
return ast.literal_eval(str_value)
|
|
except ValueError:
|
|
return str_value
|
|
except SyntaxError:
|
|
# e.g. $var, ${func}
|
|
return str_value
|
|
|
|
|
|
def extract_variables(content):
|
|
""" extract all variable names from content, which is in format $variable
|
|
|
|
Args:
|
|
content (str): string content
|
|
|
|
Returns:
|
|
list: variables list extracted from string content
|
|
|
|
Examples:
|
|
>>> extract_variables("$variable")
|
|
["variable"]
|
|
|
|
>>> extract_variables("/blog/$postid")
|
|
["postid"]
|
|
|
|
>>> extract_variables("/$var1/$var2")
|
|
["var1", "var2"]
|
|
|
|
>>> extract_variables("abc")
|
|
[]
|
|
|
|
"""
|
|
# TODO: change variable notation from $var to {{var}}
|
|
try:
|
|
return re.findall(variable_regexp, content)
|
|
except TypeError:
|
|
return []
|
|
|
|
|
|
def extract_functions(content):
|
|
""" extract all functions from string content, which are in format ${fun()}
|
|
|
|
Args:
|
|
content (str): string content
|
|
|
|
Returns:
|
|
list: functions list extracted from string content
|
|
|
|
Examples:
|
|
>>> extract_functions("${func(5)}")
|
|
["func(5)"]
|
|
|
|
>>> extract_functions("${func(a=1, b=2)}")
|
|
["func(a=1, b=2)"]
|
|
|
|
>>> extract_functions("/api/1000?_t=${get_timestamp()}")
|
|
["get_timestamp()"]
|
|
|
|
>>> extract_functions("/api/${add(1, 2)}")
|
|
["add(1, 2)"]
|
|
|
|
>>> extract_functions("/api/${add(1, 2)}?_t=${get_timestamp()}")
|
|
["add(1, 2)", "get_timestamp()"]
|
|
|
|
"""
|
|
try:
|
|
return re.findall(function_regexp, content)
|
|
except TypeError:
|
|
return []
|
|
|
|
|
|
def parse_function(content):
|
|
""" parse function name and args from string content.
|
|
|
|
Args:
|
|
content (str): string content
|
|
|
|
Returns:
|
|
dict: function meta dict
|
|
|
|
{
|
|
"func_name": "xxx",
|
|
"args": [],
|
|
"kwargs": {}
|
|
}
|
|
|
|
Examples:
|
|
>>> parse_function("func()")
|
|
{'func_name': 'func', 'args': [], 'kwargs': {}}
|
|
|
|
>>> parse_function("func(5)")
|
|
{'func_name': 'func', 'args': [5], 'kwargs': {}}
|
|
|
|
>>> parse_function("func(1, 2)")
|
|
{'func_name': 'func', 'args': [1, 2], 'kwargs': {}}
|
|
|
|
>>> parse_function("func(a=1, b=2)")
|
|
{'func_name': 'func', 'args': [], 'kwargs': {'a': 1, 'b': 2}}
|
|
|
|
>>> parse_function("func(1, 2, a=3, b=4)")
|
|
{'func_name': 'func', 'args': [1, 2], 'kwargs': {'a':3, 'b':4}}
|
|
|
|
"""
|
|
matched = function_regexp_compile.match(content)
|
|
if not matched:
|
|
raise exceptions.FunctionNotFound("{} not found!".format(content))
|
|
|
|
function_meta = {
|
|
"func_name": matched.group(1),
|
|
"args": [],
|
|
"kwargs": {}
|
|
}
|
|
|
|
args_str = matched.group(2).strip()
|
|
if args_str == "":
|
|
return function_meta
|
|
|
|
args_list = args_str.split(',')
|
|
for arg in args_list:
|
|
arg = arg.strip()
|
|
if '=' in arg:
|
|
key, value = arg.split('=')
|
|
function_meta["kwargs"][key.strip()] = parse_string_value(value.strip())
|
|
else:
|
|
function_meta["args"].append(parse_string_value(arg))
|
|
|
|
return function_meta
|
|
|
|
|
|
def parse_validator(validator):
|
|
""" parse validator, validator maybe in two format
|
|
@param (dict) validator
|
|
format1: this is kept for compatiblity with the previous versions.
|
|
{"check": "status_code", "comparator": "eq", "expect": 201}
|
|
{"check": "$resp_body_success", "comparator": "eq", "expect": True}
|
|
format2: recommended new version
|
|
{'eq': ['status_code', 201]}
|
|
{'eq': ['$resp_body_success', True]}
|
|
@return (dict) validator info
|
|
{
|
|
"check": "status_code",
|
|
"expect": 201,
|
|
"comparator": "eq"
|
|
}
|
|
"""
|
|
if not isinstance(validator, dict):
|
|
raise exceptions.ParamsError("invalid validator: {}".format(validator))
|
|
|
|
if "check" in validator and len(validator) > 1:
|
|
# format1
|
|
check_item = validator.get("check")
|
|
|
|
if "expect" in validator:
|
|
expect_value = validator.get("expect")
|
|
elif "expected" in validator:
|
|
expect_value = validator.get("expected")
|
|
else:
|
|
raise exceptions.ParamsError("invalid validator: {}".format(validator))
|
|
|
|
comparator = validator.get("comparator", "eq")
|
|
|
|
elif len(validator) == 1:
|
|
# format2
|
|
comparator = list(validator.keys())[0]
|
|
compare_values = validator[comparator]
|
|
|
|
if not isinstance(compare_values, list) or len(compare_values) != 2:
|
|
raise exceptions.ParamsError("invalid validator: {}".format(validator))
|
|
|
|
check_item, expect_value = compare_values
|
|
|
|
else:
|
|
raise exceptions.ParamsError("invalid validator: {}".format(validator))
|
|
|
|
return {
|
|
"check": check_item,
|
|
"expect": expect_value,
|
|
"comparator": comparator
|
|
}
|
|
|
|
|
|
def parse_data(content, mapping):
|
|
""" substitute variables in content with mapping
|
|
e.g.
|
|
@params
|
|
content = {
|
|
'request': {
|
|
'url': '/api/users/$uid',
|
|
'headers': {'token': '$token'}
|
|
}
|
|
}
|
|
mapping = {"$uid": 1000}
|
|
@return
|
|
{
|
|
'request': {
|
|
'url': '/api/users/1000',
|
|
'headers': {'token': '$token'}
|
|
}
|
|
}
|
|
"""
|
|
# TODO: refactor type check
|
|
# TODO: combine this with TestcaseParser
|
|
if content is None or isinstance(content, (numeric_types, bool, type)):
|
|
return content
|
|
|
|
if isinstance(content, (list, set, tuple)):
|
|
return [
|
|
parse_data(item, mapping)
|
|
for item in content
|
|
]
|
|
|
|
if isinstance(content, dict):
|
|
substituted_data = {}
|
|
for key, value in content.items():
|
|
eval_key = parse_data(key, mapping)
|
|
eval_value = parse_data(value, mapping)
|
|
substituted_data[eval_key] = eval_value
|
|
|
|
return substituted_data
|
|
|
|
# content is in string format here
|
|
for var, value in mapping.items():
|
|
if content == var:
|
|
# content is a variable
|
|
content = value
|
|
else:
|
|
if not isinstance(value, str):
|
|
value = builtin_str(value)
|
|
content = content.replace(var, value)
|
|
|
|
return content
|