From f7b18385e0fa65151fac903aced146a5bf9adebc Mon Sep 17 00:00:00 2001 From: SAVELIY BAKTURIN <96750568+zavierwaffle@users.noreply.github.com> Date: Thu, 9 Apr 2026 08:34:31 +0000 Subject: [PATCH] + TIFF test suite. --- .gitignore | 6 +- README.md | 3 +- main.py | 3 +- testdata/tiff/const.pnm | Bin 0 -> 47631 bytes testdata/tiff/const.tiff | Bin 0 -> 9746 bytes testdata/tiff/constg.pgm | Bin 0 -> 15887 bytes testdata/tiff/constg.tiff | Bin 0 -> 37420 bytes testsuites/suite.py | 50 +++++++++-- testsuites/tiff.py | 172 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 221 insertions(+), 13 deletions(-) create mode 100644 testdata/tiff/const.pnm create mode 100644 testdata/tiff/const.tiff create mode 100644 testdata/tiff/constg.pgm create mode 100644 testdata/tiff/constg.tiff create mode 100644 testsuites/tiff.py diff --git a/.gitignore b/.gitignore index cfa7910..b0b44c6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,10 @@ *.exe *.json *.out -*.log -*.sh +*.txt + +# User created files. +*.user # Python cache. __pycache__/ diff --git a/README.md b/README.md index eaf43ab..bcc7fa3 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ python main.py --executable-path <путь к исполняемому файлу> --suite <набор тестов> ``` - *Примечание*. В *nix-подобных системах команда `python` может быть недоступна, вместо неё используйте команду `python3`. + *Примечание*. В *nix-подобных системах команда `python` может быть недоступна, вместо неё используйте команду `python3`. 5. Для ускорения отладки рекомендуется создать скрипт, выполняющий шаги 3-4. 6. Для обновления тестов выполнить команду: @@ -35,6 +35,7 @@ |:--:|:--------------------|:-----------------| | 0 | Интро | `intro` | | 1 | Представление чисел | `fixed_floating` | +| 2 | TIFF | `tiff` | ## Скрипт запуска diff --git a/main.py b/main.py index 803074d..81cf129 100755 --- a/main.py +++ b/main.py @@ -5,12 +5,13 @@ import platform import testsuites.intro as intro import testsuites.fixed_floating as fixed_floating +import testsuites.tiff as tiff from testsuites import Testsuite, DynamicWrapper from typing import Dict, Any, List __TESTSUITES: List[Testsuite] = [ - intro.instance, fixed_floating.instance + intro.instance, fixed_floating.instance, tiff.instance ] __SUITENAMES: List[str] = [t.name() for t in __TESTSUITES] diff --git a/testdata/tiff/const.pnm b/testdata/tiff/const.pnm new file mode 100644 index 0000000000000000000000000000000000000000..521393feb53a0d5fcba7e9c1e638e50bfc1a01ec GIT binary patch literal 47631 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 Oe|Tkg152%v4)?|+E^Cp{>43ghqm1p?f9 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�Ofy|Ly^RUx$`wpb*a1% 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 literal 0 HcmV?d00001 diff --git a/testsuites/suite.py b/testsuites/suite.py index 02b1237..db233cb 100644 --- a/testsuites/suite.py +++ b/testsuites/suite.py @@ -46,6 +46,7 @@ class Log: class VerdictErrno(enum.Enum): ERROR_SUCCESS = "success" + ERROR_SKIPPED = "skipped" ERROR_RETURNCODE = "program returns wrong returncode" ERROR_ASSERTION = "assertion" ERROR_TIMEOUT = "timeout expired" @@ -53,6 +54,9 @@ class VerdictErrno(enum.Enum): ERROR_STDERR_IS_NOT_EMPTY = "standard error output is not empty" ERROR_TYPE_ERROR = "type error" ERROR_INVALID_FORMAT = "invalid format" + ERROR_FILE_NOT_FOUND = "file not found" + ERROR_STDOUT_EMPTY = "standard output is empty" + ERROR_STDOUT_IS_NOT_EMPTY = "standard output is not empty" ERROR_VALGRIND_MEMCHECK = "valgrind error" ERROR_GDB_ERROR = "GDB error" @@ -72,6 +76,9 @@ class Verdict: def is_failed(self) -> bool: return not self.is_success() + def is_skipped(self) -> bool: + return self.__verdict_errno == VerdictErrno.ERROR_SKIPPED + def verdict_message(self) -> str: return self.__verdict_errno.value @@ -86,9 +93,15 @@ class Verdict: return "no additional information" return self.__what + def what_presented(self) -> bool: + return not self.__what is None + def ok() -> Verdict: return Verdict(VerdictErrno.ERROR_SUCCESS) +def skip() -> Verdict: + return Verdict(VerdictErrno.ERROR_SKIPPED) + class DynamicWrapper(enum.Enum): NO_WRAPPER = "no" VALGRIND_ANALYZER = "valgrind" @@ -136,7 +149,7 @@ class Run: GDB_LOG_FILENAME = "gdb.log" GDB_NO_ERROR_MARKER = "exited normally" - def __init__(self, c_timeout: Union[float, int], c_stdin: Optional[str], c_args: Optional[List[str]], t_returncode_policy: ReturnCodePolicy, t_returncode: Optional[int] = None, t_stdout: Optional[str] = None, t_stderr_empty: bool = True): + def __init__(self, c_timeout: Union[float, int], c_stdin: Optional[str], c_args: Optional[List[str]], t_returncode_policy: ReturnCodePolicy, t_returncode: Optional[int] = None, t_stdout: Optional[str] = None, t_stderr_empty: bool = True, t_skip_when_dynamic_wrapper: Optional[DynamicWrapper] = None): self.__c_timeout = float(c_timeout) self.__c_stdin = c_stdin self.__c_args = c_args @@ -144,6 +157,7 @@ class Run: self.__t_returncode = t_returncode self.__t_stdout = t_stdout self.__t_stderr_empty = t_stderr_empty + self.__t_skip_when_dynamic_wrapper = t_skip_when_dynamic_wrapper def get_timeout(self) -> float: return self.__c_timeout @@ -182,6 +196,11 @@ class Run: def is_stderr_should_be_empty(self) -> bool: return self.__t_stderr_empty + def should_skip(self, dynamic_wrapper: DynamicWrapper) -> bool: + if self.__t_skip_when_dynamic_wrapper is None: + return False + return self.__t_skip_when_dynamic_wrapper == dynamic_wrapper + def run(self, executable_path: str, timeout_factor: float, dynamic_wrapper: DynamicWrapper) -> Optional[Runned]: cmd: List[str] = [] @@ -310,14 +329,16 @@ class Test: if policy == ReturnCodePolicy.ShouldBeZero: if actual_returncode != 0: - return Verdict(VerdictErrno.ERROR_RETURNCODE, f"expected {0}, but actual is {actual_returncode}") + what_suffix = f"" if is_empty_stderr else f" (below is what was in the stderr)" + return Verdict(VerdictErrno.ERROR_RETURNCODE, f"expected {0}, but actual is {actual_returncode}{what_suffix}", c_stderr) elif policy == ReturnCodePolicy.ShouldNotBeZero: if actual_returncode == 0: return Verdict(VerdictErrno.ERROR_RETURNCODE, f"expected non-zero returncode, but actual is {actual_returncode}") - elif policy == ReturnCodePolicy.MatchIfPresented and run.expected_returncode_presented(): + elif policy == ReturnCodePolicy.MatchIfPresented: expected_returncode = run.get_expected_returncode() if actual_returncode != expected_returncode: - return Verdict(VerdictErrno.ERROR_RETURNCODE, f"expected {expected_returncode}, but actual is {actual_returncode}") + what_suffix = f"" if is_empty_stderr else f" (below is what was in the stderr)" + return Verdict(VerdictErrno.ERROR_RETURNCODE, f"expected {expected_returncode}, but actual is {actual_returncode}{what_suffix}", c_stderr) return ok() @@ -326,7 +347,8 @@ class Test: def __print_verdict(self, i: int, verdict: Verdict, log: Log): log.println(f"{self.__base_message(i)} FAILED.") - log.println(f"{verdict.verdict_message().capitalize()}: {verdict.what()}.") + suffix = f": {verdict.what()}" if verdict.what_presented() else f"" + log.println(f"{verdict.verdict_message().capitalize()}{suffix}.") lines = verdict.extended_what() if verdict.extended_what_is_hint(): assert len(lines) == 1 @@ -339,6 +361,10 @@ class Test: for i, (run, expected) in enumerate(self.__sequence): log.println(self.__base_message(i)) + if run.should_skip(dynamic_wrapper): + log.println(f"{self.__base_message(i)} SKIPPED: {dynamic_wrapper.value} is not supported for this run.") + return skip() + runned = run.run(executable_path, timeout_factor, dynamic_wrapper) if runned is None: @@ -374,18 +400,24 @@ class Result: self.__tests = tests self.__verdicts: List[Verdict] = [] self.__passed = 0 + self.__skipped = 0 def add(self, verdict: Verdict): if verdict.is_success(): self.__passed += 1 + if verdict.is_skipped(): + self.__skipped += 1 self.__verdicts.append(verdict) def n(self) -> int: - return len(self.__verdicts) + return len(self.__verdicts) - self.__skipped def passed(self) -> int: return self.__passed + def skipped(self) -> int: + return self.__skipped + def exitcode(self) -> int: return 0 if self.n() == self.passed() else 1 @@ -401,8 +433,8 @@ class Result: def __get_total_by_category(self, category: str) -> int: total = 0 - for test, _ in zip(self.__tests, self.__verdicts): - if category in test.categories(): + for test, verdict in zip(self.__tests, self.__verdicts): + if not verdict.is_skipped() and category in test.categories(): total += 1 return total @@ -505,7 +537,7 @@ class Tester: end = now() print("=" * 30) - print(f"{result.passed()}/{result.n()} tests passed in {end - start}ms") + print(f"{result.passed()}/{result.n()} ({result.skipped()} skipped) tests passed in {end - start}ms") return result diff --git a/testsuites/tiff.py b/testsuites/tiff.py new file mode 100644 index 0000000..79a053f --- /dev/null +++ b/testsuites/tiff.py @@ -0,0 +1,172 @@ +from .suite import Tester + +from .suite import Run, Runned, Verdict +from testsuites import * + +class __TIFF(Testsuite): + __SUITE_NAME = "tiff" + __TIMEOUT = 1 + __CATEGORIES_TO_ENVNAMES = { "rgb": "RGB", "gray": "GRAY", "neg": "NEG" } + __TESTDATA = os.path.join("testdata", __SUITE_NAME) + + def __init__(self): + super().__init__(self.__SUITE_NAME, PREFIX_ENVIRONMENT_NAME, self.__CATEGORIES_TO_ENVNAMES) + + def get_tester(self) -> Tester: + tester = Tester(self.name()) + + class __Expected(Expected): + __OFFSET_TO_SHOW = 16 + + def __init__(self, output_filename: str, reference_filename: str): + super().__init__() + + self.__output_filename = output_filename + self.__reference_filename = reference_filename + + def test(self, run: Run, runned: Runned) -> Verdict: + if runned.get_stdout() != "": + return Verdict(VerdictErrno.ERROR_STDOUT_IS_NOT_EMPTY, f"below is what was in the stdout", runned.get_stdout()) + + if not os.path.exists(self.__output_filename): + return Verdict(VerdictErrno.ERROR_FILE_NOT_FOUND, f"'{self.__output_filename}' is not created by user's program") + + output = open(self.__output_filename, "rb").read() + reference = open(self.__reference_filename, "rb").read() + + n_output = len(output) + n_reference = len(reference) + n = min(n_reference, n_output) + + verdict_errno = VerdictErrno.ERROR_SUCCESS + what: Optional[str] = None + what_extended: List[str] = [] + fail = False + + for i in range(n): + if output[i] == reference[i]: + continue + + verdict_errno = VerdictErrno.ERROR_ASSERTION + + what = "wrong converting result" + + what_extended.append(f"Comparing '{self.__output_filename}' and '{self.__reference_filename}'...") + if i != 0: + what_extended.append(f". . . . . .") + + k = min(i + self.__OFFSET_TO_SHOW, n) + for j in range(i, k): + what_extended.append("%08X: %02X %02X" % (j, output[j], reference[j])) + + if (k - i) == self.__OFFSET_TO_SHOW: + what_extended.append(f". . . . . .") + + fail = True + + break + + is_reference_bigger = n_reference > n_output + if n_output != n_reference: + if not fail: + verdict_errno = VerdictErrno.ERROR_ASSERTION + what = "wrong file size" + if is_reference_bigger: + what_extended.append(f"Reference file '{self.__reference_filename}' is bigger, than actual file '{self.__output_filename}'.") + else: + what_extended.append(f"Actual file '{self.__output_filename}' is bigger, than reference file '{self.__reference_filename}'.") + + return Verdict(verdict_errno, what, what_extended) + + def __make_path_to(filename: str) -> str: + return os.path.abspath(os.path.join(self.__TESTDATA, filename)) + + def __single_test(input_basename: str, reference_basename: Optional[str], returncode: int = ERROR_SUCCESS, output_filename_override: Optional[str] = None, skip_when_dynamic_wrapper: Optional[DynamicWrapper] = None) -> SingleTest: + input_filename = __make_path_to(input_basename) + output_filename = __make_path_to(f"output.user") if output_filename_override is None else output_filename_override + reference_filename = "" if reference_basename is None else __make_path_to(reference_basename) + neg = returncode != ERROR_SUCCESS + returncode_policy = ReturnCodePolicy.ShouldBeZero if not neg else ReturnCodePolicy.MatchIfPresented + stderr_empty = not neg + run = Run(c_timeout = self.__TIMEOUT, c_stdin = None, c_args = [input_filename, output_filename], t_returncode_policy = returncode_policy, t_returncode = returncode, t_stderr_empty = stderr_empty, t_skip_when_dynamic_wrapper = skip_when_dynamic_wrapper) + expected = None + if not neg: + expected = __Expected(output_filename, reference_filename) + return run, expected + + def __args_single_test(args: List[str]) -> SingleTest: + run = Run(c_timeout = self.__TIMEOUT, c_stdin = None, c_args = args, t_returncode_policy = ReturnCodePolicy.MatchIfPresented, t_returncode = ERROR_ARGUMENTS_INVALID, t_stderr_empty = False, t_skip_when_dynamic_wrapper = DynamicWrapper.GDB_DEBUGGER) + return run, None + + def __neg_single_test(input_basename: str, returncode: int, output_filename_override: Optional[str]) -> SingleTest: + return __single_test(input_basename, None, returncode, output_filename_override, DynamicWrapper.GDB_DEBUGGER) + + def __sequence(input_basename: str, reference_basename: str) -> List[SingleTest]: + return [__single_test(input_basename, reference_basename)] + + def __neg_sequence(input_basename: str, returncode: int, output_filename_override: Optional[str]) -> List[SingleTest]: + return [__neg_single_test(input_basename, returncode, output_filename_override)] + + def __arg_sequence(args: List[str]) -> List[SingleTest]: + return [__args_single_test(args)] + + def __test(name: str, category: str, input_basename: str, reference_basename: str) -> Test: + return Test(name, [category], __sequence(input_basename, reference_basename)) + + def __neg_test(name: str, input_basename: str, returncode: int, output_filename_override: Optional[str]) -> Test: + category = "neg" + return Test(name, [category], __neg_sequence(input_basename, returncode, output_filename_override)) + + def __arg_test(name: str, args: List[str]) -> Test: + category = "neg" + return Test(name, [category], __arg_sequence(args)) + + category_counter: Dict[str, int] = dict((k, 0) for k in self.__CATEGORIES_TO_ENVNAMES.keys()) + + def __testname(category: str, prefix: str, input_basename: str): + category_counter[category] += 1 + + test_basename = input_basename.removesuffix(".tiff") + test_basename = test_basename.removesuffix(".pnm") + test_basename = test_basename.removeprefix(prefix) + test_basename = test_basename.removeprefix("_") + + name_prefix = f"Picture [format = {category}] #{category_counter[category]}" + name_suffix = f"" if test_basename == "" else f": {" ".join(test_basename.split("_")).capitalize()}" + + return f"{name_prefix}{name_suffix}" + + def t1(category: str, prefix: str, input_basename: str, reference_basename: str): + tester.add(__test(__testname(category, prefix, input_basename), category, input_basename, reference_basename)) + + def t(category: str, prefix: str, exception_basename: Set[str] = set(), exception_prefixes: Set[str] = set()): + reference_basename = f"{prefix}.{"pnm" if category == "rgb" else "pgm"}" + + for input_basename in os.listdir(self.__TESTDATA): + if not input_basename.startswith(prefix) or any(input_basename.startswith(p) for p in exception_prefixes): + continue + + if input_basename == reference_basename or input_basename in exception_basename: + continue + + t1(category, prefix, input_basename, reference_basename) + + def n(name: str, input_basename: str, returncode: int, output_filename_override: Optional[str] = None): + tester.add(__neg_test(name, input_basename, returncode, output_filename_override)) + + def na(name: str, args: List[str]): + tester.add(__arg_test(name, args)) + + t("rgb", "const", exception_prefixes = {"constg"}) + t("gray", "constg") + + n("No input file found", "this_file_does_not_exists.tiff", ERROR_CANNOT_OPEN_FILE) + n("Can't create output file", "const_tiff.pnm", ERROR_CANNOT_OPEN_FILE, os.path.abspath(os.path.join(self.__TESTDATA, "this_directory_does_not_exists", "output.pnm"))) + + na("No args", []) + na("1 arg provided", ["argument 1"]) + na("Too much arguments provided", ["argument 1", "argument 2", "argument 3"]) + + return tester + +instance = __TIFF()