fix(product-suite): keep retries in current results

This commit is contained in:
chengma
2026-07-16 18:15:24 +08:00
parent 25a08decdc
commit becfed81cd
3 changed files with 421 additions and 39 deletions
+160 -37
View File
@@ -697,6 +697,9 @@ class SuiteTaskState:
settings: dict = field(default_factory=product_suite.default_suite_settings) settings: dict = field(default_factory=product_suite.default_suite_settings)
current_job_ids: list = field(default_factory=list) current_job_ids: list = field(default_factory=list)
show_history: bool = False show_history: bool = False
generation_job_ids: list = field(default_factory=list)
generation_mode: str = "batch"
generation_retry_job_id: int = None
worker: object = None worker: object = None
thread: object = None thread: object = None
generation_run_token: str = "" generation_run_token: str = ""
@@ -1582,6 +1585,9 @@ class ProductSuiteTab(QWidget):
state.project_binding_state = "" state.project_binding_state = ""
state.last_saved_prompt = "" state.last_saved_prompt = ""
state.current_job_ids = [] state.current_job_ids = []
state.generation_job_ids = []
state.generation_mode = "batch"
state.generation_retry_job_id = None
state.done = state.failed = state.total = 0 state.done = state.failed = state.total = 0
state.started_at = None state.started_at = None
if state is self._displayed_state: if state is self._displayed_state:
@@ -2517,12 +2523,22 @@ class ProductSuiteTab(QWidget):
if state is None: if state is None:
return return
if state.generation_running(): if state.generation_running():
retrying = state.generation_mode == "retry"
if state.generation_stop_requested: if state.generation_stop_requested:
self._status("正在停止当前套图任务", "warning") self._status(
"正在停止当前图片重试"
if retrying
else "正在停止当前套图任务",
"warning",
)
return return
if self._confirm( if self._confirm(
"停止生成套图", "停止图片重试" if retrying else "停止生成套图",
"确认取消当前任务吗?已提交任务会在安全边界停止。", (
"确认停止当前图片重试吗?已提交任务会在安全边界停止。"
if retrying
else "确认取消当前任务吗?已提交任务会在安全边界停止。"
),
destructive=True, destructive=True,
): ):
state.generation_stop_requested = True state.generation_stop_requested = True
@@ -2533,14 +2549,21 @@ class ProductSuiteTab(QWidget):
"stop_requested", "stop_requested",
) )
self._apply_running_state(state) self._apply_running_state(state)
self._status("已请求停止当前套图任务", "warning") self._status(
"已请求停止当前图片重试"
if retrying
else "已请求停止当前套图任务",
"warning",
)
return return
self.start_generation(state) self.start_generation(state)
def start_generation(self, state, specs=None): def start_generation(self, state, specs=None, *, retry_job_id=None):
if state.generation_running(): if state.generation_running():
self._status("当前套图任务仍在生成", "warning") self._status("当前套图任务仍在生成", "warning")
return False return False
retry_job_id = int(retry_job_id) if retry_job_id is not None else None
retrying = retry_job_id is not None
if state is self._displayed_state: if state is self._displayed_state:
self._save_controls_to_state(state) self._save_controls_to_state(state)
template_text = None template_text = None
@@ -2595,12 +2618,16 @@ class ProductSuiteTab(QWidget):
state.generation_run_token = run_token state.generation_run_token = run_token
state.generation_stop_requested = False state.generation_stop_requested = False
state.generation_terminal_streak = 0 state.generation_terminal_streak = 0
state.generation_job_ids = []
state.generation_mode = "retry" if retrying else "batch"
state.generation_retry_job_id = retry_job_id
state.done = 0 state.done = 0
state.failed = 0 state.failed = 0
state.total = len(specs) state.total = len(specs)
state.started_at = time.monotonic() state.started_at = time.monotonic()
state.current_job_ids = [] if not retrying:
state.show_history = False state.current_job_ids = []
state.show_history = False
self._generation_run_states[run_token] = state.key self._generation_run_states[run_token] = state.key
worker.progress.connect(self._on_generation_progress_signal) worker.progress.connect(self._on_generation_progress_signal)
worker.finished.connect(self._on_generation_finished_signal) worker.finished.connect(self._on_generation_finished_signal)
@@ -2611,17 +2638,27 @@ class ProductSuiteTab(QWidget):
state, state,
run_token, run_token,
"started", "started",
{"total": len(specs), "job_ids": 0}, {
"total": len(specs),
"job_ids": 0,
"mode": state.generation_mode,
},
) )
if state is self._displayed_state: if state is self._displayed_state and not retrying:
self._loading = True self._loading = True
try: try:
self.history_button.setChecked(False) self.history_button.setChecked(False)
finally: finally:
self._loading = False self._loading = False
if state is self._displayed_state:
self._apply_running_state(state) self._apply_running_state(state)
self._refresh_results(state) self._refresh_results(state)
self._status("商品套图生成已开始,共%d张;可切换到其他任务" % len(specs), "info") self._status(
"图片重试已开始;可切换到其他任务"
if retrying
else "商品套图生成已开始,共%d张;可切换到其他任务" % len(specs),
"info",
)
return True return True
def _generation_signal_token(self, payload=None): def _generation_signal_token(self, payload=None):
@@ -2651,7 +2688,7 @@ class ProductSuiteTab(QWidget):
state.failed = int(payload.get("failed", state.failed) or 0) state.failed = int(payload.get("failed", state.failed) or 0)
job_ids = payload.get("job_ids") job_ids = payload.get("job_ids")
if job_ids is not None: if job_ids is not None:
state.current_job_ids = [int(job_id) for job_id in job_ids] self._set_generation_job_ids(state, job_ids)
state.generation_terminal_streak = 0 state.generation_terminal_streak = 0
if state is self._displayed_state: if state is self._displayed_state:
self._refresh_results(state) self._refresh_results(state)
@@ -2670,7 +2707,15 @@ class ProductSuiteTab(QWidget):
{"has_error": True}, {"has_error": True},
level="ERROR", level="ERROR",
) )
self._status("商品套图生成失败:%s" % _user_error(error), "danger") self._status(
(
"图片重试失败:%s"
if state.generation_mode == "retry"
else "商品套图生成失败:%s"
)
% _user_error(error),
"danger",
)
@Slot(dict) @Slot(dict)
def _on_generation_finished_signal(self, result): def _on_generation_finished_signal(self, result):
@@ -2678,6 +2723,8 @@ class ProductSuiteTab(QWidget):
state = self._generation_state(token) state = self._generation_state(token)
if state is None: if state is None:
return return
if result.get("job_ids") is not None:
self._set_generation_job_ids(state, result.get("job_ids"))
self._log_generation_lifecycle( self._log_generation_lifecycle(
state, state,
token, token,
@@ -2779,7 +2826,7 @@ class ProductSuiteTab(QWidget):
) )
def _generation_job_ids(self, state): def _generation_job_ids(self, state):
job_ids = list(state.current_job_ids) job_ids = list(state.generation_job_ids)
if not job_ids and state.worker is not None: if not job_ids and state.worker is not None:
worker_job_ids = getattr(state.worker, "job_ids", []) worker_job_ids = getattr(state.worker, "job_ids", [])
if not isinstance(worker_job_ids, (list, tuple, set)): if not isinstance(worker_job_ids, (list, tuple, set)):
@@ -2789,9 +2836,30 @@ class ProductSuiteTab(QWidget):
for job_id in list(worker_job_ids or []) for job_id in list(worker_job_ids or [])
] ]
if job_ids: if job_ids:
state.current_job_ids = job_ids self._set_generation_job_ids(state, job_ids)
return job_ids return job_ids
def _set_generation_job_ids(self, state, job_ids):
normalized = []
for job_id in job_ids or []:
value = int(job_id)
if value not in normalized:
normalized.append(value)
state.generation_job_ids = normalized
if state.generation_mode != "retry":
state.current_job_ids = list(normalized)
return
retry_job_id = state.generation_retry_job_id
current = [int(job_id) for job_id in state.current_job_ids]
if retry_job_id in current:
index = current.index(retry_job_id)
current[index:index + 1] = normalized
elif not state.show_history:
for job_id in normalized:
if job_id not in current:
current.append(job_id)
state.current_job_ids = current
def _generation_job_snapshot(self, state): def _generation_job_snapshot(self, state):
job_ids = self._generation_job_ids(state) job_ids = self._generation_job_ids(state)
counts = { counts = {
@@ -2865,6 +2933,7 @@ class ProductSuiteTab(QWidget):
def _finalize_generation(self, state, run_token, result, *, source): def _finalize_generation(self, state, run_token, result, *, source):
if self._generation_state(run_token) is not state: if self._generation_state(run_token) is not state:
return False return False
retrying = state.generation_mode == "retry"
snapshot = self._generation_job_snapshot(state) snapshot = self._generation_job_snapshot(state)
if snapshot["job_ids"] and ( if snapshot["job_ids"] and (
snapshot["all_terminal"] snapshot["all_terminal"]
@@ -2890,6 +2959,8 @@ class ProductSuiteTab(QWidget):
state.generation_run_token = "" state.generation_run_token = ""
state.generation_stop_requested = False state.generation_stop_requested = False
state.generation_terminal_streak = 0 state.generation_terminal_streak = 0
state.generation_job_ids = []
state.generation_retry_job_id = None
state.worker = None state.worker = None
state.thread = None state.thread = None
state.done = success + failed + cancelled state.done = success + failed + cancelled
@@ -2912,6 +2983,7 @@ class ProductSuiteTab(QWidget):
"cancelled": cancelled, "cancelled": cancelled,
"active": active, "active": active,
"elapsed_seconds": elapsed, "elapsed_seconds": elapsed,
"mode": "retry" if retrying else "batch",
}, },
level="WARNING" if active or result.get("ok") is False else "INFO", level="WARNING" if active or result.get("ok") is False else "INFO",
) )
@@ -2923,39 +2995,76 @@ class ProductSuiteTab(QWidget):
or "生成线程已结束,部分任务可稍后继续查询" or "生成线程已结束,部分任务可稍后继续查询"
) )
if state is self._displayed_state: if state is self._displayed_state:
self._message("商品套图生成未完整结束", _user_error(message)) self._message(
"图片重试未完整结束"
if retrying
else "商品套图生成未完整结束",
_user_error(message),
)
else: else:
self._status( self._status(
"套图任务%d生成未完整结束" % state.serial, (
"套图任务%d图片重试未完整结束"
if retrying
else "套图任务%d生成未完整结束"
)
% state.serial,
"danger", "danger",
) )
return True return True
if stop_requested or cancelled: if stop_requested or cancelled:
if state is self._displayed_state: if state is self._displayed_state:
self._message( if retrying:
"商品套图生成已停止", self._message(
"本轮共%d张:成功%d张,失败%d张,停止%d张;" "图片重试已停止",
"已提交任务可稍后继续查询;总用时%d秒。" "本次重试:成功%d张,失败%d张,停止%d张;"
% (total, success, failed, cancelled, elapsed), "已提交任务可稍后继续查询;总用时%d秒。"
icon=QMessageBox.Information, % (success, failed, cancelled, elapsed),
) icon=QMessageBox.Information,
)
else:
self._message(
"商品套图生成已停止",
"本轮共%d张:成功%d张,失败%d张,停止%d张;"
"已提交任务可稍后继续查询;总用时%d秒。"
% (total, success, failed, cancelled, elapsed),
icon=QMessageBox.Information,
)
self._status( self._status(
"商品套图生成已停止:成功%d张,失败%d张,停止%d张" (
"图片重试已停止:成功%d张,失败%d张,停止%d张"
if retrying
else "商品套图生成已停止:成功%d张,失败%d张,停止%d张"
)
% (success, failed, cancelled), % (success, failed, cancelled),
"warning", "warning",
) )
return True return True
if state is self._displayed_state: if state is self._displayed_state:
self._message( if retrying:
"商品套图生成完成", self._message(
"本轮共%d张:成功%d张,失败%d张,停止%d张;总用时%d秒。" "图片重试成功" if success and not failed else "图片重试失败",
% (total, success, failed, cancelled, elapsed), "本次重试:成功%d张,失败%d张,停止%d张;总用时%d秒。"
icon=QMessageBox.Information, % (success, failed, cancelled, elapsed),
icon=QMessageBox.Information,
)
else:
self._message(
"商品套图生成完成",
"本轮共%d张:成功%d张,失败%d张,停止%d张;总用时%d秒。"
% (total, success, failed, cancelled, elapsed),
icon=QMessageBox.Information,
)
if retrying:
self._status(
"图片重试成功" if success and not failed else "图片重试失败,请查看失败卡片",
"success" if success and not failed else "danger",
)
else:
self._status(
"商品套图生成完成:成功%d张,失败%d张" % (success, failed),
"success",
) )
self._status(
"商品套图生成完成:成功%d张,失败%d张" % (success, failed),
"success",
)
return True return True
def _log_generation_lifecycle( def _log_generation_lifecycle(
@@ -3012,6 +3121,8 @@ class ProductSuiteTab(QWidget):
self.generate_button.setText( self.generate_button.setText(
"正在停止..." "正在停止..."
if state.generation_stop_requested if state.generation_stop_requested
else "停止重试"
if state.generation_mode == "retry"
else "停止生成" else "停止生成"
) )
self.generate_button.setStyleSheet( self.generate_button.setStyleSheet(
@@ -3035,7 +3146,8 @@ class ProductSuiteTab(QWidget):
return return
elapsed = int(max(0, time.monotonic() - state.started_at)) if state.started_at else 0 elapsed = int(max(0, time.monotonic() - state.started_at)) if state.started_at else 0
self.elapsed_label.setText( self.elapsed_label.setText(
"套图 %d/%d(%d秒) · 失败 %d" % ( "%s %d/%d(%d秒) · 失败 %d" % (
"重试" if state.generation_mode == "retry" else "套图",
state.done, state.done,
state.total, state.total,
elapsed, elapsed,
@@ -3067,8 +3179,12 @@ class ProductSuiteTab(QWidget):
return [] return []
if state.show_history: if state.show_history:
return jobs return jobs
current = set(state.current_job_ids) by_id = {int(job.id): job for job in jobs}
return [job for job in jobs if int(job.id) in current] return [
by_id[int(job_id)]
for job_id in state.current_job_ids
if int(job_id) in by_id
]
def _refresh_results(self, state): def _refresh_results(self, state):
while self.result_grid.count(): while self.result_grid.count():
@@ -3122,12 +3238,19 @@ class ProductSuiteTab(QWidget):
if state.generation_running(): if state.generation_running():
self._message("当前任务正在生成", "请等待当前生成结束或停止后再重试单张图片。") self._message("当前任务正在生成", "请等待当前生成结束或停止后再重试单张图片。")
return return
if str(getattr(job, "status", "") or "") not in {
"failed",
"expired",
"cancelled",
}:
self._status("当前图片无需重试", "warning")
return
spec = { spec = {
"source_asset_id": job.source_asset_id, "source_asset_id": job.source_asset_id,
"job_type": job.job_type, "job_type": job.job_type,
"prompt": job.prompt, "prompt": job.prompt,
} }
self.start_generation(state, specs=[spec]) self.start_generation(state, specs=[spec], retry_job_id=job.id)
def _show_job_menu(self, job, global_position): def _show_job_menu(self, job, global_position):
menu = QMenu(self) menu = QMenu(self)
+5 -2
View File
@@ -3,7 +3,7 @@ id: T-640
title: 商品套图单张失败重试保持当前轮结果 title: 商品套图单张失败重试保持当前轮结果
phase: 7 phase: 7
deps: [T-639] deps: [T-639]
status: TODO status: DONE
created: 2026-07-16 created: 2026-07-16
--- ---
@@ -107,4 +107,7 @@ git diff --check
## 执行记录 ## 执行记录
- 待执行。 - 2026-07-16:`SuiteTaskState` 新增内存态 `generation_job_ids`、`generation_mode` 与 `generation_retry_job_id`。`generation_job_ids` 专门供当前活动请求的进度、停止、线程结束和终态看门狗使用;`current_job_ids` 只表示当前结果区的有效槽位,解决单张重试时旧成功 job 被错误纳入本次汇总的问题。未修改 SQLite schema。
- 2026-07-16:失败、过期或停止卡片的「重试」改为显式单张重试模式。worker 仍新建 job 保留历史和计费记录;新 job ID 返回后会原位替换当前结果区中的旧失败 job,其他成功图片保持不变。当前结果改按 `current_job_ids` 顺序展示;历史视图继续展示原失败与每次重试记录,从历史发起重试时不会强制退出历史视图。
- 2026-07-16:重试运行状态、停止确认、进度文案和完成弹窗改为「图片重试」语义;重试成功、失败、停止和未完整结束分别给中文反馈。正常整轮生成、停止、T-639 `run_token` 隔离与终态兜底保持原行为。
- 2026-07-16:补齐当前轮原位替换、历史记录保留、活动快照只统计新 job、重试再次失败仍可继续重试、历史视图保持和真实 QThread 完整退出测试。相关 42 项测试通过;在不包含用户未提交默认提示词改名的临时隔离 worktree 中,全量 535 项 unittest 通过,Ruff、`compileall` 与 `git diff --check` 均通过。
+256
View File
@@ -856,6 +856,7 @@ class ProductSuiteGuiTests(TempDirMixin, unittest.TestCase):
state.thread = mock.Mock() state.thread = mock.Mock()
state.generation_run_token = "watchdog-run" state.generation_run_token = "watchdog-run"
state.current_job_ids = [job.id for job in jobs] state.current_job_ids = [job.id for job in jobs]
state.generation_job_ids = [job.id for job in jobs]
state.total = len(jobs) state.total = len(jobs)
state.started_at = time.monotonic() state.started_at = time.monotonic()
tab._generation_run_states["watchdog-run"] = state.key tab._generation_run_states["watchdog-run"] = state.key
@@ -972,6 +973,7 @@ class ProductSuiteGuiTests(TempDirMixin, unittest.TestCase):
state.thread = mock.Mock() state.thread = mock.Mock()
state.generation_run_token = "thread-fallback" state.generation_run_token = "thread-fallback"
state.current_job_ids = [job.id] state.current_job_ids = [job.id]
state.generation_job_ids = [job.id]
state.total = 1 state.total = 1
state.started_at = time.monotonic() state.started_at = time.monotonic()
tab._generation_run_states["thread-fallback"] = state.key tab._generation_run_states["thread-fallback"] = state.key
@@ -1127,6 +1129,260 @@ class ProductSuiteGuiTests(TempDirMixin, unittest.TestCase):
self.assert_removed(temp_dir) self.assert_removed(temp_dir)
def test_failed_job_retry_replaces_current_slot_and_keeps_history(self):
with self.make_temp_dir() as temp_dir:
config = self._config(temp_dir)
project, sources = self._create_project_with_assets(temp_dir, config, 1)
source = sources[0]
success_path = os.path.join(temp_dir, "success.jpg")
retry_path = os.path.join(temp_dir, "retry.jpg")
self._write_image(success_path)
self._write_image(retry_path)
success_asset = image_studio.add_asset(
project.id,
"generated_main",
local_path=success_path,
parent_asset_id=source.id,
path=config["db_path"],
)
success_job = image_studio.create_job(
project.id,
source_asset_id=source.id,
job_type="白底图",
prompt="成功图",
path=config["db_path"],
)
success_job = image_studio.update_job_status(
success_job.id,
"succeeded",
output_asset_id=success_asset.id,
path=config["db_path"],
)
failed_job = image_studio.create_job(
project.id,
source_asset_id=source.id,
job_type="场景图",
prompt="失败图",
path=config["db_path"],
)
failed_job = image_studio.update_job_status(
failed_job.id,
"failed",
error="上游生成失败",
path=config["db_path"],
)
tab = ProductSuiteTab(config=config, db_path=config["db_path"])
self.addCleanup(tab.close)
state = tab._displayed_state
state.account_alias = "alias-a"
state.item_id = project.item_id
state.project_id = project.id
state.project_binding_state = project.binding_state
state.current_job_ids = [success_job.id, failed_job.id]
tab._load_state(state)
messages = []
tab._message = lambda title, message, **kwargs: messages.append(
(title, message)
)
def fake_run_jobs(jobs, **kwargs):
job = list(jobs)[0]
retry_asset = image_studio.add_asset(
project.id,
"generated_main",
local_path=retry_path,
parent_asset_id=source.id,
path=config["db_path"],
)
image_studio.update_job_status(
job.id,
"succeeded",
output_asset_id=retry_asset.id,
path=config["db_path"],
)
return {
"total": 1,
"success": 1,
"failed": 0,
"cancelled": 0,
"jobs": [],
}
with mock.patch(
"app.gui.workers.image_studio_generation.run_jobs",
side_effect=fake_run_jobs,
):
tab.retry_job(failed_job)
generation_thread = state.thread
deadline = time.monotonic() + 3
while state.worker is not None and time.monotonic() < deadline:
QTest.qWait(20)
self.app.processEvents()
while (
generation_thread is not None
and generation_thread.isRunning()
and time.monotonic() < deadline
):
QTest.qWait(20)
self.app.processEvents()
self.assertFalse(generation_thread.isRunning())
all_jobs = image_studio.list_jobs(project.id, path=config["db_path"])
retry_jobs = [
job
for job in all_jobs
if job.id not in {success_job.id, failed_job.id}
]
self.assertEqual(1, len(retry_jobs))
retry_job = retry_jobs[0]
self.assertEqual(
[success_job.id, retry_job.id],
state.current_job_ids,
)
self.assertEqual([], state.generation_job_ids)
self.assertEqual(
[success_job.id, retry_job.id],
[job.id for job in tab._jobs_for_state(state)],
)
self.assertEqual("图片重试成功", messages[-1][0])
state.show_history = True
history_ids = {job.id for job in tab._jobs_for_state(state)}
self.assertEqual(
{success_job.id, failed_job.id, retry_job.id},
history_ids,
)
self.assert_removed(temp_dir)
def test_retry_tracks_only_new_job_and_preserves_history_view(self):
with self.make_temp_dir() as temp_dir:
config = self._config(temp_dir)
project, sources = self._create_project_with_assets(temp_dir, config, 1)
source = sources[0]
success_job = image_studio.create_job(
project.id,
source_asset_id=source.id,
job_type="白底图",
prompt="成功图",
path=config["db_path"],
)
success_job = image_studio.update_job_status(
success_job.id,
"succeeded",
path=config["db_path"],
)
failed_job = image_studio.create_job(
project.id,
source_asset_id=source.id,
job_type="场景图",
prompt="失败图",
path=config["db_path"],
)
failed_job = image_studio.update_job_status(
failed_job.id,
"failed",
path=config["db_path"],
)
retry_job = image_studio.create_job(
project.id,
source_asset_id=source.id,
job_type="场景图",
prompt="重试图",
path=config["db_path"],
)
tab = ProductSuiteTab(config=config, db_path=config["db_path"])
self.addCleanup(tab.close)
state = tab._displayed_state
state.account_alias = "alias-a"
state.item_id = project.item_id
state.project_id = project.id
state.project_binding_state = project.binding_state
state.current_job_ids = [success_job.id, failed_job.id]
state.generation_mode = "retry"
state.generation_retry_job_id = failed_job.id
state.total = 1
tab._set_generation_job_ids(state, [retry_job.id])
self.assertEqual(
[success_job.id, retry_job.id],
state.current_job_ids,
)
self.assertEqual([retry_job.id], state.generation_job_ids)
snapshot = tab._generation_job_snapshot(state)
self.assertEqual(1, snapshot["job_ids"])
self.assertEqual(1, snapshot["active"])
image_studio.update_job_status(
retry_job.id,
"failed",
error="重试仍失败",
path=config["db_path"],
)
state.worker = mock.Mock()
state.thread = mock.Mock()
state.generation_run_token = "retry-failed"
state.started_at = time.monotonic()
tab._generation_run_states["retry-failed"] = state.key
messages = []
tab._message = lambda title, message, **kwargs: messages.append(
(title, message)
)
self.assertTrue(
tab._finalize_generation(
state,
"retry-failed",
{"total": 1, "success": 0, "failed": 1},
source="worker",
)
)
self.assertEqual("图片重试失败", messages[-1][0])
retry_cards = [
card
for card in tab.findChildren(SuiteResultCard)
if card.job.id == retry_job.id
]
self.assertEqual(1, len(retry_cards))
self.assertTrue(
any(
button.text() == "重试"
for button in retry_cards[0].findChildren(QPushButton)
)
)
state.show_history = True
tab._load_state(state)
with mock.patch.object(
tab,
"_start_thread",
return_value=mock.Mock(),
):
self.assertTrue(
tab.start_generation(
state,
specs=[
{
"source_asset_id": source.id,
"job_type": failed_job.job_type,
"prompt": failed_job.prompt,
}
],
retry_job_id=failed_job.id,
)
)
self.assertTrue(state.show_history)
self.assertTrue(tab.history_button.isChecked())
state.worker = None
state.thread = None
state.generation_run_token = ""
self.assert_removed(temp_dir)
def test_original_list_expands_without_internal_scrollbars(self): def test_original_list_expands_without_internal_scrollbars(self):
with self.make_temp_dir() as temp_dir: with self.make_temp_dir() as temp_dir:
config = self._config(temp_dir) config = self._config(temp_dir)