diff --git a/cms/grading/Sandbox.py b/cms/grading/Sandbox.py index 92f7232c7e..11794b9e1b 100644 --- a/cms/grading/Sandbox.py +++ b/cms/grading/Sandbox.py @@ -287,24 +287,23 @@ def __init__( # between sandboxes. self.dirs.append((None, "/dev/shm", "tmp")) - # Set common environment variables. + # Set common configuration that is relevant for multiple + # languages. + + self.set_env["PATH"] = "/usr/local/bin:/usr/bin:/bin" + # Specifically needed by Python, that searches the home for # packages. self.set_env["HOME"] = self._home_dest - # Needed on Ubuntu by PHP (and more), since /usr/bin only contains a - # symlink to one out of many alternatives. + # Needed on Ubuntu by PHP, Java, Pascal etc, since /usr/bin + # only contains a symlink to one out of many alternatives. self.maybe_add_mapped_directory("/etc/alternatives") # On Arch Linux, pypy3 is installed in `/opt` and `/usr/bin/pypy3` is # just a symlink. self.maybe_add_mapped_directory("/opt/pypy3") - # Likewise, needed by C# programs. The Mono runtime looks in - # /etc/mono/config to obtain the default DllMap, which includes, in - # particular, the System.Native assembly. - self.maybe_add_mapped_directory("/etc/mono", options="noexec") - # Tell isolate to get the sandbox ready. We do our best to cleanup # after ourselves, but we might have missed something if a previous # worker was interrupted in the middle of an execution, so we issue an @@ -769,9 +768,6 @@ def archive(self, file_cacher: FileCacher) -> str | None: # Put archive to FS sandbox_archive.seek(0) - return file_cacher.put_file_from_fobj( - sandbox_archive, "Sandbox %s" % self.get_root_path() - ) def add_mapped_directory( self, diff --git a/cms/grading/language.py b/cms/grading/language.py index e74652f40f..128964f0ac 100644 --- a/cms/grading/language.py +++ b/cms/grading/language.py @@ -21,6 +21,7 @@ import logging import os from abc import ABCMeta, abstractmethod +from cms.grading.Sandbox import Sandbox logger = logging.getLogger(__name__) @@ -135,6 +136,13 @@ def get_compilation_commands( """ pass + def configure_compilation_sandbox(self, sandbox: Sandbox): + """ + Set sandbox parameters necessary for running the compilation + commands. + """ + pass + @abstractmethod def get_evaluation_commands( self, @@ -156,6 +164,13 @@ def get_evaluation_commands( """ pass + def configure_evaluation_sandbox(self, sandbox: Sandbox): + """ + Set sandbox parameters necessary for running the evaluation + commands. + """ + pass + # It's sometimes handy to use Language objects in sets or as dict # keys. Since they have no state (they are just collections of # constants and static methods) and are designed to be used as diff --git a/cms/grading/languages/csharp_mono.py b/cms/grading/languages/csharp_mono.py index f200ff4c66..87f3269a89 100644 --- a/cms/grading/languages/csharp_mono.py +++ b/cms/grading/languages/csharp_mono.py @@ -67,3 +67,12 @@ def get_evaluation_commands( self, executable_filename, main=None, args=None): """See Language.get_evaluation_commands.""" return [["/usr/bin/mono", executable_filename]] + + def configure_compilation_sandbox(self, sandbox): + # The Mono runtime looks in /etc/mono/config to obtain the + # default DllMap, which includes, in particular, the + # System.Native assembly. + sandbox.maybe_add_mapped_directory("/etc/mono", options="noexec") + + def configure_evaluation_sandbox(self, sandbox): + sandbox.maybe_add_mapped_directory("/etc/mono", options="noexec") diff --git a/cms/grading/languages/haskell_ghc.py b/cms/grading/languages/haskell_ghc.py index 9762d60055..ca9e87ba91 100644 --- a/cms/grading/languages/haskell_ghc.py +++ b/cms/grading/languages/haskell_ghc.py @@ -64,6 +64,13 @@ def get_compilation_commands(self, executable_filename, source_filenames[0]]) return commands + def configure_compilation_sandbox(self, sandbox): + # Directory required to be visible during a compilation with GHC. + # GHC looks for the Haskell's package database in + # "/usr/lib/ghc/package.conf.d" (already visible by isolate's default, + # but it is a symlink to "/var/lib/ghc/package.conf.d") + sandbox.maybe_add_mapped_directory("/var/lib/ghc") + @staticmethod def _capitalize(string: str): dirname, basename = os.path.split(string) diff --git a/cms/grading/languages/java_jdk.py b/cms/grading/languages/java_jdk.py index 78381e2009..9b48f87647 100644 --- a/cms/grading/languages/java_jdk.py +++ b/cms/grading/languages/java_jdk.py @@ -21,6 +21,7 @@ """ +import os from shlex import quote as shell_quote from cms.grading import Language @@ -91,3 +92,10 @@ def get_evaluation_commands( command = ["/usr/bin/java", "-Deval=true", "-Xmx512M", "-Xss64M", main] + args return [unzip_command, command] + + def configure_compilation_sandbox(self, sandbox): + # the jvm conf directory is often symlinked to /etc in + # distributions, but the location of it in /etc is inconsistent. + for path in os.listdir("/etc"): + if path == "java" or path.startswith("java-"): + sandbox.add_mapped_directory(f"/etc/{path}") diff --git a/cms/grading/languages/pascal_fpc.py b/cms/grading/languages/pascal_fpc.py index 12df846fc8..aa710d8091 100644 --- a/cms/grading/languages/pascal_fpc.py +++ b/cms/grading/languages/pascal_fpc.py @@ -60,3 +60,7 @@ def get_compilation_commands(self, command += ["-O2", "-XSs", "-o%s" % executable_filename] command += [source_filenames[0]] return [command] + + def configure_compilation_sandbox(self, sandbox): + # Needed for /etc/fpc.cfg. + sandbox.maybe_add_mapped_directory("/etc") diff --git a/cms/grading/languages/python3_pypy.py b/cms/grading/languages/python3_pypy.py index e87b29ab01..64c28de174 100644 --- a/cms/grading/languages/python3_pypy.py +++ b/cms/grading/languages/python3_pypy.py @@ -79,3 +79,11 @@ def get_evaluation_commands( """See Language.get_evaluation_commands.""" args = args if args is not None else [] return [["/usr/bin/pypy3", executable_filename] + args] + + def configure_compilation_sandbox(self, sandbox): + # Needed on Arch, where /usr/bin/pypy3 is a symlink into + # /opt/pypy3. + sandbox.maybe_add_mapped_directory("/opt/pypy3") + + def configure_evaluation_sandbox(self, sandbox): + sandbox.maybe_add_mapped_directory("/opt/pypy3") diff --git a/cms/grading/steps/compilation.py b/cms/grading/steps/compilation.py index 3b56f2eaad..a8ba12be9b 100644 --- a/cms/grading/steps/compilation.py +++ b/cms/grading/steps/compilation.py @@ -29,6 +29,7 @@ from cms import config from cms.grading.Sandbox import Sandbox +from cms.grading.language import Language from cms.grading.steps.stats import StatsDict from .messages import HumanMessage, MessageCollection from .utils import generic_step @@ -66,7 +67,7 @@ def N_(message: str): def compilation_step( - sandbox: Sandbox, commands: list[list[str]] + sandbox: Sandbox, commands: list[list[str]], language: Language ) -> tuple[bool, bool | None, list[str] | None, StatsDict | None]: """Execute some compilation commands in the sandbox. @@ -79,6 +80,7 @@ def compilation_step( sandbox: the sandbox we consider, already created. commands: compilation commands to execute. + language: language of the submission return: a tuple with four items: * success: True if the sandbox did not fail, in any command; @@ -93,18 +95,14 @@ def compilation_step( """ # Set sandbox parameters suitable for compilation. - sandbox.add_mapped_directory("/etc") - # Directory required to be visible during a compilation with GHC. - # GHC looks for the Haskell's package database in - # "/usr/lib/ghc/package.conf.d" (already visible by isolate's default, - # but it is a symlink to "/var/lib/ghc/package.conf.d" - sandbox.maybe_add_mapped_directory("/var/lib/ghc") - sandbox.preserve_env = True sandbox.max_processes = config.sandbox.compilation_sandbox_max_processes sandbox.timeout = config.sandbox.compilation_sandbox_max_time_s sandbox.wallclock_timeout = 2 * sandbox.timeout + 1 sandbox.address_space = config.sandbox.compilation_sandbox_max_memory_kib * 1024 + # Set per-language sandbox parameters. + language.configure_compilation_sandbox(sandbox) + # Run the compilation commands, copying stdout and stderr to stats. stats = generic_step(sandbox, commands, "compilation", collect_output=True) if stats is None: diff --git a/cms/grading/steps/evaluation.py b/cms/grading/steps/evaluation.py index 2a26b6a075..20828b62c1 100644 --- a/cms/grading/steps/evaluation.py +++ b/cms/grading/steps/evaluation.py @@ -30,6 +30,7 @@ from cms import config from cms.grading.Sandbox import Sandbox +from cms.grading.language import Language from .messages import HumanMessage, MessageCollection from .stats import StatsDict, execution_stats @@ -83,6 +84,7 @@ def N_(message: str): def evaluation_step( sandbox: Sandbox, commands: list[list[str]], + language: Language | None, time_limit: float | None = None, memory_limit: int | None = None, dirs_map: dict[str, tuple[str | None, str | None]] | None = None, @@ -101,6 +103,8 @@ def evaluation_step( sandbox: the sandbox we consider, already created. commands: evaluation commands to execute. + language: language of the submission (or None if the commands to + execute are not from a Language's get_evaluation_commands). time_limit: time limit in seconds (applied to each command); if None, no time limit is enforced. memory_limit: memory limit in bytes (applied to each command); @@ -135,7 +139,7 @@ def evaluation_step( """ for command in commands: success = evaluation_step_before_run( - sandbox, command, time_limit, memory_limit, + sandbox, command, language, time_limit, memory_limit, None, dirs_map, writable_files, stdin_redirect, stdout_redirect, multiprocess, wait=True) if not success: @@ -152,6 +156,7 @@ def evaluation_step( def evaluation_step_before_run( sandbox: Sandbox, command: list[str], + language: Language | None, time_limit: float | None = None, memory_limit: int | None = None, wall_limit: float | None = None, @@ -222,6 +227,10 @@ def evaluation_step_before_run( sandbox.set_multiprocess(multiprocess) sandbox.close_fds = close_fds + # Configure per-language sandbox parameters. + if language: + language.configure_evaluation_sandbox(sandbox) + # Actually run the evaluation command. logger.debug("Starting execution step.") return sandbox.execute_without_std(command, wait=wait) diff --git a/cms/grading/tasktypes/Batch.py b/cms/grading/tasktypes/Batch.py index 9de82c823d..2f533e3ccc 100644 --- a/cms/grading/tasktypes/Batch.py +++ b/cms/grading/tasktypes/Batch.py @@ -244,7 +244,7 @@ def _do_compile(self, job: CompilationJob, file_cacher: FileCacher): # Run the compilation. box_success, compilation_success, text, stats = \ - compilation_step(sandbox, commands) + compilation_step(sandbox, commands, language) # Retrieve the compiled executables. job.success = box_success @@ -311,6 +311,7 @@ def _execution_step(self, job: EvaluationJob, file_cacher: FileCacher): box_success, evaluation_success, stats = evaluation_step( sandbox, commands, + language, job.time_limit, job.memory_limit, writable_files=files_allowing_write, diff --git a/cms/grading/tasktypes/Communication.py b/cms/grading/tasktypes/Communication.py index 9f702c1ae4..823b5d6291 100644 --- a/cms/grading/tasktypes/Communication.py +++ b/cms/grading/tasktypes/Communication.py @@ -229,7 +229,7 @@ def compile(self, job: CompilationJob, file_cacher: FileCacher): # Run the compilation. box_success, compilation_success, text, stats = \ - compilation_step(sandbox, commands) + compilation_step(sandbox, commands, language) # Retrieve the compiled executables. job.success = box_success @@ -324,6 +324,7 @@ def evaluate(self, job: EvaluationJob, file_cacher: FileCacher): manager_ = evaluation_step_before_run( sandbox_mgr, manager_command, + None, manager_time_limit, config.sandbox.trusted_sandbox_max_memory_kib * 1024, dirs_map=dict((fifo_dir[i], (sandbox_fifo_dir[i], "rw")) for i in indices), @@ -359,11 +360,13 @@ def evaluate(self, job: EvaluationJob, file_cacher: FileCacher): # Assumes that the actual execution of the user solution is the # last command in commands, and that the previous are "setup" # that don't need tight control. + # TODO: why can't this use normal evaluation step?? if len(commands) > 1: trusted_step(sandbox_user[i], commands[:-1]) the_process = evaluation_step_before_run( sandbox_user[i], commands[-1], + language, job.time_limit, job.memory_limit, dirs_map={fifo_dir[i]: (sandbox_fifo_dir[i], "rw")}, diff --git a/cms/grading/tasktypes/Interactive.py b/cms/grading/tasktypes/Interactive.py index 9f3d8b1498..4beea94e59 100644 --- a/cms/grading/tasktypes/Interactive.py +++ b/cms/grading/tasktypes/Interactive.py @@ -176,7 +176,7 @@ def compile(self, job, file_cacher): sandbox.create_file_from_storage(filename, digest, file_cacher) box_success, compilation_success, text, stats = compilation_step( - sandbox, commands + sandbox, commands, language ) job.success = box_success @@ -231,6 +231,7 @@ def evaluate(self, job, file_cacher): "input.txt", ], "solution_files": [executable_filename], + "solution_language": job.language, "controller_wall_limit": self.controller_wall_limit, "controller_time_limit": self.controller_time_limit, "controller_memory_limit": self.controller_memory_limit, diff --git a/cms/grading/tasktypes/TwoSteps.py b/cms/grading/tasktypes/TwoSteps.py index 720cae1e24..19586450a7 100644 --- a/cms/grading/tasktypes/TwoSteps.py +++ b/cms/grading/tasktypes/TwoSteps.py @@ -196,7 +196,7 @@ def compile(self, job: CompilationJob, file_cacher: FileCacher): # Run the compilation. box_success, compilation_success, text, stats = \ - compilation_step(sandbox, commands) + compilation_step(sandbox, commands, language) # Retrieve the compiled executables job.success = box_success @@ -253,6 +253,7 @@ def evaluate(self, job: EvaluationJob, file_cacher: FileCacher): first = evaluation_step_before_run( first_sandbox, first_command, + None, job.time_limit, job.memory_limit, dirs_map={fifo_dir: ("/fifo", "rw")}, @@ -277,6 +278,7 @@ def evaluate(self, job: EvaluationJob, file_cacher: FileCacher): second = evaluation_step_before_run( second_sandbox, second_command, + None, job.time_limit, job.memory_limit, dirs_map={fifo_dir: ("/fifo", "rw")}, diff --git a/cms/grading/tasktypes/interactive_keeper.py b/cms/grading/tasktypes/interactive_keeper.py index 0399cb77c9..576363bb9c 100644 --- a/cms/grading/tasktypes/interactive_keeper.py +++ b/cms/grading/tasktypes/interactive_keeper.py @@ -32,6 +32,7 @@ ) from cms.grading.steps.stats import merge_execution_stats from cms.grading.steps import trusted_step +from cms.grading.languagemanager import get_language # Note: we keep a separate interactive keeper (running in a separate # process) to avoid opening many file descriptors in the main worker. @@ -72,6 +73,7 @@ def main(): solution_commands = config["solution_commands"] controller_files = config["controller_files"] solution_files = config["solution_files"] + solution_language = config["solution_language"] controller_wall_limit = config.get("controller_wall_limit") controller_time_limit = config.get("controller_time_limit") controller_memory_limit = config.get("controller_memory_limit") @@ -99,9 +101,12 @@ def main(): with open(os.path.join(temp_dir, path), "rb") as g: shutil.copyfileobj(g, f) + language = get_language(solution_language) + controller_proc = evaluation_step_before_run( controller_sandbox, controller_command, + None, time_limit=controller_time_limit, memory_limit=controller_memory_limit, wall_limit=controller_wall_limit, @@ -149,6 +154,7 @@ def main(): sol_proc = evaluation_step_before_run( sandbox_sol, solution_commands[-1], + language, time_limit=solution_time_limit, wall_limit=controller_wall_limit, # the wall-clock limit mostly exists to eventually kill stuck diff --git a/cmstestsuite/unit_tests/grading/steps/compilation_test.py b/cmstestsuite/unit_tests/grading/steps/compilation_test.py index d8f105497b..7fd6a6d692 100755 --- a/cmstestsuite/unit_tests/grading/steps/compilation_test.py +++ b/cmstestsuite/unit_tests/grading/steps/compilation_test.py @@ -26,6 +26,7 @@ from cmstestsuite.unit_tests.grading.steps.fakesandbox \ import FakeSandbox from cmstestsuite.unit_tests.grading.steps.stats_test import get_stats +from cmstestsuite.unit_tests.grading.tasktypes.tasktypetestutils import LANG_1 ONE_COMMAND = [["test", "command"]] @@ -54,7 +55,7 @@ def test_single_command_success(self): with patch("cms.grading.steps.compilation.generic_step", return_value=expected_stats) as mock_generic_step: success, compilation_success, text, stats = compilation_step( - self.sandbox, ONE_COMMAND) + self.sandbox, ONE_COMMAND, LANG_1) mock_generic_step.assert_called_once_with( self.sandbox, ONE_COMMAND, "compilation", collect_output=True) @@ -73,7 +74,7 @@ def test_single_command_compilation_failed_nonzero_return(self): with patch("cms.grading.steps.compilation.generic_step", return_value=expected_stats): success, compilation_success, text, stats = compilation_step( - self.sandbox, ONE_COMMAND) + self.sandbox, ONE_COMMAND, LANG_1) # User's fault, no error needs to be logged. self.assertLoggedError(False) @@ -91,7 +92,7 @@ def test_single_command_compilation_failed_timeout(self): with patch("cms.grading.steps.compilation.generic_step", return_value=expected_stats): success, compilation_success, text, stats = compilation_step( - self.sandbox, ONE_COMMAND) + self.sandbox, ONE_COMMAND, LANG_1) # User's fault, no error needs to be logged. self.assertLoggedError(False) @@ -109,7 +110,7 @@ def test_single_command_compilation_failed_timeout_wall(self): with patch("cms.grading.steps.compilation.generic_step", return_value=expected_stats): success, compilation_success, text, stats = compilation_step( - self.sandbox, ONE_COMMAND) + self.sandbox, ONE_COMMAND, LANG_1) # User's fault, no error needs to be logged. self.assertLoggedError(False) @@ -127,7 +128,7 @@ def test_single_command_compilation_failed_signal(self): with patch("cms.grading.steps.compilation.generic_step", return_value=expected_stats): success, compilation_success, text, stats = compilation_step( - self.sandbox, ONE_COMMAND) + self.sandbox, ONE_COMMAND, LANG_1) # User's fault, no error needs to be logged. self.assertLoggedError(False) @@ -141,7 +142,7 @@ def test_single_command_sandbox_failed(self): with patch("cms.grading.steps.compilation.generic_step", return_value=None): success, compilation_success, text, stats = compilation_step( - self.sandbox, ONE_COMMAND) + self.sandbox, ONE_COMMAND, LANG_1) # Sandbox should never fail. If it does, should notify the admin. self.assertLoggedError() @@ -156,7 +157,7 @@ def test_multiple_commands_success(self): with patch("cms.grading.steps.compilation.generic_step", return_value=expected_stats) as mock_generic_step: success, compilation_success, text, stats = compilation_step( - self.sandbox, TWO_COMMANDS) + self.sandbox, TWO_COMMANDS, LANG_1) mock_generic_step.assert_called_once_with( self.sandbox, TWO_COMMANDS, "compilation", collect_output=True) diff --git a/cmstestsuite/unit_tests/grading/tasktypes/BatchTest.py b/cmstestsuite/unit_tests/grading/tasktypes/BatchTest.py index a52c7ec4a8..a157ac8e90 100755 --- a/cmstestsuite/unit_tests/grading/tasktypes/BatchTest.py +++ b/cmstestsuite/unit_tests/grading/tasktypes/BatchTest.py @@ -153,7 +153,7 @@ def test_alone_success(self): # Compilation step called correctly. self.compilation_step.assert_called_once_with( sandbox, fake_compilation_commands( - COMPILATION_COMMAND_1, ["foo.l1"], "foo")) + COMPILATION_COMMAND_1, ["foo.l1"], "foo"), LANG_1) # Results put in job, executable stored and sandbox deleted. self.assertResultsInJob(job) sandbox.get_file_to_storage.assert_called_once_with("foo", self.file_cacher, ANY) @@ -188,8 +188,12 @@ def test_alone_success_two_files(self): self.assertEqual(sandbox.create_file_from_storage.call_count, 2) # Compilation step called correctly. self.compilation_step.assert_called_once_with( - sandbox, fake_compilation_commands( - COMPILATION_COMMAND_1, ["foo.l1", "bar.l1"], "bar_foo")) + sandbox, + fake_compilation_commands( + COMPILATION_COMMAND_1, ["foo.l1", "bar.l1"], "bar_foo" + ), + LANG_1, + ) # Results put in job, executable stored and sandbox deleted. self.assertResultsInJob(job) sandbox.get_file_to_storage.assert_called_once_with("bar_foo", self.file_cacher, ANY) @@ -252,8 +256,12 @@ def test_grader_success(self): self.assertEqual(sandbox.create_file_from_storage.call_count, 3) # Compilation step called correctly. self.compilation_step.assert_called_once_with( - sandbox, fake_compilation_commands( - COMPILATION_COMMAND_1, ["foo.l1", "grader.l1"], "foo")) + sandbox, + fake_compilation_commands( + COMPILATION_COMMAND_1, ["foo.l1", "grader.l1"], "foo" + ), + LANG_1, + ) # Results put in job, executable stored and sandbox deleted. self.assertResultsInJob(job) sandbox.get_file_to_storage.assert_called_once_with("foo", self.file_cacher, ANY) @@ -354,6 +362,7 @@ def test_stdio_diff_success(self): self.evaluation_step.assert_called_once_with( sandbox, fake_evaluation_commands(EVALUATION_COMMAND_1, "foo", "foo"), + LANG_1, 2.5, 123 * 1024 * 1024, writable_files=[], stdin_redirect="input.txt", @@ -475,6 +484,7 @@ def test_fileio_diff_success(self): self.evaluation_step.assert_called_once_with( sandbox, fake_evaluation_commands(EVALUATION_COMMAND_1, "foo", "foo"), + LANG_1, 2.5, 123 * 1024 * 1024, writable_files=["myout"], stdin_redirect=None, diff --git a/cmstestsuite/unit_tests/grading/tasktypes/CommunicationTest.py b/cmstestsuite/unit_tests/grading/tasktypes/CommunicationTest.py index a810564e8b..5f1d7392e8 100755 --- a/cmstestsuite/unit_tests/grading/tasktypes/CommunicationTest.py +++ b/cmstestsuite/unit_tests/grading/tasktypes/CommunicationTest.py @@ -167,8 +167,12 @@ def test_one_file_success(self): self.assertEqual(sandbox.create_file_from_storage.call_count, 2) # Compilation step called correctly. self.compilation_step.assert_called_once_with( - sandbox, fake_compilation_commands( - COMPILATION_COMMAND_1, ["stub.l1", "foo.l1"], "foo")) + sandbox, + fake_compilation_commands( + COMPILATION_COMMAND_1, ["stub.l1", "foo.l1"], "foo" + ), + LANG_1, + ) # Results put in job, executable stored and sandbox deleted. self.assertResultsInJob(job, True, True, TEXT, STATS_OK) sandbox.get_file_to_storage.assert_called_once_with("foo", self.file_cacher, ANY) @@ -227,9 +231,12 @@ def test_many_files_success(self): self.assertEqual(sandbox.create_file_from_storage.call_count, 3) # Compilation step called correctly. self.compilation_step.assert_called_once_with( - sandbox, fake_compilation_commands( - COMPILATION_COMMAND_1, ["stub.l1", "foo.l1", "bar.l1"], - "bar_foo")) + sandbox, + fake_compilation_commands( + COMPILATION_COMMAND_1, ["stub.l1", "foo.l1", "bar.l1"], "bar_foo" + ), + LANG_1, + ) # Results put in job, executable stored and sandbox deleted. self.assertResultsInJob(job, True, True, TEXT, STATS_OK) sandbox.get_file_to_storage.assert_called_once_with("bar_foo", self.file_cacher, ANY) @@ -252,8 +259,10 @@ def test_no_stub_success(self): "foo.l1", "digest of foo.l1", self.file_cacher) # Compilation step called correctly, without the stub. self.compilation_step.assert_called_once_with( - sandbox, fake_compilation_commands( - COMPILATION_COMMAND_1, ["foo.l1"], "foo")) + sandbox, + fake_compilation_commands(COMPILATION_COMMAND_1, ["foo.l1"], "foo"), + LANG_1, + ) # Results put in job, executable stored and sandbox deleted. self.assertResultsInJob(job, True, True, TEXT, STATS_OK) sandbox.get_file_to_storage.assert_called_once_with("foo", self.file_cacher, ANY) @@ -281,8 +290,10 @@ def test_no_stub_but_stub_given_success(self): self.assertEqual(sandbox.create_file_from_storage.call_count, 2) # Compilation step called correctly, without the stub. self.compilation_step.assert_called_once_with( - sandbox, fake_compilation_commands( - COMPILATION_COMMAND_1, ["foo.l1"], "foo")) + sandbox, + fake_compilation_commands(COMPILATION_COMMAND_1, ["foo.l1"], "foo"), + LANG_1, + ) # Results put in job, executable stored and sandbox deleted. self.assertResultsInJob(job, True, True, TEXT, STATS_OK) sandbox.get_file_to_storage.assert_called_once_with("foo", self.file_cacher, ANY) @@ -391,11 +402,11 @@ def test_single_process_success(self): cmdline_usr = ["run1", "foo", "stub", "/fifo0/m_to_u0", "/fifo0/u0_to_m"] self.evaluation_step_before_run.assert_has_calls([ - call(sandbox_mgr, cmdline_mgr, 4321, 1234 * 1024 * 1024, + call(sandbox_mgr, cmdline_mgr, None, 4321, 1234 * 1024 * 1024, dirs_map={os.path.join(self.base_dir, "0"): ("/fifo0", "rw")}, writable_files=["output.txt"], stdin_redirect="input.txt", multiprocess=True), - call(sandbox_usr, cmdline_usr, 2.5, 123 * 1024 * 1024, + call(sandbox_usr, cmdline_usr, LANG_1, 2.5, 123 * 1024 * 1024, dirs_map={os.path.join(self.base_dir, "0"): ("/fifo0", "rw")}, stdin_redirect=None, stdout_redirect=None, @@ -421,7 +432,7 @@ def test_single_process_success_long_time_limit(self): tt.evaluate(job, self.file_cacher) self.evaluation_step_before_run.assert_has_calls([ - call(sandbox_mgr, ANY, 2.5 + 1, ANY, dirs_map=ANY, + call(sandbox_mgr, ANY, None, 2.5 + 1, ANY, dirs_map=ANY, writable_files=ANY, stdin_redirect=ANY, multiprocess=ANY)]) def test_single_process_missing_manager(self): @@ -594,7 +605,7 @@ def test_single_process_std_io(self): # redirects and no command line arguments. cmdline_usr = ["run1", "foo", "stub"] self.evaluation_step_before_run.assert_has_calls([ - call(sandbox_usr, cmdline_usr, ANY, ANY, dirs_map=ANY, + call(sandbox_usr, cmdline_usr, LANG_1, ANY, ANY, dirs_map=ANY, stdin_redirect="/fifo0/m_to_u0", stdout_redirect="/fifo0/u0_to_m", multiprocess=ANY)]) @@ -642,19 +653,19 @@ def test_many_processes_success(self): cmdline_usr1 = ["run1", "foo", "stub", "/fifo1/m_to_u1", "/fifo1/u1_to_m", "1"] self.evaluation_step_before_run.assert_has_calls([ - call(sandbox_mgr, cmdline_mgr, 4321, 1234 * 1024 * 1024, + call(sandbox_mgr, cmdline_mgr, None, 4321, 1234 * 1024 * 1024, dirs_map={ os.path.join(self.base_dir, "0"): ("/fifo0", "rw"), os.path.join(self.base_dir, "1"): ("/fifo1", "rw"), }, writable_files=["output.txt"], stdin_redirect="input.txt", multiprocess=True), - call(sandbox_usr0, cmdline_usr0, 2.5, 123 * 1024 * 1024, + call(sandbox_usr0, cmdline_usr0, LANG_1, 2.5, 123 * 1024 * 1024, dirs_map={os.path.join(self.base_dir, "0"): ("/fifo0", "rw")}, stdin_redirect=None, stdout_redirect=None, multiprocess=True), - call(sandbox_usr1, cmdline_usr1, 2.5, 123 * 1024 * 1024, + call(sandbox_usr1, cmdline_usr1, LANG_1, 2.5, 123 * 1024 * 1024, dirs_map={os.path.join(self.base_dir, "1"): ("/fifo1", "rw")}, stdin_redirect=None, stdout_redirect=None, @@ -683,7 +694,7 @@ def test_many_processes_success_long_time_limit(self): tt.evaluate(job, self.file_cacher) self.evaluation_step_before_run.assert_has_calls([ - call(sandbox_mgr, ANY, 2 * (2.5 + 1), ANY, dirs_map=ANY, + call(sandbox_mgr, ANY, None, 2 * (2.5 + 1), ANY, dirs_map=ANY, writable_files=ANY, stdin_redirect=ANY, multiprocess=ANY)]) def test_many_processes_first_user_failure(self): @@ -778,11 +789,11 @@ def test_many_processes_std_io(self): cmdline_usr0 = ["run1", "foo", "stub", "0"] cmdline_usr1 = ["run1", "foo", "stub", "1"] self.evaluation_step_before_run.assert_has_calls([ - call(sandbox_usr0, cmdline_usr0, ANY, ANY, dirs_map=ANY, + call(sandbox_usr0, cmdline_usr0, LANG_1, ANY, ANY, dirs_map=ANY, stdin_redirect="/fifo0/m_to_u0", stdout_redirect="/fifo0/u0_to_m", multiprocess=ANY), - call(sandbox_usr1, cmdline_usr1, ANY, ANY, dirs_map=ANY, + call(sandbox_usr1, cmdline_usr1, LANG_1, ANY, ANY, dirs_map=ANY, stdin_redirect="/fifo1/m_to_u1", stdout_redirect="/fifo1/u1_to_m", multiprocess=ANY)