1414
1515# --- BOOTSTRAP: Auto-setup environment ---
1616import bootstrap
17+
1718bootstrap .initialize ()
1819# -----------------------------------------
1920
2021import tomli # noqa: E402
2122from rich .console import Console # noqa: E402
2223from rich .panel import Panel # noqa: E402
23- from rich .progress import Progress , SpinnerColumn , TextColumn # noqa: E402
24+ from rich .progress import Progress , SpinnerColumn , Task , TextColumn # noqa: E402
2425from rich .table import Table # noqa: E402
26+ from rich .text import Text # noqa: E402
2527
2628
2729@dataclass
@@ -191,6 +193,16 @@ def _resolve_path(self, path: str, test_dir: Path) -> str:
191193 return path
192194
193195
196+ class StatusSpinnerColumn (SpinnerColumn ):
197+ """Spinner that swaps to a custom status icon when provided."""
198+
199+ def render (self , task : Task ) -> Text :
200+ icon = task .fields .get ("status_icon" )
201+ if icon :
202+ return Text .from_markup (icon )
203+ return super ().render (task )
204+
205+
194206class SpecialJudgeChecker :
195207 def check (
196208 self ,
@@ -525,7 +537,7 @@ def _execute_single_step(
525537 self ._resolve_path (str (arg ), test .path , test .path )
526538 for arg in step .get ("args" , [])
527539 ]
528-
540+
529541 # 构建环境变量
530542 step_env = os .environ .copy ()
531543 if "env" in step :
@@ -884,9 +896,10 @@ def _print_basic_summary(self, total_score: float, max_score: float) -> None:
884896class VSCodeConfigGenerator :
885897 """Generate and manage VS Code debug configurations"""
886898
887- def __init__ (self , project_root : Path , config : Config ):
899+ def __init__ (self , project_root : Path , config : Config , verbose : bool = False ):
888900 self .project_root = project_root
889901 self .config = config
902+ self .verbose = verbose
890903 self .vscode_dir = project_root / ".vscode"
891904 self .launch_file = self .vscode_dir / "launch.json"
892905 self .tasks_file = self .vscode_dir / "tasks.json"
@@ -917,24 +930,43 @@ def _generate_launch_config(
917930 self , test_case : TestCase , failed_step : Dict [str , Any ]
918931 ) -> List [Dict [str , Any ]]:
919932 """Generate launch configuration based on debug type"""
933+ target_step = failed_step
934+
935+ if "debug_step" in failed_step :
936+ step_name = failed_step ["debug_step" ]
937+ # 在测试步骤中查找匹配名字的步骤
938+ found_step = next (
939+ (s for s in test_case .run_steps if s .get ("name" ) == step_name ), None
940+ )
941+
942+ if found_step :
943+ if self .verbose :
944+ print (
945+ f"Step '{ failed_step .get ('name' )} ' failed. Using debug step '{ step_name } '."
946+ )
947+
948+ target_step = found_step
949+ else :
950+ print (f"Warning: debug step '{ step_name } ' not found in steps." )
951+
920952 debug_type = (
921- failed_step .get ("debug" , {}).get ("type" )
953+ target_step .get ("debug" , {}).get ("type" )
922954 or test_case .meta .get ("debug" , {}).get ("type" )
923955 or self .config .debug_config ["default_type" ]
924956 )
925957
926958 cwd = str (self .config .project_root )
927959 program = self ._resolve_path (
928- failed_step ["command" ], test_case .path , self .config .project_root
960+ target_step ["command" ], test_case .path , self .config .project_root
929961 )
930962 args = [
931963 self ._resolve_path (arg , test_case .path , self .config .project_root )
932- for arg in failed_step .get ("args" , [])
964+ for arg in target_step .get ("args" , [])
933965 ]
934966
935967 if debug_type == "cpp" :
936968 configs = []
937- base_name = f"Debug { test_case .meta ['name' ]} - Step { failed_step .get ('name' , 'failed step' )} "
969+ base_name = f"Debug { test_case .meta ['name' ]} - Step { target_step .get ('name' , 'failed step' )} "
938970
939971 # Add GDB configuration
940972 configs .append (
@@ -978,7 +1010,7 @@ def _generate_launch_config(
9781010 elif debug_type == "python" :
9791011 return [
9801012 {
981- "name" : f"Debug { test_case .meta ['name' ]} - Step { failed_step .get ('name' , 'failed step' )} " ,
1013+ "name" : f"Debug { test_case .meta ['name' ]} - Step { target_step .get ('name' , 'failed step' )} " ,
9821014 "type" : "python" ,
9831015 "request" : "launch" ,
9841016 "program" : program ,
@@ -1126,7 +1158,7 @@ def __init__(
11261158 JsonFormatter () if json_output else TableFormatter (self .console )
11271159 )
11281160 self .results : Dict [str , TestResult ] = {}
1129- self .vscode_generator = VSCodeConfigGenerator (Path .cwd (), self .config )
1161+ self .vscode_generator = VSCodeConfigGenerator (Path .cwd (), self .config , verbose = self . verbose )
11301162
11311163 def _save_test_history (
11321164 self ,
@@ -1394,33 +1426,48 @@ def _run_setup_steps(self) -> bool:
13941426 return True
13951427
13961428 if self .console and not isinstance (self .console , type ):
1429+ spinner_column = StatusSpinnerColumn ()
13971430 with Progress (
1398- SpinnerColumn () ,
1399- TextColumn ("[progress.description]{task.description}" ),
1431+ spinner_column ,
1432+ TextColumn ("[progress.description]{task.description}" , markup = True ),
14001433 console = self .console ,
14011434 ) as progress :
14021435 total_steps = len (self .config .setup_steps )
14031436 task = progress .add_task (
14041437 f"Running setup steps [0/{ total_steps } ]..." ,
14051438 total = total_steps ,
1439+ status_icon = "" ,
14061440 )
14071441
14081442 for i , step in enumerate (self .config .setup_steps , 1 ):
1409- step_name = step .get ("message" , "Setup step" )
1443+ step_label = step .get ("name" ) or step .get ("command" ) or "Setup step"
1444+ running_text = step .get ("message" ) or f"Running { step_label } ..."
1445+ success_text = (
1446+ step .get ("success_message" ) or f"{ step_label } completed"
1447+ )
1448+ failure_text = step .get ("failure_message" ) or f"{ step_label } failed"
1449+ step_prefix = f"Running setup steps [{ i } /{ total_steps } ]: "
14101450 progress .update (
14111451 task ,
1412- description = f"Running setup steps [ { i } / { total_steps } ]: { step_name } " ,
1452+ description = f"{ step_prefix } { running_text } " ,
14131453 completed = i - 1 ,
1454+ status_icon = "" ,
14141455 )
14151456
14161457 if not self ._run_setup_step (step ):
1417- progress .update (task , completed = total_steps )
1458+ progress .update (
1459+ task ,
1460+ description = f"{ step_prefix } { failure_text } " ,
1461+ completed = i ,
1462+ status_icon = "[red]✗[/red]" ,
1463+ )
14181464 return False
14191465
14201466 progress .update (
14211467 task ,
1422- description = f"Running setup steps [ { i } / { total_steps } ]: { step_name } " ,
1468+ description = f"{ step_prefix } { success_text } " ,
14231469 completed = i ,
1470+ status_icon = "[green]✓[/green]" ,
14241471 )
14251472 return True
14261473 else :
@@ -1440,10 +1487,11 @@ def _run_setup_step(self, step: Dict[str, Any]) -> bool:
14401487
14411488 cmd = [step ["command" ]]
14421489 if "args" in step :
1443- if isinstance (step ["args" ], list ):
1444- cmd .extend (step ["args" ])
1445- else :
1446- cmd .append (step ["args" ])
1490+ args = step ["args" ]
1491+ if isinstance (args , (list , tuple )):
1492+ cmd .extend (str (arg ) for arg in args )
1493+ elif args is not None :
1494+ cmd .append (str (args ))
14471495
14481496 process = subprocess .run (
14491497 cmd ,
0 commit comments