Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change --solutions and --tests flags behaviour #126

Merged
merged 5 commits into from
Sep 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 5 additions & 37 deletions src/sinol_make/commands/run/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,47 +282,15 @@ 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:
if os.path.isfile(os.path.join(os.getcwd(), "prog", file + ext)):
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):
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
77 changes: 72 additions & 5 deletions src/sinol_make/helpers/package_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<directory>/` prefix:
files_matching.update(glob.glob(os.path.join(os.getcwd(), solution)))
# If solution does not have `<directory>/` 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.
Expand All @@ -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):
Expand Down
29 changes: 28 additions & 1 deletion tests/commands/run/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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):
"""
Expand Down
19 changes: 1 addition & 18 deletions tests/commands/run/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down
65 changes: 65 additions & 0 deletions tests/helpers/test_package_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"]
Loading