clean up pytest

pull/635/head
samczsun 4 years ago
parent b703ce39ea
commit fabe573cb0
  1. 591
      tests/test_ast_parsing.py

@ -11,20 +11,501 @@ from deepdiff import DeepDiff
from slither import Slither from slither import Slither
# these solc versions only support legacy ast format
LEGACY_SOLC_VERS = [f"0.4.{v}" for v in range(12)] LEGACY_SOLC_VERS = [f"0.4.{v}" for v in range(12)]
SLITHER_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TEST_ROOT = os.path.join(SLITHER_ROOT, "tests", "ast-parsing")
# these are tests that are currently failing right now
XFAIL = [
"emit_0.4.0_legacy",
"emit_0.4.1_legacy",
"emit_0.4.2_legacy",
"emit_0.4.3_legacy",
"emit_0.4.4_legacy",
"emit_0.4.5_legacy",
"emit_0.4.6_legacy",
"emit_0.4.7_legacy",
"emit_0.4.8_legacy",
"emit_0.4.9_legacy",
"emit_0.4.10_legacy",
"emit_0.4.11_legacy",
"emit_0.4.12_legacy",
"emit_0.4.12_compact",
"emit_0.4.13_legacy",
"emit_0.4.13_compact",
"emit_0.4.14_legacy",
"emit_0.4.14_compact",
"emit_0.4.15_legacy",
"emit_0.4.15_compact",
"emit_0.4.16_legacy",
"emit_0.4.16_compact",
"emit_0.4.17_legacy",
"emit_0.4.17_compact",
"emit_0.4.18_legacy",
"emit_0.4.18_compact",
"emit_0.4.19_legacy",
"emit_0.4.19_compact",
"emit_0.4.20_legacy",
"emit_0.4.20_compact",
"emit_0.4.21_legacy",
"emit_0.4.21_compact",
"emit_0.4.22_legacy",
"emit_0.4.22_compact",
"emit_0.4.23_legacy",
"emit_0.4.23_compact",
"emit_0.4.24_legacy",
"emit_0.4.24_compact",
"emit_0.4.25_legacy",
"emit_0.4.25_compact",
"emit_0.4.26_legacy",
"emit_0.4.26_compact",
"for_0.4.0_legacy",
"for_0.4.1_legacy",
"for_0.4.2_legacy",
"for_0.4.3_legacy",
"for_0.4.4_legacy",
"for_0.4.5_legacy",
"for_0.4.6_legacy",
"for_0.4.7_legacy",
"for_0.4.8_legacy",
"for_0.4.9_legacy",
"for_0.4.10_legacy",
"for_0.4.11_legacy",
"for_0.4.12_legacy",
"for_0.4.13_legacy",
"for_0.4.14_legacy",
"for_0.4.15_legacy",
"for_0.4.16_legacy",
"for_0.4.17_legacy",
"for_0.4.18_legacy",
"for_0.4.19_legacy",
"for_0.4.20_legacy",
"for_0.4.21_legacy",
"for_0.4.22_legacy",
"for_0.4.23_legacy",
"for_0.4.24_legacy",
"for_0.4.25_legacy",
"for_0.4.26_legacy",
"for_0.5.0_legacy",
"for_0.5.1_legacy",
"for_0.5.2_legacy",
"for_0.5.3_legacy",
"for_0.5.4_legacy",
"for_0.5.5_legacy",
"for_0.5.6_legacy",
"for_0.5.7_legacy",
"for_0.5.8_legacy",
"for_0.5.9_legacy",
"for_0.5.10_legacy",
"for_0.5.11_legacy",
"for_0.5.12_legacy",
"for_0.5.13_legacy",
"for_0.5.14_legacy",
"for_0.5.15_legacy",
"for_0.5.16_legacy",
"for_0.5.17_legacy",
"for_0.6.0_legacy",
"for_0.6.1_legacy",
"for_0.6.2_legacy",
"for_0.6.3_legacy",
"for_0.6.4_legacy",
"for_0.6.5_legacy",
"for_0.6.6_legacy",
"for_0.6.7_legacy",
"for_0.6.8_legacy",
"for_0.6.9_legacy",
"for_0.6.10_legacy",
"for_0.6.11_legacy",
"for_0.6.12_legacy",
"for_0.7.0_legacy",
"for_0.7.1_legacy",
"function_0.6.0_legacy",
"function_0.6.1_legacy",
"function_0.6.2_legacy",
"function_0.6.3_legacy",
"function_0.6.4_legacy",
"function_0.6.5_legacy",
"function_0.6.6_legacy",
"function_0.6.7_legacy",
"function_0.6.8_legacy",
"function_0.6.9_legacy",
"function_0.6.10_legacy",
"function_0.6.11_legacy",
"function_0.6.12_legacy",
"function_0.7.0_legacy",
"function_0.7.1_legacy",
"function_0.7.1_compact",
"functioncall_0.4.0_legacy",
"functioncall_0.4.1_legacy",
"functioncall_0.4.2_legacy",
"functioncall_0.4.3_legacy",
"functioncall_0.4.4_legacy",
"functioncall_0.4.5_legacy",
"functioncall_0.4.6_legacy",
"functioncall_0.4.7_legacy",
"functioncall_0.4.8_legacy",
"functioncall_0.4.9_legacy",
"functioncall_0.4.10_legacy",
"functioncall_0.4.11_legacy",
"functioncall_0.4.12_legacy",
"functioncall_0.4.12_compact",
"functioncall_0.4.13_legacy",
"functioncall_0.4.13_compact",
"functioncall_0.4.14_legacy",
"functioncall_0.4.14_compact",
"functioncall_0.4.15_legacy",
"functioncall_0.4.15_compact",
"functioncall_0.4.16_legacy",
"functioncall_0.4.16_compact",
"functioncall_0.4.17_legacy",
"functioncall_0.4.17_compact",
"functioncall_0.4.18_legacy",
"functioncall_0.4.18_compact",
"functioncall_0.4.19_legacy",
"functioncall_0.4.19_compact",
"functioncall_0.4.20_legacy",
"functioncall_0.4.20_compact",
"functioncall_0.4.21_legacy",
"functioncall_0.4.21_compact",
"functioncall_0.4.22_legacy",
"functioncall_0.4.22_compact",
"functioncall_0.4.23_legacy",
"functioncall_0.4.23_compact",
"functioncall_0.4.24_legacy",
"functioncall_0.4.24_compact",
"functioncall_0.4.25_legacy",
"functioncall_0.4.25_compact",
"functioncall_0.4.26_legacy",
"functioncall_0.4.26_compact",
"functioncall_0.5.0_legacy",
"functioncall_0.5.1_legacy",
"functioncall_0.5.2_legacy",
"functioncall_0.5.3_legacy",
"functioncall_0.5.4_legacy",
"functioncall_0.5.5_legacy",
"functioncall_0.5.6_legacy",
"functioncall_0.5.7_legacy",
"functioncall_0.5.8_legacy",
"functioncall_0.5.9_legacy",
"functioncall_0.5.10_legacy",
"functioncall_0.5.11_legacy",
"functioncall_0.5.12_legacy",
"functioncall_0.5.13_legacy",
"functioncall_0.5.14_legacy",
"functioncall_0.5.15_legacy",
"functioncall_0.5.16_legacy",
"functioncall_0.5.17_legacy",
"functioncall_0.6.0_legacy",
"functioncall_0.6.1_legacy",
"functioncall_0.6.2_legacy",
"functioncall_0.6.2_compact",
"functioncall_0.6.3_legacy",
"functioncall_0.6.3_compact",
"functioncall_0.6.4_legacy",
"functioncall_0.6.4_compact",
"functioncall_0.6.5_legacy",
"functioncall_0.6.5_compact",
"functioncall_0.6.6_legacy",
"functioncall_0.6.6_compact",
"functioncall_0.6.7_legacy",
"functioncall_0.6.7_compact",
"functioncall_0.6.8_legacy",
"functioncall_0.6.8_compact",
"functioncall_0.6.9_legacy",
"functioncall_0.6.9_compact",
"functioncall_0.6.10_legacy",
"functioncall_0.6.10_compact",
"functioncall_0.6.11_legacy",
"functioncall_0.6.11_compact",
"functioncall_0.6.12_legacy",
"functioncall_0.6.12_compact",
"functioncall_0.7.0_legacy",
"functioncall_0.7.0_compact",
"functioncall_0.7.1_legacy",
"functioncall_0.7.1_compact",
"import_0.4.0_legacy",
"import_0.4.1_legacy",
"import_0.4.2_legacy",
"import_0.4.3_legacy",
"import_0.4.4_legacy",
"import_0.4.5_legacy",
"import_0.4.6_legacy",
"import_0.4.7_legacy",
"import_0.4.8_legacy",
"import_0.4.9_legacy",
"import_0.4.10_legacy",
"import_0.4.11_legacy",
"import_0.4.12_legacy",
"import_0.4.12_compact",
"import_0.4.13_legacy",
"import_0.4.13_compact",
"import_0.4.14_legacy",
"import_0.4.14_compact",
"import_0.4.15_legacy",
"import_0.4.15_compact",
"import_0.4.16_legacy",
"import_0.4.16_compact",
"import_0.4.17_legacy",
"import_0.4.17_compact",
"import_0.4.18_legacy",
"import_0.4.18_compact",
"import_0.4.19_legacy",
"import_0.4.19_compact",
"import_0.4.20_legacy",
"import_0.4.20_compact",
"import_0.4.21_legacy",
"import_0.4.21_compact",
"import_0.4.22_legacy",
"import_0.4.22_compact",
"import_0.4.23_legacy",
"import_0.4.23_compact",
"import_0.4.24_legacy",
"import_0.4.24_compact",
"import_0.4.25_legacy",
"import_0.4.25_compact",
"import_0.4.26_legacy",
"import_0.4.26_compact",
"import_0.5.0_legacy",
"import_0.5.0_compact",
"import_0.5.1_legacy",
"import_0.5.1_compact",
"import_0.5.2_legacy",
"import_0.5.2_compact",
"import_0.5.3_legacy",
"import_0.5.3_compact",
"import_0.5.4_legacy",
"import_0.5.4_compact",
"import_0.5.5_legacy",
"import_0.5.5_compact",
"import_0.5.6_legacy",
"import_0.5.6_compact",
"import_0.5.7_legacy",
"import_0.5.7_compact",
"import_0.5.8_legacy",
"import_0.5.8_compact",
"import_0.5.9_legacy",
"import_0.5.9_compact",
"import_0.5.10_legacy",
"import_0.5.10_compact",
"import_0.5.11_legacy",
"import_0.5.11_compact",
"import_0.5.12_legacy",
"import_0.5.12_compact",
"import_0.5.13_legacy",
"import_0.5.13_compact",
"import_0.5.14_legacy",
"import_0.5.14_compact",
"import_0.5.15_legacy",
"import_0.5.15_compact",
"import_0.5.16_legacy",
"import_0.5.16_compact",
"import_0.5.17_legacy",
"import_0.5.17_compact",
"import_0.6.0_legacy",
"import_0.6.0_compact",
"import_0.6.1_legacy",
"import_0.6.1_compact",
"import_0.6.2_legacy",
"import_0.6.2_compact",
"import_0.6.3_legacy",
"import_0.6.3_compact",
"import_0.6.4_legacy",
"import_0.6.4_compact",
"import_0.6.5_legacy",
"import_0.6.5_compact",
"import_0.6.6_legacy",
"import_0.6.6_compact",
"import_0.6.7_legacy",
"import_0.6.7_compact",
"import_0.6.8_legacy",
"import_0.6.8_compact",
"import_0.6.9_legacy",
"import_0.6.9_compact",
"import_0.6.10_legacy",
"import_0.6.10_compact",
"import_0.6.11_legacy",
"import_0.6.11_compact",
"import_0.6.12_legacy",
"import_0.6.12_compact",
"import_0.7.0_legacy",
"import_0.7.0_compact",
"import_0.7.1_legacy",
"import_0.7.1_compact",
"indexrangeaccess_0.6.1_legacy",
"indexrangeaccess_0.6.2_legacy",
"indexrangeaccess_0.6.3_legacy",
"indexrangeaccess_0.6.4_legacy",
"indexrangeaccess_0.6.5_legacy",
"indexrangeaccess_0.6.6_legacy",
"indexrangeaccess_0.6.7_legacy",
"indexrangeaccess_0.6.8_legacy",
"indexrangeaccess_0.6.9_legacy",
"indexrangeaccess_0.6.10_legacy",
"indexrangeaccess_0.6.11_legacy",
"indexrangeaccess_0.6.12_legacy",
"indexrangeaccess_0.7.0_legacy",
"indexrangeaccess_0.7.1_legacy",
"literal_0.7.0_legacy",
"literal_0.7.0_compact",
"literal_0.7.1_legacy",
"literal_0.7.1_compact",
"memberaccess_0.6.8_legacy",
"memberaccess_0.6.9_legacy",
"memberaccess_0.6.10_legacy",
"memberaccess_0.6.11_legacy",
"memberaccess_0.6.12_legacy",
"memberaccess_0.7.0_legacy",
"memberaccess_0.7.1_legacy",
"struct_0.6.0_legacy",
"struct_0.6.1_legacy",
"struct_0.6.2_legacy",
"struct_0.6.3_legacy",
"struct_0.6.4_legacy",
"struct_0.6.5_legacy",
"struct_0.6.6_legacy",
"struct_0.6.7_legacy",
"struct_0.6.8_legacy",
"struct_0.6.9_legacy",
"struct_0.6.10_legacy",
"struct_0.6.11_legacy",
"struct_0.6.12_legacy",
"struct_0.7.0_legacy",
"struct_0.7.1_legacy",
"trycatch_0.6.0_legacy",
"trycatch_0.6.1_legacy",
"trycatch_0.6.2_legacy",
"trycatch_0.6.3_legacy",
"trycatch_0.6.4_legacy",
"trycatch_0.6.5_legacy",
"trycatch_0.6.6_legacy",
"trycatch_0.6.7_legacy",
"trycatch_0.6.8_legacy",
"trycatch_0.6.9_legacy",
"trycatch_0.6.10_legacy",
"trycatch_0.6.11_legacy",
"trycatch_0.6.12_legacy",
"trycatch_0.7.0_legacy",
"trycatch_0.7.1_legacy",
"variable_0.6.5_legacy",
"variable_0.6.5_compact",
"variable_0.6.6_legacy",
"variable_0.6.6_compact",
"variable_0.6.7_legacy",
"variable_0.6.7_compact",
"variable_0.6.8_legacy",
"variable_0.6.8_compact",
"variable_0.6.9_legacy",
"variable_0.6.9_compact",
"variable_0.6.10_legacy",
"variable_0.6.10_compact",
"variable_0.6.11_legacy",
"variable_0.6.11_compact",
"variable_0.6.12_legacy",
"variable_0.6.12_compact",
"variable_0.7.0_legacy",
"variable_0.7.0_compact",
"variable_0.7.1_legacy",
"variable_0.7.1_compact",
"variabledeclaration_0.4.0_legacy",
"variabledeclaration_0.4.1_legacy",
"variabledeclaration_0.4.2_legacy",
"variabledeclaration_0.4.3_legacy",
"variabledeclaration_0.4.4_legacy",
"variabledeclaration_0.4.5_legacy",
"variabledeclaration_0.4.6_legacy",
"variabledeclaration_0.4.7_legacy",
"variabledeclaration_0.4.8_legacy",
"variabledeclaration_0.4.9_legacy",
"variabledeclaration_0.4.10_legacy",
"variabledeclaration_0.4.11_legacy",
"variabledeclaration_0.4.12_legacy",
"variabledeclaration_0.4.12_compact",
"variabledeclaration_0.4.13_legacy",
"variabledeclaration_0.4.13_compact",
"variabledeclaration_0.4.14_legacy",
"variabledeclaration_0.4.14_compact",
"variabledeclaration_0.4.15_legacy",
"variabledeclaration_0.4.15_compact",
"variabledeclaration_0.4.16_legacy",
"variabledeclaration_0.4.16_compact",
"variabledeclaration_0.4.17_legacy",
"variabledeclaration_0.4.17_compact",
"variabledeclaration_0.4.18_legacy",
"variabledeclaration_0.4.18_compact",
"variabledeclaration_0.4.19_legacy",
"variabledeclaration_0.4.19_compact",
"variabledeclaration_0.4.20_legacy",
"variabledeclaration_0.4.20_compact",
"variabledeclaration_0.4.21_legacy",
"variabledeclaration_0.4.21_compact",
"variabledeclaration_0.4.22_legacy",
"variabledeclaration_0.4.22_compact",
"variabledeclaration_0.4.23_legacy",
"variabledeclaration_0.4.23_compact",
"variabledeclaration_0.4.24_legacy",
"variabledeclaration_0.4.24_compact",
"variabledeclaration_0.4.25_legacy",
"variabledeclaration_0.4.25_compact",
"variabledeclaration_0.4.26_legacy",
"variabledeclaration_0.4.26_compact",
"variabledeclaration_0.5.0_legacy",
"variabledeclaration_0.5.1_legacy",
"variabledeclaration_0.5.2_legacy",
"variabledeclaration_0.5.3_legacy",
"variabledeclaration_0.5.4_legacy",
"variabledeclaration_0.5.5_legacy",
"variabledeclaration_0.5.6_legacy",
"variabledeclaration_0.5.7_legacy",
"variabledeclaration_0.5.8_legacy",
"variabledeclaration_0.5.9_legacy",
"variabledeclaration_0.5.10_legacy",
"variabledeclaration_0.5.11_legacy",
"variabledeclaration_0.5.12_legacy",
"variabledeclaration_0.5.13_legacy",
"variabledeclaration_0.5.14_legacy",
"variabledeclaration_0.5.15_legacy",
"variabledeclaration_0.5.16_legacy",
"variabledeclaration_0.5.17_legacy",
"variabledeclaration_0.6.0_legacy",
"variabledeclaration_0.6.1_legacy",
"variabledeclaration_0.6.2_legacy",
"variabledeclaration_0.6.3_legacy",
"variabledeclaration_0.6.4_legacy",
"variabledeclaration_0.6.5_legacy",
"variabledeclaration_0.6.6_legacy",
"variabledeclaration_0.6.7_legacy",
"variabledeclaration_0.6.8_legacy",
"variabledeclaration_0.6.9_legacy",
"variabledeclaration_0.6.10_legacy",
"variabledeclaration_0.6.11_legacy",
"variabledeclaration_0.6.12_legacy",
"variabledeclaration_0.7.0_legacy",
"variabledeclaration_0.7.1_legacy",
]
def get_solc_versions() -> List[str]: def get_solc_versions() -> List[str]:
"""
get a list of all the supported versions of solidity, sorted from earliest to latest
:return: ascending list of versions, for example ["0.4.0", "0.4.1", ...]
"""
result = subprocess.run(["solc", "--versions"], stdout=subprocess.PIPE, check=True) result = subprocess.run(["solc", "--versions"], stdout=subprocess.PIPE, check=True)
solc_versions = result.stdout.decode("utf-8").split("\n") solc_versions = result.stdout.decode("utf-8").split("\n")
# remove empty strings if any # there's an extra newline so just remove all empty strings
solc_versions = [version for version in solc_versions if version != ""] solc_versions = [version for version in solc_versions if version != ""]
solc_versions.reverse() solc_versions.reverse()
return solc_versions return solc_versions
def get_tests(solc_versions) -> Dict[str, List[str]]: def get_tests(solc_versions) -> Dict[str, List[str]]:
"""
parse the list of testcases on disk
:param solc_versions: the list of valid solidity versions
:return: a dictionary of test id to list of base solidity versions supported
"""
slither_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) slither_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
test_dir = os.path.join(slither_root, "tests", "ast-parsing") test_dir = os.path.join(slither_root, "tests", "ast-parsing")
@ -57,86 +538,112 @@ def get_tests(solc_versions) -> Dict[str, List[str]]:
return tests return tests
TestItem = namedtuple("TestItem",
["solc_version", Item = namedtuple(
"TestItem",
[
"test_id",
"base_ver",
"solc_ver",
"is_legacy", "is_legacy",
"test_file", ],
"expected_file"]) )
def get_all_test() -> List[TestItem]:
def get_all_test() -> List[Item]:
"""
generate a list of testcases by testing each test id with every solidity version for both legacy and compact ast
:return: the testcases
"""
solc_versions = get_solc_versions() solc_versions = get_solc_versions()
tests = get_tests(solc_versions) tests = get_tests(solc_versions)
slither_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
test_dir = os.path.join(slither_root, "tests", "ast-parsing")
ret = [] ret = []
for test, vers in tests.items(): for test, base_vers in tests.items():
print("running test", test, vers) print(f"generating testcases id={test} vers={base_vers}")
ver_idx = 0
for ver in solc_versions: base_ver_idx = 0
if ver_idx + 1 < len(vers) and vers[ver_idx + 1] == ver:
ver_idx += 1
test_file = os.path.join(test_dir, f"{test}-{vers[ver_idx]}.sol")
for solc_ver in solc_versions:
# if it's time to move to the next base version, do it now
if base_ver_idx + 1 < len(base_vers) and base_vers[base_ver_idx + 1] == solc_ver:
base_ver_idx += 1
for legacy_json in [True, False]: for legacy_json in [True, False]:
if not legacy_json and ver in LEGACY_SOLC_VERS: if not legacy_json and solc_ver in LEGACY_SOLC_VERS:
continue continue
flavor = "legacy" if legacy_json else "compact" ret.append(
expected_file = os.path.join(test_dir, "expected", f"{test}-{ver}-{flavor}.json") Item(
test_id=test,
ret.append(TestItem(solc_version=ver, base_ver=base_vers[base_ver_idx],
solc_ver=solc_ver,
is_legacy=legacy_json, is_legacy=legacy_json,
test_file=test_file, )
expected_file=expected_file)) )
return ret return ret
def id_test(test_item: TestItem):
return test_item.test_file + "_" + str(test_item.is_legacy) + "_" + test_item.solc_version + "_" def id_test(test_item: Item):
flavor = "legacy" if test_item.is_legacy else "compact"
return f"{test_item.test_id}_{test_item.solc_ver}_{flavor}"
def generate_output(sl: Slither) -> Dict[str, Dict[str, str]]:
output = {}
for contract in sl.contracts:
output[contract.name] = {}
for func_or_modifier in contract.functions + contract.modifiers:
output[contract.name][
func_or_modifier.full_name
] = func_or_modifier.slithir_cfg_to_dot_str(skip_expressions=True)
return output
ALL_TESTS = get_all_test() ALL_TESTS = get_all_test()
@pytest.mark.parametrize("test_item", ALL_TESTS, ids=id_test) @pytest.mark.parametrize("test_item", ALL_TESTS, ids=id_test)
def test_parsing(test_item: TestItem): def test_parsing(test_item: Item):
flavor = "legacy" if test_item.is_legacy else "compact"
test_file = os.path.join(TEST_ROOT, f"{test_item.test_id}-{test_item.base_ver}.sol")
expected_file = os.path.join(
TEST_ROOT, "expected", f"{test_item.test_id}-{test_item.solc_ver}-{flavor}.json"
)
if id_test(test_item) in XFAIL:
pytest.xfail("this test needs to be fixed")
# hacky hack hack to pick the solc version we want
env = dict(os.environ) env = dict(os.environ)
env["SOLC_VERSION"] = test_item.solc_version env["SOLC_VERSION"] = test_item.solc_ver
os.environ.clear() os.environ.clear()
os.environ.update(env) os.environ.update(env)
sl = Slither(test_item.test_file, sl = Slither(
test_file,
solc_force_legacy_json=test_item.is_legacy, solc_force_legacy_json=test_item.is_legacy,
disallow_partial=True, disallow_partial=True,
skip_analyze=True) skip_analyze=True,
)
actual = {} actual = generate_output(sl)
for contract in sl.contracts:
actual[contract.name] = {}
for func_or_modifier in contract.functions + contract.modifiers:
actual[contract.name][
func_or_modifier.full_name
] = func_or_modifier.slithir_cfg_to_dot_str(skip_expressions=True)
try: try:
with open(test_item.expected_file, "r") as f: with open(expected_file, "r") as f:
expected = json.load(f) expected = json.load(f)
except OSError as e: except OSError as e:
if e.errno != errno.ENOENT: if e.errno != errno.ENOENT:
raise raise
with open(test_item.expected_file, "w") as f: # if the expected output doesn't exist, make it
with open(expected_file, "w") as f:
json.dump(actual, f, indent=" ") json.dump(actual, f, indent=" ")
expected = actual expected = actual
diff = DeepDiff(expected, actual, ignore_order=True, verbose_level=2) diff = DeepDiff(expected, actual, ignore_order=True, verbose_level=2)
assert not diff assert not diff

Loading…
Cancel
Save