diff --git a/src/sinol_make/commands/outgen/__init__.py b/src/sinol_make/commands/outgen/__init__.py index 4c77ad1e..c32b7c5a 100644 --- a/src/sinol_make/commands/outgen/__init__.py +++ b/src/sinol_make/commands/outgen/__init__.py @@ -8,7 +8,7 @@ from sinol_make import util 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 +from sinol_make.helpers import parsers, package_util, cache, paths from sinol_make.interfaces.BaseCommand import BaseCommand @@ -61,7 +61,8 @@ def generate_outputs(self, outputs_to_generate): def calculate_md5_sums(self, tests=None): """ Calculates md5 sums for each test. - :return: Tuple (dictionary of md5 sums, list of outputs tests that need to be generated) + :return: Tuple (dictionary of md5 sums, list of outputs tests that need to be generated, + 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')) @@ -77,6 +78,7 @@ def calculate_md5_sums(self, tests=None): md5_sums = {} outputs_to_generate = [] + from_inputs = [] for file in tests: basename = os.path.basename(file) output_basename = os.path.splitext(os.path.basename(basename))[0] + '.out' @@ -85,11 +87,25 @@ def calculate_md5_sums(self, tests=None): 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) 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) - return md5_sums, outputs_to_generate + return md5_sums, outputs_to_generate, from_inputs + + def clean_cache(self, inputs): + """ + Cleans cache for the given input files. + """ + md5_sums = [util.get_file_md5(file) for file in inputs] + for solution in glob.glob(paths.get_cache_path("md5sums", "*")): + sol_cache = cache.get_cache_file(solution) + for input in md5_sums: + if input in sol_cache.tests: + del sol_cache.tests[input] + sol_cache.save(solution) def run(self, args: argparse.Namespace): args = util.init_package_command(args) @@ -101,10 +117,11 @@ def run(self, args: argparse.Namespace): cache.check_correct_solution(self.task_id) self.correct_solution = get_correct_solution(self.task_id) - md5_sums, outputs_to_generate = self.calculate_md5_sums() + 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.args.compile_mode) self.generate_outputs(outputs_to_generate) diff --git a/tests/commands/gen/test_integration.py b/tests/commands/gen/test_integration.py index 333f9847..da8c5fa7 100644 --- a/tests/commands/gen/test_integration.py +++ b/tests/commands/gen/test_integration.py @@ -9,6 +9,7 @@ from sinol_make.commands.ingen import Command as IngenCommand from sinol_make.commands.ingen.ingen_util import get_ingen from sinol_make.commands.outgen import Command as OutgenCommand +from sinol_make.commands.run import Command as RunCommand from sinol_make.helpers import package_util, paths, cache from tests.fixtures import * from tests import util @@ -306,3 +307,33 @@ def test_dangling_input_files(create_package): simple_run(["prog/geningen6.cpp"], command="ingen") assert not os.path.exists(os.path.join(create_package, "in", "gen1.in")) assert os.path.exists(os.path.join(create_package, "in", "gen2.in")) + + +@pytest.mark.parametrize("create_package", [util.get_simple_package_path()], indirect=True) +def test_outgen_cache_cleaning(create_package, capsys): + """ + Test if cache is cleaned after running outgen. + """ + simple_run(command="gen") + parser = configure_parsers() + args = parser.parse_args(["run"]) + RunCommand().run(args) + + with open(os.path.join(create_package, "prog", "abcingen.cpp"), "r") as f: + code = f.read().replace("1 3", "1 4") + with open(os.path.join(create_package, "prog", "abcingen.cpp"), "w") as f: + f.write(code) + + simple_run(command="ingen") + + # Run should fail, because input file was changed, but output file was not regenerated. + with pytest.raises(SystemExit) as e: + RunCommand().run(args) + assert e.type == SystemExit + assert e.value.code == 1 + out = capsys.readouterr().out + assert "Solution abc.cpp passed group 1 with status WA while it should pass with status OK." in out + + simple_run(command="outgen") + # Run should pass, because output file was regenerated and cache for this test was cleaned. + RunCommand().run(args)