teddecor.UnitTest.TEDTest
1from __future__ import annotations 2import ast 3import argparse 4import os 5 6from teddecor.UnitTest import RunResults, TestSuite, SaveType 7from teddecor.Util import slash 8 9 10def get_test_functions(module: ast.Module) -> list: 11 """Retrieve the test case names from the given module. 12 13 Args: 14 module (ast.Module): The module to parse test cases from. 15 16 Returns: 17 list: Test case names 18 """ 19 functions = [ 20 obj.name 21 for obj in module.body 22 if isinstance(obj, ast.FunctionDef) 23 and "test" in [decor.id for decor in obj.decorator_list] 24 ] 25 return functions if functions is not None else [] 26 27 28def get_test_classes(module: ast.Module) -> list: 29 """Retrieve the test class names from the given module. 30 31 Args: 32 module (ast.Module): The module the parse classes from. 33 34 Returns: 35 list: Test class names. 36 """ 37 klasses = [ 38 obj.name 39 for obj in module.body 40 if isinstance(obj, ast.ClassDef) and "Test" in [base.id for base in obj.bases] 41 ] 42 return klasses if klasses is not None else [] 43 44 45def get_files() -> list[str]: 46 """Gets the python files/modules from the specified directory 47 48 Args: 49 dir (str): The directory to recursively search 50 51 Returns: 52 list[str]: The python files found in the specified directory 53 """ 54 from glob import glob 55 56 return [y for x in os.walk(f".{slash()}") for y in glob(os.path.join(x[0], "*.py"))] 57 58 59def generate_run(files: list[str], arguments: str) -> TestSuite: 60 """Generates a TestSuite with the tests pulled from the found modules. 61 62 Args: 63 files (list[str]): The files/modules that have tests. 64 name (str): The name of the test suite. 65 66 Returns: 67 TestSuite: The TestSuite with all tests added to it. 68 """ 69 from sys import path 70 71 test_run = RunResults(name=arguments["name"]) 72 curdir = os.getcwd() 73 74 for file in files: 75 mdir = file.rsplit(slash(), 1)[0] 76 mname = file.split(slash())[-1].split(".")[0] 77 78 test_suite = TestSuite(name=mname) 79 test_suite.tests = [] 80 81 with open(file, "r", encoding="utf-8") as fd: 82 file_content = fd.read() 83 84 module = ast.parse(file_content) 85 runners = [] 86 runners.extend(get_test_classes(module)) 87 runners.extend(get_test_functions(module)) 88 89 os.chdir(mdir) 90 try: 91 # Bring module into scope and grab it 92 93 path.insert(0, str(os.getcwd())) 94 mod = __import__(mname) 95 96 # For each of the valid test objects add them to the test suite 97 for runner in runners: 98 if hasattr(mod, runner): 99 test_suite.append(getattr(mod, runner)) 100 101 except Exception as error: 102 # TODO: Properly look at errors and display them 103 # Don't stop as it could be module level errors that cause the code to go to this block 104 print(error) 105 106 os.chdir(curdir) 107 test_run.append(test_suite.run(display=False, regex=arguments["regex"])) 108 109 return test_run 110 111 112def get_args() -> dict: 113 """Parse the passed in arguments with `ArgParse` 114 115 Raises: 116 Exception: If the user specifies a start directory and it does not exist. 117 118 Returns: 119 dict: The key value pairs of the arguments passed in. 120 """ 121 from os import getcwd 122 123 parser = argparse.ArgumentParser(description="Process some integers.") 124 parser.add_argument( 125 "-n", 126 "--name", 127 help="The name of the group of tests that will be run.\nShows up as the test suite name.", 128 ) 129 parser.add_argument( 130 "-e", 131 "--entry", 132 help="The entry point where the scan for tests will start.", 133 ) 134 parser.add_argument( 135 "-s", 136 "--save", 137 help="The file type that the results will be saved too.", 138 ) 139 parser.add_argument( 140 "-r", 141 "--regex", 142 help="Regex to apply so only matching tests are run.", 143 ) 144 parser.add_argument( 145 "-o", 146 "--save_path", 147 help="Relative path on where to save the results.", 148 ) 149 150 args = parser.parse_args() 151 variables = { 152 "path": None, 153 "save": None, 154 "regex": None, 155 "save_path": "." + slash(), 156 "name": None, 157 } 158 159 if args.entry is None: 160 variables["path"] = str(getcwd()) 161 else: 162 from re import split 163 164 if args.entry.startswith("~"): 165 from pathlib import Path 166 167 args.entry = str(Path.home()) + args.entry[1:] 168 169 if os.path.isdir(args.entry): 170 os.chdir(slash().join(split(r"[\\/]", args.entry))) 171 variables["path"] = str(os.getcwd()) 172 else: 173 raise Exception(f"{dir} is not a directory") 174 175 if args.save is not None and args.save.lower() in ["json", "csv", "txt", "all"]: 176 if args.save == "all": 177 variables["save"] = SaveType.ALL() 178 else: 179 variables["save"] = f".{args.save.lower()}" 180 181 if args.regex is not None: 182 variables["regex"] = args.regex 183 184 if args.name is not None: 185 variables["name"] = args.name 186 else: 187 variables["name"] = variables["path"].split(slash())[-1] 188 189 if args.save_path is not None: 190 if not os.path.isdir(args.save_path): 191 os.mkdir(args.save_path) 192 193 variables["save_path"] = args.save_path 194 195 return variables 196 197 198def main(): 199 # TODO: display type 200 201 arguments = get_args() 202 203 files = get_files() 204 run = generate_run(files, arguments) 205 run.write() 206 if arguments["save"] is not None: 207 run.save(location=arguments["save_path"], ext=arguments["save"]) 208 209 210if __name__ == "__main__": 211 main()
11def get_test_functions(module: ast.Module) -> list: 12 """Retrieve the test case names from the given module. 13 14 Args: 15 module (ast.Module): The module to parse test cases from. 16 17 Returns: 18 list: Test case names 19 """ 20 functions = [ 21 obj.name 22 for obj in module.body 23 if isinstance(obj, ast.FunctionDef) 24 and "test" in [decor.id for decor in obj.decorator_list] 25 ] 26 return functions if functions is not None else []
Retrieve the test case names from the given module.
Args: module (ast.Module): The module to parse test cases from.
Returns: list: Test case names
29def get_test_classes(module: ast.Module) -> list: 30 """Retrieve the test class names from the given module. 31 32 Args: 33 module (ast.Module): The module the parse classes from. 34 35 Returns: 36 list: Test class names. 37 """ 38 klasses = [ 39 obj.name 40 for obj in module.body 41 if isinstance(obj, ast.ClassDef) and "Test" in [base.id for base in obj.bases] 42 ] 43 return klasses if klasses is not None else []
Retrieve the test class names from the given module.
Args: module (ast.Module): The module the parse classes from.
Returns: list: Test class names.
46def get_files() -> list[str]: 47 """Gets the python files/modules from the specified directory 48 49 Args: 50 dir (str): The directory to recursively search 51 52 Returns: 53 list[str]: The python files found in the specified directory 54 """ 55 from glob import glob 56 57 return [y for x in os.walk(f".{slash()}") for y in glob(os.path.join(x[0], "*.py"))]
Gets the python files/modules from the specified directory
Args: dir (str): The directory to recursively search
Returns: list[str]: The python files found in the specified directory
60def generate_run(files: list[str], arguments: str) -> TestSuite: 61 """Generates a TestSuite with the tests pulled from the found modules. 62 63 Args: 64 files (list[str]): The files/modules that have tests. 65 name (str): The name of the test suite. 66 67 Returns: 68 TestSuite: The TestSuite with all tests added to it. 69 """ 70 from sys import path 71 72 test_run = RunResults(name=arguments["name"]) 73 curdir = os.getcwd() 74 75 for file in files: 76 mdir = file.rsplit(slash(), 1)[0] 77 mname = file.split(slash())[-1].split(".")[0] 78 79 test_suite = TestSuite(name=mname) 80 test_suite.tests = [] 81 82 with open(file, "r", encoding="utf-8") as fd: 83 file_content = fd.read() 84 85 module = ast.parse(file_content) 86 runners = [] 87 runners.extend(get_test_classes(module)) 88 runners.extend(get_test_functions(module)) 89 90 os.chdir(mdir) 91 try: 92 # Bring module into scope and grab it 93 94 path.insert(0, str(os.getcwd())) 95 mod = __import__(mname) 96 97 # For each of the valid test objects add them to the test suite 98 for runner in runners: 99 if hasattr(mod, runner): 100 test_suite.append(getattr(mod, runner)) 101 102 except Exception as error: 103 # TODO: Properly look at errors and display them 104 # Don't stop as it could be module level errors that cause the code to go to this block 105 print(error) 106 107 os.chdir(curdir) 108 test_run.append(test_suite.run(display=False, regex=arguments["regex"])) 109 110 return test_run
Generates a TestSuite with the tests pulled from the found modules.
Args: files (list[str]): The files/modules that have tests. name (str): The name of the test suite.
Returns: TestSuite: The TestSuite with all tests added to it.
113def get_args() -> dict: 114 """Parse the passed in arguments with `ArgParse` 115 116 Raises: 117 Exception: If the user specifies a start directory and it does not exist. 118 119 Returns: 120 dict: The key value pairs of the arguments passed in. 121 """ 122 from os import getcwd 123 124 parser = argparse.ArgumentParser(description="Process some integers.") 125 parser.add_argument( 126 "-n", 127 "--name", 128 help="The name of the group of tests that will be run.\nShows up as the test suite name.", 129 ) 130 parser.add_argument( 131 "-e", 132 "--entry", 133 help="The entry point where the scan for tests will start.", 134 ) 135 parser.add_argument( 136 "-s", 137 "--save", 138 help="The file type that the results will be saved too.", 139 ) 140 parser.add_argument( 141 "-r", 142 "--regex", 143 help="Regex to apply so only matching tests are run.", 144 ) 145 parser.add_argument( 146 "-o", 147 "--save_path", 148 help="Relative path on where to save the results.", 149 ) 150 151 args = parser.parse_args() 152 variables = { 153 "path": None, 154 "save": None, 155 "regex": None, 156 "save_path": "." + slash(), 157 "name": None, 158 } 159 160 if args.entry is None: 161 variables["path"] = str(getcwd()) 162 else: 163 from re import split 164 165 if args.entry.startswith("~"): 166 from pathlib import Path 167 168 args.entry = str(Path.home()) + args.entry[1:] 169 170 if os.path.isdir(args.entry): 171 os.chdir(slash().join(split(r"[\\/]", args.entry))) 172 variables["path"] = str(os.getcwd()) 173 else: 174 raise Exception(f"{dir} is not a directory") 175 176 if args.save is not None and args.save.lower() in ["json", "csv", "txt", "all"]: 177 if args.save == "all": 178 variables["save"] = SaveType.ALL() 179 else: 180 variables["save"] = f".{args.save.lower()}" 181 182 if args.regex is not None: 183 variables["regex"] = args.regex 184 185 if args.name is not None: 186 variables["name"] = args.name 187 else: 188 variables["name"] = variables["path"].split(slash())[-1] 189 190 if args.save_path is not None: 191 if not os.path.isdir(args.save_path): 192 os.mkdir(args.save_path) 193 194 variables["save_path"] = args.save_path 195 196 return variables
Parse the passed in arguments with ArgParse
Raises: Exception: If the user specifies a start directory and it does not exist.
Returns: dict: The key value pairs of the arguments passed in.