from pathlib import Path from app.progress import parse_progress, progress_from_log_path def test_parse_ultralytics_epoch_progress(): progress = parse_progress( """ Epoch GPU_mem box_loss seg_loss cls_loss dfl_loss Instances Size 3/10 0G 1.234 2.345 0.456 0.789 2 640: 100%|##########| 2/2 """, status="running", ) assert progress["percent"] == 30 assert progress["current"] == 3 assert progress["total"] == 10 assert progress["unit"] == "epoch" assert progress["stage"] == "training" def test_parse_mmengine_epoch_uses_total_epochs_from_command(): progress = parse_progress( """ COMMAND: python tools/train.py config.py --max-epochs 5 Epoch(train) [2][25/50] lr: 1.0000e-03 eta: 0:00:10 """, status="running", ) assert progress["percent"] == 30 assert progress["label"] == "Epoch 2/5 · iter 25/50" assert progress["current"] == 2 assert progress["total"] == 5 def test_parse_tqdm_percent_fallback(): progress = parse_progress("Predict: 42%|#### | 21/50 [00:01<00:02]\n", status="running") assert progress["percent"] == 42 assert progress["unit"] == "percent" assert progress["stage"] == "prediction" def test_terminal_success_overrides_percent_but_keeps_context(): progress = parse_progress("Epoch 2/4 loss=0.5\n", status="success") assert progress["percent"] == 100 assert progress["stage"] == "completed" assert progress["label"] == "已完成 · Epoch 2/4" def test_progress_from_log_path_reads_tail(tmp_path: Path): log_path = tmp_path / "train.log" log_path.write_text("Epoch 1/2 loss=0.8\n", encoding="utf-8") progress = progress_from_log_path(log_path, status="running") assert progress["percent"] == 50 assert progress["source"] == "epoch_word"