From 40fdbbc46f615bc53df3764286f7a3ea48f07d1e Mon Sep 17 00:00:00 2001 From: Mateusz Masiarz Date: Thu, 16 Nov 2023 23:39:56 +0100 Subject: [PATCH] Allow solutions with long names (#133) * Allow solutions with long names * Fix printing * Add package with long names of solutions * Add tests --- src/sinol_make/commands/run/__init__.py | 74 ++++++++++++------- src/sinol_make/helpers/package_util.py | 2 +- tests/commands/run/test_integration.py | 17 +++-- tests/packages/long_solution_names/config.yml | 45 +++++++++++ .../packages/long_solution_names/in/.gitkeep | 0 .../packages/long_solution_names/out/.gitkeep | 0 .../packages/long_solution_names/prog/lsn.cpp | 9 +++ .../prog/lsn1sol_long_name.cpp | 12 +++ .../prog/lsnb10sol_long_name.cpp | 34 +++++++++ .../long_solution_names/prog/lsningen.cpp | 18 +++++ .../prog/lsns1sol_long_name.cpp | 20 +++++ .../prog/lsnsol_long_name.cpp | 17 +++++ tests/util.py | 7 ++ 13 files changed, 222 insertions(+), 33 deletions(-) create mode 100644 tests/packages/long_solution_names/config.yml create mode 100644 tests/packages/long_solution_names/in/.gitkeep create mode 100644 tests/packages/long_solution_names/out/.gitkeep create mode 100644 tests/packages/long_solution_names/prog/lsn.cpp create mode 100644 tests/packages/long_solution_names/prog/lsn1sol_long_name.cpp create mode 100644 tests/packages/long_solution_names/prog/lsnb10sol_long_name.cpp create mode 100644 tests/packages/long_solution_names/prog/lsningen.cpp create mode 100644 tests/packages/long_solution_names/prog/lsns1sol_long_name.cpp create mode 100644 tests/packages/long_solution_names/prog/lsnsol_long_name.cpp diff --git a/src/sinol_make/commands/run/__init__.py b/src/sinol_make/commands/run/__init__.py index 7d577a15..b30ab72e 100644 --- a/src/sinol_make/commands/run/__init__.py +++ b/src/sinol_make/commands/run/__init__.py @@ -61,8 +61,11 @@ def update_group_status(group_status, new_status): 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 + width = term_width - 11 # First column has 6 characters, the " | " separator has 3 characters and 2 for margin + # First column has 11 characters and each solution has 13 characters and the " | " separator has 3 characters + programs_in_row = width // 16 + if programs_in_row == 0: + return ["Terminal window is too small to display the results."], None, None previous_stdout = sys.stdout output = StringIO() @@ -94,20 +97,38 @@ def print_table_end(): print("-" * 8, end="-+-") for i in range(len(program_group)): if i != len(program_group) - 1: - print("-" * 10, end="-+-") + print("-" * 13, end="-+-") else: - print("-" * 10, end="-+") + print("-" * 13, end="-+") print() print_table_end() print(margin + "groups", end=" | ") - for program in program_group: - print("%10s" % program, end=" | ") - print() + next_row = {solution: solution for solution in program_group} + first = True + while next_row != {}: + if first: + first = False + else: + print(margin + " " * 6, end=" | ") + + for solution in program_group: + if solution in next_row: + to_print = next_row[solution] + if len(to_print) > 13: + print(to_print[:13], end=" | ") + next_row[solution] = to_print[13:] + else: + print(to_print.ljust(13), end=" | ") + del next_row[solution] + else: + print(" " * 13, end=" | ") + print() + print(8 * "-", end=" | ") for program in program_group: - print(10 * "-", end=" | ") + print(13 * "-", end=" | ") print() for group in groups: print(margin + "%6s" % group, end=" | ") @@ -141,13 +162,14 @@ def print_table_end(): points = contest.get_group_score(test_scores, scores[group]) if any([results[test].Status == Status.PENDING for test in results]): - print(" " * 3 + ("?" * len(str(scores[group]))).rjust(3) + + print(" " * 6 + ("?" * len(str(scores[group]))).rjust(3) + f'/{str(scores[group]).rjust(3)}', end=' | ') else: - print("%3s" % util.bold(util.color_green(group_status)) if group_status == Status.OK else util.bold( - util.color_red(group_status)), - "%3s/%3s" % (points, scores[group]), - end=" | ") + if group_status == Status.OK: + status_text = util.bold(util.color_green(group_status.ljust(6))) + else: + status_text = util.bold(util.color_red(group_status.ljust(6))) + print(f"{status_text}{str(points).rjust(3)}/{str(scores[group]).rjust(3)}", end=' | ') program_groups_scores[program][group] = {"status": group_status, "points": points} print() for program in program_group: @@ -155,35 +177,35 @@ def print_table_end(): print(8 * " ", end=" | ") for program in program_group: - print(10 * " ", end=" | ") + print(13 * " ", end=" | ") print() print(margin + "points", end=" | ") for program in program_group: - print(util.bold(" %3s/%3s" % (program_scores[program], possible_score)), end=" | ") + print(util.bold(" %3s/%3s" % (program_scores[program], possible_score)), end=" | ") print() print(margin + " time", end=" | ") for program in program_group: program_time = program_times[program] - print(util.bold(("%20s" % color_time(program_time[0], program_time[1])) + print(util.bold(("%23s" % color_time(program_time[0], program_time[1])) if program_time[0] < 2 * program_time[1] and program_time[0] >= 0 - else " " + 7 * '-'), end=" | ") + else " " + 7 * '-'), end=" | ") print() print(margin + "memory", end=" | ") for program in program_group: program_mem = program_memory[program] - print(util.bold(("%20s" % color_memory(program_mem[0], program_mem[1])) + print(util.bold(("%23s" % color_memory(program_mem[0], program_mem[1])) if program_mem[0] < 2 * program_mem[1] and program_mem[0] >= 0 - else " " + 7 * '-'), end=" | ") + else " " + 7 * '-'), end=" | ") print() print(8*" ", end=" | ") for program in program_group: - print(10*" ", end=" | ") + print(13*" ", end=" | ") print() def print_group_seperator(): print(8 * "-", end=" | ") for program in program_group: - print(10 * "-", end=" | ") + print(13 * "-", end=" | ") print() print_group_seperator() @@ -201,19 +223,19 @@ def print_group_seperator(): lang = package_util.get_file_lang(program) result = all_results[program][package_util.get_group(test, task_id)][test] status = result.Status - if status == Status.PENDING: print(10 * ' ', end=" | ") + if status == Status.PENDING: print(13 * ' ', end=" | ") else: print("%3s" % colorize_status(status), - ("%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=" | ") + ("%20s" % color_time(result.Time, package_util.get_time_limit(test, config, lang, task_id, args))) + if result.Time is not None else 10*" ", 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, 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(("%23s" % color_memory(result.Memory, package_util.get_memory_limit(test, config, lang, task_id, args))) + if result.Memory is not None else 13*" ", end=" | ") print() print_table_end() diff --git a/src/sinol_make/helpers/package_util.py b/src/sinol_make/helpers/package_util.py index e4bd8b5e..f0e3a0c2 100644 --- a/src/sinol_make/helpers/package_util.py +++ b/src/sinol_make/helpers/package_util.py @@ -54,7 +54,7 @@ def get_solutions_re(task_id: str) -> re.Pattern: Returns regex pattern matching all solutions for given task. :param task_id: Task id. """ - return re.compile(r"^%s[bs]?[0-9]*\.(cpp|cc|java|py|pas)$" % task_id) + return re.compile(r"^%s[bs]?[0-9]*(sol.*)?\.(cpp|cc|java|py|pas)$" % task_id) def get_executable_key(executable, task_id): diff --git a/tests/commands/run/test_integration.py b/tests/commands/run/test_integration.py index c38f4621..2953a82d 100644 --- a/tests/commands/run/test_integration.py +++ b/tests/commands/run/test_integration.py @@ -15,7 +15,7 @@ get_checker_package_path(), get_library_package_path(), get_library_string_args_package_path(), get_limits_package_path(), get_override_limits_package_path(), get_icpc_package_path(), - get_large_output_package_path()], + get_long_solution_names_package(), get_large_output_package_path()], indirect=True) def test_simple(create_package, time_tool): """ @@ -41,7 +41,8 @@ def test_simple(create_package, time_tool): @pytest.mark.parametrize("create_package", [get_simple_package_path(), get_verify_status_package_path(), get_checker_package_path(), get_library_package_path(), get_library_string_args_package_path(), get_limits_package_path(), - get_override_limits_package_path(), get_icpc_package_path()], + get_override_limits_package_path(), get_icpc_package_path(), + get_long_solution_names_package()], indirect=True) def test_wrong_solution(create_package, time_tool): """ @@ -101,6 +102,7 @@ def test_no_expected_scores(capsys, create_package, time_tool): out = capsys.readouterr().out assert "Solutions were added:" in out + assert "There was an unknown change in expected scores." not in out solution = package_util.get_files_matching_pattern(command.ID, f"{command.ID}.*")[0] assert os.path.basename(solution) in out @@ -109,9 +111,9 @@ def test_no_expected_scores(capsys, create_package, time_tool): get_checker_package_path(), get_library_package_path(), get_library_string_args_package_path(), get_limits_package_path(), get_override_limits_package_path(), get_icpc_package_path(), - get_large_output_package_path()], + get_long_solution_names_package(), get_large_output_package_path()], indirect=True) -def test_apply_suggestions(create_package, time_tool): +def test_apply_suggestions(create_package, time_tool, capsys): """ Test with no sinol_expected_scores in config.yml. Verifies that suggestions are applied. @@ -133,6 +135,8 @@ def test_apply_suggestions(create_package, time_tool): command = Command() command.run(args) + out = capsys.readouterr().out + assert "There was an unknown change in expected scores." with open(config_path, "r") as config_file: config = yaml.load(config_file, Loader=yaml.SafeLoader) assert config["sinol_expected_scores"] == expected_scores @@ -171,7 +175,7 @@ def test_incorrect_expected_scores(capsys, create_package, time_tool): @pytest.mark.parametrize("create_package", [get_simple_package_path(), get_checker_package_path(), get_library_package_path(), get_library_string_args_package_path(), - get_icpc_package_path()], + get_icpc_package_path(), get_long_solution_names_package()], indirect=True) def test_flag_tests(create_package, time_tool): """ @@ -567,7 +571,8 @@ def test_flag_tests_not_existing_tests(create_package, time_tool, capsys): @pytest.mark.parametrize("create_package", [get_simple_package_path(), get_verify_status_package_path(), get_checker_package_path(), get_library_package_path(), get_library_string_args_package_path(), get_limits_package_path(), - get_override_limits_package_path()], indirect=True) + get_override_limits_package_path(), get_long_solution_names_package()], + indirect=True) def test_results_caching(create_package, time_tool): """ Test if test results are cached. diff --git a/tests/packages/long_solution_names/config.yml b/tests/packages/long_solution_names/config.yml new file mode 100644 index 00000000..80ad7057 --- /dev/null +++ b/tests/packages/long_solution_names/config.yml @@ -0,0 +1,45 @@ +title: Package with long solution names +sinol_task_id: lsn +memory_limit: 16000 +time_limit: 1000 +scores: + 1: 25 + 2: 25 + 3: 25 + 4: 25 +sinol_expected_scores: + lsn.cpp: + expected: + 1: {points: 25, status: OK} + 2: {points: 25, status: OK} + 3: {points: 25, status: OK} + 4: {points: 25, status: OK} + points: 100 + lsn1sol_long_name.cpp: + expected: + 1: {points: 25, status: OK} + 2: {points: 25, status: OK} + 3: {points: 25, status: OK} + 4: {points: 0, status: WA} + points: 75 + lsnb10sol_long_name.cpp: + expected: + 1: {points: 25, status: OK} + 2: {points: 0, status: WA} + 3: {points: 0, status: WA} + 4: {points: 0, status: ML} + points: 25 + lsns1sol_long_name.cpp: + expected: + 1: {points: 25, status: OK} + 2: {points: 0, status: WA} + 3: {points: 0, status: WA} + 4: {points: 0, status: TL} + points: 25 + lsnsol_long_name.cpp: + expected: + 1: {points: 25, status: OK} + 2: {points: 25, status: OK} + 3: {points: 0, status: WA} + 4: {points: 0, status: RE} + points: 50 diff --git a/tests/packages/long_solution_names/in/.gitkeep b/tests/packages/long_solution_names/in/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tests/packages/long_solution_names/out/.gitkeep b/tests/packages/long_solution_names/out/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tests/packages/long_solution_names/prog/lsn.cpp b/tests/packages/long_solution_names/prog/lsn.cpp new file mode 100644 index 00000000..9d30bab0 --- /dev/null +++ b/tests/packages/long_solution_names/prog/lsn.cpp @@ -0,0 +1,9 @@ +#include + +using namespace std; + +int main() { + int a, b; + cin >> a >> b; + cout << a + b; +} diff --git a/tests/packages/long_solution_names/prog/lsn1sol_long_name.cpp b/tests/packages/long_solution_names/prog/lsn1sol_long_name.cpp new file mode 100644 index 00000000..c1b5782e --- /dev/null +++ b/tests/packages/long_solution_names/prog/lsn1sol_long_name.cpp @@ -0,0 +1,12 @@ +#include + +using namespace std; + +int main() { + int a, b; + cin >> a >> b; + if (a == 4) + cout << a + b + 1; + else + cout << a + b; +} diff --git a/tests/packages/long_solution_names/prog/lsnb10sol_long_name.cpp b/tests/packages/long_solution_names/prog/lsnb10sol_long_name.cpp new file mode 100644 index 00000000..129e4932 --- /dev/null +++ b/tests/packages/long_solution_names/prog/lsnb10sol_long_name.cpp @@ -0,0 +1,34 @@ +#include + +using namespace std; + +int rnd() { + return rand() % 100; +} + +int main() { + int a, b; + cin >> a >> b; + if (a == 1) + cout << a + b; + else if (a == 2 || a == 3) + cout << a + b + 2; + else if (a == 4) { + vector v; + for (int i = 0; i <= 10000; i++) { + int *tmp = new int[1000]; + for (int j = 0; j < 1000; j++) { + tmp[j] = rnd(); + } + v.push_back(tmp); + } + int s = 0; + for (auto i : v) { + for (int j = 0; j < 1000; j++) { + s = (s + i[j]) % 1000000007; + } + delete[] i; + } + cout << a + b; + } +} diff --git a/tests/packages/long_solution_names/prog/lsningen.cpp b/tests/packages/long_solution_names/prog/lsningen.cpp new file mode 100644 index 00000000..1794def2 --- /dev/null +++ b/tests/packages/long_solution_names/prog/lsningen.cpp @@ -0,0 +1,18 @@ +#include + +using namespace std; + +int main() { + ofstream f("lsn1a.in"); + f << "1 3\n"; + f.close(); + f.open("lsn2a.in"); + f << "2 5\n"; + f.close(); + f.open("lsn3a.in"); + f << "3 7\n"; + f.close(); + f.open("lsn4a.in"); + f << "4 9\n"; + f.close(); +} diff --git a/tests/packages/long_solution_names/prog/lsns1sol_long_name.cpp b/tests/packages/long_solution_names/prog/lsns1sol_long_name.cpp new file mode 100644 index 00000000..6979f775 --- /dev/null +++ b/tests/packages/long_solution_names/prog/lsns1sol_long_name.cpp @@ -0,0 +1,20 @@ +#include + +using namespace std; + +int main() { + int a, b; + cin >> a >> b; + if (a == 1) + cout << a + b; + else if (a == 2 || a == 3) + cout << a + b - 1; + else if (a == 4) { + time_t start = time(0); + int i = 0; + while (time(0) - start < 5) { + i++; + } + cout << a + b; + } +} diff --git a/tests/packages/long_solution_names/prog/lsnsol_long_name.cpp b/tests/packages/long_solution_names/prog/lsnsol_long_name.cpp new file mode 100644 index 00000000..f339f90d --- /dev/null +++ b/tests/packages/long_solution_names/prog/lsnsol_long_name.cpp @@ -0,0 +1,17 @@ +#include + +using namespace std; + +int main() { + int a, b; + cin >> a >> b; + if (a == 1 || a == 2) + cout << a + b; + else if (a == 3) + cout << a + b + 2; + else if (a == 4) { + int c = 0; + cout << a + b / c; + return 1; + } +} diff --git a/tests/util.py b/tests/util.py index 5b7d3fad..fd2d6d33 100644 --- a/tests/util.py +++ b/tests/util.py @@ -122,6 +122,13 @@ def get_icpc_package_path(): return os.path.join(os.path.dirname(__file__), "packages", "icpc") +def get_long_solution_names_package(): + """ + Get path to package with long solution names (/tests/packages/long_solution_names) + """ + return os.path.join(os.path.dirname(__file__), "packages", "long_solution_names") + + def get_large_output_package_path(): """ Get path to package with large output (/tests/packages/large_output)