From 0d7009448c6c9ab0d8e2350ff90eac60b15807e5 Mon Sep 17 00:00:00 2001 From: Mateusz Masiarz Date: Thu, 21 Sep 2023 20:36:19 +0200 Subject: [PATCH 1/5] Change behaviour of --solutions flag --- src/sinol_make/commands/run/__init__.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sinol_make/commands/run/__init__.py b/src/sinol_make/commands/run/__init__.py index b82b4d82..73423175 100644 --- a/src/sinol_make/commands/run/__init__.py +++ b/src/sinol_make/commands/run/__init__.py @@ -313,11 +313,23 @@ def get_solutions(self, args_solutions): return sorted(solutions, key=self.get_executable_key) else: solutions = [] + files_to_check = [] for solution in args_solutions: + if os.path.isabs(solution): + files_to_check.append(solution) + else: + # If solution already has `prog/` prefix: + files_to_check.extend(glob.glob(os.path.join(os.getcwd(), solution))) + # If solution does not have `prog/` prefix: + files_to_check.extend(glob.glob(os.path.join(os.getcwd(), "prog", solution))) + + for solution in files_to_check: if not os.path.isfile(solution): util.exit_with_error("Solution %s does not exist" % solution) if self.SOLUTIONS_RE.match(os.path.basename(solution)) is not None: solutions.append(os.path.basename(solution)) + + solutions = list(set(solutions)) return sorted(solutions, key=self.get_executable_key) From bd571620d3f20af0d99ecb4664a3900c9160a444 Mon Sep 17 00:00:00 2001 From: Mateusz Masiarz Date: Thu, 21 Sep 2023 20:36:30 +0200 Subject: [PATCH 2/5] Add tests --- tests/commands/run/test_integration.py | 27 ++++++++++++++++++++++++++ tests/commands/run/test_unit.py | 26 +++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/tests/commands/run/test_integration.py b/tests/commands/run/test_integration.py index c9837a8c..826971a8 100644 --- a/tests/commands/run/test_integration.py +++ b/tests/commands/run/test_integration.py @@ -233,6 +233,33 @@ def test_flag_solutions(capsys, create_package, time_tool): assert os.path.basename(solutions[1]) not in out +@pytest.mark.parametrize("create_package", [get_simple_package_path(), get_verify_status_package_path(), + get_checker_package_path()], indirect=True) +def test_flag_solutions_multiple(capsys, create_package, time_tool): + """ + Test flag --solutions with multiple solutions. + """ + package_path = create_package + create_ins_outs(package_path) + + task_id = package_util.get_task_id() + solutions = [ + os.path.basename(file) + for file in package_util.get_files_matching_pattern(task_id, f'{task_id}?.*') + ] + parser = configure_parsers() + args = parser.parse_args(["run", "--solutions", solutions[0], os.path.join("prog", solutions[1]), + "--time-tool", time_tool]) + command = Command() + command.run(args) + + out = capsys.readouterr().out + + assert os.path.basename(solutions[0]) in out + assert os.path.basename(solutions[1]) in out + assert os.path.basename(solutions[2]) not in out + + @pytest.mark.parametrize("create_package", [get_weak_compilation_flags_package_path()], indirect=True) def test_weak_compilation_flags(create_package): """ diff --git a/tests/commands/run/test_unit.py b/tests/commands/run/test_unit.py index eb1802a2..b1a8bfc2 100644 --- a/tests/commands/run/test_unit.py +++ b/tests/commands/run/test_unit.py @@ -25,6 +25,32 @@ def test_get_solutions(): assert solutions == ["abc.cpp"] assert "abc1.cpp" not in solutions + with tempfile.TemporaryDirectory() as tmpdir: + def create_file(name): + with open(os.path.join(tmpdir, "prog", name), "w") as f: + f.write("") + + os.chdir(tmpdir) + os.mkdir("prog") + + create_file("abc.cpp") + create_file("abc1.cpp") + create_file("abc2.cpp") + create_file("abcs1.cpp") + create_file("abcs2.cpp") + + assert command.get_solutions(None) == ["abc.cpp", "abc1.cpp", "abc2.cpp", "abcs1.cpp", "abcs2.cpp"] + assert command.get_solutions(["prog/abc.cpp"]) == ["abc.cpp"] + assert command.get_solutions(["abc.cpp"]) == ["abc.cpp"] + assert command.get_solutions([os.path.join(tmpdir, "prog", "abc.cpp")]) == ["abc.cpp"] + assert command.get_solutions(["prog/abc?.cpp"]) == ["abc1.cpp", "abc2.cpp"] + assert command.get_solutions(["abc?.cpp"]) == ["abc1.cpp", "abc2.cpp"] + assert command.get_solutions(["prog/abc*.cpp"]) == ["abc.cpp", "abc1.cpp", "abc2.cpp", "abcs1.cpp", "abcs2.cpp"] + assert command.get_solutions(["abc*.cpp"]) == ["abc.cpp", "abc1.cpp", "abc2.cpp", "abcs1.cpp", "abcs2.cpp"] + assert command.get_solutions(["prog/abc.cpp", "abc1.cpp"]) == ["abc.cpp", "abc1.cpp"] + assert command.get_solutions(["prog/abc.cpp", "abc?.cpp"]) == ["abc.cpp", "abc1.cpp", "abc2.cpp"] + assert command.get_solutions(["abc.cpp", "abc2.cpp", "abcs2.cpp"]) == ["abc.cpp", "abc2.cpp", "abcs2.cpp"] + def test_get_executable_key(): os.chdir(get_simple_package_path()) From 4c7f079f1c3941fa88f7f12b12c76e39fce10042 Mon Sep 17 00:00:00 2001 From: Mateusz Masiarz Date: Thu, 21 Sep 2023 21:03:35 +0200 Subject: [PATCH 3/5] Move function for getting solutions to `package_util`, change getting tests --- src/sinol_make/commands/run/__init__.py | 52 ++--------------- src/sinol_make/helpers/package_util.py | 78 +++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 53 deletions(-) diff --git a/src/sinol_make/commands/run/__init__.py b/src/sinol_make/commands/run/__init__.py index 73423175..a145c0dd 100644 --- a/src/sinol_make/commands/run/__init__.py +++ b/src/sinol_make/commands/run/__init__.py @@ -282,22 +282,6 @@ def get_group(self, test_path): return int("".join(filter(str.isdigit, package_util.extract_test_id(test_path, self.ID)))) - def get_executable_key(self, executable): - name = package_util.get_file_name(executable) - value = [0, 0] - if name[3] == 's': - value[0] = 1 - suffix = name.split(".")[0][4:] - elif name[3] == 'b': - value[0] = 2 - suffix = name.split(".")[0][4:] - else: - suffix = name.split(".")[0][3:] - if suffix != "": - value[1] = int(suffix) - return tuple(value) - - def get_solution_from_exe(self, executable): file = os.path.splitext(executable)[0] for ext in self.SOURCE_EXTENSIONS: @@ -305,36 +289,8 @@ def get_solution_from_exe(self, executable): return file + ext util.exit_with_error("Source file not found for executable %s" % executable) - - def get_solutions(self, args_solutions): - if args_solutions is None: - solutions = [solution for solution in os.listdir("prog/") - if self.SOLUTIONS_RE.match(solution)] - return sorted(solutions, key=self.get_executable_key) - else: - solutions = [] - files_to_check = [] - for solution in args_solutions: - if os.path.isabs(solution): - files_to_check.append(solution) - else: - # If solution already has `prog/` prefix: - files_to_check.extend(glob.glob(os.path.join(os.getcwd(), solution))) - # If solution does not have `prog/` prefix: - files_to_check.extend(glob.glob(os.path.join(os.getcwd(), "prog", solution))) - - for solution in files_to_check: - if not os.path.isfile(solution): - util.exit_with_error("Solution %s does not exist" % solution) - if self.SOLUTIONS_RE.match(os.path.basename(solution)) is not None: - solutions.append(os.path.basename(solution)) - - solutions = list(set(solutions)) - return sorted(solutions, key=self.get_executable_key) - - def get_executables(self, args_solutions): - return [package_util.get_executable(solution) for solution in self.get_solutions(args_solutions)] + return [package_util.get_executable(solution) for solution in package_util.get_solutions(self.ID, args_solutions)] def get_possible_score(self, groups): @@ -671,7 +627,7 @@ def run_solutions(self, compiled_commands, names, solutions): for test in self.tests: all_results[name][self.get_group(test)][test] = ExecutionResult(Status.CE) print() - executions.sort(key = lambda x: (self.get_executable_key(x[1]), x[2])) + executions.sort(key = lambda x: (package_util.get_executable_key(x[1]), x[2])) program_groups_scores = collections.defaultdict(dict) print_data = PrintData(0) @@ -1016,7 +972,7 @@ def set_constants(self): def validate_arguments(self, args): - compilers = compiler.verify_compilers(args, self.get_solutions(None)) + compilers = compiler.verify_compilers(args, package_util.get_solutions(self.ID, None)) def use_oiejq(): timetool_path = None @@ -1217,7 +1173,7 @@ def run(self, args): self.check_are_any_tests_to_run() self.set_scores() self.failed_compilations = [] - solutions = self.get_solutions(self.args.solutions) + solutions = package_util.get_solutions(self.ID, self.args.solutions) util.change_stack_size_to_unlimited() for solution in solutions: diff --git a/src/sinol_make/helpers/package_util.py b/src/sinol_make/helpers/package_util.py index 8ace8c8a..ade6ba10 100644 --- a/src/sinol_make/helpers/package_util.py +++ b/src/sinol_make/helpers/package_util.py @@ -45,6 +45,48 @@ def get_test_key(test, task_id): return get_group(test, task_id), test +def get_solutions_re(task_id): + return re.compile(r"^%s[bs]?[0-9]*\.(cpp|cc|java|py|pas)$" % task_id) + + +def get_executable_key(executable): + name = get_file_name(executable) + value = [0, 0] + if name[3] == 's': + value[0] = 1 + suffix = name.split(".")[0][4:] + elif name[3] == 'b': + value[0] = 2 + suffix = name.split(".")[0][4:] + else: + suffix = name.split(".")[0][3:] + if suffix != "": + value[1] = int(suffix) + return tuple(value) + + +def get_files_matching(patterns: List[str], directory: str) -> List[str]: + """ + Returns list of files matching given patterns. + If pattern is absolute path, it is returned as is. + If pattern is relative path, it is searched in current directory and in directory specified as argument. + :param patterns: List of patterns to match. + :param directory: Directory to search in. + :return: List of files matching given patterns. + """ + files_matching = set() + for solution in patterns: + if os.path.isabs(solution): + files_matching.add(solution) + else: + # If solution already has `/` prefix: + files_matching.update(glob.glob(os.path.join(os.getcwd(), solution))) + # If solution does not have `/` prefix: + files_matching.update(glob.glob(os.path.join(os.getcwd(), directory, solution))) + + return list(set(files_matching)) + + def get_tests(task_id: str, arg_tests: Union[List[str], None] = None) -> List[str]: """ Returns list of tests to run. @@ -57,11 +99,37 @@ def get_tests(task_id: str, arg_tests: Union[List[str], None] = None) -> List[st if test[-3:] == ".in"] return sorted(all_tests, key=lambda test: get_test_key(test, task_id)) else: - existing_tests = set() - for test in arg_tests: - if os.path.exists(test): - existing_tests.add(test) - return sorted(list(existing_tests), key=lambda test: get_test_key(test, task_id)) + existing_tests = [] + for test in get_files_matching(arg_tests, "in"): + if not os.path.isfile(test): + util.exit_with_error("Test %s does not exist" % test) + if os.path.splitext(test)[1] == ".in": + existing_tests.append(os.path.join("in", os.path.basename(test))) + return sorted(existing_tests, key=lambda test: get_test_key(test, task_id)) + + +def get_solutions(task_id: str, args_solutions: Union[List[str], None] = None) -> List[str]: + """ + Returns list of solutions to run. + :param task_id: Task id. + :param args_solutions: Solutions specified in command line arguments. If None, all solutions are returned. + :return: List of solutions to run. + """ + solutions_re = get_solutions_re(task_id) + if args_solutions is None: + solutions = [solution for solution in os.listdir("prog/") + if solutions_re.match(solution)] + return sorted(solutions, key=get_executable_key) + else: + solutions = [] + for solution in get_files_matching(args_solutions, "prog"): + if not os.path.isfile(solution): + util.exit_with_error("Solution %s does not exist" % solution) + if solutions_re.match(os.path.basename(solution)) is not None: + solutions.append(os.path.basename(solution)) + + solutions = list(set(solutions)) + return sorted(solutions, key=get_executable_key) def get_file_name(file_path): From 86d8334ae3136cc1f069b4817822dd337cc30f2b Mon Sep 17 00:00:00 2001 From: Mateusz Masiarz Date: Thu, 21 Sep 2023 21:03:40 +0200 Subject: [PATCH 4/5] Add tests --- tests/commands/run/test_integration.py | 2 +- tests/commands/run/test_unit.py | 45 +----------------- tests/helpers/test_package_util.py | 65 ++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 45 deletions(-) diff --git a/tests/commands/run/test_integration.py b/tests/commands/run/test_integration.py index 826971a8..9e28d86e 100644 --- a/tests/commands/run/test_integration.py +++ b/tests/commands/run/test_integration.py @@ -147,7 +147,7 @@ def test_flag_tests(create_package, time_tool): except SystemExit: pass - assert command.tests == [test] + assert command.tests == [os.path.join("in", os.path.basename(test))] @pytest.mark.parametrize("create_package", [get_checker_package_path()], indirect=True) diff --git a/tests/commands/run/test_unit.py b/tests/commands/run/test_unit.py index b1a8bfc2..683aa553 100644 --- a/tests/commands/run/test_unit.py +++ b/tests/commands/run/test_unit.py @@ -15,53 +15,10 @@ def test_get_output_file(): assert command.get_output_file("in/abc1a.in") == "out/abc1a.out" -def test_get_solutions(): - os.chdir(get_simple_package_path()) - command = get_command() - - solutions = command.get_solutions(None) - assert solutions == ["abc.cpp", "abc1.cpp", "abc2.cpp", "abc3.cpp", "abc4.cpp"] - solutions = command.get_solutions(["prog/abc.cpp"]) - assert solutions == ["abc.cpp"] - assert "abc1.cpp" not in solutions - - with tempfile.TemporaryDirectory() as tmpdir: - def create_file(name): - with open(os.path.join(tmpdir, "prog", name), "w") as f: - f.write("") - - os.chdir(tmpdir) - os.mkdir("prog") - - create_file("abc.cpp") - create_file("abc1.cpp") - create_file("abc2.cpp") - create_file("abcs1.cpp") - create_file("abcs2.cpp") - - assert command.get_solutions(None) == ["abc.cpp", "abc1.cpp", "abc2.cpp", "abcs1.cpp", "abcs2.cpp"] - assert command.get_solutions(["prog/abc.cpp"]) == ["abc.cpp"] - assert command.get_solutions(["abc.cpp"]) == ["abc.cpp"] - assert command.get_solutions([os.path.join(tmpdir, "prog", "abc.cpp")]) == ["abc.cpp"] - assert command.get_solutions(["prog/abc?.cpp"]) == ["abc1.cpp", "abc2.cpp"] - assert command.get_solutions(["abc?.cpp"]) == ["abc1.cpp", "abc2.cpp"] - assert command.get_solutions(["prog/abc*.cpp"]) == ["abc.cpp", "abc1.cpp", "abc2.cpp", "abcs1.cpp", "abcs2.cpp"] - assert command.get_solutions(["abc*.cpp"]) == ["abc.cpp", "abc1.cpp", "abc2.cpp", "abcs1.cpp", "abcs2.cpp"] - assert command.get_solutions(["prog/abc.cpp", "abc1.cpp"]) == ["abc.cpp", "abc1.cpp"] - assert command.get_solutions(["prog/abc.cpp", "abc?.cpp"]) == ["abc.cpp", "abc1.cpp", "abc2.cpp"] - assert command.get_solutions(["abc.cpp", "abc2.cpp", "abcs2.cpp"]) == ["abc.cpp", "abc2.cpp", "abcs2.cpp"] - - -def test_get_executable_key(): - os.chdir(get_simple_package_path()) - command = get_command() - assert command.get_executable_key("abc1.cpp.e") == (0, 1) - - def test_compile_solutions(create_package): package_path = create_package command = get_command(package_path) - solutions = command.get_solutions(None) + solutions = package_util.get_solutions("abc", None) result = command.compile_solutions(solutions) assert result == [True for _ in solutions] diff --git a/tests/helpers/test_package_util.py b/tests/helpers/test_package_util.py index de5f294a..44ca3ce4 100644 --- a/tests/helpers/test_package_util.py +++ b/tests/helpers/test_package_util.py @@ -36,6 +36,30 @@ def test_get_tests(create_package): tests = package_util.get_tests("abc", None) assert tests == ["in/abc1a.in", "in/abc2a.in", "in/abc3a.in", "in/abc4a.in"] + with tempfile.TemporaryDirectory() as tmpdir: + def create_file(name): + with open(os.path.join(tmpdir, "in", name), "w") as f: + f.write("") + + os.chdir(tmpdir) + os.mkdir("in") + create_file("abc0.in") + create_file("abc0a.in") + create_file("abc1ocen.in") + create_file("abc2ocen.in") + create_file("abc1a.in") + create_file("abc1b.in") + create_file("abc2a.in") + + assert set(package_util.get_tests("abc", None)) == \ + {"in/abc0.in", "in/abc0a.in", "in/abc1a.in", "in/abc1b.in", "in/abc1ocen.in", "in/abc2a.in", "in/abc2ocen.in"} + assert package_util.get_tests("abc", ["in/abc1a.in"]) == ["in/abc1a.in"] + assert package_util.get_tests("abc", ["in/abc??.in"]) == \ + ["in/abc0a.in", "in/abc1a.in", "in/abc1b.in", "in/abc2a.in"] + assert package_util.get_tests("abc", ["abc1a.in"]) == ["in/abc1a.in"] + assert package_util.get_tests("abc", ["abc?ocen.in", "abc0.in"]) == ["in/abc0.in", "in/abc1ocen.in", "in/abc2ocen.in"] + assert package_util.get_tests("abc", [os.path.join(tmpdir, "in", "abc1a.in")]) == ["in/abc1a.in"] + def test_extract_file_name(): assert package_util.get_file_name("in/abc1a.in") == "abc1a.in" @@ -203,3 +227,44 @@ def test_validate_files(create_package, capsys): package_util.validate_test_names(task_id) out = capsys.readouterr().out assert "def1a.out" in out + + +def test_get_executable_key(): + os.chdir(get_simple_package_path()) + assert package_util.get_executable_key("abc1.cpp.e") == (0, 1) + + +def test_get_solutions(): + os.chdir(get_simple_package_path()) + + solutions = package_util.get_solutions("abc", None) + assert solutions == ["abc.cpp", "abc1.cpp", "abc2.cpp", "abc3.cpp", "abc4.cpp"] + solutions = package_util.get_solutions("abc", ["prog/abc.cpp"]) + assert solutions == ["abc.cpp"] + assert "abc1.cpp" not in solutions + + with tempfile.TemporaryDirectory() as tmpdir: + def create_file(name): + with open(os.path.join(tmpdir, "prog", name), "w") as f: + f.write("") + + os.chdir(tmpdir) + os.mkdir("prog") + + create_file("abc.cpp") + create_file("abc1.cpp") + create_file("abc2.cpp") + create_file("abcs1.cpp") + create_file("abcs2.cpp") + + assert package_util.get_solutions("abc", None) == ["abc.cpp", "abc1.cpp", "abc2.cpp", "abcs1.cpp", "abcs2.cpp"] + assert package_util.get_solutions("abc", ["prog/abc.cpp"]) == ["abc.cpp"] + assert package_util.get_solutions("abc", ["abc.cpp"]) == ["abc.cpp"] + assert package_util.get_solutions("abc", [os.path.join(tmpdir, "prog", "abc.cpp")]) == ["abc.cpp"] + assert package_util.get_solutions("abc", ["prog/abc?.cpp"]) == ["abc1.cpp", "abc2.cpp"] + assert package_util.get_solutions("abc", ["abc?.cpp"]) == ["abc1.cpp", "abc2.cpp"] + assert package_util.get_solutions("abc", ["prog/abc*.cpp"]) == ["abc.cpp", "abc1.cpp", "abc2.cpp", "abcs1.cpp", "abcs2.cpp"] + assert package_util.get_solutions("abc", ["abc*.cpp"]) == ["abc.cpp", "abc1.cpp", "abc2.cpp", "abcs1.cpp", "abcs2.cpp"] + assert package_util.get_solutions("abc", ["prog/abc.cpp", "abc1.cpp"]) == ["abc.cpp", "abc1.cpp"] + assert package_util.get_solutions("abc", ["prog/abc.cpp", "abc?.cpp"]) == ["abc.cpp", "abc1.cpp", "abc2.cpp"] + assert package_util.get_solutions("abc", ["abc.cpp", "abc2.cpp", "abcs2.cpp"]) == ["abc.cpp", "abc2.cpp", "abcs2.cpp"] From a1fb46e7dc139511fc2ba2a08a227a67ef3d19e2 Mon Sep 17 00:00:00 2001 From: Mateusz Masiarz Date: Thu, 21 Sep 2023 21:08:48 +0200 Subject: [PATCH 5/5] Refactor --- src/sinol_make/commands/run/__init__.py | 2 +- src/sinol_make/helpers/package_util.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sinol_make/commands/run/__init__.py b/src/sinol_make/commands/run/__init__.py index a145c0dd..f8a08475 100644 --- a/src/sinol_make/commands/run/__init__.py +++ b/src/sinol_make/commands/run/__init__.py @@ -968,7 +968,7 @@ def set_group_result(solution, group, result): def set_constants(self): self.ID = package_util.get_task_id() self.SOURCE_EXTENSIONS = ['.c', '.cpp', '.py', '.java'] - self.SOLUTIONS_RE = re.compile(r"^%s[bs]?[0-9]*\.(cpp|cc|java|py|pas)$" % self.ID) + self.SOLUTIONS_RE = package_util.get_solutions_re(self.ID) def validate_arguments(self, args): diff --git a/src/sinol_make/helpers/package_util.py b/src/sinol_make/helpers/package_util.py index ade6ba10..ac08f953 100644 --- a/src/sinol_make/helpers/package_util.py +++ b/src/sinol_make/helpers/package_util.py @@ -84,7 +84,7 @@ def get_files_matching(patterns: List[str], directory: str) -> List[str]: # If solution does not have `/` prefix: files_matching.update(glob.glob(os.path.join(os.getcwd(), directory, solution))) - return list(set(files_matching)) + return list(files_matching) def get_tests(task_id: str, arg_tests: Union[List[str], None] = None) -> List[str]: @@ -128,7 +128,6 @@ def get_solutions(task_id: str, args_solutions: Union[List[str], None] = None) - if solutions_re.match(os.path.basename(solution)) is not None: solutions.append(os.path.basename(solution)) - solutions = list(set(solutions)) return sorted(solutions, key=get_executable_key)