Skip to content

Commit

Permalink
Add function for validating test names (#114)
Browse files Browse the repository at this point in the history
* Add function for validating tests, change `extract_test_id` function

* Change `get_group` function

* Add test for `validate_files` function

* Refactor

* Refactor `extract_test_id` function

* Add functions for better searching for files

* Bump version for release
  • Loading branch information
MasloMaslane authored Sep 19, 2023
1 parent 8e35b27 commit fe6797e
Show file tree
Hide file tree
Showing 16 changed files with 225 additions and 132 deletions.
4 changes: 3 additions & 1 deletion src/sinol_make/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

from sinol_make import util, oiejq

__version__ = "1.5.6"

__version__ = "1.5.7"


def configure_parsers():
parser = argparse.ArgumentParser(
Expand Down
5 changes: 3 additions & 2 deletions src/sinol_make/commands/export/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def get_generated_tests(self):
util.exit_with_error('Failed to run ingen.')

tests = glob.glob(os.path.join(working_dir, f'{self.task_id}*.in'))
return [package_util.extract_test_id(test) for test in tests]
return [package_util.extract_test_id(test, self.task_id) for test in tests]

def copy_package_required_files(self, target_dir: str):
"""
Expand Down Expand Up @@ -74,7 +74,7 @@ def copy_package_required_files(self, target_dir: str):
tests_to_copy = []
for ext in ['in', 'out']:
for test in glob.glob(os.path.join(os.getcwd(), ext, f'{self.task_id}*.{ext}')):
if package_util.extract_test_id(test) not in generated_tests:
if package_util.extract_test_id(test, self.task_id) not in generated_tests:
tests_to_copy.append(test)

if len(tests_to_copy) > 0:
Expand Down Expand Up @@ -132,6 +132,7 @@ def run(self, args: argparse.Namespace):

self.args = args
self.task_id = package_util.get_task_id()
package_util.validate_test_names(self.task_id)

with open(os.path.join(os.getcwd(), 'config.yml'), 'r') as config_file:
config = yaml.load(config_file, Loader=yaml.FullLoader)
Expand Down
1 change: 1 addition & 0 deletions src/sinol_make/commands/gen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def run(self, args: argparse.Namespace):

self.args = args
self.task_id = package_util.get_task_id()
package_util.validate_test_names(self.task_id)
self.ingen = gen_util.get_ingen(self.task_id, args.ingen_path)
print(util.info(f'Using ingen file {os.path.basename(self.ingen)}'))

Expand Down
13 changes: 5 additions & 8 deletions src/sinol_make/commands/gen/gen_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@ def ingen_exists(task_id):
:param task_id: task id, for example abc
:return: True if exists, False otherwise
"""
return len(glob.glob(os.path.join(os.getcwd(), 'prog', task_id + 'ingen.*'))) > 0
return package_util.any_files_matching_pattern(task_id, f'{task_id}ingen.*')


def get_ingen(task_id=None, ingen_path=None):
def get_ingen(task_id, ingen_path=None):
"""
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. If None, then
will return any ingen matching "*ingen.*"
: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
"""
Expand All @@ -35,9 +34,7 @@ def get_ingen(task_id=None, ingen_path=None):
else:
util.exit_with_error(f'Ingen source file {ingen_path} does not exist.')

if task_id is None:
task_id = '*'
ingen = glob.glob(os.path.join(os.getcwd(), 'prog', task_id + 'ingen.*'))
ingen = package_util.get_files_matching_pattern(task_id, f'{task_id}ingen.*')
if len(ingen) == 0:
util.exit_with_error(f'Ingen source file for task {task_id} does not exist.')

Expand Down Expand Up @@ -78,7 +75,7 @@ def get_correct_solution(task_id):
:param task_id: task id, for example abc
:return: path to correct solution or None if not found
"""
correct_solution = glob.glob(os.path.join(os.getcwd(), 'prog', task_id + '.*'))
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]
Expand Down
5 changes: 3 additions & 2 deletions src/sinol_make/commands/inwer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def verify_and_print_table(self) -> Dict[str, TestResult]:
sorted_tests = sorted(self.tests, key=lambda x: x[0])
executions: List[InwerExecution] = []
for test in sorted_tests:
results[test] = TestResult(test)
results[test] = TestResult(test, self.task_id)
executions.append(InwerExecution(test, results[test].test_name, self.inwer_executable))

has_terminal, terminal_width, terminal_height = util.get_terminal_size()
Expand Down Expand Up @@ -123,6 +123,7 @@ def run(self, args: argparse.Namespace):
util.exit_if_not_package()

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)
if self.inwer is None:
if args.inwer_path is None:
Expand All @@ -133,7 +134,7 @@ def run(self, args: argparse.Namespace):
print(f'Verifying with inwer {util.bold(relative_path)}')

self.cpus = args.cpus or mp.cpu_count()
self.tests = package_util.get_tests(args.tests)
self.tests = package_util.get_tests(self.task_id, args.tests)

if len(self.tests) == 0:
util.exit_with_error('No tests found.')
Expand Down
2 changes: 1 addition & 1 deletion src/sinol_make/commands/inwer/inwer_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def get_inwer_path(task_id: str, path = None) -> Union[str, None]:
Returns path to inwer executable for given task or None if no inwer was found.
"""
if path is None:
inwers = glob.glob(os.path.join(os.getcwd(), 'prog', f'{task_id}inwer.*'))
inwers = package_util.get_files_matching_pattern(task_id, f'{task_id}inwer.*')
if len(inwers) == 0:
return None
return inwers[0]
Expand Down
4 changes: 2 additions & 2 deletions src/sinol_make/commands/inwer/structs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ class TestResult:
valid: bool
output: str

def __init__(self, test_path):
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))
self.test_group = str(package_util.get_group(self.test_path, task_id))

