diff --git a/src/sinol_make/commands/run/__init__.py b/src/sinol_make/commands/run/__init__.py index b82b4d82..f8a08475 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,24 +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 = [] - for solution in args_solutions: - 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)) - 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): @@ -659,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) @@ -1000,11 +968,11 @@ 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): - 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 @@ -1205,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..ac08f953 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(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,36 @@ 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)) + + return sorted(solutions, key=get_executable_key) def get_file_name(file_path): diff --git a/tests/commands/run/test_integration.py b/tests/commands/run/test_integration.py index c9837a8c..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) @@ -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..683aa553 100644 --- a/tests/commands/run/test_unit.py +++ b/tests/commands/run/test_unit.py @@ -15,27 +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 - - -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"]