Skip to content

Commit ae4eb7c

Browse files
committed
do instrument generated regression tests
1 parent 7cf7bcd commit ae4eb7c

8 files changed

Lines changed: 61 additions & 54 deletions

File tree

codeflash/code_utils/instrument_existing_tests.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -632,16 +632,15 @@ def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
632632

633633

634634
def inject_async_profiling_into_existing_test(
635-
test_path: Path,
635+
test_string: str,
636636
call_positions: list[CodePosition],
637637
function_to_optimize: FunctionToOptimize,
638638
tests_project_root: Path,
639639
mode: TestingMode = TestingMode.BEHAVIOR,
640+
test_path: Path | None = None,
640641
) -> tuple[bool, str | None]:
641642
"""Inject profiling for async function calls by setting environment variables before each call."""
642-
with test_path.open(encoding="utf8") as f:
643-
test_code = f.read()
644-
643+
test_code = test_string
645644
try:
646645
tree = ast.parse(test_code)
647646
except SyntaxError:
@@ -704,6 +703,7 @@ def detect_frameworks_from_code(code: str) -> dict[str, str]:
704703

705704

706705
def inject_profiling_into_existing_test(
706+
test_string: str,
707707
test_path: Path,
708708
call_positions: list[CodePosition],
709709
function_to_optimize: FunctionToOptimize,
@@ -715,7 +715,7 @@ def inject_profiling_into_existing_test(
715715
from codeflash.languages.javascript.instrument import inject_profiling_into_existing_js_test
716716

717717
return inject_profiling_into_existing_js_test(
718-
test_path, call_positions, function_to_optimize, tests_project_root, mode.value
718+
test_string=test_string, call_positions=call_positions, function_to_optimize=function_to_optimize, tests_project_root=tests_project_root, mode= mode.value, test_path=test_path
719719
)
720720

721721
if is_java():
@@ -725,15 +725,14 @@ def inject_profiling_into_existing_test(
725725

726726
if function_to_optimize.is_async:
727727
return inject_async_profiling_into_existing_test(
728-
test_path, call_positions, function_to_optimize, tests_project_root, mode
728+
test_string=test_string, call_positions=call_positions, function_to_optimize=function_to_optimize, tests_project_root=tests_project_root, mode=mode.value, test_path=test_path
729729
)
730730

731-
with test_path.open(encoding="utf8") as f:
732-
test_code = f.read()
733731

734-
used_frameworks = detect_frameworks_from_code(test_code)
732+
733+
used_frameworks = detect_frameworks_from_code(test_string)
735734
try:
736-
tree = ast.parse(test_code)
735+
tree = ast.parse(test_string)
737736
except SyntaxError:
738737
logger.exception(f"Syntax error in code in file - {test_path}")
739738
return False, None

codeflash/languages/base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,18 +567,20 @@ def ensure_runtime_environment(self, project_root: Path) -> bool:
567567

568568
def instrument_existing_test(
569569
self,
570-
test_path: Path,
570+
test_string: str,
571571
call_positions: Sequence[Any],
572572
function_to_optimize: Any,
573573
tests_project_root: Path,
574574
mode: str,
575+
test_path: Path | None
575576
) -> tuple[bool, str | None]:
576577
"""Inject profiling code into an existing test file.
577578
578579
Wraps function calls with capture/benchmark instrumentation for
579580
behavioral verification and performance benchmarking.
580581
581582
Args:
583+
test_string: String containing the test file contents.
582584
test_path: Path to the test file.
583585
call_positions: List of code positions where the function is called.
584586
function_to_optimize: The function being optimized.

codeflash/languages/java/instrumentation.py

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -232,13 +232,11 @@ def instrument_for_benchmarking(
232232

233233

234234
def instrument_existing_test(
235-
test_path: Path,
236-
call_positions: Sequence,
235+
test_string: str,
237236
function_to_optimize: Any, # FunctionToOptimize or FunctionToOptimize
238-
tests_project_root: Path,
239237
mode: str, # "behavior" or "performance"
240-
analyzer: JavaAnalyzer | None = None,
241-
output_class_suffix: str | None = None, # Suffix for renamed class
238+
test_path: Path | None = None,
239+
test_class_name: str | None = None,
242240
) -> tuple[bool, str | None]:
243241
"""Inject profiling code into an existing test file.
244242
@@ -248,7 +246,7 @@ def instrument_existing_test(
248246
3. For performance mode: adds timing instrumentation with stdout markers
249247
250248
Args:
251-
test_path: Path to the test file.
249+
test_string: String to the test file.
252250
call_positions: List of code positions where the function is called.
253251
function_to_optimize: The function being optimized.
254252
tests_project_root: Root directory of tests.
@@ -260,16 +258,16 @@ def instrument_existing_test(
260258
Tuple of (success, modified_source).
261259
262260
"""
263-
try:
264-
source = test_path.read_text(encoding="utf-8")
265-
except Exception as e:
266-
logger.exception("Failed to read test file %s: %s", test_path, e)
267-
return False, f"Failed to read test file: {e}"
268-
261+
source = test_string
269262
func_name = _get_function_name(function_to_optimize)
270263

271264
# Get the original class name from the file name
272-
original_class_name = test_path.stem # e.g., "AlgorithmsTest"
265+
if test_path:
266+
original_class_name = test_path.stem # e.g., "AlgorithmsTest"
267+
elif test_class_name is not None:
268+
original_class_name = test_class_name
269+
else:
270+
raise ValueError("test_path or test_class_name must be provided")
273271

274272
# Determine the new class name based on mode
275273
if mode == "behavior":
@@ -298,7 +296,7 @@ def instrument_existing_test(
298296
modified_source = _add_behavior_instrumentation(modified_source, original_class_name, func_name)
299297

300298
logger.debug("Java %s testing for %s: renamed class %s -> %s", mode, func_name, original_class_name, new_class_name)
301-
299+
# Why return True here?
302300
return True, modified_source
303301

304302

@@ -800,6 +798,7 @@ def instrument_generated_java_test(
800798
function_name: str,
801799
qualified_name: str,
802800
mode: str, # "behavior" or "performance"
801+
function_to_optimize: FunctionToOptimize,
803802
) -> str:
804803
"""Instrument a generated Java test for behavior or performance testing.
805804
@@ -834,26 +833,31 @@ def instrument_generated_java_test(
834833

835834
original_class_name = class_match.group(1)
836835

837-
# Rename class based on mode
838-
if mode == "behavior":
839-
new_class_name = f"{original_class_name}__perfinstrumented"
840-
else:
841-
new_class_name = f"{original_class_name}__perfonlyinstrumented"
842-
843-
# Rename all references to the original class name in the source.
844-
# This includes the class declaration, return types, constructor calls, etc.
845-
modified_code = re.sub(
846-
rf"\b{re.escape(original_class_name)}\b", new_class_name, test_code
847-
)
848836

849837
# For performance mode, add timing instrumentation
850838
# Use original class name (without suffix) in timing markers for consistency with Python
851839
if mode == "performance":
840+
841+
842+
# Rename class based on mode
843+
if mode == "behavior":
844+
new_class_name = f"{original_class_name}__perfinstrumented"
845+
else:
846+
new_class_name = f"{original_class_name}__perfonlyinstrumented"
847+
848+
# Rename all references to the original class name in the source.
849+
# This includes the class declaration, return types, constructor calls, etc.
850+
modified_code = re.sub(
851+
rf"\b{re.escape(original_class_name)}\b", new_class_name, test_code
852+
)
853+
852854
modified_code = _add_timing_instrumentation(
853855
modified_code,
854856
original_class_name, # Use original name in markers, not the renamed class
855857
function_name,
856858
)
859+
elif mode == "behavior":
860+
_ , modified_code = instrument_existing_test(test_string=test_code, mode=mode, function_to_optimize=function_to_optimize, test_class_name=original_class_name)
857861

858862
logger.debug("Instrumented generated Java test for %s (mode=%s)", function_name, mode)
859863
return modified_code

codeflash/languages/java/support.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,12 @@ def ensure_runtime_environment(self, project_root: Path) -> bool:
283283

284284
def instrument_existing_test(
285285
self,
286-
test_path: Path,
286+
test_string: str,
287287
call_positions: Sequence[Any],
288288
function_to_optimize: Any,
289289
tests_project_root: Path,
290290
mode: str,
291+
test_path: Path | None
291292
) -> tuple[bool, str | None]:
292293
"""Inject profiling code into an existing test file."""
293294
return instrument_existing_test(

codeflash/languages/javascript/instrument.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -626,18 +626,20 @@ def transform_expect_calls(
626626

627627

628628
def inject_profiling_into_existing_js_test(
629-
test_path: Path,
629+
test_string: str,
630630
call_positions: list[CodePosition],
631631
function_to_optimize: FunctionToOptimize,
632632
tests_project_root: Path,
633633
mode: str = TestingMode.BEHAVIOR,
634+
test_path: Path | None = None,
634635
) -> tuple[bool, str | None]:
635636
"""Inject profiling code into an existing JavaScript test file.
636637
637638
This function wraps function calls with codeflash.capture() or codeflash.capturePerf()
638639
to enable behavioral verification and performance benchmarking.
639640
640641
Args:
642+
test_string: String contents of the test file.
641643
test_path: Path to the test file.
642644
call_positions: List of code positions where the function is called.
643645
function_to_optimize: The function being optimized.
@@ -648,13 +650,7 @@ def inject_profiling_into_existing_js_test(
648650
Tuple of (success, instrumented_code).
649651
650652
"""
651-
try:
652-
with test_path.open(encoding="utf8") as f:
653-
test_code = f.read()
654-
except Exception as e:
655-
logger.error(f"Failed to read test file {test_path}: {e}")
656-
return False, None
657-
653+
test_code = test_string
658654
# Get the relative path for test identification
659655
try:
660656
rel_path = test_path.relative_to(tests_project_root)

codeflash/languages/javascript/support.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1936,11 +1936,12 @@ def ensure_runtime_environment(self, project_root: Path) -> bool:
19361936

19371937
def instrument_existing_test(
19381938
self,
1939-
test_path: Path,
1939+
test_string: str,
19401940
call_positions: Sequence[Any],
19411941
function_to_optimize: Any,
19421942
tests_project_root: Path,
19431943
mode: str,
1944+
test_path: Path|None,
19441945
) -> tuple[bool, str | None]:
19451946
"""Inject profiling code into an existing JavaScript test file.
19461947
@@ -1961,6 +1962,7 @@ def instrument_existing_test(
19611962
from codeflash.languages.javascript.instrument import inject_profiling_into_existing_js_test
19621963

19631964
return inject_profiling_into_existing_js_test(
1965+
test_string=test_string,
19641966
test_path=test_path,
19651967
call_positions=list(call_positions),
19661968
function_to_optimize=function_to_optimize,

codeflash/optimization/function_optimizer.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1849,13 +1849,15 @@ def instrument_existing_tests(self, function_to_all_tests: dict[str, set[Functio
18491849
logger.debug(f"Failed to instrument test file {test_file} for behavior testing")
18501850
continue
18511851

1852-
success, injected_perf_test = self.language_support.instrument_existing_test(
1853-
test_path=path_obj_test_file,
1854-
call_positions=[test.position for test in tests_in_file_list],
1855-
function_to_optimize=self.function_to_optimize,
1856-
tests_project_root=self.test_cfg.tests_project_rootdir,
1857-
mode="performance",
1858-
)
1852+
with path_obj_test_file.open("r", encoding="utf8") as f:
1853+
injected_behavior_test_source = f.read()
1854+
success, injected_perf_test = self.language_support.instrument_existing_test(
1855+
test_string=injected_behavior_test_source,
1856+
call_positions=[test.position for test in tests_in_file_list],
1857+
function_to_optimize=self.function_to_optimize,
1858+
tests_project_root=self.test_cfg.tests_project_rootdir,
1859+
mode="performance",
1860+
)
18591861
if not success:
18601862
logger.debug(f"Failed to instrument test file {test_file} for performance testing")
18611863
continue

codeflash/verification/verifier.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def generate_tests(
109109

110110
# Instrument for behavior verification (renames class)
111111
instrumented_behavior_test_source = instrument_generated_java_test(
112-
test_code=generated_test_source, function_name=func_name, qualified_name=qualified_name, mode="behavior"
112+
test_code=generated_test_source, function_name=func_name, qualified_name=qualified_name, mode="behavior", function_to_optimize=function_to_optimize
113113
)
114114

115115
# Instrument for performance measurement (adds timing markers)
@@ -118,6 +118,7 @@ def generate_tests(
118118
function_name=func_name,
119119
qualified_name=qualified_name,
120120
mode="performance",
121+
function_to_optimize=function_to_optimize
121122
)
122123

123124
logger.debug(f"Instrumented Java tests locally for {func_name}")

0 commit comments

Comments
 (0)