self.verified = False
self.valid = False
Expand Down
67 changes: 35 additions & 32 deletions src/sinol_make/commands/run/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def update_group_status(group_status, new_status):
return group_status


def print_view(term_width, term_height, program_groups_scores, all_results, print_data: PrintData, names, executions,
def print_view(term_width, term_height, task_id, program_groups_scores, all_results, print_data: PrintData, names, executions,
groups, scores, tests, possible_score, cpus, hide_memory, config, contest, args):
width = term_width - 13 # First column has 6 characters, the " | " separator has 3 characters and 4 for margin
programs_in_row = width // 13 # Each program has 10 characters and the " | " separator has 3 characters
Expand All @@ -79,7 +79,7 @@ def print_view(term_width, term_height, program_groups_scores, all_results, prin
for solution in names:
lang = package_util.get_file_lang(solution)
for test in tests:
time_sum += package_util.get_time_limit(test, config, lang, args)
time_sum += package_util.get_time_limit(test, config, lang, task_id, args)

time_remaining = (len(executions) - print_data.i - 1) * 2 * time_sum / cpus / 1000.0
title = 'Done %4d/%4d. Time remaining (in the worst case): %5d seconds.' \
Expand Down Expand Up @@ -122,17 +122,17 @@ def print_table_end():
if results[test].Time is not None:
if program_times[program][0] < results[test].Time:
program_times[program] = (results[test].Time, package_util.get_time_limit(test, config,
lang, args))
lang, task_id, args))
elif status == Status.TL:
program_times[program] = (2 * package_util.get_time_limit(test, config, lang, args),
package_util.get_time_limit(test, config, lang, args))
program_times[program] = (2 * package_util.get_time_limit(test, config, lang, task_id, args),
package_util.get_time_limit(test, config, lang, task_id, args))
if results[test].Memory is not None:
if program_memory[program][0] < results[test].Memory:
program_memory[program] = (results[test].Memory, package_util.get_memory_limit(test, config,
lang, args))
lang, task_id, args))
elif status == Status.ML:
program_memory[program] = (2 * package_util.get_memory_limit(test, config, lang, args),
package_util.get_memory_limit(test, config, lang, args))
program_memory[program] = (2 * package_util.get_memory_limit(test, config, lang, task_id, args),
package_util.get_memory_limit(test, config, lang, task_id, args))
if status == Status.PENDING:
group_status = Status.PENDING
else:
Expand Down Expand Up @@ -187,29 +187,29 @@ def print_group_seperator():

