125 lines
5.9 KiB
Python
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()
|