diff --git a/setup.cfg b/setup.cfg index 8003957a..b7a266b8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,6 +30,7 @@ install_requires = importlib-resources psutil packaging + sio3pack==1.0.0.dev1 [options.packages.find] where = src diff --git a/src/sinol_make/__init__.py b/src/sinol_make/__init__.py index ffa53349..3ad4a9b1 100644 --- a/src/sinol_make/__init__.py +++ b/src/sinol_make/__init__.py @@ -6,13 +6,17 @@ from sinol_make import util, sio2jail from sinol_make.helpers import cache, oicompare +from sinol_make.sio3pack.package import SIO3Package # Required for side effects from sinol_make.task_type.normal import NormalTaskType # noqa from sinol_make.task_type.interactive import InteractiveTaskType # noqa +# SIO3Pack +from sio3pack.exceptions import SIO3PackException -__version__ = "1.9.7" + +__version__ = "2.0.0.dev1" def configure_parsers(): @@ -93,6 +97,7 @@ def main_exn(): def main(): new_version = None try: + SIO3Package().from_db(2137) if util.is_dev(__version__): print(util.warning('You are using a development version of sinol-make. ' 'It may be unstable and contain bugs.')) @@ -102,6 +107,11 @@ def main(): util.exit_with_error(err) except SystemExit as err: exit(err.code) + except SIO3PackException as err: + print(traceback.format_exc()) + util.exit_with_error(f'{err}\n' + 'If that is a bug, please report it or submit a bugfix: ' + 'https://github.com/sio2project/sinol-make/#reporting-bugs-and-contributing-code') except Exception: print(traceback.format_exc()) util.exit_with_error('An error occurred while running the command.\n' diff --git a/src/sinol_make/commands/chkwer/__init__.py b/src/sinol_make/commands/chkwer/__init__.py index 55d3e1ca..bd25ed26 100644 --- a/src/sinol_make/commands/chkwer/__init__.py +++ b/src/sinol_make/commands/chkwer/__init__.py @@ -108,7 +108,7 @@ def run(self, args): util.exit_with_error("chkwer can be run only for normal tasks.") self.cpus = args.cpus or util.default_cpu_count() - self.tests = package_util.get_tests(self.task_id, args.tests) + self.tests = package_util.get_tests(args.tests) if len(self.tests) == 0: util.exit_with_error("No tests found.") diff --git a/src/sinol_make/commands/doc/__init__.py b/src/sinol_make/commands/doc/__init__.py index b647dc11..fe3bc718 100644 --- a/src/sinol_make/commands/doc/__init__.py +++ b/src/sinol_make/commands/doc/__init__.py @@ -6,6 +6,7 @@ from sinol_make import util from sinol_make.helpers import package_util, paths from sinol_make.interfaces.BaseCommand import BaseCommand +from sinol_make.sio3pack.package import SIO3Package class Command(BaseCommand): @@ -92,7 +93,7 @@ def run(self, args: argparse.Namespace): # when it is not provided by the user, instead of using the default # behavior of defaulting to None. if not hasattr(args, 'latex_compiler'): - config = package_util.get_config() + config = SIO3Package().get_config() args.latex_compiler = config.get('sinol_latex_compiler', 'auto') if args.latex_compiler == 'pdflatex': @@ -104,13 +105,13 @@ def run(self, args: argparse.Namespace): elif args.latex_compiler == 'auto': self.compilation_method = 'pdflatex' for extension in ['ps', 'eps']: - if glob.glob(os.path.join(os.getcwd(), 'doc', f'*.{extension}')) != []: + if glob.glob(os.path.join(os.getcwd(), 'doc', f'*.{extension}')): #TODO: SIO3Pack? self.compilation_method = 'latex_dvi' else: util.exit_with_error("Unrecognized latex compiler") if args.files == []: - self.files = glob.glob(os.path.join(os.getcwd(), 'doc', '*.tex')) + self.files = glob.glob(os.path.join(os.getcwd(), 'doc', '*.tex')) #TODO: SIO3Pack? else: self.files = [] for file in args.files: diff --git a/src/sinol_make/commands/export/__init__.py b/src/sinol_make/commands/export/__init__.py index 7eeb367d..79c1fd91 100644 --- a/src/sinol_make/commands/export/__init__.py +++ b/src/sinol_make/commands/export/__init__.py @@ -6,11 +6,11 @@ import tempfile import argparse -from sinol_make import util, contest_types -from sinol_make.commands.ingen.ingen_util import get_ingen, compile_ingen, run_ingen, ingen_exists +from sinol_make import util, contest_types, SIO3Package +from sinol_make.commands.ingen.ingen_util import get_ingen_path, compile_ingen, run_ingen, ingen_exists from sinol_make.helpers import package_util, parsers, paths from sinol_make.interfaces.BaseCommand import BaseCommand -from sinol_make.commands.outgen import Command as OutgenCommand, compile_correct_solution, get_correct_solution +from sinol_make.commands.outgen import Command as OutgenCommand, compile_correct_solution from sinol_make.commands.doc import Command as DocCommand from sinol_make.interfaces.Errors import UnknownContestType @@ -54,7 +54,7 @@ def generate_input_tests(self): shutil.copytree(os.path.join(os.getcwd(), 'prog'), prog_dir) if ingen_exists(self.task_id): - ingen_path = get_ingen(self.task_id) + ingen_path = get_ingen_path(self.task_id) ingen_path = os.path.join(prog_dir, os.path.basename(ingen_path)) ingen_exe = compile_ingen(ingen_path, self.args, self.args.compile_mode) if not run_ingen(ingen_exe, in_dir): @@ -75,7 +75,7 @@ def generate_output_files(self): outputs.append(os.path.join(out_dir, os.path.basename(test).replace('.in', '.out'))) if len(outputs) > 0: outgen = OutgenCommand() - correct_solution_exe = compile_correct_solution(get_correct_solution(self.task_id), self.args, + correct_solution_exe = compile_correct_solution(SIO3Package().get_correct_solution(), self.args, self.args.compile_mode) outgen.args = self.args outgen.correct_solution_exe = correct_solution_exe diff --git a/src/sinol_make/commands/ingen/__init__.py b/src/sinol_make/commands/ingen/__init__.py index 6f45acaa..7742146d 100644 --- a/src/sinol_make/commands/ingen/__init__.py +++ b/src/sinol_make/commands/ingen/__init__.py @@ -3,7 +3,7 @@ import os from sinol_make import util -from sinol_make.commands.ingen.ingen_util import get_ingen, compile_ingen, run_ingen +from sinol_make.commands.ingen.ingen_util import get_ingen_path, compile_ingen, run_ingen from sinol_make.helpers import parsers, package_util, paths from sinol_make.interfaces.BaseCommand import BaseCommand @@ -65,9 +65,9 @@ def run(self, args: argparse.Namespace): self.task_id = package_util.get_task_id() util.change_stack_size_to_unlimited() - self.ingen = get_ingen(self.task_id, args.ingen_path) - print(f'Using ingen file {os.path.basename(self.ingen)}') - self.ingen_exe = compile_ingen(self.ingen, self.args, self.args.compile_mode, self.args.fsanitize) + self.ingen_path = get_ingen_path(args.ingen_path) + print(f'Using ingen file {os.path.basename(self.ingen_path)}') + self.ingen_exe = compile_ingen(self.ingen_path, self.args, self.args.compile_mode, self.args.fsanitize) previous_tests = [] try: @@ -88,8 +88,8 @@ def run(self, args: argparse.Namespace): self.delete_dangling_files(dates) with open(paths.get_cache_path("input_tests"), "w") as f: - f.write("\n".join(glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in")))) + f.write("\n".join(glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in")))) # TODO: refactor if not self.args.no_validate: - tests = sorted(glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in"))) + tests = sorted(glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in"))) # TODO: refactor package_util.validate_tests(tests, self.args.cpus, 'input') diff --git a/src/sinol_make/commands/ingen/ingen_util.py b/src/sinol_make/commands/ingen/ingen_util.py index 2c6eecb5..496d64ad 100644 --- a/src/sinol_make/commands/ingen/ingen_util.py +++ b/src/sinol_make/commands/ingen/ingen_util.py @@ -8,43 +8,43 @@ from sinol_make.helpers import package_util, compiler, compile -def ingen_exists(task_id): +def ingen_exists(): """ Checks if ingen source file exists. - :param task_id: task id, for example abc :return: True if exists, False otherwise """ - return package_util.any_files_matching_pattern(task_id, f'{task_id}ingen.*') + task_id = package_util.get_task_id() + return package_util.any_files_matching_pattern(f'{task_id}ingen.*') -def get_ingen(task_id, ingen_path=None): +def get_ingen_path(ingen_path=None) -> str: """ Find ingen source file in `prog/` directory. If `ingen_path` is specified, then it will be used (if exists). - :param task_id: task id, for example abc. :param ingen_path: path to ingen source file :return: path to ingen source file or None if not found """ + task_id = package_util.get_task_id() if ingen_path is not None: if os.path.exists(ingen_path): return ingen_path else: util.exit_with_error(f'Ingen source file {ingen_path} does not exist.') - ingen = package_util.get_files_matching_pattern(task_id, f'{task_id}ingen.*') + ingen = package_util.get_files_matching_pattern(f'{task_id}ingen.*') if len(ingen) == 0: util.exit_with_error(f'Ingen source file for task {task_id} does not exist.') # Sio2 first chooses shell scripts, then non-shell source codes. correct_ingen = None for i in ingen: - if os.path.splitext(i)[1] == '.sh': + if os.path.splitext(i.path)[1] == '.sh': correct_ingen = i break if correct_ingen is None: correct_ingen = ingen[0] - return correct_ingen + return correct_ingen.path def compile_ingen(ingen_path: str, args: argparse.Namespace, compilation_flags='default', use_fsanitize=False): diff --git a/src/sinol_make/commands/inwer/__init__.py b/src/sinol_make/commands/inwer/__init__.py index a050a759..e7774f96 100644 --- a/src/sinol_make/commands/inwer/__init__.py +++ b/src/sinol_make/commands/inwer/__init__.py @@ -8,6 +8,8 @@ from functools import cmp_to_key from typing import Dict, List +from sio3pack.test import Test + from sinol_make import util, contest_types from sinol_make.structs.inwer_structs import TestResult, InwerExecution, VerificationResult, TableData from sinol_make.helpers import package_util, printer, paths, parsers @@ -65,21 +67,21 @@ def verify_test(execution: InwerExecution) -> VerificationResult: out.decode('utf-8') ) - def verify_and_print_table(self) -> Dict[str, TestResult]: + def verify_and_print_table(self) -> Dict[Test, TestResult]: """ Verifies all tests and prints the results in a table. :return: dictionary of TestResult objects """ results = {} - sorted_tests = sorted(self.tests, key=lambda test: package_util.get_group(test, self.task_id)) + sorted_tests = sorted(self.tests, key=lambda test: test.group) executions: List[InwerExecution] = [] for test in sorted_tests: - results[test] = TestResult(test, self.task_id) - executions.append(InwerExecution(test, results[test].test_name, self.inwer_executable)) + results[test] = TestResult(test) + executions.append(InwerExecution(test.in_file.path, results[test].test_name, self.inwer_executable)) has_terminal, terminal_width, terminal_height = util.get_terminal_size() - table_data = TableData(results, 0, self.task_id) + table_data = TableData(results, 0) if has_terminal: run_event = threading.Event() run_event.set() @@ -117,21 +119,17 @@ def verify_tests_order(self): """ Verifies if tests are in correct order. """ - def get_id(test, func=str.isalpha): - basename = os.path.basename(os.path.splitext(test)[0]) - return "".join(filter(func, basename[len(self.task_id):])) - ocen = sorted([test for test in self.tests if test.endswith('ocen.in')], - key=lambda test: int("".join(filter(str.isdigit, get_id(test, str.isdigit))))) + ocen = sorted([test for test in self.tests if test.in_file.path.endswith('ocen.in')], + key=lambda test: test.test_id) tests = list(set(self.tests) - set(ocen)) last_id = None last_test = None for test in ocen: - basename = os.path.basename(os.path.splitext(test)[0]) - test_id = int("".join(filter(str.isdigit, basename))) + test_id = int("".join(filter(str.isdigit, test.test_id))) if last_id is not None and test_id != last_id + 1: - util.exit_with_error(f'Test {os.path.basename(test)} is in wrong order. ' - f'Last test was {os.path.basename(last_test)}.') + util.exit_with_error(f'Test {test.test_id} is in wrong order. ' + f'Last test was {last_test.test_id}.') last_id = test_id last_test = test @@ -152,9 +150,9 @@ def is_next(last, curr): last = 'a' + last return last == curr - def compare_id(test1, test2): - id1 = get_id(test1) - id2 = get_id(test2) + def compare_id(test1: Test, test2: Test): + id1 = test1.test_id + id2 = test2.test_id if id1 == id2: return 0 if len(id1) == len(id2): @@ -166,17 +164,17 @@ def compare_id(test1, test2): return 1 groups = {} - for group in package_util.get_groups(self.tests, self.task_id): - groups[group] = sorted([test for test in tests if package_util.get_group(test, self.task_id) == group], + for group in package_util.get_groups(): + groups[group] = sorted([test for test in tests if test.group == group], key=cmp_to_key(compare_id)) for group, group_tests in groups.items(): last_id = None last_test = None for test in group_tests: - test_id = get_id(test) + test_id = "".join(filter(not str.isdigit, test.test_id)) if last_id is not None and not is_next(last_id, test_id): - util.exit_with_error(f'Test {os.path.basename(test)} is in wrong order. ' - f'Last test was {os.path.basename(last_test)}.') + util.exit_with_error(f'Test {test.test_id} is in wrong order. ' + f'Last test was {last_test.test_id}.') last_id = test_id last_test = test @@ -184,8 +182,8 @@ def run(self, args: argparse.Namespace): args = util.init_package_command(args) self.task_id = package_util.get_task_id() - package_util.validate_test_names(self.task_id) - self.inwer = inwer_util.get_inwer_path(self.task_id, args.inwer_path) + package_util.validate_test_names() + self.inwer = inwer_util.get_inwer_path(args.inwer_path) if self.inwer is None: if args.inwer_path is None: util.exit_with_error('No inwer found in `prog/` directory.') @@ -195,17 +193,17 @@ def run(self, args: argparse.Namespace): print(f'Verifying with inwer {util.bold(relative_path)}') self.cpus = args.cpus or util.default_cpu_count() - self.tests = package_util.get_tests(self.task_id, args.tests) + self.tests = package_util.get_tests(args.tests) self.contest_type = contest_types.get_contest_type() if len(self.tests) == 0: util.exit_with_error('No tests found.') else: - print('Verifying tests: ' + util.bold(', '.join(self.tests))) + print('Verifying tests: ' + util.bold(', '.join([test.test_id for test in self.tests]))) util.change_stack_size_to_unlimited() self.inwer_executable = inwer_util.compile_inwer(self.inwer, args, args.compile_mode, args.fsanitize) - results: Dict[str, TestResult] = self.verify_and_print_table() + results: Dict[Test, TestResult] = self.verify_and_print_table() print('') failed_tests = [] diff --git a/src/sinol_make/commands/inwer/inwer_util.py b/src/sinol_make/commands/inwer/inwer_util.py index b8cc31cd..fcd26cb8 100644 --- a/src/sinol_make/commands/inwer/inwer_util.py +++ b/src/sinol_make/commands/inwer/inwer_util.py @@ -2,10 +2,12 @@ import os import sys from io import StringIO -from typing import Union +from typing import Union, List import argparse +from sio3pack.test import Test + from sinol_make import util from sinol_make.commands.inwer import TestResult, TableData from sinol_make.helpers import compile, package_util @@ -13,15 +15,16 @@ from sinol_make.interfaces.Errors import CompilationError -def get_inwer_path(task_id: str, path=None) -> Union[str, None]: +def get_inwer_path(path=None) -> Union[str, None]: """ Returns path to inwer executable for given task or None if no inwer was found. """ + task_id = package_util.get_task_id() if path is None: - inwers = package_util.get_files_matching_pattern(task_id, f'{task_id}inwer.*') + inwers = package_util.get_files_matching_pattern(f'{task_id}inwer.*') if len(inwers) == 0: return None - return inwers[0] + return inwers[0].path else: inwer = os.path.join(os.getcwd(), path) if os.path.exists(inwer): @@ -46,9 +49,9 @@ def compile_inwer(inwer_path: str, args: argparse.Namespace, compilation_flags=' return inwer_exe -def sort_tests(tests, task_id): +def sort_tests(tests: List[Test]) -> List[Test]: # First sort by group, then by test name. - tests.sort(key=lambda test: [package_util.get_group(test, task_id), test]) + tests.sort(key=lambda test: [test.group, test.test_id]) return tests @@ -67,8 +70,8 @@ def print_view(term_width, term_height, table_data: TableData): for result in results.values(): column_lengths[0] = max(column_lengths[0], len(result.test_name)) column_lengths[1] = max(column_lengths[1], len(result.test_group)) - tests.append(result.test_path) - tests = sort_tests(tests, table_data.task_id) + tests.append(result.test) + tests = sort_tests(tests) column_lengths[3] = max(10, term_width - column_lengths[0] - column_lengths[1] - column_lengths[ 2] - 9 - 3) # 9 is for " | " between columns, 3 for margin. diff --git a/src/sinol_make/commands/outgen/__init__.py b/src/sinol_make/commands/outgen/__init__.py index fc71abf8..b05f1682 100644 --- a/src/sinol_make/commands/outgen/__init__.py +++ b/src/sinol_make/commands/outgen/__init__.py @@ -1,11 +1,15 @@ import argparse import glob import os +from typing import List + import yaml import multiprocessing as mp -from sinol_make import util +from sio3pack.test import Test + +from sinol_make import util, SIO3Package from sinol_make.commands.outgen.outgen_util import get_correct_solution, compile_correct_solution, generate_output from sinol_make.structs.gen_structs import OutputGenerationArguments from sinol_make.helpers import parsers, package_util, cache, paths @@ -62,7 +66,7 @@ def calculate_md5_sums(self, tests=None): list of input tests based on which the output tests will be generated) """ if tests is None: - tests = glob.glob(os.path.join(os.getcwd(), 'in', '*.in')) + tests = SIO3Package().get_tests() old_md5_sums = None try: @@ -76,27 +80,27 @@ def calculate_md5_sums(self, tests=None): md5_sums = {} outputs_to_generate = [] from_inputs = [] - for file in tests: - basename = os.path.basename(file) + for test in tests: + basename = os.path.basename(test.in_file.path) output_basename = os.path.splitext(os.path.basename(basename))[0] + '.out' output_path = os.path.join(os.getcwd(), 'out', output_basename) - md5_sums[basename] = util.get_file_md5(file) + md5_sums[basename] = util.get_file_md5(test.in_file.path) if old_md5_sums is None or old_md5_sums.get(basename, '') != md5_sums[basename]: outputs_to_generate.append(output_path) - from_inputs.append(file) + from_inputs.append(test) elif not os.path.exists(output_path): # If output file does not exist, generate it. outputs_to_generate.append(output_path) - from_inputs.append(file) + from_inputs.append(test) return md5_sums, outputs_to_generate, from_inputs - def clean_cache(self, inputs): + def clean_cache(self, tests: List[Test]): """ Cleans cache for the given input files. """ - md5_sums = [util.get_file_md5(file) for file in inputs] + md5_sums = [util.get_file_md5(file.in_file.path) for file in tests] for solution in glob.glob(paths.get_cache_path("md5sums", "*")): sol_cache = cache.get_cache_file(solution) for input in md5_sums: @@ -112,17 +116,17 @@ def run(self, args: argparse.Namespace): self.task_type = package_util.get_task_type_cls() if not self.task_type.run_outgen(): util.exit_with_error('Output generation is not supported for this task type.') - package_util.validate_test_names(self.task_id) + package_util.validate_test_names() util.change_stack_size_to_unlimited() - cache.check_correct_solution(self.task_id) - self.correct_solution = get_correct_solution(self.task_id) + cache.check_correct_solution() + self.correct_solution = package_util.get_correct_solution() md5_sums, outputs_to_generate, from_inputs = self.calculate_md5_sums() if len(outputs_to_generate) == 0: print(util.info('All output files are up to date.')) else: self.clean_cache(from_inputs) - self.correct_solution_exe = compile_correct_solution(self.correct_solution, self.args, + self.correct_solution_exe = compile_correct_solution(self.correct_solution.path, self.args, self.args.compile_mode) self.generate_outputs(outputs_to_generate) with open(os.path.join(os.getcwd(), 'in', '.md5sums'), 'w') as f: diff --git a/src/sinol_make/commands/outgen/outgen_util.py b/src/sinol_make/commands/outgen/outgen_util.py index 8557345b..a89543dc 100644 --- a/src/sinol_make/commands/outgen/outgen_util.py +++ b/src/sinol_make/commands/outgen/outgen_util.py @@ -9,18 +9,6 @@ from sinol_make.helpers import package_util, compiler, compile -def get_correct_solution(task_id): - """ - Returns path to correct solution for given task. - :param task_id: task id, for example abc - :return: path to correct solution or None if not found - """ - correct_solution = package_util.get_files_matching_pattern(task_id, f'{task_id}.*') - if len(correct_solution) == 0: - util.exit_with_error(f'Correct solution for task {task_id} does not exist.') - return correct_solution[0] - - def compile_correct_solution(solution_path: str, args: argparse.Namespace, compilation_flags='default'): """ Compiles correct solution and returns path to compiled executable. diff --git a/src/sinol_make/commands/run/__init__.py b/src/sinol_make/commands/run/__init__.py index 1ce6efc7..4e546777 100644 --- a/src/sinol_make/commands/run/__init__.py +++ b/src/sinol_make/commands/run/__init__.py @@ -337,13 +337,13 @@ def compile_solutions(self, solutions): return compilation_results def compile(self, solution, dest=None, use_extras=False, clear_cache=False, name=None): - compile_log_file = paths.get_compilation_log_path("%s.compile_log" % package_util.get_file_name(solution)) + compile_log_file = paths.get_compilation_log_path("%s.compile_log" % os.path.basename(solution)) source_file = os.path.join(os.getcwd(), "prog", self.get_solution_from_exe(solution)) if dest: output = dest else: output = paths.get_executables_path(package_util.get_executable(solution)) - name = name or "file " + package_util.get_file_name(solution) + name = name or "file " + os.path.basename(solution) extra_compilation_args = [] extra_compilation_files = [] @@ -515,7 +515,7 @@ def get_whole_groups(self): Returns a list of groups for which all tests were run. """ group_sizes = {} - for test in package_util.get_tests(self.ID): + for test in package_util.get_tests(): group = package_util.get_group(test, self.ID) if group not in group_sizes: group_sizes[group] = 0 @@ -760,7 +760,7 @@ def set_constants(self): self.SOLUTIONS_RE = package_util.get_solutions_re(self.ID) def validate_arguments(self, args): - compilers = compiler.verify_compilers(args, package_util.get_solutions(self.ID, None)) + compilers = compiler.verify_compilers(args, [solution.path for solution in package_util.get_solutions()]) def use_sio2jail(): timetool_path = None @@ -939,7 +939,7 @@ def run(self, args): args = util.init_package_command(args) self.set_constants() - package_util.validate_test_names(self.ID) + package_util.validate_test_names() self.args = args self.config = package_util.get_config() try: @@ -965,7 +965,7 @@ def run(self, args): lib = package_util.get_files_matching_pattern(self.ID, f'{self.ID}lib.*') self.has_lib = len(lib) != 0 - self.tests = package_util.get_tests(self.ID, self.args.tests) + self.tests = package_util.get_tests(self.args.tests) self.test_md5sums = {os.path.basename(test): util.get_file_md5(test) for test in self.tests} self.check_are_any_tests_to_run() self.set_scores() diff --git a/src/sinol_make/commands/verify/__init__.py b/src/sinol_make/commands/verify/__init__.py index c65484d2..f325c328 100644 --- a/src/sinol_make/commands/verify/__init__.py +++ b/src/sinol_make/commands/verify/__init__.py @@ -153,7 +153,7 @@ def run(self, args: argparse.Namespace): print(util.bold(' Generating tests '.center(util.get_terminal_size()[1], '='))) gen = GenCommand() gen.run(self.prepare_args(gen)) - self.verify_scores(package_util.get_groups(package_util.get_all_inputs(self.task_id), self.task_id)) + self.verify_scores(package_util.get_groups()) # Generate problem statements print(util.bold(' Generating problem statements '.center(util.get_terminal_size()[1], '='))) @@ -161,7 +161,7 @@ def run(self, args: argparse.Namespace): doc.run(self.prepare_args(doc)) # Run inwer - if inwer_util.get_inwer_path(self.task_id) is None: + if inwer_util.get_inwer_path() is None: print(util.warning("Package doesn't have inwer.")) else: print(util.bold(' Running inwer '.center(util.get_terminal_size()[1], '='))) diff --git a/src/sinol_make/contest_types/__init__.py b/src/sinol_make/contest_types/__init__.py index 8f3a72a6..0feb7ef9 100644 --- a/src/sinol_make/contest_types/__init__.py +++ b/src/sinol_make/contest_types/__init__.py @@ -5,11 +5,12 @@ from sinol_make.helpers.func_cache import cache_result from sinol_make.helpers.package_util import get_config from sinol_make.interfaces.Errors import UnknownContestType +from sinol_make.sio3pack.package import SIO3Package @cache_result(cwd=True) def get_contest_type(): - config = get_config() + config = SIO3Package().get_config() contest_type = config.get("sinol_contest_type", "default").lower() if contest_type == "default": diff --git a/src/sinol_make/helpers/cache.py b/src/sinol_make/helpers/cache.py index 5c3923eb..d6a2b385 100644 --- a/src/sinol_make/helpers/cache.py +++ b/src/sinol_make/helpers/cache.py @@ -163,17 +163,16 @@ def has_file_changed(file_path: str) -> bool: return True -def check_correct_solution(task_id: str): +def check_correct_solution(): """ Checks if correct solution has changed. If it did, removes cache for input files. - :param task_id: Task id """ try: - solution = package_util.get_correct_solution(task_id) + solution = package_util.get_correct_solution() except FileNotFoundError: return - if has_file_changed(solution) and os.path.exists(os.path.join(os.getcwd(), 'in', '.md5sums')): + if has_file_changed(solution.path) and os.path.exists(os.path.join(os.getcwd(), 'in', '.md5sums')): os.unlink(os.path.join(os.getcwd(), 'in', '.md5sums')) diff --git a/src/sinol_make/helpers/package_util.py b/src/sinol_make/helpers/package_util.py index f9d46d35..4553c772 100644 --- a/src/sinol_make/helpers/package_util.py +++ b/src/sinol_make/helpers/package_util.py @@ -1,66 +1,31 @@ import os import re -import yaml import glob import fnmatch import multiprocessing as mp from enum import Enum from typing import List, Union, Dict, Any, Tuple, Type +from sio3pack.files import File +from sio3pack.test import Test + from sinol_make.helpers.func_cache import cache_result -from sinol_make import util, contest_types +from sinol_make import util, contest_types, SIO3Package from sinol_make.helpers import paths from sinol_make.task_type import BaseTaskType @cache_result(cwd=True) def get_task_id() -> str: - config = get_config() - if "sinol_task_id" in config: - return config["sinol_task_id"] - else: - print(util.warning("sinol_task_id not specified in config.yml. Using task id from directory name.")) - task_id = os.path.split(os.getcwd())[-1] - if len(task_id) == 3: - return task_id - else: - util.exit_with_error("Invalid task id. Task id should be 3 characters long.") - - -def extract_test_id(test_path, task_id): - """ - Extracts test group and number from test path. - For example for test abc1a.in it returns 1a. - :param test_path: Path to test file. - :param task_id: Task id. - :return: Test group and number. - """ - return os.path.split(os.path.splitext(test_path)[0])[1][len(task_id):] - + return SIO3Package().short_name -def get_group(test_path, task_id): - if extract_test_id(test_path, task_id).endswith("ocen"): - return 0 - return int("".join(re.search(r'\d+', extract_test_id(test_path, task_id)).group())) - - -def get_groups(tests, task_id): - return sorted(list(set([get_group(test, task_id) for test in tests]))) - - -def get_test_key(test, task_id): - return get_group(test, task_id), test +def get_groups(): + tests = SIO3Package().get_tests() + return sorted([list(set(test.group)) for test in tests]) def get_config(): - try: - with open(os.path.join(os.getcwd(), "config.yml"), "r") as config_file: - return yaml.load(config_file, Loader=yaml.FullLoader) or {} - except FileNotFoundError: - # Potentially redundant with util:exit_if_not_package - util.exit_with_error("You are not in a package directory (couldn't find config.yml in current directory).") - except yaml.YAMLError as e: - util.exit_with_error("config.yml is not a valid YAML. Fix it before continuing:\n" + str(e)) + return SIO3Package().config def get_solutions_re(task_id: str) -> re.Pattern: @@ -71,9 +36,9 @@ def get_solutions_re(task_id: str) -> re.Pattern: return re.compile(r"^%s[bs]?[0-9]*(_.*)?\.(c|cpp|cc|py)$" % task_id) -def get_executable_key(executable, task_id): - name = get_file_name(executable) - task_id_len = len(task_id) +def get_executable_key(path_to_exe): + name = os.path.basename(path_to_exe) + task_id_len = len(get_task_id()) value = [0, 0] if name[task_id_len] == 's': value[0] = 1 @@ -94,103 +59,113 @@ def get_executable_key(executable, task_id): return tuple(value) -def get_files_matching(patterns: List[str], directory: str) -> List[str]: +def get_matching_tests(tests: List[Test], patterns: List[str]) -> List[Test]: """ - 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. + Returns list of tests matching given path patterns. + :param tests: List of all tests available. :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]: + :return: List of tests with paths matching given path patterns. + """ + matching_tests = set() + for pattern in patterns: + matched_to_pattern = set() + for test in tests: + # if absolute path is given, match it directly + if os.path.isabs(pattern) and fnmatch.fnmatch(test.in_file.path, pattern): + matched_to_pattern.add(test) + else: + # if relative path is given, match it with current working directory + pattern_relative = os.path.join(os.getcwd(), pattern) + if fnmatch.fnmatch(test.in_file.path, pattern_relative): + matched_to_pattern.add(test) + else: + # if pattern is given, match it with tests filename + if fnmatch.fnmatch(os.path.basename(test.in_file.path), pattern): + matched_to_pattern.add(test) + if len(matched_to_pattern) == 0: + util.exit_with_error("Test %s does not exist" % pattern) + matching_tests.update(matched_to_pattern) + + return list(matching_tests) + +def get_matching_files(files: List[File], patterns: List[str]) -> List[File]: + """ + Returns list of files matching given path patterns. + :param files: List of all files available. + :param patterns: List of patterns to match. + :return: List of files with paths matching given path patterns. + """ + matching_files = set() + for pattern in patterns: + matched_to_pattern = set() + for file in files: + # if absolute path is given, match it directly + if os.path.isabs(pattern) and fnmatch.fnmatch(file.path, pattern): + matched_to_pattern.add(file) + else: + # if relative path is given, match it with current working directory + pattern_relative = os.path.join(os.getcwd(), pattern) + if fnmatch.fnmatch(file.path, pattern_relative): + matched_to_pattern.add(file) + else: + # if pattern is given, match it with filename + if fnmatch.fnmatch(os.path.basename(file.path), pattern): + matched_to_pattern.add(file) + if len(matched_to_pattern) == 0: + util.exit_with_error("File %s does not exist" % pattern) + matching_files.update(matched_to_pattern) + + return list(matching_files) + +def get_tests(arg_tests: Union[List[str], None] = None) -> List[Test]: #ZwracaƂo iny """ Returns list of tests to run. - :param task_id: Task id. :param arg_tests: Tests specified in command line arguments. If None, all tests are returned. :return: List of tests to run. """ + tests = SIO3Package().get_tests() if arg_tests is None: - all_tests = ["in/%s" % test for test in os.listdir("in/") - if test[-3:] == ".in"] - return sorted(all_tests, key=lambda test: get_test_key(test, task_id)) + return sorted(tests, key=lambda test: test.group) else: - 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)) + matching_tests = get_matching_tests(tests, arg_tests) + return sorted(matching_tests, key=lambda test: test.group) -def get_solutions(task_id: str, args_solutions: Union[List[str], None] = None) -> List[str]: +def get_solutions(args_solutions: Union[List[str], None] = None) -> List[File]: """ 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. + :return: List of paths of solutions to run. """ - solutions_re = get_solutions_re(task_id) + task_id = get_task_id() + solutions = [s.get('file').path for s in SIO3Package().model_solutions] if args_solutions is None: - solutions = [solution for solution in os.listdir("prog/") - if solutions_re.match(solution)] - return sorted(solutions, key=lambda solution: get_executable_key(solution, task_id)) + return sorted(solutions, key=lambda path: get_executable_key(path, task_id)) 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)) + matching_solutions = get_matching_files(solutions, args_solutions) + return sorted(matching_solutions, key=lambda solution: get_executable_key(solution, task_id)) - return sorted(solutions, key=lambda solution: get_executable_key(solution, task_id)) - -def get_correct_solution(task_id: str) -> str: +def get_correct_solution() -> File: """ Returns path to correct solution. - :param task_id: Task id. :return: Path to correct solution. """ - correct_solution = get_solutions(task_id, [f'{task_id}.*']) + task_id = get_task_id() + correct_solution = get_solutions([f'{task_id}.*']) if len(correct_solution) == 0: raise FileNotFoundError("Correct solution not found.") - return os.path.join(os.getcwd(), "prog", correct_solution[0]) - - -def get_file_name(file_path): - return os.path.split(file_path)[1] + return correct_solution[0] def get_file_name_without_extension(file_path): - return os.path.splitext(get_file_name(file_path))[0] + return os.path.splitext(os.path.basename(file_path))[0] def get_executable(file_path): return os.path.basename(file_path) + ".e" -def get_executable_path(solution: str) -> str: - """ - Returns path to compiled executable for given solution. - """ - return paths.get_executables_path(get_executable(solution)) - - def get_file_lang(file_path): return os.path.splitext(file_path)[1][1:].lower() @@ -200,7 +175,7 @@ class LimitTypes(Enum): MEMORY_LIMIT = 2 -def _get_limit_from_dict(dict: Dict[str, Any], limit_type: LimitTypes, test_id: str, test_group: str, test_path: str, +def _get_limit_from_dict(dict: Dict[str, Any], limit_type: LimitTypes, test: Test, allow_test_limit: bool = False): if limit_type == LimitTypes.TIME_LIMIT: limit_name = "time_limit" @@ -211,13 +186,15 @@ def _get_limit_from_dict(dict: Dict[str, Any], limit_type: LimitTypes, test_id: else: raise ValueError("Invalid limit type.") + test_id = test.test_id + test_group = test.group if plural_limit_name in dict: if test_id in dict[plural_limit_name] and test_id != "0": if allow_test_limit: return dict[plural_limit_name][test_id] else: util.exit_with_error( - f'{os.path.basename(test_path)}: Specifying limit for a single test is not allowed in sinol-make.') + f'{test.test_id}: Specifying limit for a single test is not allowed in sinol-make.') elif test_group in dict[plural_limit_name]: return dict[plural_limit_name][test_group] if limit_name in dict: @@ -226,14 +203,12 @@ def _get_limit_from_dict(dict: Dict[str, Any], limit_type: LimitTypes, test_id: return None -def _get_limit(limit_type: LimitTypes, test_path: str, config: Dict[str, Any], lang: str, task_id: str): - test_id = extract_test_id(test_path, task_id) - test_group = str(get_group(test_path, task_id)) +def _get_limit(limit_type: LimitTypes, test: Test, config: Dict[str, Any], lang: str): contest_type = contest_types.get_contest_type() allow_test_limit = config.get("sinol_undocumented_test_limits", False) or contest_type.allow_per_test_limits() - global_limit = _get_limit_from_dict(config, limit_type, test_id, test_group, test_path, allow_test_limit) + global_limit = _get_limit_from_dict(config, limit_type, test, allow_test_limit) override_limits_dict = config.get("override_limits", {}).get(lang, {}) - overriden_limit = _get_limit_from_dict(override_limits_dict, limit_type, test_id, test_group, test_path, + overriden_limit = _get_limit_from_dict(override_limits_dict, limit_type, test, allow_test_limit) if overriden_limit is not None: return overriden_limit @@ -243,13 +218,13 @@ def _get_limit(limit_type: LimitTypes, test_path: str, config: Dict[str, Any], l else: if limit_type == LimitTypes.TIME_LIMIT: util.exit_with_error( - f'Time limit was not defined for test {os.path.basename(test_path)} in config.yml.') + f'Time limit was not defined for test {test.test_id} in config.yml.') elif limit_type == LimitTypes.MEMORY_LIMIT: util.exit_with_error( - f'Memory limit was not defined for test {os.path.basename(test_path)} in config.yml.') + f'Memory limit was not defined for test {test.test_id} in config.yml.') -def get_time_limit(test_path, config, lang, task_id, args=None): +def get_time_limit(test_path, config, lang, args=None): """ Returns time limit for given test. """ @@ -257,10 +232,10 @@ def get_time_limit(test_path, config, lang, task_id, args=None): return args.tl * 1000 str_config = util.stringify_keys(config) - return _get_limit(LimitTypes.TIME_LIMIT, test_path, str_config, lang, task_id) + return _get_limit(LimitTypes.TIME_LIMIT, test_path, str_config, lang) -def get_memory_limit(test_path, config, lang, task_id, args=None): +def get_memory_limit(test_path, config, lang, args=None): """ Returns memory limit for given test. """ @@ -268,7 +243,7 @@ def get_memory_limit(test_path, config, lang, task_id, args=None): return int(args.ml * 1024) str_config = util.stringify_keys(config) - return _get_limit(LimitTypes.MEMORY_LIMIT, test_path, str_config, lang, task_id) + return _get_limit(LimitTypes.MEMORY_LIMIT, test_path, str_config, lang) def get_in_tests_re(task_id: str) -> re.Pattern: @@ -279,60 +254,57 @@ def get_out_tests_re(task_id: str) -> re.Pattern: return re.compile(r'^%s(([0-9]+)([a-z]?[a-z0-9]*))\.out$' % re.escape(task_id)) -def validate_test_names(task_id): +def validate_test_names(): """ Checks if all files in the package have valid names. """ - - def get_invalid_files(path, pattern): + def get_invalid_files(files: List[File], pattern): invalid_files = [] - for file in glob.glob(os.path.join(os.getcwd(), path)): - if not pattern.match(os.path.basename(file)): - invalid_files.append(os.path.basename(file)) + for file in files: + if not pattern.match(os.path.basename(file.path)): + invalid_files.append(os.path.basename(file.path)) return invalid_files + tests = SIO3Package().get_tests() + task_id = get_task_id() + tests_ins = list(map(lambda test: test.in_file, tests)) + tests_outs = list(map(lambda test: test.out_file, tests)) in_test_re = get_in_tests_re(task_id) - invalid_in_tests = get_invalid_files(os.path.join("in", "*.in"), in_test_re) + invalid_in_tests = get_invalid_files(tests_ins, in_test_re) if len(invalid_in_tests) > 0: util.exit_with_error(f'Input tests with invalid names: {", ".join(invalid_in_tests)}.') out_test_re = get_out_tests_re(task_id) - invalid_out_tests = get_invalid_files(os.path.join("out", "*.out"), out_test_re) + invalid_out_tests = get_invalid_files(tests_outs, out_test_re) if len(invalid_out_tests) > 0: util.exit_with_error(f'Output tests with invalid names: {", ".join(invalid_out_tests)}.') -def get_all_code_files(task_id: str) -> List[str]: +def get_all_code_files() -> List[File]: """ Returns all code files in package. - :param task_id: Task id. :return: List of code files. """ - result = glob.glob(os.path.join(os.getcwd(), "prog", f"{task_id}ingen.sh")) - for ext in ["c", "cpp", "py", "java"]: - result += glob.glob(os.path.join(os.getcwd(), f"prog/{task_id}*.{ext}")) - return result + return SIO3Package().model_solutions -def get_files_matching_pattern(task_id: str, pattern: str) -> List[str]: +def get_files_matching_pattern(pattern: str) -> List[File]: """ Returns all files in package matching given pattern. - :param task_id: Task id. :param pattern: Pattern to match. :return: List of files matching the pattern. """ - all_files = get_all_code_files(task_id) - return [file for file in all_files if fnmatch.fnmatch(os.path.basename(file), pattern)] + all_files = get_all_code_files() + return [file for file in all_files if fnmatch.fnmatch(os.path.basename(file.path), pattern)] -def any_files_matching_pattern(task_id: str, pattern: str) -> bool: +def any_files_matching_pattern(pattern: str) -> bool: """ Returns True if any file in package matches given pattern. - :param task_id: Task id. :param pattern: Pattern to match. :return: True if any file in package matches given pattern. """ - return len(get_files_matching_pattern(task_id, pattern)) > 0 + return len(get_files_matching_pattern(pattern)) > 0 def check_if_contest_type_changed(contest_type): @@ -412,13 +384,8 @@ def validate_tests(tests: List[str], cpus: int, type: str = 'input'): print(util.info(f'All {type} tests are valid!')) -def get_all_inputs(task_id): - in_test_re = get_in_tests_re(task_id) - inputs = [] - for file in glob.glob(os.path.join(os.getcwd(), "in", "*.in")): - if in_test_re.match(os.path.basename(file)): - inputs.append(file) - return inputs +def get_all_inputs() -> List[File]: + return [file.in_file for file in SIO3Package().get_tests()] def get_task_type_cls() -> Type[BaseTaskType]: @@ -430,7 +397,7 @@ def get_task_type(timetool_name, timetool_path) -> BaseTaskType: return task_type_cls(timetool_name, timetool_path) -def get_out_from_in(test) -> str: +def get_out_from_in(test) -> str: #TODO not needed? """ Returns path to output file corresponding to given input file. """ diff --git a/src/sinol_make/sio3pack/__init__.py b/src/sinol_make/sio3pack/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/sinol_make/sio3pack/package.py b/src/sinol_make/sio3pack/package.py new file mode 100644 index 00000000..dd9fc137 --- /dev/null +++ b/src/sinol_make/sio3pack/package.py @@ -0,0 +1,22 @@ +import os + +from sio3pack import Package +from sio3pack import LocalFile + + +def _get_local_file(): + return LocalFile(os.getcwd()) + + +class SIO3Package: + """ + Singleton class for package base class. + """ + + _instance = None + + def __new__(cls) -> Package: + if cls._instance is None: + cls._instance = Package.from_file(_get_local_file()) + return cls._instance + diff --git a/src/sinol_make/structs/inwer_structs.py b/src/sinol_make/structs/inwer_structs.py index 8329ba9c..3c5d5c4c 100644 --- a/src/sinol_make/structs/inwer_structs.py +++ b/src/sinol_make/structs/inwer_structs.py @@ -2,6 +2,8 @@ from dataclasses import dataclass from typing import Dict +from sio3pack.test import Test + from sinol_make.helpers import package_util @@ -14,10 +16,9 @@ class TestResult: valid: bool output: str - def __init__(self, test_path, task_id): - self.test_path = test_path - self.test_name = os.path.split(test_path)[-1] - self.test_group = str(package_util.get_group(self.test_path, task_id)) + def __init__(self, test: Test): + self.test = test + self.test_name = os.path.split(self.test_path)[-1] self.verified = False self.valid = False @@ -34,15 +35,12 @@ class TableData: Data used for printing table with verification results. """ - # Dictionary with test path as key and verification result as value. - results: Dict[str, TestResult] + # Dictionary with test as key and verification result as value. + results: Dict[Test, TestResult] # Number of executions finished i: int - # Task id - task_id: str - @dataclass class InwerExecution: test_path: str diff --git a/src/sinol_make/task_type/__init__.py b/src/sinol_make/task_type/__init__.py index 428be58c..ba02edda 100644 --- a/src/sinol_make/task_type/__init__.py +++ b/src/sinol_make/task_type/__init__.py @@ -97,10 +97,10 @@ def additional_files_to_compile(self) -> List[Tuple[str, str, str, bool, bool]]: """ ret = [] task_id = package_util.get_task_id() - checker = package_util.get_files_matching_pattern(task_id, f'{task_id}chk.*') + checker = package_util.get_files_matching_pattern(f'{task_id}chk.*') if len(checker) > 0: self.has_checker = True - checker = checker[0] + checker = checker[0].path checker_basename = os.path.basename(checker) self.checker_path = paths.get_executables_path(checker_basename + ".e") ret += [(checker, self.checker_path, "checker", True, True)] diff --git a/src/sinol_make/util.py b/src/sinol_make/util.py index fbb058bf..c1c60691 100644 --- a/src/sinol_make/util.py +++ b/src/sinol_make/util.py @@ -12,7 +12,7 @@ from sinol_make.helpers import paths, cache from sinol_make.helpers.func_cache import cache_result from sinol_make.structs.status_structs import Status - +from sinol_make.sio3pack.package import SIO3Package @cache_result() def get_commands(): @@ -55,12 +55,20 @@ def find_and_chdir_package(): return False +def instantiate_package(): + """ + Function to instantiate package from current directory. + """ + SIO3Package() + + def init_package_command(args): """ Updates arguments with contest specific overrides for commands that require being in package directory """ exit_if_not_package() + instantiate_package() contest = get_contest_type() contest.verify_config() return contest.argument_overrides(args) diff --git a/tests/commands/run/test_unit.py b/tests/commands/run/test_unit.py index 2ce6a818..19d40e6b 100644 --- a/tests/commands/run/test_unit.py +++ b/tests/commands/run/test_unit.py @@ -35,7 +35,7 @@ def test_execution(create_package, time_tool): assert result == [True] create_ins_outs(package_path) - test = package_util.get_tests("abc", None)[0] + test = package_util.get_tests(None)[0] with open(os.path.join(package_path, "config.yml"), "r") as config_file: config = yaml.load(config_file, Loader=yaml.FullLoader) @@ -52,7 +52,7 @@ def test_run_solutions(create_package, time_tool): command.args = argparse.Namespace(solutions_report=False, time_tool=time_tool, compile_mode='default', hide_memory=False) create_ins_outs(package_path) - command.tests = package_util.get_tests("abc", None) + command.tests = package_util.get_tests(None) command.test_md5sums = {os.path.basename(test): util.get_file_md5(test) for test in command.tests} command.groups = list(sorted(set([command.get_group(test) for test in command.tests]))) command.scores = command.config["scores"] @@ -456,7 +456,7 @@ def test_get_valid_input_files(create_package): package_path = create_package command = get_command(package_path) create_ins_outs(package_path) - command.tests = package_util.get_tests(command.ID, None) + command.tests = package_util.get_tests(None) outputs = glob.glob(os.path.join(package_path, "out", "*.out")) os.unlink(outputs[0]) diff --git a/tests/helpers/test_package_util.py b/tests/helpers/test_package_util.py index ae27c69f..94b7827f 100644 --- a/tests/helpers/test_package_util.py +++ b/tests/helpers/test_package_util.py @@ -34,7 +34,7 @@ def test_get_tests(create_package): os.chdir(create_package) task_id = package_util.get_task_id() create_ins(create_package, task_id) - tests = package_util.get_tests("abc", None) + tests = package_util.get_tests(None) assert tests == ["in/abc1a.in", "in/abc2a.in", "in/abc3a.in", "in/abc4a.in"] with tempfile.TemporaryDirectory() as tmpdir: @@ -52,18 +52,14 @@ def create_file(name): create_file("abc1b.in") create_file("abc2a.in") - assert set(package_util.get_tests("abc", None)) == \ + assert set(package_util.get_tests(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"]) == \ + assert package_util.get_tests(["in/abc1a.in"]) == ["in/abc1a.in"] + assert package_util.get_tests(["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" + assert package_util.get_tests(["abc1a.in"]) == ["in/abc1a.in"] + assert package_util.get_tests(["abc?ocen.in", "abc0.in"]) == ["in/abc0.in", "in/abc1ocen.in", "in/abc2ocen.in"] + assert package_util.get_tests([os.path.join(tmpdir, "in", "abc1a.in")]) == ["in/abc1a.in"] def test_get_executable():