diff --git a/pythonFiles/tests/unittestadapter/.data/utils_complex_tree/__init__.py b/pythonFiles/tests/unittestadapter/.data/utils_complex_tree/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pythonFiles/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/__init__.py b/pythonFiles/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pythonFiles/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/__init__.py b/pythonFiles/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pythonFiles/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/test_utils_complex_tree.py b/pythonFiles/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/test_utils_complex_tree.py new file mode 100644 index 000000000000..8f57fb880ff1 --- /dev/null +++ b/pythonFiles/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/test_utils_complex_tree.py @@ -0,0 +1,8 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import unittest + + +class TreeOne(unittest.TestCase): + def test_one(self): + assert True diff --git a/pythonFiles/tests/unittestadapter/expected_discovery_test_output.py b/pythonFiles/tests/unittestadapter/expected_discovery_test_output.py index 3043ec158a2e..0b253a084734 100644 --- a/pythonFiles/tests/unittestadapter/expected_discovery_test_output.py +++ b/pythonFiles/tests/unittestadapter/expected_discovery_test_output.py @@ -4,6 +4,7 @@ import os from unittestadapter.utils import TestNodeTypeEnum from .helpers import TEST_DATA_PATH +import pathlib skip_unittest_folder_discovery_output = { "path": os.fspath(TEST_DATA_PATH / "unittest_skip"), @@ -66,3 +67,87 @@ ], "id_": os.fspath(TEST_DATA_PATH / "unittest_skip"), } + +complex_tree_file_path = os.fsdecode( + pathlib.PurePath( + TEST_DATA_PATH, + "utils_complex_tree", + "test_outer_folder", + "test_inner_folder", + "test_utils_complex_tree.py", + ) +) +complex_tree_expected_output = { + "name": "utils_complex_tree", + "type_": TestNodeTypeEnum.folder, + "path": os.fsdecode(pathlib.PurePath(TEST_DATA_PATH, "utils_complex_tree")), + "children": [ + { + "name": "test_outer_folder", + "type_": TestNodeTypeEnum.folder, + "path": os.fsdecode( + pathlib.PurePath( + TEST_DATA_PATH, "utils_complex_tree", "test_outer_folder" + ) + ), + "children": [ + { + "name": "test_inner_folder", + "type_": TestNodeTypeEnum.folder, + "path": os.fsdecode( + pathlib.PurePath( + TEST_DATA_PATH, + "utils_complex_tree", + "test_outer_folder", + "test_inner_folder", + ) + ), + "children": [ + { + "name": "test_utils_complex_tree.py", + "type_": TestNodeTypeEnum.file, + "path": complex_tree_file_path, + "children": [ + { + "name": "TreeOne", + "type_": TestNodeTypeEnum.class_, + "path": complex_tree_file_path, + "children": [ + { + "name": "test_one", + "type_": TestNodeTypeEnum.test, + "path": complex_tree_file_path, + "lineno": "7", + "id_": complex_tree_file_path + + "\\" + + "TreeOne" + + "\\" + + "test_one", + "runID": "utils_complex_tree.test_outer_folder.test_inner_folder.test_utils_complex_tree.TreeOne.test_one", + }, + ], + "id_": complex_tree_file_path + "\\" + "TreeOne", + } + ], + "id_": complex_tree_file_path, + } + ], + "id_": os.fsdecode( + pathlib.PurePath( + TEST_DATA_PATH, + "utils_complex_tree", + "test_outer_folder", + "test_inner_folder", + ) + ), + }, + ], + "id_": os.fsdecode( + pathlib.PurePath( + TEST_DATA_PATH, "utils_complex_tree", "test_outer_folder" + ) + ), + } + ], + "id_": os.fsdecode(pathlib.PurePath(TEST_DATA_PATH, "utils_complex_tree")), +} diff --git a/pythonFiles/tests/unittestadapter/test_discovery.py b/pythonFiles/tests/unittestadapter/test_discovery.py index 7d7db772a4a4..f0276d6e0f0b 100644 --- a/pythonFiles/tests/unittestadapter/test_discovery.py +++ b/pythonFiles/tests/unittestadapter/test_discovery.py @@ -231,3 +231,25 @@ def test_unit_skip() -> None: expected_discovery_test_output.skip_unittest_folder_discovery_output, ) assert "error" not in actual + + +def test_complex_tree() -> None: + """This test specifically tests when different start_dir and top_level_dir are provided.""" + start_dir = os.fsdecode( + pathlib.PurePath( + TEST_DATA_PATH, + "utils_complex_tree", + "test_outer_folder", + "test_inner_folder", + ) + ) + pattern = "test_*.py" + top_level_dir = os.fsdecode(pathlib.PurePath(TEST_DATA_PATH, "utils_complex_tree")) + uuid = "some-uuid" + actual = discover_tests(start_dir, pattern, top_level_dir, uuid) + assert actual["status"] == "success" + assert "error" not in actual + assert is_same_tree( + actual.get("tests"), + expected_discovery_test_output.complex_tree_expected_output, + ) diff --git a/pythonFiles/unittestadapter/discovery.py b/pythonFiles/unittestadapter/discovery.py index e8f602a22fb3..3d1c54ef2a68 100644 --- a/pythonFiles/unittestadapter/discovery.py +++ b/pythonFiles/unittestadapter/discovery.py @@ -86,7 +86,13 @@ def discover_tests( loader = unittest.TestLoader() suite = loader.discover(start_dir, pattern, top_level_dir) - tests, error = build_test_tree(suite, cwd) # test tree built succesfully here. + # If the top level directory is not provided, then use the start directory. + if top_level_dir is None: + top_level_dir = start_dir + + tests, error = build_test_tree( + suite, top_level_dir + ) # test tree built successfully here. except Exception: error.append(traceback.format_exc()) diff --git a/pythonFiles/unittestadapter/utils.py b/pythonFiles/unittestadapter/utils.py index 2c5ebf09abc7..5632e69b09c7 100644 --- a/pythonFiles/unittestadapter/utils.py +++ b/pythonFiles/unittestadapter/utils.py @@ -94,12 +94,13 @@ def build_test_node(path: str, name: str, type_: TestNodeTypeEnum) -> TestNode: def get_child_node( name: str, path: str, type_: TestNodeTypeEnum, root: TestNode ) -> TestNode: - """Find a child node in a test tree given its name and type. If the node doesn't exist, create it.""" + """Find a child node in a test tree given its name, type and path. If the node doesn't exist, create it. + Path is required to distinguish between nodes with the same name and type.""" try: result = next( node for node in root["children"] - if node["name"] == name and node["type_"] == type_ + if node["name"] == name and node["type_"] == type_ and node["path"] == path ) except StopIteration: result = build_test_node(path, name, type_) @@ -109,7 +110,7 @@ def get_child_node( def build_test_tree( - suite: unittest.TestSuite, test_directory: str + suite: unittest.TestSuite, top_level_directory: str ) -> Tuple[Union[TestNode, None], List[str]]: """Build a test tree from a unittest test suite. @@ -152,8 +153,10 @@ def build_test_tree( } """ error = [] - directory_path = pathlib.PurePath(test_directory) - root = build_test_node(test_directory, directory_path.name, TestNodeTypeEnum.folder) + directory_path = pathlib.PurePath(top_level_directory) + root = build_test_node( + top_level_directory, directory_path.name, TestNodeTypeEnum.folder + ) for test_case in get_test_case(suite): test_id = test_case.id() @@ -185,7 +188,7 @@ def build_test_tree( ) # Find/build file node. - path_components = [test_directory] + folders + [py_filename] + path_components = [top_level_directory] + folders + [py_filename] file_path = os.fsdecode(pathlib.PurePath("/".join(path_components))) current_node = get_child_node( py_filename, file_path, TestNodeTypeEnum.file, current_node