Files
Copies/tests/test_return_outputs.py

125 lines
5.9 KiB
Python

import contextlib
import io
import itertools
import runpy
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
from copienator import EvaluationWorkspace, atomic_write_json, configuration
from copienator.commands import clean, giving_names
class ReturnOutputTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.workspace = EvaluationWorkspace(Path(self.temp.name))
self.workspace.copies_dir.mkdir()
atomic_write_json(self.workspace.copies_dir / "Copie01.json", {"name": "Student"})
(self.workspace.copies_dir / "Copie01.pdf").write_bytes(b"original")
atomic_write_json(self.workspace.correction_file, {})
(self.workspace.root / "names").write_text("Student\n")
self.source = self.workspace.root / "BGnot" / "Copie01"
self.source.mkdir(parents=True)
(self.source / "Concat.jpg").write_bytes(b"jpeg")
(self.source / "Concat_F.pdf").write_bytes(b"pdf")
atomic_write_json(self.source / "score.json", {"Ex 1": "4"})
atomic_write_json(self.source / "info.json", {
"Ex 1": {"present": True, "not_empty": True, "touched": True, "score": "4"}
})
self.answer_option = patch.object(configuration, "RETURN_ANSWERS_ENABLED", False)
self.answer_option.start()
self.addCleanup(self.answer_option.stop)
self.destination = self.workspace.return_dir / "Student (01)"
def prepare(self, jpeg=True, pdf=True):
with patch.object(configuration, "RETURN_JPEG_ENABLED", jpeg), patch.object(
configuration, "RETURN_PDF_ENABLED", pdf
), contextlib.redirect_stdout(io.StringIO()):
self.assertEqual(giving_names.run(self.workspace, annotation_dir="BGnot"), 0)
def test_all_combinations_and_reenable_preserve_sources_and_scores(self):
for jpeg, pdf in itertools.product((True, False), repeat=2):
with self.subTest(jpeg=jpeg, pdf=pdf):
self.prepare()
self.prepare(jpeg, pdf)
expected = {"score.json", "info.json"}
if jpeg:
expected.add("Student.jpg")
if pdf:
expected.add("Student.pdf")
self.assertEqual({p.name for p in self.destination.iterdir()}, expected)
self.assertEqual((self.source / "Concat.jpg").read_bytes(), b"jpeg")
self.assertEqual((self.source / "Concat_F.pdf").read_bytes(), b"pdf")
self.assertEqual(
(self.destination / "score.json").read_bytes(),
(self.source / "score.json").read_bytes(),
)
self.prepare()
self.assertEqual((self.destination / "Student.jpg").read_bytes(), b"jpeg")
self.assertEqual((self.destination / "Student.pdf").read_bytes(), b"pdf")
def test_disabling_removes_regular_files_and_broken_links_only(self):
self.prepare()
jpg = self.destination / "Student.jpg"
jpg.unlink()
jpg.write_bytes(b"old fallback copy")
pdf = self.destination / "Student.pdf"
pdf.unlink()
pdf.symlink_to(self.source / "missing.pdf")
unrelated = self.destination / "notes.txt"
unrelated.write_text("keep")
self.prepare(False, False)
self.assertEqual({p.name for p in self.destination.iterdir()}, {"score.json", "info.json", "notes.txt"})
self.assertTrue((self.source / "Concat_F.pdf").is_file())
def test_fallback_source_respects_options(self):
self.source.parent.rename(self.workspace.root / "Anot")
(self.workspace.root / "BGnot").mkdir()
self.prepare(False, True)
self.assertFalse((self.destination / "Student.jpg").exists())
self.assertEqual((self.destination / "Student.pdf").read_bytes(), b"pdf")
self.assertTrue((self.destination / "score.json").is_file())
self.assertTrue((self.destination / "info.json").is_file())
def test_cleanup_preserves_enabled_returns_without_jpeg(self):
self.prepare(False, True)
with patch.object(configuration, "RETURN_JPEG_ENABLED", False):
plan = clean.build_cleanup_plan(self.workspace)
with contextlib.redirect_stdout(io.StringIO()):
clean.apply_cleanup(self.workspace, plan)
self.assertFalse(self.source.exists())
self.assertEqual((self.destination / "Student.pdf").read_bytes(), b"pdf")
self.assertFalse((self.destination / "Student.pdf").is_symlink())
self.assertTrue((self.destination / "score.json").is_file())
self.assertTrue((self.destination / "info.json").is_file())
def test_cleanup_accepts_scores_only_but_still_requires_scores(self):
self.prepare(False, False)
with patch.object(configuration, "RETURN_JPEG_ENABLED", False):
plan = clean.build_cleanup_plan(self.workspace)
self.assertIn(self.destination / "score.json", plan.kept_files)
(self.destination / "score.json").unlink()
with self.assertRaisesRegex(clean.CliError, "score.json"):
clean.build_cleanup_plan(self.workspace)
def test_older_config_defaults_to_enabled(self):
old_config = self.workspace.root / "old_config.py"
old_config.write_text("ALWAYS_CROP = False\n")
with patch.dict("os.environ", {"COPIENATOR_CONFIG": str(old_config)}):
loaded = runpy.run_path(configuration.__file__)
self.assertIs(loaded["RETURN_JPEG_ENABLED"], True)
self.assertIs(loaded["RETURN_PDF_ENABLED"], True)
self.assertIs(loaded["RETURN_ANSWERS_ENABLED"], False)
self.assertIs(loaded["RETURN_ANSWERS_CONTEXT"], False)
self.assertIs(loaded["RETURN_ANSWERS_QUESTION"], True)
self.assertIs(loaded["RETURN_ANSWERS_SOLUTION"], False)
self.assertNotIn("RETURN_JSON_ENABLED", loaded)
if __name__ == "__main__":
unittest.main()