Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions test/infinicore/framework/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ def execute(self, file_path, test_args) -> OperatorResult:
test_summary = TestSummary()
test_summary.process_operator_result(result, test_results)

# Store saved report file if available
result.saved_file = runner.saved_file
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The saved_file field is declared as type str with default value "", but runner.saved_file can be None (when save is not requested or when saving fails). Consider either changing the type annotation to Optional[str] or ensuring runner.saved_file is always a string (e.g., runner.saved_file or "").

Suggested change
result.saved_file = runner.saved_file
result.saved_file = runner.saved_file or ""

Copilot uses AI. Check for mistakes.

except Exception as e:
result.success = False
result.error_message = str(e)
Expand Down
8 changes: 5 additions & 3 deletions test/infinicore/framework/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class OperatorResult:
stdout: str = ""
stderr: str = ""
timing: TestTiming = field(default_factory=TestTiming)
saved_file: str = "" # Path to the saved report file

@property
def status_icon(self):
Expand Down Expand Up @@ -262,11 +263,12 @@ def collect_report_entry(self, op_name, test_cases, args, op_paths, results_list
def save_report(self, save_path):
"""
Delegates the actual writing to save_json_report.
Returns the actual file path that was saved (with timestamp).
"""
if not self.report_entries:
return
# Call the external utility
save_json_report(save_path, self.report_entries)
return None
# Call the external utility and get the actual saved path
return save_json_report(save_path, self.report_entries)

def _prepare_entry_logic(self, op_name, test_cases, args, op_paths, results_list):
"""
Expand Down
8 changes: 5 additions & 3 deletions test/infinicore/framework/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def __init__(self, operator_test_class, args=None):
"""
self.operator_test = operator_test_class()
self.args = args or get_args()
self.saved_file = None # Store the path of saved report

def run(self):
"""Execute the complete test suite
Expand Down Expand Up @@ -56,7 +57,7 @@ def run(self):
summary_passed = runner.print_summary()

if getattr(self.args, "save", None):
self._save_report(runner)
self.saved_file = self._save_report(runner)

# Both conditions must be True for overall success
# - has_no_failures: no test failures during execution
Expand Down Expand Up @@ -98,14 +99,15 @@ def _save_report(self, runner):
results_list=runner.test_results,
)

# 4. Save to File
test_summary.save_report(self.args.save)
# 3. Save to File and return the file name
return test_summary.save_report(self.args.save)

except Exception as e:
import traceback

traceback.print_exc()
print(f"⚠️ Failed to save report: {e}")
return None

def _infer_op_path(self, method, lib_prefix):
"""
Expand Down
8 changes: 7 additions & 1 deletion test/infinicore/framework/test_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def test(self, target_ops=None, json_cases_list=None, global_exec_args=None):

self.summary.print_header(display_location, len(test_files))

saved_files = []
for f, run_args in zip(test_files, test_configs):

# Inject prepared args (whether from JSON or Local global) into Executor
Expand All @@ -146,6 +147,10 @@ def test(self, target_ops=None, json_cases_list=None, global_exec_args=None):
self.results.append(result)
self.summary.print_live_result(result)

# Collect saved report files
if hasattr(result, "saved_file") and result.saved_file:
saved_files.append(result.saved_file)

if result.success:
self._accumulate_timing(result.timing)

Expand All @@ -160,7 +165,8 @@ def test(self, target_ops=None, json_cases_list=None, global_exec_args=None):
ops_dir=display_location,
total_expected=len(test_files),
)
return all_passed

return all_passed, saved_files
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring for the test method should be updated to document the return value. Since the method now returns a tuple of (success: bool, saved_files: list), this should be documented in the docstring with a Returns section explaining both values.

Copilot uses AI. Check for mistakes.

def _accumulate_timing(self, timing):
self.cumulative_timing.torch_host += timing.torch_host
Expand Down
5 changes: 5 additions & 0 deletions test/infinicore/framework/utils/json_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ def save_json_report(save_path, total_results):
"""
Saves the report list to a JSON file with specific custom formatting
(Compact for short lines, Expanded for long lines).

Returns:
str: The actual file path that was saved (with timestamp), or None if failed.
"""
directory, filename = os.path.split(save_path)
name, ext = os.path.splitext(filename)
Expand Down Expand Up @@ -85,11 +88,13 @@ def _to_json(obj):
f.write(f"{I4}{close_entry}\n")
f.write("]\n")
print(f" ✅ Saved.")
return final_path
except Exception as e:
import traceback

traceback.print_exc()
print(f" ❌ Save failed: {e}")
return None


def _write_field(f, key, value, indent, sub_indent, close_comma=""):
Expand Down
20 changes: 11 additions & 9 deletions test/infinicore/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,24 +130,24 @@ def load_and_override_cases(load_paths, args):

for f_path in files_to_read:
try:
with open(f_path, 'r', encoding='utf-8') as f:
with open(f_path, "r", encoding="utf-8") as f:
data = json.load(f)

# Unify as a list to handle both single dict and list of dicts
current_batch = data if isinstance(data, list) else [data]

valid_batch = []
for item in current_batch:
# We only require the 'operator' field to identify the test case.
if isinstance(item, dict) and "operator" in item:
valid_batch.append(item)
else:
skipped_count += 1

if valid_batch:
cases.extend(valid_batch)
loaded_count += 1

except Exception as e:
# Log warning only; do not crash the program on bad files to ensure flow continuity.
print(f"❌ Error loading {f_path.name}: {e}")
Expand All @@ -173,7 +173,7 @@ def load_and_override_cases(load_paths, args):
cli_active_devices.append(device_name)

print(f"\n[Config Processing]")

for case in cases:
if "args" not in case or case["args"] is None:
case["args"] = {}
Expand Down Expand Up @@ -283,9 +283,11 @@ def main():
print(f"Benchmark mode: {args.bench.upper()} timing")

# 3. Initialize and Execute
test_manager = TestManager(ops_dir=args.ops_dir, verbose=verbose, bench_mode=bench)
test_manager = TestManager(
ops_dir=args.ops_dir, verbose=verbose, bench_mode=bench
)

success = test_manager.test(json_cases_list=json_cases)
success, _ = test_manager.test(json_cases_list=json_cases)

# ==========================================================================
# Branch 2: Local Scan Mode
Expand Down Expand Up @@ -330,7 +332,7 @@ def main():
ops_dir=args.ops_dir, verbose=args.verbose, bench_mode=args.bench
)

success = test_manager.test(
success, _ = test_manager.test(
target_ops=target_ops, global_exec_args=global_exec_args
)
sys.exit(0 if success else 1)
Expand Down