Skip to content

Commit c6c9d95

Browse files
committed
few missing things
1 parent 6bd39f7 commit c6c9d95

File tree

5 files changed

+669
-169
lines changed

5 files changed

+669
-169
lines changed

codeflash/verification/parse_test_output.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,30 @@ def parse_func(file_path: Path) -> XMLParser:
4040
matches_re_end = re.compile(r"!######(.*?):(.*?)([^\.:]*?):(.*?):(.*?):(.*?)######!")
4141

4242

43+
start_pattern = re.compile(r"!\$######([^:]*):([^:]*):([^:]*):([^:]*):([^:]+)######\$!")
44+
end_pattern = re.compile(r"!######([^:]*):([^:]*):([^:]*):([^:]*):([^:]+):([^:]+)######!")
45+
46+
47+
def calculate_function_throughput_from_test_results(test_results: TestResults, function_name: str) -> int:
48+
"""Calculate function throughput from TestResults by extracting performance stdout.
49+
50+
A completed execution is defined as having both a start tag and matching end tag from performance wrappers.
51+
Start: !$######test_module:test_function:function_name:loop_index:iteration_id######$!
52+
End: !######test_module:test_function:function_name:loop_index:iteration_id:duration######!
53+
"""
54+
start_matches = start_pattern.findall(test_results.perf_stdout or "")
55+
end_matches = end_pattern.findall(test_results.perf_stdout or "")
56+
57+
end_matches_truncated = [end_match[:5] for end_match in end_matches]
58+
end_matches_set = set(end_matches_truncated)
59+
60+
function_throughput = 0
61+
for start_match in start_matches:
62+
if start_match in end_matches_set and len(start_match) > 2 and start_match[2] == function_name:
63+
function_throughput += 1
64+
return function_throughput
65+
66+
4367
def parse_test_return_values_bin(file_location: Path, test_files: TestFiles, test_config: TestConfig) -> TestResults:
4468
test_results = TestResults()
4569
if not file_location.exists():

tests/test_add_runtime_comments.py

Lines changed: 207 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1902,4 +1902,210 @@ def test_bubble_sort(input, expected_output):
19021902

