Buckets:
ktongue/docker_container / .vscode-server /extensions /donjayamanne.python-environment-manager-1.2.7 /pythonFiles /unittestadapter /utils.py
| # Copyright (c) Microsoft Corporation. All rights reserved. | |
| # Licensed under the MIT License. | |
| import argparse | |
| import enum | |
| import inspect | |
| import os | |
| import pathlib | |
| import sys | |
| import unittest | |
| from typing import List, Tuple, Union | |
| script_dir = pathlib.Path(__file__).parent.parent | |
| sys.path.append(os.fspath(script_dir)) | |
| sys.path.append(os.fspath(script_dir / "lib" / "python")) | |
| from typing_extensions import TypedDict | |
| # Types | |
| # Inherit from str so it's JSON serializable. | |
| class TestNodeTypeEnum(str, enum.Enum): | |
| class_ = "class" | |
| file = "file" | |
| folder = "folder" | |
| test = "test" | |
| class TestData(TypedDict): | |
| name: str | |
| path: str | |
| type_: TestNodeTypeEnum | |
| id_: str | |
| class TestItem(TestData): | |
| lineno: str | |
| runID: str | |
| class TestNode(TestData): | |
| children: "List[TestNode | TestItem]" | |
| # Helper functions for data retrieval. | |
| def get_test_case(suite): | |
| """Iterate through a unittest test suite and return all test cases.""" | |
| for test in suite: | |
| if isinstance(test, unittest.TestCase): | |
| yield test | |
| else: | |
| for test_case in get_test_case(test): | |
| yield test_case | |
| def get_source_line(obj) -> str: | |
| """Get the line number of a test case start line.""" | |
| try: | |
| sourcelines, lineno = inspect.getsourcelines(obj) | |
| except Exception: | |
| try: | |
| # tornado-specific, see https://github.com/microsoft/vscode-python/issues/17285. | |
| sourcelines, lineno = inspect.getsourcelines(obj.orig_method) | |
| except Exception: | |
| return "*" | |
| # Return the line number of the first line of the test case definition. | |
| for i, v in enumerate(sourcelines): | |
| if v.strip().startswith(("def", "async def")): | |
| return str(lineno + i) | |
| return "*" | |
| # Helper functions for test tree building. | |
| def build_test_node(path: str, name: str, type_: TestNodeTypeEnum) -> TestNode: | |
| """Build a test node with no children. A test node can be a folder, a file or a class.""" | |
| ## figure out if we are folder, file, or class | |
| id_gen = path | |
| if type_ == TestNodeTypeEnum.folder or type_ == TestNodeTypeEnum.file: | |
| id_gen = path | |
| else: | |
| # means we have to build test node for class | |
| id_gen = path + "\\" + name | |
| return {"path": path, "name": name, "type_": type_, "children": [], "id_": id_gen} | |
| 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.""" | |
| try: | |
| result = next( | |
| node | |
| for node in root["children"] | |
| if node["name"] == name and node["type_"] == type_ | |
| ) | |
| except StopIteration: | |
| result = build_test_node(path, name, type_) | |
| root["children"].append(result) | |
| return result # type:ignore | |
| def build_test_tree( | |
| suite: unittest.TestSuite, test_directory: str | |
| ) -> Tuple[Union[TestNode, None], List[str]]: | |
| """Build a test tree from a unittest test suite. | |
| This function returns the test tree, and any errors found by unittest. | |
| If no tests were discovered, return `None` and a list of errors (if any). | |
| Test tree structure: | |
| { | |
| "path": <test_directory path>, | |
| "type": "folder", | |
| "name": <folder name>, | |
| "children": [ | |
| { files and folders } | |
| ... | |
| { | |
| "path": <file path>, | |
| "name": filename.py, | |
| "type_": "file", | |
| "children": [ | |
| { | |
| "path": <class path>, | |
| "name": <class name>, | |
| "type_": "class", | |
| "children": [ | |
| { | |
| "path": <test path>, | |
| "name": <test name>, | |
| "type_": "test", | |
| "lineno": <line number> | |
| "id_": <test case id following format in line 196>, | |
| } | |
| ], | |
| "id_": <class path path following format after path> | |
| } | |
| ], | |
| "id_": <file path> | |
| } | |
| ], | |
| "id_": <test_directory path> | |
| } | |
| """ | |
| error = [] | |
| directory_path = pathlib.PurePath(test_directory) | |
| root = build_test_node(test_directory, directory_path.name, TestNodeTypeEnum.folder) | |
| for test_case in get_test_case(suite): | |
| test_id = test_case.id() | |
| if test_id.startswith("unittest.loader._FailedTest"): | |
| error.append(str(test_case._exception)) # type: ignore | |
| elif test_id.startswith("unittest.loader.ModuleSkipped"): | |
| components = test_id.split(".") | |
| class_name = f"{components[-1]}.py" | |
| # Find/build class node. | |
| file_path = os.fsdecode(os.path.join(directory_path, class_name)) | |
| current_node = get_child_node( | |
| class_name, file_path, TestNodeTypeEnum.file, root | |
| ) | |
| else: | |
| # Get the static test path components: filename, class name and function name. | |
| components = test_id.split(".") | |
| *folders, filename, class_name, function_name = components | |
| py_filename = f"{filename}.py" | |
| current_node = root | |
| # Find/build nodes for the intermediate folders in the test path. | |
| for folder in folders: | |
| current_node = get_child_node( | |
| folder, | |
| os.fsdecode(pathlib.PurePath(current_node["path"], folder)), | |
| TestNodeTypeEnum.folder, | |
| current_node, | |
| ) | |
| # Find/build file node. | |
| path_components = [test_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 | |
| ) | |
| # Find/build class node. | |
| current_node = get_child_node( | |
| class_name, file_path, TestNodeTypeEnum.class_, current_node | |
| ) | |
| # Get test line number. | |
| test_method = getattr(test_case, test_case._testMethodName) | |
| lineno = get_source_line(test_method) | |
| # Add test node. | |
| test_node: TestItem = { | |
| "name": function_name, | |
| "path": file_path, | |
| "lineno": lineno, | |
| "type_": TestNodeTypeEnum.test, | |
| "id_": file_path + "\\" + class_name + "\\" + function_name, | |
| "runID": test_id, | |
| } # concatenate class name and function test name | |
| current_node["children"].append(test_node) | |
| if not root["children"]: | |
| root = None | |
| return root, error | |
| def parse_unittest_args(args: List[str]) -> Tuple[str, str, Union[str, None]]: | |
| """Parse command-line arguments that should be forwarded to unittest to perform discovery. | |
| Valid unittest arguments are: -v, -s, -p, -t and their long-form counterparts, | |
| however we only care about the last three. | |
| The returned tuple contains the following items | |
| - start_directory: The directory where to start discovery, defaults to . | |
| - pattern: The pattern to match test files, defaults to test*.py | |
| - top_level_directory: The top-level directory of the project, defaults to None, | |
| and unittest will use start_directory behind the scenes. | |
| """ | |
| arg_parser = argparse.ArgumentParser() | |
| arg_parser.add_argument("--start-directory", "-s", default=".") | |
| arg_parser.add_argument("--pattern", "-p", default="test*.py") | |
| arg_parser.add_argument("--top-level-directory", "-t", default=None) | |
| parsed_args, _ = arg_parser.parse_known_args(args) | |
| return ( | |
| parsed_args.start_directory, | |
| parsed_args.pattern, | |
| parsed_args.top_level_directory, | |
| ) | |
Xet Storage Details
- Size:
- 8.07 kB
- Xet hash:
- bdc22d86344c18123c80c180cae0fa948f11019b30a22197299ee049ddf6c750
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.