2047 lines
72 KiB
Python
2047 lines
72 KiB
Python
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
|