diff --git a/cms/grading/Sandbox.py b/cms/grading/Sandbox.py index 07a7205a7a..8248eff302 100644 --- a/cms/grading/Sandbox.py +++ b/cms/grading/Sandbox.py @@ -198,7 +198,7 @@ class Sandbox: def __init__( self, box_index: int, - file_cacher: FileCacher | None, + shard: int | None, name: str | None = None, temp_dir: str | None = None, ): @@ -207,15 +207,13 @@ def __init__( box_index: index of this sandbox within the service that wants to run it. No two boxes with the same `box_index` should exist at the same time. - file_cacher: an instance of the FileCacher class - (to interact with FS), if the sandbox needs it. + shard: the shard index of the service this sandbox runs in, if any. name: name of the sandbox, which might appear in the path and in system logs. temp_dir: temporary directory to use; if None, use the default temporary directory specified in the configuration. """ - self.file_cacher: FileCacher | None = file_cacher self.name: str = name if name is not None else "unnamed" self.temp_dir: str = ( temp_dir if temp_dir is not None else config.global_.temp_dir @@ -226,12 +224,11 @@ def __init__( # the range [0, 1000) for other uses (command-line scripts like cmsMake # or direct console users of isolate). Inside each range ids are # assigned sequentially, with a wrap-around. - if file_cacher is None or file_cacher.service is None: + if shard is None: box_id = box_index else: BOXES_PER_SHARD = 1000 assert box_index < BOXES_PER_SHARD - shard = file_cacher.service.shard # Note that "shard % 64" might hide misconfiguration. # However, since shard numbers are global, there is no good way # to have a number in the [0, num_workers_on_this_machine) range. @@ -510,18 +507,18 @@ def create_file(self, path: str, executable: bool = False) -> typing.BinaryIO: return file_ def create_file_from_storage( - self, path: str, digest: str, executable: bool = False + self, path: str, digest: str, file_cacher: FileCacher, executable: bool = False ): """Write a file taken from FS in the sandbox. path: relative path of the file inside the sandbox. digest: digest of the file in FS. + file_cacher: a FileCacher instance. executable: to set permissions. """ - assert self.file_cacher is not None with self.create_file(path, executable) as dest_fobj: - self.file_cacher.get_file_to_fobj(digest, dest_fobj) + file_cacher.get_file_to_fobj(digest, dest_fobj) def create_file_from_string( self, path: str, content: bytes, executable: bool = False @@ -592,11 +589,16 @@ def get_file_to_string(self, path: str, maxlen: int | None = 1024) -> bytes: return file_.read(maxlen) def get_file_to_storage( - self, path: str, description: str = "", trunc_len: int | None = None + self, + path: str, + file_cacher: FileCacher, + description: str = "", + trunc_len: int | None = None, ) -> str: """Put a sandbox file in FS and return its digest. path: relative path of the file inside the sandbox. + file_cacher: a FileCacher instance. description: the description for FS. trunc_len: if None, does nothing; otherwise, before returning truncate it at the specified length. @@ -604,9 +606,8 @@ def get_file_to_storage( return: the digest of the file. """ - assert self.file_cacher is not None with self.get_file(path, trunc_len=trunc_len) as file_: - return self.file_cacher.put_file_from_fobj(file_, description) + return file_cacher.put_file_from_fobj(file_, description) def stat_file(self, path: str) -> os.stat_result: """Return the stats of a file in the sandbox. diff --git a/cms/grading/steps/trusted.py b/cms/grading/steps/trusted.py index dce3d297ab..946c7093ac 100644 --- a/cms/grading/steps/trusted.py +++ b/cms/grading/steps/trusted.py @@ -34,6 +34,7 @@ "wrong". """ +from cms.db.filecacher import FileCacher import logging import re @@ -209,6 +210,7 @@ def trusted_step( def checker_step( sandbox: Sandbox, + file_cacher: FileCacher, checker_digest: str | None, input_digest: str, correct_output_digest: str, @@ -220,6 +222,7 @@ def checker_step( sandbox: the sandbox to run the checker in; should already contain input, correct output, and user output; the checker is instead copied from the managers. + file_cacher: the file cacher to use to load the checker. checker_digest: digest of the checker, will be fetched as "checker"; if None, an appropriate error for bad configuration of the task will be generated. @@ -250,12 +253,12 @@ def checker_step( logger.error("Configuration error: missing checker in task managers.") return False, None, None, None sandbox.create_file_from_storage(CHECKER_FILENAME, checker_digest, - executable=True) + file_cacher, executable=True) # Copy input and correct output in the sandbox. - sandbox.create_file_from_storage(CHECKER_INPUT_FILENAME, input_digest) + sandbox.create_file_from_storage(CHECKER_INPUT_FILENAME, input_digest, file_cacher) sandbox.create_file_from_storage(CHECKER_CORRECT_OUTPUT_FILENAME, - correct_output_digest) + correct_output_digest, file_cacher) # Execute the checker and ensure success, or log an error. command = ["./%s" % CHECKER_FILENAME, diff --git a/cms/grading/tasktypes/Batch.py b/cms/grading/tasktypes/Batch.py index d3403c1e09..9ccbd97539 100644 --- a/cms/grading/tasktypes/Batch.py +++ b/cms/grading/tasktypes/Batch.py @@ -238,7 +238,7 @@ def _do_compile(self, job, file_cacher): # Copy required files in the sandbox (includes the grader if present). for filename, digest in filenames_and_digests_to_get.items(): - sandbox.create_file_from_storage(filename, digest) + sandbox.create_file_from_storage(filename, digest, file_cacher) # Run the compilation. box_success, compilation_success, text, stats = \ @@ -252,6 +252,7 @@ def _do_compile(self, job, file_cacher): if box_success and compilation_success: digest = sandbox.get_file_to_storage( executable_filename, + file_cacher, "Executable %s for %s" % (executable_filename, job.info)) job.executables[executable_filename] = \ Executable(executable_filename, digest) @@ -300,9 +301,9 @@ def _execution_step(self, job, file_cacher): # Put the required files into the sandbox for filename, digest in executables_to_get.items(): - sandbox.create_file_from_storage(filename, digest, executable=True) + sandbox.create_file_from_storage(filename, digest, file_cacher, executable=True) for filename, digest in files_to_get.items(): - sandbox.create_file_from_storage(filename, digest) + sandbox.create_file_from_storage(filename, digest, file_cacher) # Actually performs the execution box_success, evaluation_success, stats = evaluation_step( @@ -346,6 +347,7 @@ def _execution_step(self, job, file_cacher): if job.get_output: job.user_output = sandbox.get_file_to_storage( self._actual_output, + file_cacher, "Output file in job %s" % job.info, trunc_len=100 * 1024) diff --git a/cms/grading/tasktypes/Communication.py b/cms/grading/tasktypes/Communication.py index 9effe91008..b2d3dd9a83 100644 --- a/cms/grading/tasktypes/Communication.py +++ b/cms/grading/tasktypes/Communication.py @@ -223,7 +223,7 @@ def compile(self, job, file_cacher): # Copy all required files in the sandbox. for filename, digest in filenames_and_digests_to_get.items(): - sandbox.create_file_from_storage(filename, digest) + sandbox.create_file_from_storage(filename, digest, file_cacher) # Run the compilation. box_success, compilation_success, text, stats = \ @@ -237,6 +237,7 @@ def compile(self, job, file_cacher): if box_success and compilation_success: digest = sandbox.get_file_to_storage( executable_filename, + file_cacher, "Executable %s for %s" % (executable_filename, job.info)) job.executables[executable_filename] = \ Executable(executable_filename, digest) @@ -282,9 +283,9 @@ def evaluate(self, job, file_cacher): sandbox_mgr = create_sandbox(0, file_cacher, name="manager_evaluate") job.sandboxes.append(sandbox_mgr.get_root_path()) sandbox_mgr.create_file_from_storage( - self.MANAGER_FILENAME, manager_digest, executable=True) + self.MANAGER_FILENAME, manager_digest, file_cacher, executable=True) sandbox_mgr.create_file_from_storage( - self.INPUT_FILENAME, job.input) + self.INPUT_FILENAME, job.input, file_cacher) # Create the user sandbox(es) and copy the executable. sandbox_user = [ @@ -293,7 +294,7 @@ def evaluate(self, job, file_cacher): job.sandboxes.extend(s.get_root_path() for s in sandbox_user) for i in indices: sandbox_user[i].create_file_from_storage( - executable_filename, executable_digest, executable=True) + executable_filename, executable_digest, file_cacher, executable=True) # Start the manager. Redirecting to stdin is unnecessary, but for # historical reasons the manager can choose to read from there @@ -425,6 +426,7 @@ def evaluate(self, job, file_cacher): if sandbox_mgr.file_exists(self.OUTPUT_FILENAME): job.user_output = sandbox_mgr.get_file_to_storage( self.OUTPUT_FILENAME, + file_cacher, "Output file in job %s" % job.info, trunc_len=100 * 1024) else: diff --git a/cms/grading/tasktypes/TwoSteps.py b/cms/grading/tasktypes/TwoSteps.py index cea2b43df3..b658d2e74f 100644 --- a/cms/grading/tasktypes/TwoSteps.py +++ b/cms/grading/tasktypes/TwoSteps.py @@ -190,7 +190,7 @@ def compile(self, job, file_cacher): job.sandboxes.append(sandbox.get_root_path()) for filename, digest in files_to_get.items(): - sandbox.create_file_from_storage(filename, digest) + sandbox.create_file_from_storage(filename, digest, file_cacher) # Run the compilation. box_success, compilation_success, text, stats = \ @@ -204,6 +204,7 @@ def compile(self, job, file_cacher): if box_success and compilation_success: digest = sandbox.get_file_to_storage( executable_filename, + file_cacher, "Executable %s for %s" % (executable_filename, job.info)) job.executables[executable_filename] = \ @@ -242,9 +243,10 @@ def evaluate(self, job, file_cacher): for filename, digest in first_executables_to_get.items(): first_sandbox.create_file_from_storage(filename, digest, + file_cacher, executable=True) for filename, digest in first_files_to_get.items(): - first_sandbox.create_file_from_storage(filename, digest) + first_sandbox.create_file_from_storage(filename, digest, file_cacher) first = evaluation_step_before_run( first_sandbox, @@ -265,9 +267,10 @@ def evaluate(self, job, file_cacher): for filename, digest in second_executables_to_get.items(): second_sandbox.create_file_from_storage(filename, digest, + file_cacher, executable=True) for filename, digest in second_files_to_get.items(): - second_sandbox.create_file_from_storage(filename, digest) + second_sandbox.create_file_from_storage(filename, digest, file_cacher) second = evaluation_step_before_run( second_sandbox, @@ -324,6 +327,7 @@ def evaluate(self, job, file_cacher): if job.get_output: job.user_output = second_sandbox.get_file_to_storage( TwoSteps.OUTPUT_FILENAME, + file_cacher, "Output file in job %s" % job.info, trunc_len=100 * 1024) diff --git a/cms/grading/tasktypes/util.py b/cms/grading/tasktypes/util.py index 2c0148f997..e3825c6134 100644 --- a/cms/grading/tasktypes/util.py +++ b/cms/grading/tasktypes/util.py @@ -62,7 +62,8 @@ def create_sandbox(box_index: int, file_cacher: FileCacher, name: str | None = N """ try: - sandbox = Sandbox(box_index, file_cacher, name=name) + shard = file_cacher.service.shard if file_cacher.service is not None else None + sandbox = Sandbox(box_index, shard, name=name) except OSError: err_msg = "Couldn't create sandbox." logger.error(err_msg, exc_info=True) @@ -272,12 +273,13 @@ def eval_output( # function, but the type checker isn't smart enough for that assert user_output_digest is not None sandbox.create_file_from_storage(EVAL_USER_OUTPUT_FILENAME, - user_output_digest) + user_output_digest, + file_cacher) checker_digest = job.managers[checker_codename].digest \ if checker_codename in job.managers else None success, outcome, text, admin_text = checker_step( - sandbox, checker_digest, job.input, job.output, + sandbox, file_cacher, checker_digest, job.input, job.output, EVAL_USER_OUTPUT_FILENAME, extra_args) delete_sandbox(sandbox, job, success) diff --git a/cmstestsuite/unit_tests/grading/steps/fakesandbox.py b/cmstestsuite/unit_tests/grading/steps/fakesandbox.py index df3b9dd316..b11b0468cc 100644 --- a/cmstestsuite/unit_tests/grading/steps/fakesandbox.py +++ b/cmstestsuite/unit_tests/grading/steps/fakesandbox.py @@ -34,8 +34,8 @@ class FakeSandbox(Sandbox): answer get_file or get_file_to_string. """ - def __init__(self, file_cacher, name=None, temp_dir=None): - super().__init__(0, file_cacher, name, temp_dir) + def __init__(self, shard, name=None, temp_dir=None): + super().__init__(0, shard, name, temp_dir) self._fake_files = {} self._fake_execute_data = deque() diff --git a/cmstestsuite/unit_tests/grading/steps/trusted_test.py b/cmstestsuite/unit_tests/grading/steps/trusted_test.py index 34eac1d635..22fc507d7c 100755 --- a/cmstestsuite/unit_tests/grading/steps/trusted_test.py +++ b/cmstestsuite/unit_tests/grading/steps/trusted_test.py @@ -237,7 +237,7 @@ def setUp(self): super().setUp() # By default, any file request succeeds. self.file_cacher = MagicMock() - self.sandbox = FakeSandbox(self.file_cacher) + self.sandbox = FakeSandbox(None) patcher = patch("cms.grading.steps.trusted.trusted_step") self.addCleanup(patcher.stop) @@ -266,7 +266,7 @@ def test_success(self): self.mock_trusted_step.return_value = (True, True, {}) self.set_checker_output(b"0.123\n", b"Text.\n") - ret = checker_step(self.sandbox, "c_dig", "i_dig", "co_dig", "o") + ret = checker_step(self.sandbox, self.file_cacher, "c_dig", "i_dig", "co_dig", "o") self.assertEqual(ret, (True, 0.123, ["Text."], None)) self.file_cacher.get_file_to_fobj.assert_has_calls([ @@ -284,7 +284,7 @@ def test_sandbox_failure(self): # Output files are ignored. self.set_checker_output(b"0.123\n", b"Text.\n") - ret = checker_step(self.sandbox, "c_dig", "i_dig", "co_dig", "o") + ret = checker_step(self.sandbox, self.file_cacher, "c_dig", "i_dig", "co_dig", "o") self.assertEqual(ret, (False, None, None, None)) self.assertLoggedError() @@ -294,13 +294,13 @@ def test_checker_failure(self): # Output files are ignored. self.set_checker_output(b"0.123\n", b"Text.\n") - ret = checker_step(self.sandbox, "c_dig", "i_dig", "co_dig", "o") + ret = checker_step(self.sandbox, self.file_cacher, "c_dig", "i_dig", "co_dig", "o") self.assertEqual(ret, (False, None, None, None)) self.assertLoggedError() def test_missing_checker(self): - ret = checker_step(self.sandbox, None, "i_dig", "co_dig", "o") + ret = checker_step(self.sandbox, self.file_cacher, None, "i_dig", "co_dig", "o") self.mock_trusted_step.assert_not_called() self.assertEqual(ret, (False, None, None, None)) @@ -308,14 +308,14 @@ def test_missing_checker(self): def test_checker_already_in_sandbox(self): self.sandbox.fake_file(trusted.CHECKER_FILENAME, b"something") - ret = checker_step(self.sandbox, "c_dig", "i_dig", "co_dig", "o") + ret = checker_step(self.sandbox, self.file_cacher, "c_dig", "i_dig", "co_dig", "o") self.assertEqual(ret, (False, None, None, None)) self.assertLoggedError() def test_input_already_in_sandbox(self): self.sandbox.fake_file(trusted.CHECKER_INPUT_FILENAME, b"something") - ret = checker_step(self.sandbox, "c_dig", "i_dig", "co_dig", "o") + ret = checker_step(self.sandbox, self.file_cacher, "c_dig", "i_dig", "co_dig", "o") self.assertEqual(ret, (False, None, None, None)) self.assertLoggedError() @@ -323,7 +323,7 @@ def test_input_already_in_sandbox(self): def test_correct_output_already_in_sandbox(self): self.sandbox.fake_file(trusted.CHECKER_CORRECT_OUTPUT_FILENAME, b"something") - ret = checker_step(self.sandbox, "c_dig", "i_dig", "co_dig", "o") + ret = checker_step(self.sandbox, self.file_cacher, "c_dig", "i_dig", "co_dig", "o") self.assertEqual(ret, (False, None, None, None)) self.assertLoggedError() @@ -332,7 +332,7 @@ def test_invalid_checker_outcome(self): self.mock_trusted_step.return_value = (True, True, {}) self.set_checker_output(b"A0.123\n", b"Text.\n") - ret = checker_step(self.sandbox, "c_dig", "i_dig", "co_dig", "o") + ret = checker_step(self.sandbox, self.file_cacher, "c_dig", "i_dig", "co_dig", "o") self.assertEqual(ret, (False, None, None, None)) self.assertLoggedError() @@ -341,7 +341,7 @@ def test_invalid_checker_text(self): self.mock_trusted_step.return_value = (True, True, {}) self.set_checker_output(b"0.123\n", INVALID_UTF8) - ret = checker_step(self.sandbox, "c_dig", "i_dig", "co_dig", "o") + ret = checker_step(self.sandbox, self.file_cacher, "c_dig", "i_dig", "co_dig", "o") self.assertEqual(ret, (False, None, None, None)) self.assertLoggedError() @@ -350,7 +350,7 @@ def test_missing_checker_outcome(self): self.mock_trusted_step.return_value = (True, True, {}) self.set_checker_output(None, b"Text.\n") - ret = checker_step(self.sandbox, "c_dig", "i_dig", "co_dig", "o") + ret = checker_step(self.sandbox, self.file_cacher, "c_dig", "i_dig", "co_dig", "o") self.assertEqual(ret, (False, None, None, None)) self.assertLoggedError() @@ -359,7 +359,7 @@ def test_missing_checker_text(self): self.mock_trusted_step.return_value = (True, True, {}) self.set_checker_output(b"0.123\n", None) - ret = checker_step(self.sandbox, "c_dig", "i_dig", "co_dig", "o") + ret = checker_step(self.sandbox, self.file_cacher, "c_dig", "i_dig", "co_dig", "o") self.assertEqual(ret, (False, None, None, None)) self.assertLoggedError() diff --git a/cmstestsuite/unit_tests/grading/tasktypes/BatchTest.py b/cmstestsuite/unit_tests/grading/tasktypes/BatchTest.py index 576647baf9..a52c7ec4a8 100755 --- a/cmstestsuite/unit_tests/grading/tasktypes/BatchTest.py +++ b/cmstestsuite/unit_tests/grading/tasktypes/BatchTest.py @@ -145,10 +145,10 @@ def test_alone_success(self): tt.compile(job, self.file_cacher) # Sandbox created with the correct file cacher and name. - self.Sandbox.assert_called_once_with(0, self.file_cacher, name="compile") + self.Sandbox.assert_called_once_with(0, self.file_cacher.service.shard, name="compile") # For alone, we only need the user source file. sandbox.create_file_from_storage.assert_has_calls( - [call("foo.l1", "digest of foo.l1")], any_order=True) + [call("foo.l1", "digest of foo.l1", self.file_cacher)], any_order=True) self.assertEqual(sandbox.create_file_from_storage.call_count, 1) # Compilation step called correctly. self.compilation_step.assert_called_once_with( @@ -156,7 +156,7 @@ def test_alone_success(self): COMPILATION_COMMAND_1, ["foo.l1"], "foo")) # Results put in job, executable stored and sandbox deleted. self.assertResultsInJob(job) - sandbox.get_file_to_storage.assert_called_once_with("foo", ANY) + sandbox.get_file_to_storage.assert_called_once_with("foo", self.file_cacher, ANY) sandbox.cleanup.assert_called_once_with(delete=True) def test_alone_failure_missing_file(self): @@ -180,11 +180,11 @@ def test_alone_success_two_files(self): tt.compile(job, self.file_cacher) # Sandbox created with the correct file cacher and name. - self.Sandbox.assert_called_once_with(0, self.file_cacher, name="compile") + self.Sandbox.assert_called_once_with(0, self.file_cacher.service.shard, name="compile") # For alone, we only need the user source file. sandbox.create_file_from_storage.assert_has_calls( - [call("foo.l1", "digest of foo.l1"), - call("bar.l1", "digest of bar.l1")], any_order=True) + [call("foo.l1", "digest of foo.l1", self.file_cacher), + call("bar.l1", "digest of bar.l1", self.file_cacher)], any_order=True) self.assertEqual(sandbox.create_file_from_storage.call_count, 2) # Compilation step called correctly. self.compilation_step.assert_called_once_with( @@ -192,7 +192,7 @@ def test_alone_success_two_files(self): COMPILATION_COMMAND_1, ["foo.l1", "bar.l1"], "bar_foo")) # Results put in job, executable stored and sandbox deleted. self.assertResultsInJob(job) - sandbox.get_file_to_storage.assert_called_once_with("bar_foo", ANY) + sandbox.get_file_to_storage.assert_called_once_with("bar_foo", self.file_cacher, ANY) sandbox.cleanup.assert_called_once_with(delete=True) def test_alone_compilation_failure(self): @@ -241,13 +241,13 @@ def test_grader_success(self): tt.compile(job, self.file_cacher) # Sandbox created with the correct file cacher and name. - self.Sandbox.assert_called_once_with(0, self.file_cacher, name="compile") + self.Sandbox.assert_called_once_with(0, self.file_cacher.service.shard, name="compile") # For grader we need the user source, the grader, and any other # relevant manager (in this case, the header). sandbox.create_file_from_storage.assert_has_calls([ - call("foo.l1", "digest of foo.l1"), - call("grader.l1", "digest of grader.l1"), - call("grader.hl1", "digest of grader.hl1"), + call("foo.l1", "digest of foo.l1", self.file_cacher), + call("grader.l1", "digest of grader.l1", self.file_cacher), + call("grader.hl1", "digest of grader.hl1", self.file_cacher), ], any_order=True) self.assertEqual(sandbox.create_file_from_storage.call_count, 3) # Compilation step called correctly. @@ -256,7 +256,7 @@ def test_grader_success(self): COMPILATION_COMMAND_1, ["foo.l1", "grader.l1"], "foo")) # Results put in job, executable stored and sandbox deleted. self.assertResultsInJob(job) - sandbox.get_file_to_storage.assert_called_once_with("foo", ANY) + sandbox.get_file_to_storage.assert_called_once_with("foo", self.file_cacher, ANY) sandbox.cleanup.assert_called_once_with(delete=True) def test_grader_failure_missing_grader(self): @@ -341,12 +341,12 @@ def test_stdio_diff_success(self): tt.evaluate(job, self.file_cacher) # Sandbox created with the correct file cacher and name. - self.Sandbox.assert_called_once_with(0, self.file_cacher, name="evaluate") + self.Sandbox.assert_called_once_with(0, self.file_cacher.service.shard, name="evaluate") # We need input (with the default filename for redirection) and # executable copied in the sandbox. sandbox.create_file_from_storage.assert_has_calls([ - call("foo", "digest of foo", executable=True), - call("input.txt", "digest of input"), + call("foo", "digest of foo", self.file_cacher, executable=True), + call("input.txt", "digest of input", self.file_cacher), ], any_order=True) self.assertEqual(sandbox.create_file_from_storage.call_count, 2) # Evaluation step called with the right arguments, in particular @@ -437,7 +437,7 @@ def test_stdio_diff_get_output_success(self): # With get_output, submission is run, output is eval'd, and in addition # we store (a truncation of) the user output. sandbox.get_file_to_storage.assert_called_once_with( - "output.txt", ANY, trunc_len=ANY) + "output.txt", self.file_cacher, ANY, trunc_len=ANY) self.assertEqual(job.user_output, "digest of output.txt") self.evaluation_step.assert_called_once() self.eval_output.assert_called_once() @@ -462,12 +462,12 @@ def test_fileio_diff_success(self): tt.evaluate(job, self.file_cacher) # Sandbox created with the correct file cacher and name. - self.Sandbox.assert_called_once_with(0, self.file_cacher, name="evaluate") + self.Sandbox.assert_called_once_with(0, self.file_cacher.service.shard, name="evaluate") # We need input (with the filename specified in the parameters) and # executable copied in the sandbox. sandbox.create_file_from_storage.assert_has_calls([ - call("foo", "digest of foo", executable=True), - call("myin", "digest of input"), + call("foo", "digest of foo", self.file_cacher, executable=True), + call("myin", "digest of input", self.file_cacher), ], any_order=True) self.assertEqual(sandbox.create_file_from_storage.call_count, 2) # Evaluation step called with the right arguments, in particular diff --git a/cmstestsuite/unit_tests/grading/tasktypes/CommunicationTest.py b/cmstestsuite/unit_tests/grading/tasktypes/CommunicationTest.py index 54ef11bce2..a810564e8b 100755 --- a/cmstestsuite/unit_tests/grading/tasktypes/CommunicationTest.py +++ b/cmstestsuite/unit_tests/grading/tasktypes/CommunicationTest.py @@ -159,11 +159,11 @@ def test_one_file_success(self): tt.compile(job, self.file_cacher) # Sandbox created with the correct file cacher and name. - self.Sandbox.assert_called_once_with(0, self.file_cacher, name="compile") + self.Sandbox.assert_called_once_with(0, self.file_cacher.service.shard, name="compile") # We need all user source files, and the stub for the same language. sandbox.create_file_from_storage.assert_has_calls( - [call("foo.l1", "digest of foo.l1"), - call("stub.l1", "digest of stub.l1")], any_order=True) + [call("foo.l1", "digest of foo.l1", self.file_cacher), + call("stub.l1", "digest of stub.l1", self.file_cacher)], any_order=True) self.assertEqual(sandbox.create_file_from_storage.call_count, 2) # Compilation step called correctly. self.compilation_step.assert_called_once_with( @@ -171,7 +171,7 @@ def test_one_file_success(self): COMPILATION_COMMAND_1, ["stub.l1", "foo.l1"], "foo")) # 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", ANY) + sandbox.get_file_to_storage.assert_called_once_with("foo", self.file_cacher, ANY) sandbox.cleanup.assert_called_once_with(delete=True) def test_one_file_compilation_failure(self): @@ -217,13 +217,13 @@ def test_many_files_success(self): # Sandbox created with the correct file cacher and name. self.Sandbox.assert_called_once_with(0, - self.file_cacher, + self.file_cacher.service.shard, name="compile") # We need all user source files in addition to the stub. sandbox.create_file_from_storage.assert_has_calls( - [call("foo.l1", "digest of foo.l1"), - call("bar.l1", "digest of bar.l1"), - call("stub.l1", "digest of stub.l1")], any_order=True) + [call("foo.l1", "digest of foo.l1", self.file_cacher), + call("bar.l1", "digest of bar.l1", self.file_cacher), + call("stub.l1", "digest of stub.l1", self.file_cacher)], any_order=True) self.assertEqual(sandbox.create_file_from_storage.call_count, 3) # Compilation step called correctly. self.compilation_step.assert_called_once_with( @@ -232,7 +232,7 @@ def test_many_files_success(self): "bar_foo")) # 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", ANY) + sandbox.get_file_to_storage.assert_called_once_with("bar_foo", self.file_cacher, ANY) sandbox.cleanup.assert_called_once_with(delete=True) def test_no_stub_success(self): @@ -246,17 +246,17 @@ def test_no_stub_success(self): # Sandbox created with the correct file cacher and name. self.Sandbox.assert_called_once_with(0, - self.file_cacher, + self.file_cacher.service.shard, name="compile") sandbox.create_file_from_storage.assert_called_once_with( - "foo.l1", "digest of foo.l1") + "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")) # 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", ANY) + sandbox.get_file_to_storage.assert_called_once_with("foo", self.file_cacher, ANY) sandbox.cleanup.assert_called_once_with(delete=True) def test_no_stub_but_stub_given_success(self): @@ -271,13 +271,13 @@ def test_no_stub_but_stub_given_success(self): # Sandbox created with the correct file cacher and name. self.Sandbox.assert_called_once_with(0, - self.file_cacher, + self.file_cacher.service.shard, name="compile") # The stub is put in the sandbox because it is a manager with an # extension that hints that it could be useful for compilations. sandbox.create_file_from_storage.assert_has_calls( - [call("foo.l1", "digest of foo.l1"), - call("stub.l1", "digest of stub.l1")], any_order=True) + [call("foo.l1", "digest of foo.l1", self.file_cacher), + call("stub.l1", "digest of stub.l1", self.file_cacher)], any_order=True) self.assertEqual(sandbox.create_file_from_storage.call_count, 2) # Compilation step called correctly, without the stub. self.compilation_step.assert_called_once_with( @@ -285,7 +285,7 @@ def test_no_stub_but_stub_given_success(self): COMPILATION_COMMAND_1, ["foo.l1"], "foo")) # 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", ANY) + sandbox.get_file_to_storage.assert_called_once_with("foo", self.file_cacher, ANY) sandbox.cleanup.assert_called_once_with(delete=True) @@ -368,19 +368,19 @@ def test_single_process_success(self): # Sandboxes created with the correct file cacher and names. self.Sandbox.assert_has_calls([ - call(0, self.file_cacher, name="manager_evaluate"), - call(1, self.file_cacher, name="user_evaluate_0"), + call(0, self.file_cacher.service.shard, name="manager_evaluate"), + call(1, self.file_cacher.service.shard, name="user_evaluate_0"), ], any_order=False) self.assertEqual(self.Sandbox.call_count, 2) # We need input (with the default filename for redirection) and # executable copied in the sandbox. sandbox_mgr.create_file_from_storage.assert_has_calls([ - call("manager", "digest of manager", executable=True), - call("input.txt", "digest of input"), + call("manager", "digest of manager", self.file_cacher, executable=True), + call("input.txt", "digest of input", self.file_cacher), ], any_order=True) self.assertEqual(sandbox_mgr.create_file_from_storage.call_count, 2) sandbox_usr.create_file_from_storage.assert_has_calls([ - call("foo", "digest of foo", executable=True), + call("foo", "digest of foo", self.file_cacher, executable=True), ], any_order=True) self.assertEqual(sandbox_usr.create_file_from_storage.call_count, 1) # Evaluation step called with the right arguments, in particular @@ -561,7 +561,7 @@ def test_single_process_get_output_success(self): # With get_output, submission is run, output is eval'd, and in addition # we store (a truncation of) the user output. sandbox_mgr.get_file_to_storage.assert_called_once_with( - "output.txt", ANY, trunc_len=ANY) + "output.txt", self.file_cacher, ANY, trunc_len=ANY) self.assertEqual(job.user_output, "digest of output.txt") self.evaluation_step_after_run.assert_called() self.extract_outcome_and_text.assert_called_once() @@ -613,22 +613,22 @@ def test_many_processes_success(self): # Sandboxes created with the correct file cacher and names. self.Sandbox.assert_has_calls([ - call(0, self.file_cacher, name="manager_evaluate"), - call(1, self.file_cacher, name="user_evaluate_0"), - call(2, self.file_cacher, name="user_evaluate_1"), + call(0, self.file_cacher.service.shard, name="manager_evaluate"), + call(1, self.file_cacher.service.shard, name="user_evaluate_0"), + call(2, self.file_cacher.service.shard, name="user_evaluate_1"), ], any_order=False) self.assertEqual(self.Sandbox.call_count, 3) # We need input (with the default filename for redirection) and # executable copied in the sandbox. sandbox_mgr.create_file_from_storage.assert_has_calls([ - call("manager", "digest of manager", executable=True), - call("input.txt", "digest of input"), + call("manager", "digest of manager", self.file_cacher, executable=True), + call("input.txt", "digest of input", self.file_cacher), ], any_order=True) self.assertEqual(sandbox_mgr.create_file_from_storage.call_count, 2) # Same content in both user sandboxes. for s in [sandbox_usr0, sandbox_usr1]: s.create_file_from_storage.assert_has_calls([ - call("foo", "digest of foo", executable=True), + call("foo", "digest of foo", self.file_cacher, executable=True), ], any_order=True) self.assertEqual(s.create_file_from_storage.call_count, 1) # Evaluation step called with the right arguments, in particular