152 lines
6.0 KiB
Python
152 lines
6.0 KiB
Python
"""Evaluation files override shared settings without leaking across evaluations."""
|
|
import contextlib
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
from copienator import EvaluationWorkspace
|
|
|
|
|
|
REPOSITORY = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
class LocalConfigurationTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.temporary = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.temporary.cleanup)
|
|
self.root = Path(self.temporary.name)
|
|
self.evaluation = self.root / "Exam"
|
|
self.evaluation.mkdir()
|
|
(self.evaluation / "labels").write_text("Ex 1\n")
|
|
self.global_config = self.root / "config.py"
|
|
self.global_config.write_text("MODEL_FLASH_ID = 'global-model'\nRETURN_PDF_ENABLED = False\n")
|
|
|
|
def run_python(self, source, *, explicit=True):
|
|
env = dict(os.environ, PYTHONPATH=str(REPOSITORY))
|
|
env.pop("COPIENATOR_CONFIG", None)
|
|
if explicit:
|
|
env["COPIENATOR_CONFIG"] = str(self.global_config)
|
|
result = subprocess.run([sys.executable, "-c", source], cwd=self.root, env=env,
|
|
capture_output=True, text=True)
|
|
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
|
return result.stdout
|
|
|
|
def test_partial_local_config_inherits_global_and_defaults(self):
|
|
(self.evaluation / "config.py").write_text("MODEL_FLASH_ID = 'local-model'\n")
|
|
for explicit in (False, True):
|
|
with self.subTest(explicit=explicit):
|
|
self.run_python("""
|
|
from copienator import configuration as c
|
|
c.use_evaluation('Exam')
|
|
assert c.MODEL_FLASH_ID == 'local-model'
|
|
assert c.RETURN_PDF_ENABLED is False
|
|
assert c.RETURN_JPEG_ENABLED is True
|
|
assert c.CONFIG_PATH.name == 'config.py'
|
|
assert c.LOCAL_CONFIG_PATH.parent.name == 'Exam'
|
|
c.use_evaluation(None)
|
|
assert c.MODEL_FLASH_ID == 'global-model'
|
|
""", explicit=explicit)
|
|
|
|
def test_switching_evaluations_removes_previous_overrides(self):
|
|
(self.evaluation / "config.py").write_text("RETURN_JPEG_ENABLED = False\n")
|
|
(self.root / "Other").mkdir()
|
|
self.run_python("""
|
|
from copienator import configuration as c
|
|
c.use_evaluation('Exam')
|
|
assert c.RETURN_JPEG_ENABLED is False
|
|
c.use_evaluation('Other')
|
|
assert c.RETURN_JPEG_ENABLED is True
|
|
assert c.RETURN_PDF_ENABLED is False
|
|
assert c.LOCAL_CONFIG_PATH is None
|
|
""")
|
|
|
|
def test_command_loads_config_for_directory_and_nested_target(self):
|
|
(self.evaluation / "config.py").write_text("MODEL_FLASH_ID = 'local-model'\n")
|
|
self.run_python("""
|
|
from copienator import configuration as c
|
|
from copienator.cli import execute, evaluation_parser, target_parser
|
|
from copienator.commands import correction
|
|
|
|
def check(args):
|
|
assert correction.settings.MODEL_FLASH_ID == 'local-model'
|
|
return 0
|
|
assert execute(evaluation_parser('test'), ['Exam'], check) == 0
|
|
c.use_evaluation(None)
|
|
assert execute(target_parser('test'), ['Exam/labels'], check) == 0
|
|
""")
|
|
|
|
def test_annotation_limits_and_gui_defaults_follow_active_config(self):
|
|
(self.evaluation / "config.py").write_text(
|
|
"ANNOTATION_MAX_HEIGHT_PX = 12345\nALWAYS_CROP = True\n")
|
|
self.run_python("""
|
|
from copienator import configuration as c
|
|
from copienator.commands import annotating_by_label, annotating_with_checks
|
|
from copienator_gui.workflow import build_workflow
|
|
c.use_evaluation('Exam')
|
|
assert annotating_by_label.annotation_limits()[0] == 12345
|
|
assert annotating_with_checks.annotation_limits()[0] == 12345
|
|
steps = {step.id: step for step in build_workflow(False)}
|
|
assert steps['crop_blank_margins'].auto_start_first_visit
|
|
""")
|
|
|
|
@unittest.skipUnless(os.environ.get("DISPLAY"), "GUI display unavailable")
|
|
def test_gui_switches_evaluation_settings(self):
|
|
(self.evaluation / "config.py").write_text("ALWAYS_CROP = True\n")
|
|
(self.root / "Other").mkdir()
|
|
self.run_python("""
|
|
from pathlib import Path
|
|
from copienator import configuration as c
|
|
from copienator_gui.app import CopienatorApp
|
|
app = CopienatorApp(Path.cwd(), False, Path('Exam').resolve())
|
|
try:
|
|
assert app.step_by_id['crop_blank_margins'].auto_start_first_visit
|
|
app.evaluation_var.set(str(Path('Other').resolve()))
|
|
app._load_evaluation()
|
|
assert not app.step_by_id['crop_blank_margins'].auto_start_first_visit
|
|
assert c.LOCAL_CONFIG_PATH is None
|
|
finally:
|
|
app.destroy()
|
|
""")
|
|
|
|
def test_explicit_command_option_overrides_local_config(self):
|
|
(self.evaluation / "config.py").write_text("RETURN_ANSWERS_CONTEXT = True\n")
|
|
self.run_python("""
|
|
from unittest.mock import patch
|
|
from copienator.commands import reading_annotations as command
|
|
from copienator import configuration as c
|
|
|
|
def check(workspace, **options):
|
|
assert c.RETURN_ANSWERS_CONTEXT is True
|
|
assert options['return_answers_context'] is False
|
|
return 0
|
|
with patch.object(command, 'run', check):
|
|
assert command.main(['Exam', '--no-return-answers-context']) == 0
|
|
""")
|
|
|
|
def test_invalid_local_config_reports_error_before_handler(self):
|
|
(self.evaluation / "config.py").write_text("ANNOTATION_MAX_HEIGHT_PX = 0\n")
|
|
self.run_python("""
|
|
from unittest.mock import Mock
|
|
from copienator.cli import execute, evaluation_parser
|
|
handler = Mock()
|
|
assert execute(evaluation_parser('test'), ['Exam'], handler) == 1
|
|
handler.assert_not_called()
|
|
""")
|
|
|
|
def test_local_names_replace_global_including_an_empty_roster(self):
|
|
(self.root / "names").write_text("Global\n")
|
|
workspace = EvaluationWorkspace(self.evaluation, self.root)
|
|
self.assertEqual(workspace.names_file(), self.root / "names")
|
|
local = self.evaluation / "names"
|
|
local.write_text("Local\n")
|
|
self.assertEqual(workspace.names_file(), local)
|
|
local.write_text("")
|
|
self.assertEqual(workspace.names_file().read_text(), "")
|
|
with contextlib.chdir(self.root):
|
|
self.assertEqual(EvaluationWorkspace(self.evaluation).names_file(), local)
|
|
local.unlink()
|
|
self.assertEqual(EvaluationWorkspace(self.evaluation).names_file(), self.root / "names")
|