473 lines
18 KiB
Python
473 lines
18 KiB
Python
"""Comprehensive unit tests for TestResults.file_to_no_of_tests method."""
|
|
|
|
from collections import Counter
|
|
from pathlib import Path
|
|
|
|
from codeflash.models.models import FunctionTestInvocation, InvocationId, TestResults, TestType
|
|
|
|
|
|
class TestFileToNoOfTests:
|
|
"""Test suite for TestResults.file_to_no_of_tests method."""
|
|
|
|
def test_empty_test_results(self):
|
|
"""Test with empty test results."""
|
|
test_results = TestResults()
|
|
counter = test_results.file_to_no_of_tests([])
|
|
assert counter == Counter()
|
|
assert len(counter) == 0
|
|
|
|
def test_empty_test_functions_to_remove(self):
|
|
"""Test with empty list of test functions to remove."""
|
|
test_results = TestResults()
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path="test.module",
|
|
test_class_name="TestClass",
|
|
test_function_name="test_function",
|
|
function_getting_tested="target_func",
|
|
iteration_id="1",
|
|
),
|
|
file_name=Path("/tmp/test_file.py"),
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=TestType.GENERATED_REGRESSION,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
counter = test_results.file_to_no_of_tests([])
|
|
assert counter == Counter({Path("/tmp/test_file.py"): 1})
|
|
|
|
def test_single_test_not_removed(self):
|
|
"""Test with a single test that should not be removed."""
|
|
test_results = TestResults()
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path="test.module",
|
|
test_class_name="TestClass",
|
|
test_function_name="test_keep",
|
|
function_getting_tested="target_func",
|
|
iteration_id="1",
|
|
),
|
|
file_name=Path("/tmp/test_file.py"),
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=TestType.GENERATED_REGRESSION,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
counter = test_results.file_to_no_of_tests(["test_remove"])
|
|
assert counter == Counter({Path("/tmp/test_file.py"): 1})
|
|
|
|
def test_single_test_removed(self):
|
|
"""Test with a single test that should be removed."""
|
|
test_results = TestResults()
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path="test.module",
|
|
test_class_name="TestClass",
|
|
test_function_name="test_remove",
|
|
function_getting_tested="target_func",
|
|
iteration_id="1",
|
|
),
|
|
file_name=Path("/tmp/test_file.py"),
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=TestType.GENERATED_REGRESSION,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
counter = test_results.file_to_no_of_tests(["test_remove"])
|
|
assert counter == Counter()
|
|
|
|
def test_multiple_tests_same_file(self):
|
|
"""Test with multiple tests in the same file."""
|
|
test_results = TestResults()
|
|
for i in range(5):
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path="test.module",
|
|
test_class_name="TestClass",
|
|
test_function_name=f"test_func_{i}",
|
|
function_getting_tested="target_func",
|
|
iteration_id=str(i),
|
|
),
|
|
file_name=Path("/tmp/test_file.py"),
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=TestType.GENERATED_REGRESSION,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
counter = test_results.file_to_no_of_tests([])
|
|
assert counter == Counter({Path("/tmp/test_file.py"): 5})
|
|
|
|
def test_multiple_tests_different_files(self):
|
|
"""Test with multiple tests in different files."""
|
|
test_results = TestResults()
|
|
files = [Path(f"/tmp/test_file_{i}.py") for i in range(3)]
|
|
for i, file_path in enumerate(files):
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path=f"test.module{i}",
|
|
test_class_name="TestClass",
|
|
test_function_name=f"test_func_{i}",
|
|
function_getting_tested="target_func",
|
|
iteration_id=str(i),
|
|
),
|
|
file_name=file_path,
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=TestType.GENERATED_REGRESSION,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
counter = test_results.file_to_no_of_tests([])
|
|
expected = Counter({files[0]: 1, files[1]: 1, files[2]: 1})
|
|
assert counter == expected
|
|
|
|
def test_mixed_test_types(self):
|
|
"""Test with different test types - only GENERATED_REGRESSION should be counted."""
|
|
test_results = TestResults()
|
|
test_types = [
|
|
TestType.EXISTING_UNIT_TEST,
|
|
TestType.INSPIRED_REGRESSION,
|
|
TestType.GENERATED_REGRESSION,
|
|
TestType.REPLAY_TEST,
|
|
TestType.CONCOLIC_COVERAGE_TEST,
|
|
TestType.INIT_STATE_TEST,
|
|
]
|
|
|
|
for i, test_type in enumerate(test_types):
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path="test.module",
|
|
test_class_name="TestClass",
|
|
test_function_name=f"test_func_{i}",
|
|
function_getting_tested="target_func",
|
|
iteration_id=str(i),
|
|
),
|
|
file_name=Path(f"/tmp/test_file_{i}.py"),
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=test_type,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
|
|
counter = test_results.file_to_no_of_tests([])
|
|
# Only the GENERATED_REGRESSION test should be counted
|
|
assert counter == Counter({Path("/tmp/test_file_2.py"): 1})
|
|
|
|
def test_partial_removal(self):
|
|
"""Test removing some but not all tests from a file."""
|
|
test_results = TestResults()
|
|
test_names = ["test_keep_1", "test_remove_1", "test_keep_2", "test_remove_2"]
|
|
|
|
for name in test_names:
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path="test.module",
|
|
test_class_name="TestClass",
|
|
test_function_name=name,
|
|
function_getting_tested="target_func",
|
|
iteration_id=name,
|
|
),
|
|
file_name=Path("/tmp/test_file.py"),
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=TestType.GENERATED_REGRESSION,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
|
|
counter = test_results.file_to_no_of_tests(["test_remove_1", "test_remove_2"])
|
|
assert counter == Counter({Path("/tmp/test_file.py"): 2}) # Only test_keep_1 and test_keep_2
|
|
|
|
def test_none_test_function_name(self):
|
|
"""Test with None test_function_name."""
|
|
test_results = TestResults()
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path="test.module",
|
|
test_class_name="TestClass",
|
|
test_function_name=None,
|
|
function_getting_tested="target_func",
|
|
iteration_id="1",
|
|
),
|
|
file_name=Path("/tmp/test_file.py"),
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=TestType.GENERATED_REGRESSION,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
# None should not match any string in test_functions_to_remove
|
|
counter = test_results.file_to_no_of_tests(["test_remove"])
|
|
assert counter == Counter({Path("/tmp/test_file.py"): 1})
|
|
|
|
def test_duplicate_file_paths(self):
|
|
"""Test counting with duplicate file paths across multiple tests."""
|
|
test_results = TestResults()
|
|
file_path = Path("/tmp/test_file.py")
|
|
|
|
# Add multiple tests with the same file path
|
|
for i in range(3):
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path="test.module",
|
|
test_class_name="TestClass",
|
|
test_function_name=f"test_func_{i}",
|
|
function_getting_tested="target_func",
|
|
iteration_id=str(i),
|
|
),
|
|
file_name=file_path,
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=TestType.GENERATED_REGRESSION,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
|
|
counter = test_results.file_to_no_of_tests([])
|
|
assert counter == Counter({file_path: 3})
|
|
|
|
def test_complex_scenario(self):
|
|
"""Test complex scenario with mixed conditions."""
|
|
test_results = TestResults()
|
|
|
|
# File 1: Mix of test types
|
|
for i, test_type in enumerate([TestType.GENERATED_REGRESSION, TestType.EXISTING_UNIT_TEST]):
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path="test.module1",
|
|
test_class_name="TestClass",
|
|
test_function_name=f"test_file1_{i}",
|
|
function_getting_tested="target_func",
|
|
iteration_id=str(i),
|
|
),
|
|
file_name=Path("/tmp/file1.py"),
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=test_type,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
|
|
# File 2: Tests to be removed and kept
|
|
for name in ["test_keep", "test_remove"]:
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path="test.module2",
|
|
test_class_name="TestClass",
|
|
test_function_name=name,
|
|
function_getting_tested="target_func",
|
|
iteration_id=name,
|
|
),
|
|
file_name=Path("/tmp/file2.py"),
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=TestType.GENERATED_REGRESSION,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
|
|
# File 3: All GENERATED_REGRESSION tests
|
|
for i in range(3):
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path="test.module3",
|
|
test_class_name="TestClass",
|
|
test_function_name=f"test_file3_{i}",
|
|
function_getting_tested="target_func",
|
|
iteration_id=str(i),
|
|
),
|
|
file_name=Path("/tmp/file3.py"),
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=TestType.GENERATED_REGRESSION,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
|
|
counter = test_results.file_to_no_of_tests(["test_remove"])
|
|
expected = Counter(
|
|
{
|
|
Path("/tmp/file1.py"): 1, # Only 1 GENERATED_REGRESSION test
|
|
Path("/tmp/file2.py"): 1, # Only test_keep (test_remove is excluded)
|
|
Path("/tmp/file3.py"): 3, # All 3 tests
|
|
}
|
|
)
|
|
assert counter == expected
|
|
|
|
def test_case_sensitivity(self):
|
|
"""Test that function name matching is case-sensitive."""
|
|
test_results = TestResults()
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path="test.module",
|
|
test_class_name="TestClass",
|
|
test_function_name="Test_Function",
|
|
function_getting_tested="target_func",
|
|
iteration_id="1",
|
|
),
|
|
file_name=Path("/tmp/test_file.py"),
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=TestType.GENERATED_REGRESSION,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
|
|
# Should not remove because case doesn't match
|
|
counter = test_results.file_to_no_of_tests(["test_function"])
|
|
assert counter == Counter({Path("/tmp/test_file.py"): 1})
|
|
|
|
# Should remove with correct case
|
|
counter = test_results.file_to_no_of_tests(["Test_Function"])
|
|
assert counter == Counter()
|
|
|
|
def test_windows_paths(self):
|
|
"""Test with Windows-style paths."""
|
|
test_results = TestResults()
|
|
windows_path = Path("C:\\Users\\test\\test_file.py")
|
|
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path="test.module",
|
|
test_class_name="TestClass",
|
|
test_function_name="test_func",
|
|
function_getting_tested="target_func",
|
|
iteration_id="1",
|
|
),
|
|
file_name=windows_path,
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=TestType.GENERATED_REGRESSION,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
|
|
counter = test_results.file_to_no_of_tests([])
|
|
assert counter == Counter({windows_path: 1})
|
|
|
|
def test_relative_and_absolute_paths(self):
|
|
"""Test with both relative and absolute paths."""
|
|
test_results = TestResults()
|
|
paths = [
|
|
Path("/absolute/path/test.py"),
|
|
Path("relative/path/test.py"),
|
|
Path("./current/dir/test.py"),
|
|
Path("../parent/dir/test.py"),
|
|
]
|
|
|
|
for i, path in enumerate(paths):
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path=f"test.module{i}",
|
|
test_class_name="TestClass",
|
|
test_function_name=f"test_func_{i}",
|
|
function_getting_tested="target_func",
|
|
iteration_id=str(i),
|
|
),
|
|
file_name=path,
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=TestType.GENERATED_REGRESSION,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
|
|
counter = test_results.file_to_no_of_tests([])
|
|
expected = Counter(dict.fromkeys(paths, 1))
|
|
assert counter == expected
|
|
|
|
def test_large_removal_list(self):
|
|
"""Test with a large list of functions to remove."""
|
|
test_results = TestResults()
|
|
num_tests = 100
|
|
removal_list = [f"test_remove_{i}" for i in range(50)]
|
|
|
|
for i in range(num_tests):
|
|
test_name = f"test_remove_{i}" if i < 50 else f"test_keep_{i}"
|
|
test_results.add(
|
|
FunctionTestInvocation(
|
|
id=InvocationId(
|
|
test_module_path="test.module",
|
|
test_class_name="TestClass",
|
|
test_function_name=test_name,
|
|
function_getting_tested="target_func",
|
|
iteration_id=str(i),
|
|
),
|
|
file_name=Path("/tmp/test_file.py"),
|
|
did_pass=True,
|
|
runtime=100,
|
|
test_framework="pytest",
|
|
test_type=TestType.GENERATED_REGRESSION,
|
|
return_value=None,
|
|
timed_out=False,
|
|
loop_index=1,
|
|
)
|
|
)
|
|
|
|
counter = test_results.file_to_no_of_tests(removal_list)
|
|
assert counter == Counter({Path("/tmp/test_file.py"): 50}) # 50 kept, 50 removed
|