19031903
# Check that comments were added
19041904
modified_source = result.generated_tests[0].generated_original_test_source
1905-
assert modified_source == expected
1905+
assert modified_source == expected
1906+
1907+
def test_async_basic_runtime_comment_addition(self, test_config):
1908+
"""Test basic functionality of adding runtime comments to async test functions."""
1909+
os.chdir(test_config.project_root_path)
1910+
test_source = """async def test_async_bubble_sort():
1911+
codeflash_output = await async_bubble_sort([3, 1, 2])
1912+
assert codeflash_output == [1, 2, 3]
1913+
"""
1914+
1915+
generated_test = GeneratedTests(
1916+
generated_original_test_source=test_source,
1917+
instrumented_behavior_test_source="",
1918+
instrumented_perf_test_source="",
1919+
behavior_file_path=test_config.tests_root / "test_module__unit_test_0.py",
1920+
perf_file_path=test_config.tests_root / "test_perf.py",
1921+
)
1922+
generated_tests = GeneratedTestsList(generated_tests=[generated_test])
1923+
1924+
original_test_results = TestResults()
1925+
optimized_test_results = TestResults()
1926+
1927+
original_invocation = self.create_test_invocation("test_async_bubble_sort", 500_000, iteration_id='0') # 500μs
1928+
optimized_invocation = self.create_test_invocation("test_async_bubble_sort", 300_000, iteration_id='0') # 300μs
1929+
1930+
original_test_results.add(original_invocation)
1931+
optimized_test_results.add(optimized_invocation)
1932+
original_runtimes = original_test_results.usable_runtime_data_by_test_case()
1933+
optimized_runtimes = optimized_test_results.usable_runtime_data_by_test_case()
1934+
result = add_runtime_comments_to_generated_tests(generated_tests, original_runtimes, optimized_runtimes)
1935+
1936+
modified_source = result.generated_tests[0].generated_original_test_source
1937+
assert "# 500μs -> 300μs" in modified_source
1938+
assert "codeflash_output = await async_bubble_sort([3, 1, 2]) # 500μs -> 300μs" in modified_source
1939+
1940+
def test_async_multiple_test_functions(self, test_config):
1941+
os.chdir(test_config.project_root_path)
1942+
test_source = """async def test_async_bubble_sort():
1943+
codeflash_output = await async_quick_sort([3, 1, 2])
1944+
assert codeflash_output == [1, 2, 3]
1945+
1946+
async def test_async_quick_sort():
1947+
codeflash_output = await async_quick_sort([5, 2, 8])
1948+
assert codeflash_output == [2, 5, 8]
1949+
1950+
def helper_function():
1951+
return "not a test"
1952+
"""
1953+
generated_test = GeneratedTests(
1954+
generated_original_test_source=test_source,
1955+
instrumented_behavior_test_source="",
1956+
instrumented_perf_test_source="",
1957+
behavior_file_path=test_config.tests_root / "test_module__unit_test_0.py",
1958+
perf_file_path=test_config.tests_root / "test_perf.py"
1959+
)
1960+
1961+
generated_tests = GeneratedTestsList(generated_tests=[generated_test])
1962+
1963+
original_test_results = TestResults()
1964+
optimized_test_results = TestResults()
1965+
1966+
original_test_results.add(self.create_test_invocation("test_async_bubble_sort", 500_000, iteration_id='0'))
1967+
original_test_results.add(self.create_test_invocation("test_async_quick_sort", 800_000, iteration_id='0'))
1968+
1969+
optimized_test_results.add(self.create_test_invocation("test_async_bubble_sort", 300_000, iteration_id='0'))
1970+
optimized_test_results.add(self.create_test_invocation("test_async_quick_sort", 600_000, iteration_id='0'))
1971+
1972+
original_runtimes = original_test_results.usable_runtime_data_by_test_case()
1973+
optimized_runtimes = optimized_test_results.usable_runtime_data_by_test_case()
1974+
1975+
result = add_runtime_comments_to_generated_tests(generated_tests, original_runtimes, optimized_runtimes)
1976+
1977+
modified_source = result.generated_tests[0].generated_original_test_source
1978+
1979+
assert "# 500μs -> 300μs" in modified_source
1980+
assert "# 800μs -> 600μs" in modified_source
1981+
assert (
1982+
"helper_function():" in modified_source
1983+
and "# " not in modified_source.split("helper_function():")[1].split("\n")[0]
1984+
)
1985+
1986+
def test_async_class_method(self, test_config):
1987+
os.chdir(test_config.project_root_path)
1988+
test_source = '''class TestAsyncClass:
1989+
async def test_async_function(self):
1990+
codeflash_output = await some_async_function()
1991+
assert codeflash_output == expected
1992+
'''
1993+
generated_test = GeneratedTests(
1994+
generated_original_test_source=test_source,
1995+
instrumented_behavior_test_source="",
1996+
instrumented_perf_test_source="",
1997+
behavior_file_path=test_config.tests_root / "test_module__unit_test_0.py",
1998+
perf_file_path=test_config.tests_root / "test_perf.py"
1999+
)
2000+
2001+
generated_tests = GeneratedTestsList(generated_tests=[generated_test])
2002+
2003+
invocation_id = InvocationId(
2004+
test_module_path="tests.test_module__unit_test_0",
2005+
test_class_name="TestAsyncClass",
2006+
test_function_name="test_async_function",
2007+
function_getting_tested="some_async_function",
2008+
iteration_id="0",
2009+
)
2010+
2011+
original_runtimes = {invocation_id: [2000000000]} # 2s in nanoseconds
2012+
optimized_runtimes = {invocation_id: [1000000000]} # 1s in nanoseconds
2013+
2014+
result = add_runtime_comments_to_generated_tests(generated_tests, original_runtimes, optimized_runtimes)
2015+
2016+
expected_source = '''class TestAsyncClass:
2017+
async def test_async_function(self):
2018+
codeflash_output = await some_async_function() # 2.00s -> 1.00s (100% faster)
2019+
assert codeflash_output == expected
2020+
'''
2021+
2022+
assert len(result.generated_tests) == 1
2023+
assert result.generated_tests[0].generated_original_test_source == expected_source
2024+
2025+
def test_async_mixed_sync_and_async_functions(self, test_config):
2026+
os.chdir(test_config.project_root_path)
2027+
test_source = """def test_sync_function():
2028+
codeflash_output = sync_function([1, 2, 3])
2029+
assert codeflash_output == [1, 2, 3]
2030+
2031+
async def test_async_function():
2032+
codeflash_output = await async_function([4, 5, 6])
2033+
assert codeflash_output == [4, 5, 6]
2034+
2035+
def test_another_sync():
2036+
result = another_sync_func()
2037+
assert result is True
2038+
"""
2039+
generated_test = GeneratedTests(
2040+
generated_original_test_source=test_source,
2041+
instrumented_behavior_test_source="",
2042+
instrumented_perf_test_source="",
2043+
behavior_file_path=test_config.tests_root / "test_module__unit_test_0.py",
2044+
perf_file_path=test_config.tests_root / "test_perf.py"
2045+
)
2046+
2047+
generated_tests = GeneratedTestsList(generated_tests=[generated_test])
2048+
2049+
original_test_results = TestResults()
2050+
optimized_test_results = TestResults()
2051+
2052+
# Add test invocations for all test functions
2053+
original_test_results.add(self.create_test_invocation("test_sync_function", 400_000, iteration_id='0'))
2054+
original_test_results.add(self.create_test_invocation("test_async_function", 600_000, iteration_id='0'))
2055+
original_test_results.add(self.create_test_invocation("test_another_sync", 200_000, iteration_id='0'))
2056+
2057+
optimized_test_results.add(self.create_test_invocation("test_sync_function", 200_000, iteration_id='0'))
2058+
optimized_test_results.add(self.create_test_invocation("test_async_function", 300_000, iteration_id='0'))
2059+
optimized_test_results.add(self.create_test_invocation("test_another_sync", 100_000, iteration_id='0'))
2060+
2061+
original_runtimes = original_test_results.usable_runtime_data_by_test_case()
2062+
optimized_runtimes = optimized_test_results.usable_runtime_data_by_test_case()
2063+
2064+
result = add_runtime_comments_to_generated_tests(generated_tests, original_runtimes, optimized_runtimes)
2065+
2066+
modified_source = result.generated_tests[0].generated_original_test_source
2067+
2068+
assert "# 400μs -> 200μs" in modified_source
2069+
assert "# 600μs -> 300μs" in modified_source
2070+
assert "# 200μs -> 100μs" in modified_source
2071+
2072+
assert "async def test_async_function():" in modified_source
2073+
assert "await async_function([4, 5, 6])" in modified_source
2074+
2075+
def test_async_complex_await_patterns(self, test_config):
2076+
os.chdir(test_config.project_root_path)
2077+
test_source = """async def test_complex_async():
2078+
# Multiple await calls
2079+
result1 = await async_func1()
2080+
codeflash_output = await async_func2(result1)
2081+
result3 = await async_func3(codeflash_output)
2082+
assert result3 == expected
2083+
2084+
# Await in context manager
2085+
async with async_context() as ctx:
2086+
final_result = await ctx.process()
2087+
assert final_result is not None
2088+
"""
2089+
generated_test = GeneratedTests(
2090+
generated_original_test_source=test_source,
2091+
instrumented_behavior_test_source="",
2092+
instrumented_perf_test_source="",
2093+
behavior_file_path=test_config.tests_root / "test_module__unit_test_0.py",
2094+
perf_file_path=test_config.tests_root / "test_perf.py"
2095+
)
2096+
2097+
generated_tests = GeneratedTestsList(generated_tests=[generated_test])
2098+
2099+
original_test_results = TestResults()
2100+
optimized_test_results = TestResults()
2101+
2102+
original_test_results.add(self.create_test_invocation("test_complex_async", 750_000, iteration_id='1')) # 750μs
2103+
optimized_test_results.add(self.create_test_invocation("test_complex_async", 450_000, iteration_id='1')) # 450μs
2104+
2105+
original_runtimes = original_test_results.usable_runtime_data_by_test_case()
2106+
optimized_runtimes = optimized_test_results.usable_runtime_data_by_test_case()
2107+
2108+
result = add_runtime_comments_to_generated_tests(generated_tests, original_runtimes, optimized_runtimes)
2109+
2110+
modified_source = result.generated_tests[0].generated_original_test_source
2111+
assert "# 750μs -> 450μs" in modified_source

0 commit comments

Comments
 (0)