import os import tempfile import tkinter as tk import unittest from pathlib import Path from unittest.mock import call, patch from copienator.answer_origins import save_origins from copienator_gui.manual_resolution import ManualResolutionPanel @unittest.skipUnless(os.environ.get("DISPLAY"), "requires a display") class ManualResolutionPanelTests(unittest.TestCase): def setUp(self): self.temp = tempfile.TemporaryDirectory() self.addCleanup(self.temp.cleanup) self.evaluation = Path(self.temp.name) self.root = tk.Tk() self.addCleanup(self.root.destroy) self.path = self.evaluation / "manual_resolutions.txt" def panel(self): panel = ManualResolutionPanel(self.root, lambda: self.evaluation) panel.pack() self.root.update() return panel def test_preview_editor_and_each_pdf_pair_use_shared_resolution_rules(self): content = "### Instructions\nCopie01 Ex 1 x> |Ex 2\n\nCopie02 Ex 3 ss Ex 4|\n" self.path.write_text(content, encoding="utf-8") paths = [] for copy, label in (("01", "Ex 1_new"), ("01", "Ex 2_old"), ("02", "Ex 3"), ("02", "Ex 4")): path = self.evaluation / "Copies" / f"Copie{copy}" / f"{label}.pdf" path.parent.mkdir(parents=True, exist_ok=True) path.touch() paths.append(path) copy_paths = [] for copy in ("01", "02"): pdf = self.evaluation / "Copies" / f"Copie{copy}.pdf" pdf.touch() copy_paths.extend((pdf, pdf.with_suffix(""))) panel = self.panel() self.assertEqual(panel.text.get("1.0", "end-1c"), content) self.assertEqual(len(panel.pdf_buttons), 4) self.assertEqual([button.cget("text") for button in panel.pdf_buttons], ["Source : Ex 1", "Cible : Ex 2", "Source : Ex 3", "Cible : Ex 4"]) self.assertEqual([button.cget("text") for button in panel.copy_buttons], ["pdf", "dir", "pdf", "dir"]) with patch("copienator_gui.manual_resolution.open_path") as opened: panel.editor_button.invoke() for button in panel.pdf_buttons: button.invoke() for button in panel.copy_buttons: button.invoke() self.assertEqual(opened.call_args_list, [call(self.path), *map(call, paths), *map(call, copy_paths)]) def test_reload_updates_the_displayed_target_and_its_pdf_action(self): self.path.write_text("Copie01 A -> B|\n", encoding="utf-8") copies = self.evaluation / "Copies" / "Copie01" copies.mkdir(parents=True) (copies / "A.pdf").touch() (copies / "C.pdf").touch() panel = self.panel() self.path.write_text("Copie01 A -> C|\n", encoding="utf-8") panel.reload_button.invoke() self.assertEqual(panel.text.get("1.0", "end-1c"), "Copie01 A -> C|\n") self.assertEqual(panel.pdf_buttons[1].cget("text"), "Cible : C") with patch("copienator_gui.manual_resolution.open_path") as opened: panel.pdf_buttons[1].invoke() opened.assert_called_once_with(copies / "C.pdf") def test_populated_labels_appear_under_each_relevant_conflict(self): content = "Copie01 A -> B|\nCopie02 A -> B|\nCopie01 A -> C|" self.path.write_text(content, encoding="utf-8") copies = self.evaluation / "Copies" / "Copie01" copies.mkdir(parents=True) for label, filename in (("label2", "label2_new.pdf"), ("label1", "label1.pdf"), ("stale", "stale.pdf"), ("archived", "archived_old.pdf")): pdf = copies / filename pdf.write_bytes(b"answer") save_origins(self.evaluation, "01", label, pdf, ["A"]) (copies / "stale.pdf").write_bytes(b"changed answer") panel = self.panel() expected = ( "Copie01 A -> B|\n# -> label1, label2\n" "Copie02 A -> B|\n" "Copie01 A -> C|\n# -> label1, label2" ) self.assertEqual(panel.text.get("1.0", "end-1c"), expected) self.assertEqual(self.path.read_text(encoding="utf-8"), content) self.assertEqual(len(panel.pdf_buttons), 6) self.assertIn("3 instruction(s)", panel.status.cget("text")) (copies / "label1.pdf").unlink() panel.reload() self.assertEqual(panel.text.get("1.0", "end-1c"), expected.replace("label1, label2", "label2")) def test_reload_keeps_invalid_lines_visible_and_removes_stale_actions(self): self.path.write_text("Copie01 A -> B|\n", encoding="utf-8") panel = self.panel() self.path.write_text("bad instruction\nCopie02 C sx D\n", encoding="utf-8") panel.reload() self.assertIn("bad instruction", panel.text.get("1.0", "end")) self.assertIn("lignes invalides : 1", panel.status.cget("text")) self.assertEqual(len(panel.pdf_buttons), 2) self.assertEqual(len(panel.copy_buttons), 2) self.path.unlink() panel.reload() self.assertFalse(panel.pdf_buttons) self.assertFalse(panel.copy_buttons) self.assertEqual(str(panel.editor_button.cget("state")), "disabled") def test_missing_source_does_not_prevent_opening_destination(self): self.path.write_text("Copie01 A -> B|\n", encoding="utf-8") destination = self.evaluation / "Copies" / "Copie01" / "B.pdf" destination.parent.mkdir(parents=True) destination.touch() panel = self.panel() with patch("copienator_gui.manual_resolution.open_path") as opened, patch( "copienator_gui.manual_resolution.messagebox.showerror" ) as error: panel.pdf_buttons[0].invoke() opened.assert_not_called() panel.pdf_buttons[1].invoke() opened.assert_called_once_with(destination) self.assertIn("A.pdf", error.call_args.args[1])