import os import tempfile from pathlib import Path from codeflash.discovery.discover_unit_tests import ( analyze_imports_in_test_file, discover_unit_tests, filter_test_files_by_imports, ) from codeflash.discovery.functions_to_optimize import FunctionToOptimize from codeflash.models.models import FunctionParent, TestsInFile, TestType from codeflash.verification.verification_utils import TestConfig def test_unit_test_discovery_pytest(): project_path = Path(__file__).parent.parent.resolve() / "code_to_optimize" tests_path = project_path / "tests" / "pytest" test_config = TestConfig( tests_root=tests_path, project_root_path=project_path, test_framework="pytest", tests_project_rootdir=tests_path.parent, ) tests, _, _ = discover_unit_tests(test_config) assert len(tests) > 0 def test_benchmark_test_discovery_pytest(): project_path = Path(__file__).parent.parent.resolve() / "code_to_optimize" tests_path = project_path / "tests" / "pytest" / "benchmarks" test_config = TestConfig( tests_root=tests_path, project_root_path=project_path, test_framework="pytest", tests_project_rootdir=tests_path.parent, ) tests, _, _ = discover_unit_tests(test_config) assert len(tests) == 1 # Should not discover benchmark tests def test_unit_test_discovery_unittest(): project_path = Path(__file__).parent.parent.resolve() / "code_to_optimize" test_path = project_path / "tests" / "unittest" test_config = TestConfig( tests_root=project_path, project_root_path=project_path, test_framework="unittest", tests_project_rootdir=project_path.parent, ) os.chdir(project_path) tests, _, _ = discover_unit_tests(test_config) # assert len(tests) > 0 # Unittest discovery within a pytest environment does not work def test_benchmark_unit_test_discovery_pytest(): with tempfile.TemporaryDirectory() as tmpdirname: # Create a dummy test file test_file_path = Path(tmpdirname) / "test_dummy.py" test_file_content = """ from bubble_sort import sorter def test_benchmark_sort(benchmark): benchmark(sorter, [5, 4, 3, 2, 1, 0]) def test_normal_test(): assert sorter(list(reversed(range(100)))) == list(range(100)) def test_normal_test2(): assert sorter(list(reversed(range(100)))) == list(range(100))""" test_file_path.write_text(test_file_content) path_obj_tempdirname = Path(tmpdirname) # Create a file that the test file is testing code_file_path = path_obj_tempdirname / "bubble_sort.py" code_file_content = """ def sorter(arr): return sorted(arr)""" code_file_path.write_text(code_file_content) # Create a TestConfig with the temporary directory as the root test_config = TestConfig( tests_root=path_obj_tempdirname, project_root_path=path_obj_tempdirname, test_framework="pytest", tests_project_rootdir=path_obj_tempdirname.parent, ) # Discover tests tests, _, _ = discover_unit_tests(test_config) assert len(tests) == 1 assert "bubble_sort.sorter" in tests assert len(tests["bubble_sort.sorter"]) == 2 functions = [test.tests_in_file.test_function for test in tests["bubble_sort.sorter"]] assert "test_normal_test" in functions assert "test_normal_test2" in functions assert "test_benchmark_sort" not in functions def test_discover_tests_pytest_with_temp_dir_root(): with tempfile.TemporaryDirectory() as tmpdirname: # Create a dummy test file test_file_path = Path(tmpdirname) / "test_dummy.py" test_file_content = ( "import pytest\n" "from dummy_code import dummy_function\n\n" "def test_dummy_function():\n" " assert dummy_function() is True\n" "@pytest.mark.parametrize('param', [True])\n" "def test_dummy_parametrized_function(param):\n" " assert dummy_function() is True\n" ) test_file_path.write_text(test_file_content) path_obj_tempdirname = Path(tmpdirname) # Create a file that the test file is testing code_file_path = path_obj_tempdirname / "dummy_code.py" code_file_content = "def dummy_function():\n return True\n" code_file_path.write_text(code_file_content) # Create a TestConfig with the temporary directory as the root test_config = TestConfig( tests_root=path_obj_tempdirname, project_root_path=path_obj_tempdirname, test_framework="pytest", tests_project_rootdir=path_obj_tempdirname.parent, ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config) # Check if the dummy test file is discovered assert len(discovered_tests) == 1 assert len(discovered_tests["dummy_code.dummy_function"]) == 2 dummy_tests = discovered_tests["dummy_code.dummy_function"] assert all(test.tests_in_file.test_file.resolve() == test_file_path.resolve() for test in dummy_tests) assert {test.tests_in_file.test_function for test in dummy_tests} == { "test_dummy_parametrized_function[True]", "test_dummy_function", } def test_discover_tests_pytest_with_multi_level_dirs(): with tempfile.TemporaryDirectory() as tmpdirname: path_obj_tmpdirname = Path(tmpdirname) # Create multi-level directories level1_dir = path_obj_tmpdirname / "level1" level2_dir = level1_dir / "level2" level2_dir.mkdir(parents=True) # Create code files at each level root_code_file_path = path_obj_tmpdirname / "root_code.py" root_code_file_content = "def root_function():\n return True\n" root_code_file_path.write_text(root_code_file_content) level1_code_file_path = level1_dir / "level1_code.py" level1_code_file_content = "def level1_function():\n return True\n" level1_code_file_path.write_text(level1_code_file_content) level2_code_file_path = level2_dir / "level2_code.py" level2_code_file_content = "def level2_function():\n return True\n" level2_code_file_path.write_text(level2_code_file_content) # Create a test file at the root level root_test_file_path = path_obj_tmpdirname / "test_root.py" root_test_file_content = ( "from root_code import root_function\n\n" "def test_root_function():\n" " assert True\n" " assert root_function() is True\n" ) root_test_file_path.write_text(root_test_file_content) # Create a test file at level 1 level1_test_file_path = level1_dir / "test_level1.py" level1_test_file_content = ( "from level1_code import level1_function\n\n" "def test_level1_function():\n" " assert True\n" " assert level1_function() is True\n" ) level1_test_file_path.write_text(level1_test_file_content) # Create a test file at level 2 level2_test_file_path = level2_dir / "test_level2.py" level2_test_file_content = ( "from level2_code import level2_function\n\n" "def test_level2_function():\n" " assert True\n" " assert level2_function() is True\n" ) level2_test_file_path.write_text(level2_test_file_content) # Create a TestConfig with the temporary directory as the root test_config = TestConfig( tests_root=path_obj_tmpdirname, project_root_path=path_obj_tmpdirname, test_framework="pytest", tests_project_rootdir=path_obj_tmpdirname.parent, ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config) # Check if the test files at all levels are discovered assert len(discovered_tests) == 3 discovered_root_test = next(iter(discovered_tests["root_code.root_function"])).tests_in_file.test_file assert discovered_root_test.resolve() == root_test_file_path.resolve() discovered_level1_test = next( iter(discovered_tests["level1.level1_code.level1_function"]) ).tests_in_file.test_file assert discovered_level1_test.resolve() == level1_test_file_path.resolve() discovered_level2_test = next( iter(discovered_tests["level1.level2.level2_code.level2_function"]) ).tests_in_file.test_file assert discovered_level2_test.resolve() == level2_test_file_path.resolve() def test_discover_tests_pytest_dirs(): with tempfile.TemporaryDirectory() as tmpdirname: path_obj_tmpdirname = Path(tmpdirname) # Create multi-level directories level1_dir = Path(tmpdirname) / "level1" level2_dir = level1_dir / "level2" level2_dir.mkdir(parents=True) level3_dir = level1_dir / "level3" level3_dir.mkdir(parents=True) # Create code files at each level root_code_file_path = path_obj_tmpdirname / "root_code.py" root_code_file_content = "def root_function():\n return True\n" root_code_file_path.write_text(root_code_file_content) level1_code_file_path = level1_dir / "level1_code.py" level1_code_file_content = "def level1_function():\n return True\n" level1_code_file_path.write_text(level1_code_file_content) level2_code_file_path = level2_dir / "level2_code.py" level2_code_file_content = "def level2_function():\n return True\n" level2_code_file_path.write_text(level2_code_file_content) level3_code_file_path = level3_dir / "level3_code.py" level3_code_file_content = "def level3_function():\n return True\n" level3_code_file_path.write_text(level3_code_file_content) # Create a test file at the root level root_test_file_path = path_obj_tmpdirname / "test_root.py" root_test_file_content = ( "from root_code import root_function\n\n" "def test_root_function():\n" " assert True\n" " assert root_function() is True\n" ) root_test_file_path.write_text(root_test_file_content) # Create a test file at level 1 level1_test_file_path = level1_dir / "test_level1.py" level1_test_file_content = ( "from level1_code import level1_function\n\n" "def test_level1_function():\n" " assert True\n" " assert level1_function() is True\n" ) level1_test_file_path.write_text(level1_test_file_content) # Create a test file at level 2 level2_test_file_path = level2_dir / "test_level2.py" level2_test_file_content = ( "from level2_code import level2_function\n\n" "def test_level2_function():\n" " assert True\n" " assert level2_function() is True\n" ) level2_test_file_path.write_text(level2_test_file_content) level3_test_file_path = level3_dir / "test_level3.py" level3_test_file_content = ( "from level3_code import level3_function\n\n" "def test_level3_function():\n" " assert True\n" " assert level3_function() is True\n" ) level3_test_file_path.write_text(level3_test_file_content) # Create a TestConfig with the temporary directory as the root test_config = TestConfig( tests_root=path_obj_tmpdirname, project_root_path=path_obj_tmpdirname, test_framework="pytest", tests_project_rootdir=path_obj_tmpdirname.parent, ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config) # Check if the test files at all levels are discovered assert len(discovered_tests) == 4 discovered_root_test = next(iter(discovered_tests["root_code.root_function"])).tests_in_file.test_file assert discovered_root_test.resolve() == root_test_file_path.resolve() discovered_level1_test = next( iter(discovered_tests["level1.level1_code.level1_function"]) ).tests_in_file.test_file assert discovered_level1_test.resolve() == level1_test_file_path.resolve() discovered_level2_test = next( iter(discovered_tests["level1.level2.level2_code.level2_function"]) ).tests_in_file.test_file assert discovered_level2_test.resolve() == level2_test_file_path.resolve() discovered_level3_test = next( iter(discovered_tests["level1.level3.level3_code.level3_function"]) ).tests_in_file.test_file assert discovered_level3_test.resolve() == level3_test_file_path.resolve() def test_discover_tests_pytest_with_class(): with tempfile.TemporaryDirectory() as tmpdirname: path_obj_tmpdirname = Path(tmpdirname) # Create a code file with a class code_file_path = path_obj_tmpdirname / "some_class_code.py" code_file_content = "class SomeClass:\n def some_method(self):\n return True\n" code_file_path.write_text(code_file_content) # Create a test file with a test class and a test method test_file_path = path_obj_tmpdirname / "test_some_class.py" test_file_content = ( "from some_class_code import SomeClass\n\n" "def test_some_method():\n" " instance = SomeClass()\n" " assert instance.some_method() is True\n" ) test_file_path.write_text(test_file_content) # Create a TestConfig with the temporary directory as the root test_config = TestConfig( tests_root=path_obj_tmpdirname, project_root_path=path_obj_tmpdirname, test_framework="pytest", tests_project_rootdir=path_obj_tmpdirname.parent, ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config) # Check if the test class and method are discovered assert len(discovered_tests) == 1 discovered_class_test = next( iter(discovered_tests["some_class_code.SomeClass.some_method"]) ).tests_in_file.test_file assert discovered_class_test.resolve() == test_file_path.resolve() def test_discover_tests_pytest_with_double_nested_directories(): with tempfile.TemporaryDirectory() as tmpdirname: path_obj_tmpdirname = Path(tmpdirname) # Create nested directories nested_dir = path_obj_tmpdirname / "nested" / "more_nested" nested_dir.mkdir(parents=True) # Create a code file with a class in the nested directory code_file_path = nested_dir / "nested_class_code.py" code_file_content = "class NestedClass:\n def nested_method(self):\n return True\n" code_file_path.write_text(code_file_content) # Create a test file with a test class and a test method in the nested directory test_file_path = nested_dir / "test_nested_class.py" test_file_content = ( "from nested_class_code import NestedClass\n\n" "def test_nested_method():\n" " instance = NestedClass()\n" " assert instance.nested_method() is True\n" ) test_file_path.write_text(test_file_content) # Create a TestConfig with the temporary directory as the root test_config = TestConfig( tests_root=path_obj_tmpdirname, project_root_path=path_obj_tmpdirname, test_framework="pytest", tests_project_rootdir=path_obj_tmpdirname.parent, ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config) # Check if the test class and method are discovered assert len(discovered_tests) == 1 discovered_nested_test = next( iter(discovered_tests["nested.more_nested.nested_class_code.NestedClass.nested_method"]) ).tests_in_file.test_file assert discovered_nested_test.resolve() == test_file_path.resolve() def test_discover_tests_with_code_in_dir_and_test_in_subdir(): with tempfile.TemporaryDirectory() as tmpdirname: path_obj_tmpdirname = Path(tmpdirname) # Create a directory for the code file code_dir = path_obj_tmpdirname / "code" code_dir.mkdir() # Create a code file in the code directory code_file_path = code_dir / "some_code.py" code_file_content = "def some_function():\n return True\n" code_file_path.write_text(code_file_content) # Create a subdirectory for the test file within the code directory test_subdir = code_dir / "tests" test_subdir.mkdir() # Create a test file in the test subdirectory test_file_path = test_subdir / "test_some_code.py" test_file_content = ( "import sys\n" "import os\n" # I am suspicious of this line, we should not need to insert the code directory into the path "sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))\n" "from some_code import some_function\n\n" "def test_some_function():\n" " assert some_function() is True\n" ) test_file_path.write_text(test_file_content) # Create a TestConfig with the code directory as the root test_config = TestConfig( tests_root=test_subdir, project_root_path=path_obj_tmpdirname, test_framework="pytest", tests_project_rootdir=test_subdir.parent, ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config) # Check if the test file is discovered and associated with the code file assert len(discovered_tests) == 1 discovered_test_file = next(iter(discovered_tests["code.some_code.some_function"])).tests_in_file.test_file assert discovered_test_file.resolve() == test_file_path.resolve() def test_discover_tests_pytest_with_nested_class(): with tempfile.TemporaryDirectory() as tmpdirname: path_obj_tmpdirname = Path(tmpdirname) # Create a code file with a nested class code_file_path = path_obj_tmpdirname / "nested_class_code.py" code_file_content = ( "class OuterClass:\n class InnerClass:\n def inner_method(self):\n return True\n" ) code_file_path.write_text(code_file_content) # Create a test file with a test for the nested class method test_file_path = path_obj_tmpdirname / "test_nested_class.py" test_file_content = ( "from nested_class_code import OuterClass\n\n" "def test_inner_method():\n" " instance = OuterClass.InnerClass()\n" " assert instance.inner_method() is True\n" ) test_file_path.write_text(test_file_content) # Create a TestConfig with the temporary directory as the root test_config = TestConfig( tests_root=path_obj_tmpdirname, project_root_path=path_obj_tmpdirname, test_framework="pytest", tests_project_rootdir=path_obj_tmpdirname.parent, ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config) # Check if the test for the nested class method is discovered assert len(discovered_tests) == 1 discovered_inner_test = next( iter(discovered_tests["nested_class_code.OuterClass.InnerClass.inner_method"]) ).tests_in_file.test_file assert discovered_inner_test.resolve() == test_file_path.resolve() def test_discover_tests_pytest_separate_moduledir(): with tempfile.TemporaryDirectory() as tmpdirname: rootdir = Path(tmpdirname) # Create a code file with a nested class codedir = rootdir / "src" / "mypackage" codedir.mkdir(parents=True) code_file_path = codedir / "code.py" code_file_content = "def find_common_tags(articles):\n if not articles:\n return set()\n" code_file_path.write_text(code_file_content) # Create a test file with a test for the nested class method testdir = rootdir / "tests" testdir.mkdir() test_file_path = testdir / "test_code.py" test_file_content = ( "from mypackage.code import find_common_tags\n\n" "def test_common_tags():\n" " assert find_common_tags(None) == set()\n" ) test_file_path.write_text(test_file_content) # Create a TestConfig with the temporary directory as the root test_config = TestConfig( tests_root=testdir, project_root_path=codedir.parent.resolve(), test_framework="pytest", tests_project_rootdir=testdir.parent, ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config) # Check if the test for the nested class method is discovered assert len(discovered_tests) == 1 discovered_test_file = next(iter(discovered_tests["mypackage.code.find_common_tags"])).tests_in_file.test_file assert discovered_test_file.resolve() == test_file_path.resolve() def test_unittest_discovery_with_pytest(): with tempfile.TemporaryDirectory() as tmpdirname: path_obj_tmpdirname = Path(tmpdirname) # Create a simple code file code_file_path = path_obj_tmpdirname / "calculator.py" code_file_content = """ class Calculator: def add(self, a, b): return a + b """ code_file_path.write_text(code_file_content) # Create a unittest test file test_file_path = path_obj_tmpdirname / "test_calculator.py" test_file_content = """ import unittest from calculator import Calculator class TestCalculator(unittest.TestCase): def test_add(self): calc = Calculator() self.assertEqual(calc.add(2, 2), 4) """ test_file_path.write_text(test_file_content) # Configure test discovery test_config = TestConfig( tests_root=path_obj_tmpdirname, project_root_path=path_obj_tmpdirname, test_framework="pytest", # Using pytest framework to discover unittest tests tests_project_rootdir=path_obj_tmpdirname.parent, ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config) # Verify the unittest was discovered assert len(discovered_tests) == 1 assert "calculator.Calculator.add" in discovered_tests assert len(discovered_tests["calculator.Calculator.add"]) == 1 calculator_test = next(iter(discovered_tests["calculator.Calculator.add"])) assert calculator_test.tests_in_file.test_file.resolve() == test_file_path.resolve() assert calculator_test.tests_in_file.test_function == "test_add" def test_unittest_discovery_with_pytest_parent_class(): with tempfile.TemporaryDirectory() as tmpdirname: path_obj_tmpdirname = Path(tmpdirname) # Create a simple code file code_file_path = path_obj_tmpdirname / "calculator.py" code_file_content = """ class Calculator: def add(self, a, b): return a + b """ code_file_path.write_text(code_file_content) # Create a base test class file base_test_file_path = path_obj_tmpdirname / "base_test.py" base_test_content = """ import unittest class BaseTestCase(unittest.TestCase): def setUp(self): self.setup_called = True def tearDown(self): self.setup_called = False def assert_setup_called(self): self.assertTrue(self.setup_called, "Setup was not called") """ base_test_file_path.write_text(base_test_content) # Create a unittest test file that extends the base test test_file_path = path_obj_tmpdirname / "test_calculator.py" test_file_content = """ from base_test import BaseTestCase from calculator import Calculator class ExtendedTestCase(BaseTestCase): def setUp(self): super().setUp() self.calc = Calculator() class TestCalculator(ExtendedTestCase): def test_add(self): self.assert_setup_called() self.assertEqual(self.calc.add(2, 2), 4) """ test_file_path.write_text(test_file_content) # Configure test discovery test_config = TestConfig( tests_root=path_obj_tmpdirname, project_root_path=path_obj_tmpdirname, test_framework="pytest", # Using pytest framework to discover unittest tests tests_project_rootdir=path_obj_tmpdirname.parent, ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config) # Verify the unittest was discovered assert len(discovered_tests) == 2 assert "calculator.Calculator.add" in discovered_tests assert len(discovered_tests["calculator.Calculator.add"]) == 1 calculator_test = next(iter(discovered_tests["calculator.Calculator.add"])) assert calculator_test.tests_in_file.test_file.resolve() == test_file_path.resolve() assert calculator_test.tests_in_file.test_function == "test_add" def test_unittest_discovery_with_pytest_private(): with tempfile.TemporaryDirectory() as tmpdirname: path_obj_tmpdirname = Path(tmpdirname) # Create a simple code file code_file_path = path_obj_tmpdirname / "calculator.py" code_file_content = """ class Calculator: def add(self, a, b): return a + b """ code_file_path.write_text(code_file_content) # Create a unittest test file with a private test method (prefixed with _) test_file_path = path_obj_tmpdirname / "test_calculator.py" test_file_content = """ import unittest from calculator import Calculator class TestCalculator(unittest.TestCase): def _test_add(self): # Private test method should not be discovered calc = Calculator() self.assertEqual(calc.add(2, 2), 4) """ test_file_path.write_text(test_file_content) # Configure test discovery test_config = TestConfig( tests_root=path_obj_tmpdirname, project_root_path=path_obj_tmpdirname, test_framework="pytest", # Using pytest framework to discover unittest tests tests_project_rootdir=path_obj_tmpdirname.parent, ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config) # Verify no tests were discovered assert len(discovered_tests) == 0 assert "calculator.Calculator.add" not in discovered_tests def test_unittest_discovery_with_pytest_subtest(): with tempfile.TemporaryDirectory() as tmpdirname: path_obj_tmpdirname = Path(tmpdirname) # Create a simple code file code_file_path = path_obj_tmpdirname / "calculator.py" code_file_content = """ class Calculator: def add(self, a, b): return a + b """ code_file_path.write_text(code_file_content) # Create a unittest test file with parameterized tests test_file_path = path_obj_tmpdirname / "test_calculator.py" test_file_content = """ import unittest from calculator import Calculator class TestCalculator(unittest.TestCase): def test_add_with_parameters(self): calc = Calculator() test_cases = [ {"a": 2, "b": 2, "expected": 4}, {"a": 0, "b": 0, "expected": 0}, {"a": -1, "b": 1, "expected": 0}, {"a": 10, "b": -5, "expected": 5} ] for case in test_cases: with self.subTest(a=case["a"], b=case["b"]): result = calc.add(case["a"], case["b"]) self.assertEqual(result, case["expected"]) """ test_file_path.write_text(test_file_content) # Configure test discovery test_config = TestConfig( tests_root=path_obj_tmpdirname, project_root_path=path_obj_tmpdirname, test_framework="pytest", # Using pytest framework to discover unittest tests tests_project_rootdir=path_obj_tmpdirname.parent, ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config) # Verify the unittest was discovered assert len(discovered_tests) == 1 assert "calculator.Calculator.add" in discovered_tests assert len(discovered_tests["calculator.Calculator.add"]) == 1 calculator_test = next(iter(discovered_tests["calculator.Calculator.add"])) assert calculator_test.tests_in_file.test_file.resolve() == test_file_path.resolve() assert calculator_test.tests_in_file.test_function == "test_add_with_parameters" def test_unittest_discovery_with_pytest_fixture(): with tempfile.TemporaryDirectory() as tmpdirname: path_obj_tmpdirname = Path(tmpdirname) # Create a simple code file code_file_path = path_obj_tmpdirname / "topological_sort.py" code_file_content = """ import uuid from collections import defaultdict class Graph: def __init__(self, vertices: int): self.vertices=vertices def dummy_fn(self): return 1 def topologicalSort(self): return self.vertices """ code_file_path.write_text(code_file_content) # Create a unittest test file with parameterized tests test_file_path = path_obj_tmpdirname / "test_topological_sort.py" test_file_content = """ from topological_sort import Graph import pytest @pytest.fixture def g(): return Graph(6) def test_topological_sort(g): assert g.dummy_fn() == 1 assert g.topologicalSort() == 6 """ test_file_path.write_text(test_file_content) # Configure test discovery test_config = TestConfig( tests_root=path_obj_tmpdirname, project_root_path=path_obj_tmpdirname, test_framework="pytest", # Using pytest framework to discover unittest tests tests_project_rootdir=path_obj_tmpdirname.parent, ) fto = FunctionToOptimize( function_name="topologicalSort", file_path=code_file_path, parents=[FunctionParent(name="Graph", type="ClassDef")], ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config, file_to_funcs_to_optimize={code_file_path: [fto]}) # Verify the unittest was discovered assert len(discovered_tests) == 2 assert "topological_sort.Graph.topologicalSort" in discovered_tests assert len(discovered_tests["topological_sort.Graph.topologicalSort"]) == 1 tpsort_test = next(iter(discovered_tests["topological_sort.Graph.topologicalSort"])) assert tpsort_test.tests_in_file.test_file.resolve() == test_file_path.resolve() assert tpsort_test.tests_in_file.test_function == "test_topological_sort" def test_unittest_discovery_with_pytest_class_fixture(): with tempfile.TemporaryDirectory() as tmpdirname: path_obj_tmpdirname = Path(tmpdirname) # Create a simple code file code_file_path = path_obj_tmpdirname / "router_file.py" code_file_content = """ from __future__ import annotations import hashlib import json class Router: model_names: list cache_responses = False tenacity = None def __init__( # noqa: PLR0915 self, model_list = None, ) -> None: self.model_list = model_list self.model_id_to_deployment_index_map = {} self.model_name_to_deployment_indices = {} def _generate_model_id(self, model_group, litellm_params): # Optimized: Use list and join instead of string concatenation in loop # This avoids creating many temporary string objects (O(n) vs O(n²) complexity) parts = [model_group] for k, v in litellm_params.items(): if isinstance(k, str): parts.append(k) elif isinstance(k, dict): parts.append(json.dumps(k)) else: parts.append(str(k)) if isinstance(v, str): parts.append(v) elif isinstance(v, dict): parts.append(json.dumps(v)) else: parts.append(str(v)) concat_str = "".join(parts) hash_object = hashlib.sha256(concat_str.encode()) return hash_object.hexdigest() def _add_model_to_list_and_index_map( self, model, model_id = None ) -> None: idx = len(self.model_list) self.model_list.append(model) # Update model_id index for O(1) lookup if model_id is not None: self.model_id_to_deployment_index_map[model_id] = idx elif model.get("model_info", {}).get("id") is not None: self.model_id_to_deployment_index_map[model["model_info"]["id"]] = idx # Update model_name index for O(1) lookup model_name = model.get("model_name") if model_name: if model_name not in self.model_name_to_deployment_indices: self.model_name_to_deployment_indices[model_name] = [] self.model_name_to_deployment_indices[model_name].append(idx) def _build_model_id_to_deployment_index_map(self, model_list): # First populate the model_list self.model_list = [] for _, model in enumerate(model_list): # Extract model_info from the model dict model_info = model.get("model_info", {}) model_id = model_info.get("id") # If no ID exists, generate one using the same logic as set_model_list if model_id is None: model_name = model.get("model_name", "") litellm_params = model.get("litellm_params", {}) model_id = self._generate_model_id(model_name, litellm_params) # Update the model_info in the original list if "model_info" not in model: model["model_info"] = {} model["model_info"]["id"] = model_id self._add_model_to_list_and_index_map(model=model, model_id=model_id) """ code_file_path.write_text(code_file_content) # Create a unittest test file with parameterized tests test_file_path = path_obj_tmpdirname / "test_router_file.py" test_file_content = """ import pytest from router_file import Router class TestRouterIndexManagement: @pytest.fixture def router(self): return Router(model_list=[]) def test_build_model_id_to_deployment_index_map(self, router): model_list = [ { "model_name": "gpt-3.5-turbo", "litellm_params": {"model": "gpt-3.5-turbo"}, "model_info": {"id": "model-1"}, }, { "model_name": "gpt-4", "litellm_params": {"model": "gpt-4"}, "model_info": {"id": "model-2"}, }, ] # Test: Build index from model list router._build_model_id_to_deployment_index_map(model_list) # Verify: model_list is populated assert len(router.model_list) == 2 # Verify: model_id_to_deployment_index_map is correctly built assert router.model_id_to_deployment_index_map["model-1"] == 0 assert router.model_id_to_deployment_index_map["model-2"] == 1 """ test_file_path.write_text(test_file_content) # Configure test discovery test_config = TestConfig( tests_root=path_obj_tmpdirname, project_root_path=path_obj_tmpdirname, test_framework="pytest", # Using pytest framework to discover unittest tests tests_project_rootdir=path_obj_tmpdirname.parent, ) fto = FunctionToOptimize( function_name="_build_model_id_to_deployment_index_map", file_path=code_file_path, parents=[FunctionParent(name="Router", type="ClassDef")], ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config, file_to_funcs_to_optimize={code_file_path: [fto]}) # Verify the unittest was discovered assert len(discovered_tests) == 1 assert "router_file.Router._build_model_id_to_deployment_index_map" in discovered_tests assert len(discovered_tests["router_file.Router._build_model_id_to_deployment_index_map"]) == 1 router_test = next(iter(discovered_tests["router_file.Router._build_model_id_to_deployment_index_map"])) assert router_test.tests_in_file.test_file.resolve() == test_file_path.resolve() assert router_test.tests_in_file.test_function == "test_build_model_id_to_deployment_index_map" def test_unittest_discovery_with_pytest_parameterized(): with tempfile.TemporaryDirectory() as tmpdirname: path_obj_tmpdirname = Path(tmpdirname) # Create a simple code file code_file_path = path_obj_tmpdirname / "calculator.py" code_file_content = """ class Calculator: def add(self, a, b): return a + b def multiply(self, a, b): return a * b """ code_file_path.write_text(code_file_content) # Create a unittest test file with different parameterized patterns test_file_path = path_obj_tmpdirname / "test_calculator.py" test_file_content = """ import unittest from parameterized import parameterized from calculator import Calculator class TestCalculator(unittest.TestCase): # Test with named parameters @parameterized.expand([ ("positive_numbers", 2, 2, 4), ("zeros", 0, 0, 0), ("negative_and_positive", -1, 1, 0), ("negative_result", 10, -15, -5), ]) def test_add(self, name, a, b, expected): calc = Calculator() result = calc.add(a, b) self.assertEqual(result, expected) # Test with unnamed parameters @parameterized.expand([ (2, 3, 6), (0, 5, 0), (-2, 3, -6), ]) def test_multiply(self, a, b, expected): calc = Calculator() result = calc.multiply(a, b) self.assertEqual(result, expected) # Test with mixed naming patterns @parameterized.expand([ ("test with spaces", 1, 1, 2), ("test_with_underscores", 2, 2, 4), ("test.with.dots", 3, 3, 6), ("test-with-hyphens", 4, 4, 8), ]) def test_add_mixed(self, name, a, b, expected): calc = Calculator() result = calc.add(a, b) self.assertEqual(result, expected) """ test_file_path.write_text(test_file_content) # Configure test discovery test_config = TestConfig( tests_root=path_obj_tmpdirname, project_root_path=path_obj_tmpdirname, test_framework="pytest", tests_project_rootdir=path_obj_tmpdirname.parent, ) # Discover tests discovered_tests, _, _ = discover_unit_tests(test_config) # Verify the basic structure assert len(discovered_tests) == 2 # Should have tests for both add and multiply assert "calculator.Calculator.add" in discovered_tests assert "calculator.Calculator.multiply" in discovered_tests # Import Filtering Tests def test_analyze_imports_direct_function_import(): """Test that direct function imports are detected.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import target_function, other_function def test_target(): assert target_function() is True """ test_file.write_text(test_content) target_functions = {"target_function", "missing_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_star_import(): """Test that star imports trigger conservative processing.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import * def test_something(): assert something() is True """ test_file.write_text(test_content) target_functions = {"target_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is False with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import * def test_target(): assert target_function() is True """ test_file.group test_file.write_text(test_content) target_functions = {"mymodule.target_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import * def test_target(): assert target_function_extended() is True """ test_file.write_text(test_content) # Should not match - target_function != target_function_extended target_functions = {"mymodule.target_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is False with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import * def test_something(): x = 42 assert x == 42 """ test_file.write_text(test_content) target_functions = {"mymodule.target_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is False with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import * def test_something(): message = "calling target_function" assert "target_function" in message """ test_file.write_text(test_content) target_functions = {"mymodule.target_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) # String literals are ast.Constant nodes, not ast.Name nodes, so they don't match assert should_process is False with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import target_function from othermodule import * def test_target(): assert target_function() is True assert other_func() is True """ test_file.write_text(test_content) target_functions = {"mymodule.target_function", "othermodule.other_func"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_module_import(): """Test module imports with function access patterns.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ import mymodule def test_target(): assert mymodule.target_function() is True """ test_file.write_text(test_content) target_functions = {"target_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_dynamic_import(): """Test detection of dynamic imports.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ import importlib def test_dynamic(): module = importlib.import_module("mymodule") assert module.target_function() is True """ test_file.write_text(test_content) target_functions = {"target_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_builtin_import(): """Test detection of __import__ calls.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ def test_builtin_import(): module = __import__("mymodule") assert module.target_function() is True """ test_file.write_text(test_content) target_functions = {"target_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_no_matching_imports(): """Test that files with no matching imports are filtered out.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from unrelated_module import unrelated_function def test_unrelated(): assert unrelated_function() is True """ test_file.write_text(test_content) target_functions = {"target_function", "another_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is False def test_analyze_qualified_names(): with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from target_module import some_function def test_target(): assert some_function() is True """ test_file.write_text(test_content) target_functions = {"target_module.some_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_syntax_error(): """Test handling of files with syntax errors.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import target_function def test_target( # Syntax error - missing closing parenthesis assert target_function() is True """ test_file.write_text(test_content) target_functions = {"target_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) # Should be conservative with unparseable files assert should_process is True def test_filter_test_files_by_imports(): with tempfile.TemporaryDirectory() as tmpdirname: tmpdir = Path(tmpdirname) # Create test file that imports target function relevant_test = tmpdir / "test_relevant.py" relevant_test.write_text(""" from mymodule import target_function def test_target(): assert target_function() is True """) # Create test file that doesn't import target function irrelevant_test = tmpdir / "test_irrelevant.py" irrelevant_test.write_text(""" from othermodule import other_function def test_other(): assert other_function() is True """) # Create test file with star import (should not be processed) star_test = tmpdir / "test_star.py" star_test.write_text(""" from mymodule import * def test_star(): assert something() is True """) file_to_test_map = { relevant_test: [ TestsInFile( test_file=relevant_test, test_function="test_target", test_class=None, test_type=TestType.EXISTING_UNIT_TEST, ) ], irrelevant_test: [ TestsInFile( test_file=irrelevant_test, test_function="test_other", test_class=None, test_type=TestType.EXISTING_UNIT_TEST, ) ], star_test: [ TestsInFile( test_file=star_test, test_function="test_star", test_class=None, test_type=TestType.EXISTING_UNIT_TEST, ) ], } target_functions = {"target_function"} filtered_map = filter_test_files_by_imports(file_to_test_map, target_functions) # Should filter out irrelevant_test assert len(filtered_map) == 1 assert relevant_test in filtered_map assert irrelevant_test not in filtered_map def test_filter_test_files_no_target_functions(): """Test that filtering is skipped when no target functions are provided.""" with tempfile.TemporaryDirectory() as tmpdirname: tmpdir = Path(tmpdirname) test_file = tmpdir / "test_example.py" test_file.write_text("def test_something(): pass") file_to_test_map = { test_file: [ TestsInFile( test_file=test_file, test_function="test_something", test_class=None, test_type=TestType.EXISTING_UNIT_TEST, ) ] } # No target functions provided filtered_map = filter_test_files_by_imports(file_to_test_map, set()) # Should return original map unchanged assert filtered_map == file_to_test_map def test_discover_unit_tests_with_import_filtering(): """Test the full discovery process with import filtering.""" with tempfile.TemporaryDirectory() as tmpdirname: tmpdir = Path(tmpdirname) # Create a code file code_file = tmpdir / "mycode.py" code_file.write_text(""" def target_function(): return True def other_function(): return False """) # Create relevant test file relevant_test = tmpdir / "test_relevant.py" relevant_test.write_text(""" from mycode import target_function def test_target(): assert target_function() is True """) # Create irrelevant test file irrelevant_test = tmpdir / "test_irrelevant.py" irrelevant_test.write_text(""" from mycode import other_function def test_other(): assert other_function() is False """) # Configure test discovery test_config = TestConfig( tests_root=tmpdir, project_root_path=tmpdir, test_framework="pytest", tests_project_rootdir=tmpdir.parent ) all_tests, _, _ = discover_unit_tests(test_config) assert len(all_tests) == 2 fto = FunctionToOptimize(function_name="target_function", file_path=code_file, parents=[]) filtered_tests, _, _ = discover_unit_tests(test_config, file_to_funcs_to_optimize={code_file: [fto]}) assert len(filtered_tests) >= 1 assert "mycode.target_function" in filtered_tests def test_analyze_imports_conditional_import(): """Test detection of conditional imports within functions.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ def test_conditional(): if some_condition: from mymodule import target_function assert target_function() is True """ test_file.write_text(test_content) target_functions = {"target_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_function_name_in_code(): """Test detection of function names used directly in code.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ import mymodule def test_indirect(): func_name = "target_function" func = getattr(mymodule, func_name) # The analyzer should detect target_function usage result = target_function() assert result is True """ test_file.write_text(test_content) target_functions = {"target_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_aliased_imports(): """Test handling of aliased imports.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import target_function as tf, other_function as of def test_aliased(): assert tf() is True assert of() is False """ test_file.write_text(test_content) target_functions = {"target_function", "missing_function"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_underscore_function_names(): with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from bubble_module import sort_function def test_bubble(): assert sort_function([3,1,2]) == [1,2,3] """ test_file.write_text(test_content) target_functions = {"bubble_sort"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is False def test_discover_unit_tests_filtering_different_modules(): """Test import filtering with test files from completely different modules.""" with tempfile.TemporaryDirectory() as tmpdirname: tmpdir = Path(tmpdirname) # Create target code file target_file = tmpdir / "target_module.py" target_file.write_text(""" def target_function(): return True """) # Create unrelated code file unrelated_file = tmpdir / "unrelated_module.py" unrelated_file.write_text(""" def unrelated_function(): return False """) # Create test file that imports target function relevant_test = tmpdir / "test_target.py" relevant_test.write_text(""" from target_module import target_function def test_target(): assert target_function() is True """) # Create test file that imports unrelated function irrelevant_test = tmpdir / "test_unrelated.py" irrelevant_test.write_text(""" from unrelated_module import unrelated_function def test_unrelated(): assert unrelated_function() is False """) # Configure test discovery test_config = TestConfig( tests_root=tmpdir, project_root_path=tmpdir, test_framework="pytest", tests_project_rootdir=tmpdir.parent ) # Test without filtering all_tests, _, _ = discover_unit_tests(test_config) assert len(all_tests) == 2 # Should find both functions fto = FunctionToOptimize(function_name="target_function", file_path=target_file, parents=[]) filtered_tests, _, _ = discover_unit_tests(test_config, file_to_funcs_to_optimize={target_file: [fto]}) assert len(filtered_tests) == 1 assert "target_module.target_function" in filtered_tests assert "unrelated_module.unrelated_function" not in filtered_tests def test_analyze_imports_aliased_class_method(): with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from pydantic_ai.profiles.google import ( GoogleJsonSchemaTransformer as pydantic_ai_profiles_google_GoogleJsonSchemaTransformer, ) def test_target(): ret = pydantic_ai_profiles_google_GoogleJsonSchemaTransformer.transform(*args, **kwargs) assert ret is not None """ test_file.write_text(test_content) target_functions = {"GoogleJsonSchemaTransformer.transform"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_method(): with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from code_to_optimize.topological_sort import Graph def test_topological_sort(): g = Graph(6) g.addEdge(5, 2) g.addEdge(5, 0) g.addEdge(4, 0) g.addEdge(4, 1) g.addEdge(2, 3) g.addEdge(3, 1) assert g.topologicalSort()[0] == [5, 4, 2, 3, 1, 0] """ test_file.write_text(test_content) target_functions = {"Graph.topologicalSort"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_fixture(): with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from code_to_optimize.topological_sort import Graph import pytest @pytest.fixture def g(): return Graph(6) def test_topological_sort(g): g.addEdge(5, 2) g.addEdge(5, 0) g.addEdge(4, 0) g.addEdge(4, 1) g.addEdge(2, 3) g.addEdge(3, 1) assert g.topologicalSort()[0] == [5, 4, 2, 3, 1, 0] """ test_file.write_text(test_content) target_functions = {"Graph.topologicalSort"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_class_fixture(): with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ import pytest from router_file import Router class TestRouterIndexManagement: @pytest.fixture def router(self): return Router(model_list=[]) def test_build_model_id_to_deployment_index_map(self, router): model_list = [ { "model_name": "gpt-3.5-turbo", "litellm_params": {"model": "gpt-3.5-turbo"}, "model_info": {"id": "model-1"}, }, { "model_name": "gpt-4", "litellm_params": {"model": "gpt-4"}, "model_info": {"id": "model-2"}, }, ] # Test: Build index from model list router._build_model_id_to_deployment_index_map(model_list) # Verify: model_list is populated assert len(router.model_list) == 2 # Verify: model_id_to_deployment_index_map is correctly built assert router.model_id_to_deployment_index_map["model-1"] == 0 assert router.model_id_to_deployment_index_map["model-2"] == 1 """ test_file.write_text(test_content) target_functions = {"Router._build_model_id_to_deployment_index_map"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_aliased_class_method_negative(): with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from pydantic_ai.profiles.google import ( GoogleJsonSchemaTransformer as pydantic_ai_profiles_google_GoogleJsonSchemaTransformer, ) def test_target(): ret = pydantic_ai_profiles_google_GoogleJsonSchemaTransformer.validate(*args, **kwargs) assert ret is not None """ test_file.write_text(test_content) # Looking for transform but code uses validate - should not match target_functions = {"GoogleJsonSchemaTransformer.transform"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is False def test_analyze_imports_class_with_multiple_methods(): """Test importing a class when looking for multiple methods of that class.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import MyClass def test_methods(): obj = MyClass() assert obj.method1() is True assert obj.method2() is False assert obj.method3() == 42 """ test_file.write_text(test_content) # Looking for multiple methods of the same class target_functions = {"MyClass.method1", "MyClass.method2", "MyClass.method3"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_class_method_with_nested_classes(): """Test importing nested classes and their methods.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import OuterClass def test_nested(): outer = OuterClass() inner = outer.InnerClass() assert inner.inner_method() is True """ test_file.write_text(test_content) # This would require more complex analysis of nested classes # Currently only direct class.method patterns are supported target_functions = {"OuterClass.InnerClass.inner_method"} should_process = analyze_imports_in_test_file(test_file, target_functions) # Our fix detects OuterClass from OuterClass.InnerClass.inner_method # This is overly broad but conservative (better to include than exclude) assert should_process is True def test_analyze_imports_class_method_partial_match(): """Test that partial class names don't match incorrectly.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import GraphBuilder def test_builder(): builder = GraphBuilder() assert builder.build() is not None """ test_file.write_text(test_content) # Looking for Graph.topologicalSort, not GraphBuilder target_functions = {"Graph.topologicalSort"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is False def test_analyze_imports_class_method_with_inheritance(): """Test importing a child class when looking for parent class methods.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import ChildClass def test_inherited(): child = ChildClass() # Assuming ChildClass inherits from ParentClass assert child.parent_method() is True """ test_file.write_text(test_content) # Looking for parent class method, but only child is imported target_functions = {"ParentClass.parent_method"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is False def test_analyze_imports_class_static_and_class_methods(): """Test importing a class and calling static/class methods.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import MyClass def test_static_and_class_methods(): # Static method call assert MyClass.static_method() is True # Class method call result = MyClass.class_method() assert result == "expected" # Instance method call obj = MyClass() assert obj.instance_method() is False """ test_file.write_text(test_content) target_functions = {"MyClass.static_method", "MyClass.class_method", "MyClass.instance_method"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_multiple_classes_same_module(): """Test importing multiple classes from the same module.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import ClassA, ClassB, ClassC def test_multiple_classes(): a = ClassA() b = ClassB() c = ClassC() assert a.methodA() is True assert b.methodB() is False assert c.methodC() == 42 """ test_file.write_text(test_content) # Looking for methods from different classes target_functions = {"ClassA.methodA", "ClassB.methodB", "ClassD.methodD"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True # ClassA and ClassB are imported def test_analyze_imports_class_method_case_sensitive(): """Test that class name matching is case-sensitive.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import graph def test_lowercase(): g = graph() assert g.topologicalSort() is not None """ test_file.write_text(test_content) # Looking for Graph (capital G), but imported graph (lowercase) target_functions = {"Graph.topologicalSort"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is False def test_analyze_imports_class_from_submodule(): """Test importing a class from a submodule.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from package.subpackage.module import MyClass def test_submodule_class(): obj = MyClass() assert obj.my_method() is True """ test_file.write_text(test_content) target_functions = {"MyClass.my_method"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_aliased_class_with_methods(): """Test importing a class with an alias and looking for its methods.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import Graph as G def test_aliased_class(): graph = G(10) result = graph.topologicalSort() assert result is not None """ test_file.write_text(test_content) target_functions = {"Graph.topologicalSort"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_class_property_access(): """Test importing a class and accessing properties (not methods).""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import MyClass def test_properties(): obj = MyClass() # Accessing properties, not methods assert obj.size == 10 assert obj.name == "test" """ test_file.write_text(test_content) # Looking for methods, but only properties are accessed # Our fix conservatively includes when class is imported target_functions = {"MyClass.get_size", "MyClass.get_name"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True # Conservative approach def test_analyze_imports_class_constructor_params(): """Test class import when looking for __init__ method.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import MyClass def test_constructor(): # Testing the constructor obj1 = MyClass() obj2 = MyClass(10) obj3 = MyClass(size=20, name="test") assert obj1 is not None assert obj2 is not None assert obj3 is not None """ test_file.write_text(test_content) # __init__ is a special method that would require additional logic target_functions = {"MyClass.__init__"} should_process = analyze_imports_in_test_file(test_file, target_functions) # Our fix now detects MyClass from MyClass.__init__ assert should_process is True def test_analyze_imports_class_method_chaining(): """Test method chaining on imported classes.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import Builder def test_chaining(): result = Builder().add_item("a").add_item("b").build() assert result is not None """ test_file.write_text(test_content) # Method chaining requires tracking object types through chained calls target_functions = {"Builder.add_item", "Builder.build"} should_process = analyze_imports_in_test_file(test_file, target_functions) # Currently detects Builder import and methods assert should_process is True def test_analyze_imports_mixed_function_and_class_imports(): """Test mixed imports of functions and classes from the same module.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from mymodule import MyClass, standalone_function, AnotherClass def test_mixed(): # Using class method obj = MyClass() assert obj.method() is True # Using standalone function assert standalone_function() is False # Using another class other = AnotherClass() assert other.other_method() == 42 """ test_file.write_text(test_content) target_functions = {"MyClass.method", "standalone_function", "YetAnotherClass.method"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True # MyClass.method and standalone_function are imported def test_analyze_imports_class_with_module_prefix(): """Test looking for fully qualified class methods.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from code_to_optimize.topological_sort import Graph def test_fully_qualified(): g = Graph(5) assert g.topologicalSort() == [4, 3, 2, 1, 0] """ test_file.write_text(test_content) # Looking with full module path would require more complex module resolution target_functions = {"code_to_optimize.topological_sort.Graph.topologicalSort"} should_process = analyze_imports_in_test_file(test_file, target_functions) # Currently not supported - would need to match module path with imports assert should_process is False def test_analyze_imports_reimport_in_function(): """Test class import inside a function.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ def test_local_import(): from mymodule import MyClass obj = MyClass() assert obj.method() is True """ test_file.write_text(test_content) target_functions = {"MyClass.method"} should_process = analyze_imports_in_test_file(test_file, target_functions) assert should_process is True def test_analyze_imports_class_in_type_annotation(): """Test class used only in type annotations.""" with tempfile.TemporaryDirectory() as tmpdirname: test_file = Path(tmpdirname) / "test_example.py" test_content = """ from typing import Optional from mymodule import MyClass def helper_function(obj: Optional[MyClass]) -> bool: if obj: return obj.method() return False def test_with_type_annotation(): # MyClass is imported but only used in type annotation result = helper_function(None) assert result is False """ test_file.write_text(test_content) target_functions = {"MyClass.method"} should_process = analyze_imports_in_test_file(test_file, target_functions) # MyClass is imported, so class.method pattern should match assert should_process is True def test_discover_unit_tests_caching(): tests_root = Path(__file__).parent.resolve() / "tests" project_root_path = tests_root.parent.resolve() test_config = TestConfig( tests_root=tests_root, project_root_path=project_root_path, test_framework="pytest", tests_project_rootdir=project_root_path, use_cache=False, ) non_cached_function_to_tests, non_cached_num_discovered_tests, non_cached_num_discovered_replay_tests = ( discover_unit_tests(test_config) ) cache_config = TestConfig( tests_root=tests_root, project_root_path=project_root_path, test_framework="pytest", tests_project_rootdir=project_root_path, use_cache=True, ) tests, num_discovered_tests, num_discovered_replay_tests = discover_unit_tests(cache_config) assert non_cached_num_discovered_tests == num_discovered_tests assert non_cached_function_to_tests == tests assert non_cached_num_discovered_replay_tests == num_discovered_replay_tests