""" Author -- Michael Widrich, Andreas Schörgenhumer Contact -- schoergenhumer@ml.jku.at Date -- 04.03.2022 ############################################################################### The following copyright statement applies to all code within this file. Copyright statement: This material, no matter whether in printed or electronic form, may be used for personal and non-commercial educational use only. Any reproduction of this manuscript, no matter whether as a whole or in parts, no matter whether in printed or in electronic form, requires explicit prior acceptance of the authors. ############################################################################### Images taken from: https://pixabay.com/ """ import hashlib import os import shutil import sys from glob import glob import dill as pkl def print_outs(outs, line_token="-"): print(line_token * 40) print(outs, end="" if isinstance(outs, str) and outs.endswith("\n") else "\n") print(line_token * 40) ex_file = "ex2.py" full_points = 15 points = full_points python = sys.executable solutions_dir = os.path.join("unittest", "solutions") outputs_dir = os.path.join("unittest", "outputs") # Remove previous outputs folder shutil.rmtree(outputs_dir, ignore_errors=True) inputs = sorted(glob(os.path.join("unittest", "unittest_input_*"), recursive=True)) if not len(inputs): raise FileNotFoundError("Could not find unittest_input_* files") with open(os.path.join(solutions_dir, "counts.pkl"), "rb") as f: sol_counts = pkl.load(f) for test_i, input_folder in enumerate(inputs): comment = "" fcall = "" with open(os.devnull, "w") as null: # sys.stdout = null try: from ex2 import validate_images proper_import = True except Exception as e: outs = "" errs = e points -= full_points / len(inputs) proper_import = False finally: sys.stdout.flush() sys.stdout = sys.__stdout__ if proper_import: with open(os.devnull, "w") as null: # sys.stdout = null try: input_basename = os.path.basename(input_folder) output_dir = os.path.join(outputs_dir, input_basename) logfilepath = output_dir + ".log" formatter = "06d" counts = validate_images(input_dir=input_folder, output_dir=output_dir, log_file=logfilepath, formatter=formatter) fcall = f'validate_images(\n\tinput_dir="{input_folder}",\n\toutput_dir="{output_dir}",\n\tlog_file="{logfilepath}",\n\tformatter="{formatter}"\n)' errs = "" try: with open(os.path.join(outputs_dir, f"{input_basename}.log"), "r") as lfh: logfile = lfh.read() except FileNotFoundError: # two cases: # 1) no invalid files and thus no log file -> ok -> equal to empty tlogfile # 2) invalid files but no log file -> not ok -> will fail the comparison with tlogfile (below) logfile = "" with open(os.path.join(solutions_dir, f"{input_basename}.log"), "r") as lfh: # must replace the separator that was used when creating the solution files tlogfile = lfh.read().replace("\\", os.path.sep) files = sorted(glob(os.path.join(outputs_dir, input_basename, "**", "*"), recursive=True)) hashing_function = hashlib.sha256() for file in files: with open(file, "rb") as fh: hashing_function.update(fh.read()) hash = hashing_function.digest() hashing_function = hashlib.sha256() tfiles = sorted(glob(os.path.join(solutions_dir, input_basename, "**", "*"), recursive=True)) for file in tfiles: with open(file, "rb") as fh: hashing_function.update(fh.read()) thash = hashing_function.digest() tcounts = sol_counts[input_basename] if not counts == tcounts: points -= full_points / len(inputs) comment = f"Function should return {tcounts} but returned {counts}" elif not [f.split(os.path.sep)[-2:] for f in files] == [f.split(os.path.sep)[-2:] for f in tfiles]: points -= full_points / len(inputs) comment = f"Contents of output directory do not match (see directory 'solutions')" elif not hash == thash: points -= full_points / len(inputs) comment = f"Hash value of the files in the output directory do not match (see directory 'solutions')" elif not logfile == tlogfile: points -= full_points / len(inputs) comment = f"Contents of logfiles do not match (see directory 'solutions')" except Exception as e: outs = "" errs = e points -= full_points / len(inputs) finally: sys.stdout.flush() sys.stdout = sys.__stdout__ print() print_outs(f"Test {test_i}", line_token="#") print("Function call:") print_outs(fcall) if errs: print(f"Some unexpected errors occurred:") print_outs(f"{type(errs).__name__}: {errs}") else: print("Notes:") print_outs("No issues found" if comment == "" else comment) # due to floating point calculations it could happen that we get -0 here if points < 0: assert abs(points) < 1e-7, f"points were {points} < 0: error when subtracting points?" points = abs(points) print(f"Current points: {points:.2f}") print(f"\nEstimated points upon submission: {points:.2f} (out of {full_points:.2f})") if points < full_points: print(f"Check the folder '{outputs_dir}' to see where your errors are") else: shutil.rmtree(os.path.join(outputs_dir)) print(f"This is only an estimate, see 'Instructions for submitting homework' in Moodle " f"for common mistakes that can still lead to 0 points.")