95 lines
4.9 KiB
Python
95 lines
4.9 KiB
Python
import tempfile
|
|
import unittest
|
|
from contextlib import ExitStack
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
from copienator import CliError, EvaluationWorkspace, ExitCode
|
|
from copienator.annotation_data import AnnotationLoadResult
|
|
from copienator.commands import annotating as simple
|
|
from copienator.commands import annotating_by_label as grouped
|
|
from copienator.commands import annotating_with_checks as checks
|
|
from copienator.commands import import_annotations
|
|
|
|
|
|
class AnnotationImportCleanupTests(unittest.TestCase):
|
|
def setUp(self):
|
|
temporary = tempfile.TemporaryDirectory()
|
|
self.addCleanup(temporary.cleanup)
|
|
self.root = Path(temporary.name)
|
|
self.workspace = EvaluationWorkspace(self.root / "Exam")
|
|
for directory in ("Copies", "Par label"):
|
|
(self.workspace.root / directory).mkdir(parents=True)
|
|
(self.workspace.root / "labels").write_text("A\n")
|
|
(self.workspace.root / "correction.json").write_text("{}")
|
|
self.import_dir = self.root / "Annotées"
|
|
self.import_dir.mkdir()
|
|
self.patch = patch.object(import_annotations.configuration, "IMPORT_DIR", self.import_dir)
|
|
self.patch.start()
|
|
self.addCleanup(self.patch.stop)
|
|
|
|
def test_cleanup_empties_folder_without_following_child_symlinks(self):
|
|
(self.import_dir / "old.pdf").write_bytes(b"return")
|
|
nested = self.import_dir / "nested"
|
|
nested.mkdir()
|
|
(nested / "old.jpg").write_bytes(b"return")
|
|
outside = self.root / "keep"
|
|
outside.mkdir()
|
|
(outside / "keep.pdf").write_bytes(b"keep")
|
|
(self.import_dir / "link").symlink_to(outside, target_is_directory=True)
|
|
import_annotations.clear_import_directory(self.workspace)
|
|
self.assertTrue(self.import_dir.is_dir())
|
|
self.assertEqual(list(self.import_dir.iterdir()), [])
|
|
self.assertTrue((outside / "keep.pdf").is_file())
|
|
|
|
def test_cleanup_handles_unconfigured_or_missing_folder(self):
|
|
for configured in (None, "", self.root / "absent"):
|
|
with self.subTest(configured=configured), \
|
|
patch.object(import_annotations.configuration, "IMPORT_DIR", configured):
|
|
import_annotations.clear_import_directory(self.workspace)
|
|
self.assertFalse((self.root / "absent").exists())
|
|
|
|
def test_cleanup_rejects_an_ancestor_of_evaluation(self):
|
|
with patch.object(import_annotations.configuration, "IMPORT_DIR", self.root), \
|
|
self.assertRaises(CliError):
|
|
import_annotations.clear_import_directory(self.workspace)
|
|
self.assertTrue(self.workspace.root.is_dir())
|
|
|
|
def test_all_modes_clean_only_when_checked_and_successful(self):
|
|
for module in (simple, checks, grouped):
|
|
for clear_import, warnings in ((False, []), (True, []), (True, ["missing answer"])):
|
|
with self.subTest(mode=module.__name__, checked=clear_import, warnings=warnings):
|
|
old = self.import_dir / "old.pdf"
|
|
old.write_bytes(b"return")
|
|
with ExitStack() as patches:
|
|
patches.enter_context(patch.object(
|
|
module, "load_annotation_data",
|
|
return_value=AnnotationLoadResult({"01": {"A": {}}}, warnings),
|
|
))
|
|
if module is simple:
|
|
patches.enter_context(patch.object(
|
|
simple, "process_correction", return_value=ExitCode.SUCCESS
|
|
))
|
|
elif module is checks:
|
|
patches.enter_context(patch.object(
|
|
checks, "_render_student", return_value="success"
|
|
))
|
|
else:
|
|
patches.enter_context(patch.object(
|
|
grouped, "_generate_groups", return_value=(1, 0)
|
|
))
|
|
patches.enter_context(patch.object(
|
|
grouped, "_load_label_groups", return_value=[["A"]]
|
|
))
|
|
targets = (self.workspace.root,) if module is checks else ()
|
|
status = module.run(self.workspace, *targets, clear_import=clear_import)
|
|
self.assertEqual(status, ExitCode.PARTIAL if warnings else ExitCode.SUCCESS)
|
|
self.assertEqual(old.exists(), not (clear_import and not warnings))
|
|
|
|
def test_cli_forwards_cleanup_flag_for_each_mode(self):
|
|
for module in (simple, checks, grouped):
|
|
with self.subTest(mode=module.__name__), \
|
|
patch.object(module, "run", return_value=ExitCode.SUCCESS) as run:
|
|
self.assertEqual(module.main([str(self.workspace.root), "--clear-import"]), 0)
|
|
self.assertTrue(run.call_args.kwargs["clear_import"])
|