from __future__ import annotations import os import tempfile import unittest from pathlib import Path from unittest.mock import patch from PIL import Image from copienator import atomic_write_json from copienator.annotation_modifications import DRAFT, stage_baseline from copienator.filesystem import staged_files from copienator_gui.app import CopienatorApp @unittest.skipUnless(os.environ.get("DISPLAY"), "Tk tests require a display") class AnnotationModificationGuiTests(unittest.TestCase): def setUp(self): self.temp = tempfile.TemporaryDirectory() self.root = Path(self.temp.name) (self.root / "Copies/Copie43").mkdir(parents=True) (self.root / "labels").write_text("Ex 1\nEx 2\n") self.output = self.root / "Bnot/Copie43" images = {"Ex 1": Image.new("RGB", (100, 120), "blue")} scores = {"Ex 1": "3", "Ex 2": ""} info = {label: {"present": label == "Ex 1", "not_empty": label == "Ex 1", "touched": False, "score": score} for label, score in scores.items()} with staged_files(self.output) as staging: images["Ex 1"].save(staging / "Ex 1.jpg") atomic_write_json(staging / "score.json", scores) atomic_write_json(staging / "info.json", info) stage_baseline(self.root, "Bnot", self.output, staging, images, {"Ex 1": 20}, info, ["Ex 1"], scores) self.app = CopienatorApp(Path.cwd(), False, self.root) self.app.state_store.update_step("read_annotations", last_run_variant="standard") self.app.tree.selection_set("modify_annotations") self.app.update() self.panel = self.app.manual_panel def tearDown(self): for callback in self.app.tk.splitlist(self.app.tk.call("after", "info")): self.app.after_cancel(callback) self.app.destroy() self.temp.cleanup() def test_add_validate_edit_reload_and_cut_only_body(self): from copienator.annotation_modifications import read_instructions self.assertEqual(self.app.arg_vars["annotation_dir"].get(), "Bnot") self.assertEqual(self.app.run_button.cget("text"), "Make modifications") self.panel.copy_var.set("43") self.panel.source_var.set("Ex 1") self.panel.target_var.set("Ex 2") self.panel.add_instruction() path = self.root / DRAFT self.assertIn("Copie43 Ex 1 -> Ex 2|", path.read_text()) item = read_instructions(path.read_text(), "Bnot")[0] with patch("copienator_gui.annotation_modifications.CutHelper") as helper: self.panel.open_cut(self.root, item) self.assertEqual(helper.call_args.args[1].size, (100, 100)) helper.call_args.args[2]("c{40}2>") self.assertEqual(self.panel.cut_result.get(), "Copie43 Ex 1 c{40}2> Ex 2|") with patch("copienator_gui.annotation_modifications.messagebox.showerror") as error: self.panel.add_instruction() # duplicate source error.assert_called_once() path.write_text("bad instruction") self.panel.reload() with patch("copienator_gui.annotation_modifications.open_path") as editor: self.panel.open_editor() # malformed files must remain editable editor.assert_called_once_with(path) def test_workflow_is_between_reader_and_publisher_and_builds_command(self): steps = list(self.app.step_by_id) self.assertLess(steps.index("read_annotations"), steps.index("modify_annotations")) self.assertLess(steps.index("modify_annotations"), steps.index("giving_names")) command = self.app._make_command() self.assertIn("modify-annotations", command) self.assertEqual(command[-2:], ["--annotation-dir", "Bnot"])