| 1 | from types import SimpleNamespace |
| 2 | |
| 3 | from cli.progress_display import ProgressDisplay |
| 4 | |
| 5 | |
| 6 | class _FakeProgress: |
| 7 | def __init__(self): |
| 8 | self.tasks = {} |
| 9 | self.removed = [] |
| 10 | self._next_id = 1 |
| 11 | self.console = SimpleNamespace(print=lambda *_args, **_kwargs: None) |
| 12 | |
| 13 | def add_task(self, description, total, completed=0, detail="", **kwargs): |
| 14 | task_id = self._next_id |
| 15 | self._next_id += 1 |
| 16 | self.tasks[task_id] = { |
| 17 | "description": description, |
| 18 | "total": total, |
| 19 | "completed": completed, |
| 20 | "detail": detail, |
| 21 | } |
| 22 | self.tasks[task_id].update(kwargs) |
| 23 | return task_id |
| 24 | |
| 25 | def update(self, task_id, **kwargs): |
| 26 | self.tasks[task_id].update(kwargs) |
| 27 | |
| 28 | def advance(self, task_id, advance=1): |
| 29 | self.tasks[task_id]["completed"] = self.tasks[task_id].get("completed", 0) + advance |
| 30 | |
| 31 | def remove_task(self, task_id): |
| 32 | self.removed.append(task_id) |
| 33 | self.tasks.pop(task_id, None) |
| 34 | |
| 35 | |
| 36 | class _FakeProgressContext: |
| 37 | def __init__(self, progress): |
| 38 | self.progress = progress |
| 39 | self.exited = False |
| 40 | |
| 41 | def __enter__(self): |
| 42 | return self.progress |
| 43 | |
| 44 | def __exit__(self, *_args): |
| 45 | self.exited = True |
| 46 | |
| 47 | |
| 48 | def test_single_url_overall_progress_follows_item_count(monkeypatch): |
| 49 | display = ProgressDisplay() |
| 50 | fake_progress = _FakeProgress() |
| 51 | fake_ctx = _FakeProgressContext(fake_progress) |
| 52 | monkeypatch.setattr(display, "create_progress", lambda: fake_ctx) |
| 53 | |
| 54 | display.start_download_session(1) |
| 55 | overall_task_id = display._overall_task_id |
| 56 | assert overall_task_id is not None |
| 57 | assert fake_progress.tasks[overall_task_id]["total"] == 1 |
| 58 | |
| 59 | display.start_url(1, 1, "https://example.com/u") |
| 60 | display.set_item_total(5, "作品待下载") |
| 61 | assert fake_progress.tasks[overall_task_id]["total"] == 5 |
| 62 | assert fake_progress.tasks[overall_task_id]["completed"] == 0 |
| 63 | |
| 64 | display.advance_item("success", "a1") |
| 65 | display.advance_item("failed", "a2") |
| 66 | assert fake_progress.tasks[overall_task_id]["completed"] == 2 |
| 67 | |
| 68 | display.complete_url(SimpleNamespace(success=3, failed=1, skipped=1)) |
| 69 | assert fake_progress.tasks[overall_task_id]["completed"] == 5 |
| 70 | |
| 71 | |
| 72 | def test_multi_url_overall_progress_stays_url_based(monkeypatch): |
| 73 | display = ProgressDisplay() |
| 74 | fake_progress = _FakeProgress() |
| 75 | fake_ctx = _FakeProgressContext(fake_progress) |
| 76 | monkeypatch.setattr(display, "create_progress", lambda: fake_ctx) |
| 77 | |
| 78 | display.start_download_session(2) |
| 79 | overall_task_id = display._overall_task_id |
| 80 | assert overall_task_id is not None |
| 81 | assert fake_progress.tasks[overall_task_id]["total"] == 2 |
| 82 | |
| 83 | display.start_url(1, 2, "https://example.com/u1") |
| 84 | display.set_item_total(8, "作品待下载") |
| 85 | display.advance_item("success", "a1") |
| 86 | assert fake_progress.tasks[overall_task_id]["completed"] == 0 |
| 87 | |
| 88 | display.complete_url(SimpleNamespace(success=8, failed=0, skipped=0)) |
| 89 | assert fake_progress.tasks[overall_task_id]["completed"] == 1 |
| 90 | |
| 91 | display.start_url(2, 2, "https://example.com/u2") |
| 92 | display.fail_url("url failed") |
| 93 | assert fake_progress.tasks[overall_task_id]["completed"] == 2 |
| 94 |