last_group = None
for test in tests:
group = package_util.get_group(test)
group = package_util.get_group(test, task_id)
if last_group != group:
if last_group is not None:
print_group_seperator()
last_group = group

print(margin + "%6s" % package_util.extract_test_id(test), end=" | ")
print(margin + "%6s" % package_util.extract_test_id(test, task_id), end=" | ")
for program in program_group:
lang = package_util.get_file_lang(program)
result = all_results[program][package_util.get_group(test)][test]
result = all_results[program][package_util.get_group(test, task_id)][test]
status = result.Status
if status == Status.PENDING: print(10 * ' ', end=" | ")
else:
print("%3s" % colorize_status(status),
("%17s" % color_time(result.Time, package_util.get_time_limit(test, config, lang, args)))
("%17s" % color_time(result.Time, package_util.get_time_limit(test, config, lang, task_id, args)))
if result.Time is not None else 7*" ", end=" | ")
print()
if not hide_memory:
print(8*" ", end=" | ")
for program in program_group:
lang = package_util.get_file_lang(program)
result = all_results[program][package_util.get_group(test)][test]
print(("%20s" % color_memory(result.Memory, package_util.get_memory_limit(test, config, lang, args)))
result = all_results[program][package_util.get_group(test, task_id)][test]
print(("%20s" % color_memory(result.Memory, package_util.get_memory_limit(test, config, lang, task_id, args)))
if result.Memory is not None else 10*" ", end=" | ")
print()

Expand Down Expand Up @@ -277,9 +277,9 @@ def extract_file_name(self, file_path):


def get_group(self, test_path):
if package_util.extract_test_id(test_path).endswith("ocen"):
if package_util.extract_test_id(test_path, self.ID).endswith("ocen"):
return 0
return int("".join(filter(str.isdigit, package_util.extract_test_id(test_path))))
return int("".join(filter(str.isdigit, package_util.extract_test_id(test_path, self.ID))))


def get_executable_key(self, executable):
Expand Down Expand Up @@ -604,7 +604,7 @@ def run_solution(self, data_for_execution: ExecutionData):
"""

(name, executable, test, time_limit, memory_limit, timetool_path) = data_for_execution
file_no_ext = paths.get_executions_path(name, package_util.extract_test_id(test))
file_no_ext = paths.get_executions_path(name, package_util.extract_test_id(test, self.ID))
output_file = file_no_ext + ".out"
result_file = file_no_ext + ".res"
hard_time_limit_in_s = math.ceil(2 * time_limit / 1000.0)
Expand Down Expand Up @@ -640,8 +640,10 @@ def run_solutions(self, compiled_commands, names, solutions):
lang = package_util.get_file_lang(name)
if result:
for test in self.tests:
executions.append((name, executable, test, package_util.get_time_limit(test, self.config, lang, self.args),
package_util.get_memory_limit(test, self.config, lang, self.args), self.timetool_path))
executions.append((name, executable, test,
package_util.get_time_limit(test, self.config, lang, self.ID, self.args),
package_util.get_memory_limit(test, self.config, lang, self.ID, self.args),
self.timetool_path))
all_results[name][self.get_group(test)][test] = ExecutionResult(Status.PENDING)
os.makedirs(paths.get_executions_path(name), exist_ok=True)
else:
Expand All @@ -658,8 +660,8 @@ def run_solutions(self, compiled_commands, names, solutions):
run_event = threading.Event()
run_event.set()
thr = threading.Thread(target=printer.printer_thread,
args=(run_event, print_view, program_groups_scores, all_results, print_data, names,
executions, self.groups, self.scores, self.tests, self.possible_score,
args=(run_event, print_view, self.ID, program_groups_scores, all_results, print_data,
names, executions, self.groups, self.scores, self.tests, self.possible_score,
self.cpus, self.args.hide_memory, self.config, self.contest, self.args))
thr.start()

Expand All @@ -681,7 +683,7 @@ def run_solutions(self, compiled_commands, names, solutions):
run_event.clear()
thr.join()

print("\n".join(print_view(terminal_width, terminal_height, program_groups_scores, all_results, print_data,
print("\n".join(print_view(terminal_width, terminal_height, self.ID, program_groups_scores, all_results, print_data,
names, executions, self.groups, self.scores, self.tests, self.possible_score,
self.cpus, self.args.hide_memory, self.config, self.contest, self.args)[0]))

Expand Down Expand Up @@ -738,15 +740,15 @@ 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():
group = package_util.get_group(test)
for test in package_util.get_tests(self.ID):
group = package_util.get_group(test, self.ID)
if group not in group_sizes:
group_sizes[group] = 0
group_sizes[group] += 1

run_group_sizes = {}
for test in self.tests:
group = package_util.get_group(test)
group = package_util.get_group(test, self.ID)
if group not in run_group_sizes:
run_group_sizes[group] = 0
run_group_sizes[group] += 1
Expand Down Expand Up @@ -1027,7 +1029,7 @@ def exit(self):
cnt=len(self.failed_compilations), letter='' if len(self.failed_compilations) == 1 else 's'))

def set_scores(self):
self.tests = package_util.get_tests(self.args.tests)
self.tests = package_util.get_tests(self.ID, self.args.tests)
self.groups = self.get_groups(self.tests)
self.scores = collections.defaultdict(int)

Expand Down Expand Up @@ -1071,10 +1073,10 @@ def get_valid_input_files(self):
Returns list of input files that have corresponding output file.
"""
output_tests = glob.glob(os.path.join(os.getcwd(), "out", "*.out"))
output_tests_ids = [package_util.extract_test_id(test) for test in output_tests]
output_tests_ids = [package_util.extract_test_id(test, self.ID) for test in output_tests]
valid_input_files = []
for test in self.tests:
if package_util.extract_test_id(test) in output_tests_ids:
if package_util.extract_test_id(test, self.ID) in output_tests_ids:
valid_input_files.append(test)
return valid_input_files

Expand Down Expand Up @@ -1129,6 +1131,7 @@ def run(self, args):
util.exit_if_not_package()

self.set_constants()
package_util.validate_test_names(self.ID)
self.args = args
with open(os.path.join(os.getcwd(), "config.yml"), 'r') as config:
try:
Expand All @@ -1150,7 +1153,7 @@ def run(self, args):
print("Task: %s (tag: %s)" % (title, self.ID))
self.cpus = args.cpus or mp.cpu_count()

checker = glob.glob(os.path.join(os.getcwd(), "prog", f'{self.ID}chk.*'))
checker = package_util.get_files_matching_pattern(self.ID, f'{self.ID}chk.*')
if len(checker) != 0:
print(util.info("Checker found: %s" % os.path.basename(checker[0])))
self.checker = checker[0]
Expand All @@ -1163,7 +1166,7 @@ def run(self, args):
else:
self.checker = None

lib = glob.glob(os.path.join(os.getcwd(), "prog", f'{self.ID}lib.*'))
lib = package_util.get_files_matching_pattern(self.ID, f'{self.ID}lib.*')
self.has_lib = len(lib) != 0

self.set_scores()
Expand All @@ -1176,8 +1179,8 @@ def run(self, args):
lang = package_util.get_file_lang(solution)
for test in self.tests:
# The functions will exit if the limits are not set
_ = package_util.get_time_limit(test, self.config, lang, self.args)
_ = package_util.get_memory_limit(test, self.config, lang, self.args)
_ = package_util.get_time_limit(test, self.config, lang, self.ID, self.args)
_ = package_util.get_memory_limit(test, self.config, lang, self.ID, self.args)

results, all_results = self.compile_and_run(solutions)
self.check_errors(all_results)
Expand Down
Loading

0 comments on commit fe6797e

Please sign in to comment.