diff --git a/__pycache__/ex6_recursion.cpython-312.pyc b/__pycache__/ex6_recursion.cpython-312.pyc new file mode 100644 index 0000000..6f5f897 Binary files /dev/null and b/__pycache__/ex6_recursion.cpython-312.pyc differ diff --git a/ex6_recursion.md b/ex6_recursion.md new file mode 100644 index 0000000..9799818 --- /dev/null +++ b/ex6_recursion.md @@ -0,0 +1,23 @@ +# Aufgabe 6 - Rekursion - 20P + +## a - `sum_of_subtree` - 10P + +- Basisfall `None` [3P] +- Rekursiver Fall: + - Aufruf der Funktion auf `left` und `right` [3P] + - Addition der Teilbaum Summen [1P] + - Setzen von `tree.mark` [2P] + - Rückgabe der Summe [1P] + +- Pattern Matching nicht verwendet [0P] + +## b - `cut_at` - 10P + +- Basisfall `None` [3P] +- Basisfall `mark == at` [3P] + - Dieser Fall konnte auch mit dem Rekursiven Fall kombiniert werden (`if mark == at` ist okay :)) +- Rekursiver Fall: + - Aufruf der Funktion auf `left` und `right` [3P] + - Rückgabe des neuen Baumes [1P] + +- Pattern Matching nicht verwendet [0P] diff --git a/ex6_recursion.py b/ex6_recursion.py new file mode 100644 index 0000000..40626b4 --- /dev/null +++ b/ex6_recursion.py @@ -0,0 +1,49 @@ +# Aufgabe 6: Rekursion +from dataclasses import dataclass +from typing import Optional + + +@dataclass +class Node[T]: + mark: T + left: Optional["Node[T]"] = None + right: Optional["Node[T]"] = None + + +type BTree[T] = Optional[Node[T]] + + +# a) +def sum_of_subtree(tree: BTree[int]) -> int: + match tree: + case None: + return 0 + case Node(_, left, right): + tree.mark += sum_of_subtree(left) + sum_of_subtree(right) + return tree.mark + + +# b) +def cut_at[T](tree: BTree[T], at: T) -> BTree[T]: + match tree: + case None: + return None + case Node(mark, _, _) if mark == at: + return None + case Node(mark, left, right): # f(mark) + return Node(mark, cut_at(left, at), cut_at(right, at)) + + +if __name__ == "__main__": + t1 = None + assert sum_of_subtree(t1) == 0 + assert t1 is None + + t3 = Node(1, Node(2, Node(3), Node(4)), Node(5, Node(6))) + assert sum_of_subtree(t3) == 21 + assert t3 == Node(21, Node(9, Node(3), Node(4)), Node(11, Node(6))) + + assert cut_at(None, 42) is None + t = Node(1, Node(2, Node(3), Node(4)), Node(3, Node(3), Node(6))) + assert cut_at(t, 1) is None + assert cut_at(t, 3) == Node(1, Node(2, None, Node(4))) diff --git a/exam.pdf b/exam.pdf new file mode 100644 index 0000000..e13e434 Binary files /dev/null and b/exam.pdf differ diff --git a/test14.py b/test14.py deleted file mode 100644 index 4d6c526..0000000 --- a/test14.py +++ /dev/null @@ -1,964 +0,0 @@ -from dataclasses import dataclass, field, fields, is_dataclass, InitVar -from types import NoneType, FunctionType, GenericAlias -from typing import get_args, Callable, Iterable, Iterator, Type, Optional, TypeAliasType, ForwardRef, Any, Literal -from enum import Enum -import unittest -from unittest import mock -from unittest.mock import patch -from functools import wraps -import signal -import io -import importlib -import importlib.util -import sys -import subprocess -import os -import shutil -import re -import math -import inspect -import json -import ast -import copy -import functools -import itertools - -output = "" - - -class TimeoutException(Exception): - pass - - -class Timeout: - def __init__(self, seconds, msg=None) -> None: - if msg is None: - msg = f"Test timed out after {seconds} seconds." - self.seconds = seconds - self.msg = msg - - def handle_timeout(self, signal, frame): - raise TimeoutException(self.msg) - - def __enter__(self): - signal.signal(signal.SIGALRM, self.handle_timeout) - signal.alarm(self.seconds) - - def __exit__(self, exc_type, exc_val, exc_tb): - signal.alarm(0) - -class IterablePure: - def __init__(self, i: Optional[Iterable] = None, repeat: bool = False): - if i is not None: - self.i = i - else: - def endless(): - c = 0 - while True: - yield c - c += 1 - self.i = endless() - - self.repeat = repeat - - def __iter__(self): - if self.repeat: - while True: - yield from self.i - else: - yield from self.i - - -def resolve_type_alias(t: Type) -> Type: - while isinstance(t, TypeAliasType | GenericAlias) and hasattr(t, "__value__"): - if isinstance(t, TypeAliasType): - t = t.__value__ - elif isinstance(t, GenericAlias): - t = t.__value__[*t.__args__] - return t - - -def check_annot(function: Callable, expected: list[Type], ret=None): - actual = function.__annotations__ - if "self" in actual: - del actual["self"] - return_type = actual["return"] if "return" in actual else None - actual = list(actual.values())[:-1] if "return" in actual else list(actual.values()) - for a, e in zip(actual, expected): - a = resolve_type_alias(a) - e = resolve_type_alias(e) - if a != e: - return False - return resolve_type_alias(ret) == resolve_type_alias(return_type) - - -def check_lambda_annotations(module, f_name: str, param_types: list[object], return_type: object = None) -> bool: - annotation = module.__annotations__[f_name] - if annotation.__name__ != 'Callable': - return False - - all_types = annotation.__args__ - types, ret_type = all_types[:-1], all_types[-1] - if ret_type != return_type: - return False - - for param, param_type in zip(types, param_types): - while isinstance(param, TypeAliasType): - param = param.__value__ - while isinstance(param_type, TypeAliasType): - param_type = param_type.__value__ - if param != param_type: - return False - return True - - -def check_attributes(o: object, inherited_attr: list[str], new_attr: list[str]) -> bool: - attr = [f.name for f in fields(o)] - if attr != inherited_attr + new_attr: - return False - - for attr in inherited_attr: - if attr in o.__annotations__: - return False - return True - - -def check_no_if_used(f: Callable) -> bool: - tree = ast.parse(inspect.getsource(f)) - for node in ast.walk(tree): - if isinstance(node, ast.If): - return False - return True - -def check_comprehension_used(f: Callable) -> bool: - source = inspect.getsource(f) - tree = ast.parse(source) - - for node in ast.walk(tree): - if isinstance(node, (ast.ListComp, ast.SetComp, ast.DictComp, ast.GeneratorExp)): - return True - return False - - -def check_no_sideeffects(f: Callable, *args) -> bool: - args_copy = tuple(copy.deepcopy(a) for a in args) - f(*args) - return args == args_copy - - -def check_function_used(f: Callable, name: str) -> bool: - source = inspect.getsource(f) - if source.startswith(4 * " "): - source = "\n".join(l[4:] for l in source.split("\n")) - return name in [c.func.id for c in ast.walk(ast.parse(source)) if isinstance(c, ast.Call) and isinstance(c.func, ast.Name)] - - -def check_function_not_used(f: Callable, name: str) -> bool: - source = inspect.getsource(f) - if source.startswith(4 * " "): - source = "\n".join(l[4:] for l in source.split("\n")) - return name not in [c.func.id for c in ast.walk(ast.parse(source)) if isinstance(c, ast.Call) and isinstance(c.func, ast.Name)] - - -def check_pattern_matching_used(f: Callable) -> bool: - source = inspect.getsource(f) - tree = ast.parse(source) - return any(isinstance(node, ast.Match) for node in ast.walk(tree)) - - -def check_gen(g: Iterator, res: Iterable, check_end: bool = True, max: int = 100) -> bool: - if not isinstance(g, Iterator): - return False - for i, x in enumerate(res): - el = next(g) - if el != x: - return False - if i >= max: - break - if check_end: - try: - next(g) - except StopIteration: - return True - return False - return True - - -def check_only_one_line(f: Callable) -> bool: - if f.__name__ == '': - return True - inspect.getsource(f) - lines = [line for line in inspect.getsource(f).split('\n') if not line.strip().startswith('#') and line.strip() != ''] - return len(lines) == 2 - - -def mock_print(*args, sep=None, end=None, **_kwargs): - global output - if sep is None: - sep = " " - if end is None: - end = "\n" - args = [str(a) for a in args] - output += str(sep.join(args)) + end - - -def mock_input(*arg: str): - inputs = [s for s in arg] - - def input_function(arg=None): - if arg is None: - arg = "" - nonlocal inputs - if len(inputs) == 0: - raise Exception("More input asked than given") - p = inputs[0] - inputs = inputs[1:] - mock_print(arg, p, sep="") - return p - return input_function - - -def input_error(*arg): - raise Exception("Input is not allowed here") - - -@patch("builtins.print", mock_print) -@patch("builtins.input", input_error) -def check_global_statements(unit: unittest.TestCase, module_name: str): - global output - output = "" - with Timeout(2): - if module_name in sys.modules.keys(): - del sys.modules[module_name] - module = importlib.import_module(module_name) - unit.assertEqual(output, "") - has_global_asserts = any(isinstance(node, ast.Assert) - for node in ast.iter_child_nodes(ast.parse(inspect.getsource(module)))) - unit.assertFalse(has_global_asserts) - - -def no_input_test(test_function: Callable) -> Callable: - @wraps(test_function) - @patch("builtins.print", mock_print) - @patch("builtins.input", input_error) - def inner(self): - global output - output = "" - with Timeout(2): - test_function(self) - return inner - - -class Ex1_sequences(unittest.TestCase): - def test1_global_statements(self): - check_global_statements(self, "ex1_sequences") - - @no_input_test - def test1_annot(self): - global output - output = "" - with Timeout(2): - import ex1_sequences - self.assertTrue(check_annot(ex1_sequences.count_iterations, [int, int], int)) - - @no_input_test - def test1_count_iterations(self): - global output - output = "" - with Timeout(2): - import ex1_sequences - self.assertEqual(ex1_sequences.count_iterations(10, 6), 1) - self.assertEqual(ex1_sequences.count_iterations(7, 6), 3) - self.assertEqual(ex1_sequences.count_iterations(4, 2), 1) - self.assertEqual(ex1_sequences.count_iterations(70, 2), 204) - self.assertEqual(ex1_sequences.count_iterations(3, 2), 3) - self.assertEqual(ex1_sequences.count_iterations(7, 4), 11) - self.assertEqual(ex1_sequences.count_iterations(37, 20), 6) - self.assertEqual(ex1_sequences.count_iterations(14, 4), 23) - self.assertEqual(ex1_sequences.count_iterations(8, 2), 2) - self.assertEqual(ex1_sequences.count_iterations(4, 3), 1) - - -class Ex2_dicts(unittest.TestCase): - def test2_global_statements(self): - check_global_statements(self, "ex2_dicts") - - @no_input_test - def test2_annot(self): - global output - output = "" - with Timeout(2): - import ex2_dicts - self.assertTrue(check_annot(ex2_dicts.lines, [dict[int, set[str]], str], set[int])) - self.assertTrue(check_annot(ex2_dicts.invert, [dict[int, set[str]]], dict[str, set[int]])) - self.assertTrue(check_annot(ex2_dicts.add_line, [dict[int, set[str]], set[str]], None)) - - @no_input_test - def test2_lines(self): - global output - output = "" - with Timeout(2): - import ex2_dicts - example1 = { - 1: {"Hauptbahnhof", "Paduaallee", "Stadttheater"}, - 2: {"Hauptbahnhof", "Stadttheater", "Hornusstraße", "Johanneskirche"}, - 3: {"Am Lindenwäldle", "Hauptbahnhof", "Stadttheater", "Johanneskirche"}, - 5: {"Europaplatz", "Stadttheater", "Am Lindenwäldle"}, - } - example2 = { - 1: {"stat1", "stat2", "stat3"}, - 3: {"stat3", "stat4"}, - 19: {"stat1", "stat2", "stat3", "stat4"} - } - self.assertEqual(ex2_dicts.lines(example1, "Hauptbahnhof"), {1, 2, 3}) - self.assertEqual(ex2_dicts.lines(example1, "Paduaallee"), {1}) - self.assertEqual(ex2_dicts.lines(example1, "Stadttheater"), {1, 2, 3, 5}) - self.assertEqual(ex2_dicts.lines(example1, "Stadttheater"), {1, 2, 3, 5}) - self.assertEqual(ex2_dicts.lines(example1, "Johanneskirche"), {2, 3}) - self.assertEqual(ex2_dicts.lines(example1, "Hornusstraße"), {2}) - self.assertEqual(ex2_dicts.lines(example1, "Am Lindenwäldle"), {3, 5}) - self.assertEqual(ex2_dicts.lines(example1, "Europaplatz"), {5}) - self.assertEqual(ex2_dicts.lines(example2, "stat1"), {1, 19}) - self.assertEqual(ex2_dicts.lines(example2, "stat2"), {1, 19}) - self.assertEqual(ex2_dicts.lines(example2, "stat3"), {1, 3, 19}) - self.assertEqual(ex2_dicts.lines(example2, "stat4"), {3, 19}) - - self.assertEqual(ex2_dicts.lines(example1, "Anywhere"), set()) - self.assertEqual(ex2_dicts.lines(example2, "Hauptbahnhof"), set()) - - self.assertEqual(ex2_dicts.lines({}, "Hauptbahnhof"), set()) - self.assertEqual(ex2_dicts.lines({}, "Anywhere"), set()) - - self.assertTrue(check_no_sideeffects(ex2_dicts.lines, example1, "Hauptbahnhof")) - - @no_input_test - def test2_invert(self): - global output - output = "" - with Timeout(2): - import ex2_dicts - example1 = { - 1: {"Hauptbahnhof", "Paduaallee", "Stadttheater"}, - 2: {"Hauptbahnhof", "Stadttheater", "Hornusstraße", "Johanneskirche"}, - 3: {"Am Lindenwäldle", "Hauptbahnhof", "Stadttheater", "Johanneskirche"}, - 5: {"Europaplatz", "Stadttheater", "Am Lindenwäldle"}, - } - example2 = { - 1: {"stat1", "stat2", "stat3"}, - 3: {"stat3", "stat4"}, - 19: {"stat1", "stat2", "stat3", "stat4"} - } - self.assertDictEqual(ex2_dicts.invert(example1),{ - 'Stadttheater': {1, 2, 3, 5}, - 'Hauptbahnhof': {1, 2, 3}, - 'Paduaallee': {1}, - 'Johanneskirche': {2, 3}, - 'Hornusstraße': {2}, - 'Am Lindenwäldle': {3, 5}, - 'Europaplatz': {5} - }) - self.assertDictEqual(ex2_dicts.invert(example2), { - 'stat1': {1, 19}, - 'stat2': {1, 19}, - 'stat3': {1, 3, 19}, - 'stat4': {3, 19} - }) - self.assertEqual(ex2_dicts.invert({}), {}) - self.assertTrue(check_no_sideeffects(ex2_dicts.invert, example1)) - - @no_input_test - def test2_add_line(self): - global output - output = "" - with Timeout(2): - import ex2_dicts - example1 = { - 1: {"Hauptbahnhof", "Paduaallee", "Stadttheater"}, - 2: {"Hauptbahnhof", "Stadttheater", "Hornusstraße", "Johanneskirche"}, - 3: {"Am Lindenwäldle", "Hauptbahnhof", "Stadttheater", "Johanneskirche"}, - 5: {"Europaplatz", "Stadttheater", "Am Lindenwäldle"}, - } - example2 = { - 1: {"stat1", "stat2", "stat3"}, - 3: {"stat3", "stat4"}, - 19: {"stat1", "stat2", "stat3", "stat4"} - } - ex2_dicts.add_line( - example1, - { - "Technische Fakultät", - "Hauptbahnhof", - "Stadttheater", - "Europaplatz", - "Hornusstraße", - }, - ) - self.assertDictEqual(example1, { - 1: {"Hauptbahnhof", "Paduaallee", "Stadttheater"}, - 2: {"Hauptbahnhof", "Stadttheater", "Hornusstraße", "Johanneskirche"}, - 3: {"Am Lindenwäldle", "Hauptbahnhof", "Stadttheater", "Johanneskirche"}, - 4: {"Technische Fakultät", "Hauptbahnhof", "Stadttheater", "Europaplatz", "Hornusstraße"}, - 5: {"Europaplatz", "Stadttheater", "Am Lindenwäldle"} - }) - - ex2_dicts.add_line(example2, {"stat1", "stat2", "stat3"}) - self.assertDictEqual(example2, { - 1: {"stat1", "stat2", "stat3"}, - 2: {"stat1", "stat2", "stat3"}, - 3: {"stat3", "stat4"}, - 19: {"stat1", "stat2", "stat3", "stat4"} - }) - - ex2_dicts.add_line(example2, {"foo", "bar"}) - self.assertDictEqual(example2, { - 1: {"stat1", "stat2", "stat3"}, - 2: {"stat1", "stat2", "stat3"}, - 3: {"stat3", "stat4"}, - 4: {"foo", "bar"}, - 19: {"stat1", "stat2", "stat3", "stat4"} - }) - - example3 = {} - for i in range(1, 100): - ex2_dicts.add_line(example3, {f"stat{i}"}) - for j in range(1, i + 1): - self.assertEqual(example3[j], {f"stat{j}"}) - self.assertIsNone(ex2_dicts.add_line({}, {"foo"})) - - -class Ex3_strings(unittest.TestCase): - def test3_global_statements(self): - check_global_statements(self, "ex3_strings") - - @no_input_test - def test3_annot(self): - global output - output = "" - with Timeout(2): - import ex3_strings - self.assertTrue(check_annot(ex3_strings.s2_in_s1, [str, str], bool)) - self.assertTrue(check_annot(ex3_strings.split_text, [str], list[str])) - - @no_input_test - def test3_s2_in_s1(self): - global output - output = "" - with Timeout(2): - import ex3_strings - self.assertTrue(ex3_strings.s2_in_s1("function", "fn") is True) - self.assertTrue(ex3_strings.s2_in_s1("function", "ufnction") is False) - self.assertTrue(ex3_strings.s2_in_s1("function", "fnn") is True) - self.assertTrue(ex3_strings.s2_in_s1("function", "fcc") is False) - self.assertTrue(ex3_strings.s2_in_s1("teststring", "tssr") is True) - self.assertTrue(ex3_strings.s2_in_s1("test", "test") is True) - self.assertTrue(ex3_strings.s2_in_s1("cons", "cs") is True) - self.assertTrue(ex3_strings.s2_in_s1("co", "oc") is False) - self.assertTrue(ex3_strings.s2_in_s1("false", "flase") is False) - - self.assertTrue(ex3_strings.s2_in_s1("foo", "") is True) - self.assertTrue(ex3_strings.s2_in_s1("", "bar") is False) - - @no_input_test - def test3_split_text(self): - global output - output = "" - with Timeout(2): - import ex3_strings - self.assertListEqual(ex3_strings.split_text("You're a lizard, Harry!"), [ - 'You', "'", 're', ' ', 'a', ' ', 'lizard', ', ', 'Harry', '!' - ]) - self.assertListEqual(ex3_strings.split_text("Luke! I'm your father!!"), [ - 'Luke', '! ', 'I', "'", 'm', ' ', 'your', ' ', 'father', '!!' - ]) - self.assertListEqual(ex3_strings.split_text("*Stay away from her, you $!#@!*"), [ - '*', 'Stay', ' ', 'away', ' ', 'from', ' ', 'her', ', ', 'you', ' $!#@!*' - ]) - self.assertListEqual(ex3_strings.split_text("hello world"), ["hello", " ", "world"]) - self.assertListEqual(ex3_strings.split_text("test$$case"), ["test", "$$", "case"]) - self.assertListEqual(ex3_strings.split_text(""), []) - self.assertListEqual(ex3_strings.split_text("word"), ["word"]) - self.assertListEqual(ex3_strings.split_text("138904642&(/%$)(=)"), ["138904642&(/%$)(=)"]) - - -class Ex4_dataclasses(unittest.TestCase): - def test4_global_statements(self): - check_global_statements(self, "ex5_dataclasses") - - @no_input_test - def test4_annot(self): - global output - output = "" - with Timeout(2): - import ex5_dataclasses - self.assertTrue(is_dataclass(ex5_dataclasses.Vehicle)) - self.assertTrue(check_attributes(ex5_dataclasses.Vehicle, [], ["seats", "hp", "ccm", "weight"])) - self.assertDictEqual(ex5_dataclasses.Vehicle.__annotations__, { - "seats": int, - "hp": int, - "ccm": int, - "weight": int - }) - - @no_input_test - def test4_Vehicle(self): - global output - output = "" - with Timeout(2): - import ex5_dataclasses - # seats - self.assertRaises(BaseException, ex5_dataclasses.Vehicle.__init__, 0, 5, 5, 5) - self.assertRaises(BaseException, ex5_dataclasses.Vehicle.__init__, -5, 5, 5, 5) - self.assertRaises(BaseException, ex5_dataclasses.Vehicle.__init__, 10, 5, 5, 5) - self.assertRaises(BaseException, ex5_dataclasses.Vehicle.__init__, 165, 5, 5, 5) - ex5_dataclasses.Vehicle(1, 5, 5, 5) - ex5_dataclasses.Vehicle(5, 5, 5, 5) - ex5_dataclasses.Vehicle(9, 5, 5, 5) - - # hp - self.assertRaises(BaseException, ex5_dataclasses.Vehicle.__init__, 5, 0, 5, 5) - self.assertRaises(BaseException, ex5_dataclasses.Vehicle.__init__, 5, -5, 5, 5) - ex5_dataclasses.Vehicle(5, 1, 5, 5) - ex5_dataclasses.Vehicle(5, 5, 5, 5) - ex5_dataclasses.Vehicle(5, 9, 5, 5) - - # ccm - self.assertRaises(BaseException, ex5_dataclasses.Vehicle.__init__, 5, 5, 0, 5) - self.assertRaises(BaseException, ex5_dataclasses.Vehicle.__init__, 5, 5, -5, 5) - ex5_dataclasses.Vehicle(5, 5, 1, 5) - ex5_dataclasses.Vehicle(5, 5, 5, 5) - ex5_dataclasses.Vehicle(5, 5, 9, 5) - - # weight - self.assertRaises(BaseException, ex5_dataclasses.Vehicle.__init__, 5, 5, 5, 0) - self.assertRaises(BaseException, ex5_dataclasses.Vehicle.__init__, 5, 5, 5, -5) - ex5_dataclasses.Vehicle(5, 5, 5, 1) - ex5_dataclasses.Vehicle(5, 5, 5, 5) - ex5_dataclasses.Vehicle(5, 5, 5, 9) - - @no_input_test - def test4_Vehicle_fun_factor(self): - global output - output = "" - with Timeout(2): - import ex5_dataclasses - v1 = ex5_dataclasses.Vehicle(1, 1, 1, 1) - v2 = ex5_dataclasses.Vehicle(2, 1, 4, 2) - v3 = ex5_dataclasses.Vehicle(1, 4, 5, 2) - v4 = ex5_dataclasses.Vehicle(5, 6, 3, 9) - v5 = ex5_dataclasses.Vehicle(3, 1, 6, 2) - v6 = ex5_dataclasses.Vehicle(4, 2, 7, 1) - - self.assertEqual(v1.fun_factor(), 11) - self.assertEqual(v2.fun_factor(), 7) - self.assertEqual(v3.fun_factor(), 22.5) - self.assertEqual(v4.fun_factor(), 7) - self.assertEqual(v5.fun_factor(), 8) - self.assertEqual(v6.fun_factor(), 27) - - @no_input_test - def test4_Vehicle_gt(self): - global output - output = "" - with Timeout(2): - import ex5_dataclasses - v1 = ex5_dataclasses.Vehicle(1, 1, 1, 1) - v2 = ex5_dataclasses.Vehicle(2, 1, 4, 2) - v3 = ex5_dataclasses.Vehicle(1, 4, 5, 2) - v4 = ex5_dataclasses.Vehicle(5, 6, 3, 9) - v5 = ex5_dataclasses.Vehicle(3, 1, 6, 2) - v6 = ex5_dataclasses.Vehicle(4, 2, 7, 1) - - self.assertTrue(v1 > v2) - self.assertTrue(v3 > v2) - self.assertTrue(v6 > v3) - self.assertTrue(v5 > v4) - self.assertTrue(v5 > v2) - - @no_input_test - def test4_Car(self): - global output - output = "" - with Timeout(2): - import ex5_dataclasses - self.assertTrue(is_dataclass(ex5_dataclasses.Car)) - self.assertTrue(issubclass(ex5_dataclasses.Car, ex5_dataclasses.Vehicle)) - self.assertTrue(check_attributes(ex5_dataclasses.Car, ["seats", "hp", "ccm", "weight"], ["spoiler"])) - self.assertDictEqual(ex5_dataclasses.Car.__annotations__, {"spoiler": bool}) - self.assertTrue(ex5_dataclasses.Car.__post_init__ is ex5_dataclasses.Vehicle.__post_init__) - self.assertTrue(check_function_used(ex5_dataclasses.Car.fun_factor, "super")) - c1 = ex5_dataclasses.Car(5, 5, 5, 5, True) - c2 = ex5_dataclasses.Car(5, 5, 5, 5, False) - with patch.object(ex5_dataclasses.Vehicle, "fun_factor", return_value=1337): - self.assertEqual(c1.fun_factor(), 1337.2) - self.assertEqual(c2.fun_factor(), 1337) - - @no_input_test - def test4_Motorcycle(self): - global output - output = "" - with Timeout(2): - import ex5_dataclasses - self.assertTrue(is_dataclass(ex5_dataclasses.Motorcycle)) - self.assertTrue(issubclass(ex5_dataclasses.Motorcycle, ex5_dataclasses.Vehicle)) - self.assertTrue(check_attributes(ex5_dataclasses.Motorcycle, ["seats", "hp", "ccm", "weight"], ["sidecar"])) - self.assertDictEqual(ex5_dataclasses.Motorcycle.__annotations__, {"sidecar": bool}) - self.assertTrue(check_function_used(ex5_dataclasses.Motorcycle.fun_factor, "super")) - with patch("ex5_dataclasses.Vehicle.__post_init__") as vehicle_post_init: - ex5_dataclasses.Motorcycle(3, 1, 1, 1, True) - vehicle_post_init.assert_called_once() - self.assertRaises(BaseException, ex5_dataclasses.Motorcycle.__init__, 3, 1, 1, 1, False) - self.assertRaises(BaseException, ex5_dataclasses.Motorcycle.__init__, 6, 1, 1, 1, False) - self.assertRaises(BaseException, ex5_dataclasses.Motorcycle.__init__, 1, 1, 1, 1, True) - self.assertRaises(BaseException, ex5_dataclasses.Motorcycle.__init__, 4, 1, 1, 1, True) - with patch.object(ex5_dataclasses.Vehicle, "fun_factor", return_value=1337): - m1 = ex5_dataclasses.Motorcycle(3, 5, 5, 5, True) - m2 = ex5_dataclasses.Motorcycle(2, 5, 5, 5, False) - self.assertAlmostEqual(m1.fun_factor(), 3208.8) - self.assertAlmostEqual(m2.fun_factor(), 668.5) - - -class Ex5_automaton(unittest.TestCase): - def test5_global_statements(self): - check_global_statements(self, "ex4_automaton") - - @no_input_test - def test5_State(self): - global output - output = "" - with Timeout(2): - import ex4_automaton - self.assertTrue(issubclass(ex4_automaton.State, Enum)) - self.assertFalse(is_dataclass(ex4_automaton.State)) - states = ex4_automaton.State._member_names_ - self.assertTrue(states == ["q0", "q1", "q2", "qe"] or states == ["Q0", "Q1", "Q2", "QE"]) - - @no_input_test - def test5_delta_annot(self): - global output - output = "" - with Timeout(2): - from ex4_automaton import delta, Alphabet, State - self.assertTrue(check_annot(delta, [State, Alphabet], ret=State)) - - @no_input_test - def test5_delta_match(self): - global output - output = "" - with Timeout(2): - import ex4_automaton - self.assertTrue(check_pattern_matching_used(ex4_automaton.delta)) - - @no_input_test - def test5_delta(self): - global output - output = "" - with Timeout(2): - import ex4_automaton - q0 = ex4_automaton.State.q0 if "q0" in ex4_automaton.State._member_names_ else ex4_automaton.State.Q0 - q1 = ex4_automaton.State.q1 if "q1" in ex4_automaton.State._member_names_ else ex4_automaton.State.Q1 - q2 = ex4_automaton.State.q2 if "q2" in ex4_automaton.State._member_names_ else ex4_automaton.State.Q2 - qe = ex4_automaton.State.qe if "qe" in ex4_automaton.State._member_names_ else ex4_automaton.State.QE - self.assertEqual(ex4_automaton.delta(q0, "a"), q1) - self.assertEqual(ex4_automaton.delta(q1, "a"), q0) - self.assertEqual(ex4_automaton.delta(q1, "b"), q1) - self.assertEqual(ex4_automaton.delta(q0, "b"), q2) - self.assertEqual(ex4_automaton.delta(q2, "a"), q2) - self.assertEqual(ex4_automaton.delta(q2, "c"), q1) - self.assertEqual(ex4_automaton.delta(q2, "b"), qe) - - @no_input_test - def test5_automaton(self): - global output - output = "" - with Timeout(2): - import ex4_automaton - M = ex4_automaton.automaton() - self.assertEqual(resolve_type_alias(ex4_automaton.Alphabet), resolve_type_alias(Literal["a", "b", "c"])) - self.assertEqual(M.delta, ex4_automaton.delta) - self.assertEqual(M.start, ex4_automaton.State.q0 if "q0" in ex4_automaton.State._member_names_ else ex4_automaton.State.Q0) - self.assertEqual(M.finals, frozenset({ex4_automaton.State.q0 if "q0" in ex4_automaton.State._member_names_ else ex4_automaton.State.Q0})) - self.assertTrue(check_annot(ex4_automaton.automaton, [], ex4_automaton.Automaton[ex4_automaton.State, ex4_automaton.Alphabet])) - - -class Ex6_recursion(unittest.TestCase): - def test6_global_statements(self): - check_global_statements(self, "ex6_recursion") - - @no_input_test - def test6_annot(self): - global output - output = "" - with Timeout(2): - import ex6_recursion - type_params = ex6_recursion.filter_tree.__type_params__ - self.assertEqual(len(type_params), 1) - T = type_params[0] - self.assertTrue(check_annot(ex6_recursion.filter_tree, [Callable[[T], bool], ex6_recursion.Tree[T]], list[T])) - self.assertTrue(check_annot(ex6_recursion.mirror_tree, [ex6_recursion.Tree[Any]], None)) - - @no_input_test - def test6_filter_tree(self): - global output - output = "" - with Timeout(2): - import ex6_recursion - Node = ex6_recursion.Node - example_node1 = Node("zero", Node("one"), Node("two", Node("three"), Node("four"))) - example_node2 = Node(0, Node(5, Node(3)), Node(1)) - example_node3 = None - example_node4 = Node(0) - example_node5 = Node(1, Node(2), Node(3)) - self.assertListEqual(ex6_recursion.filter_tree(lambda x: 'e' in x, example_node1), ['zero', 'one', 'three']) - self.assertListEqual(ex6_recursion.filter_tree(lambda x: 'o' in x, example_node1), ['zero', 'one', 'two', 'four']) - self.assertListEqual(ex6_recursion.filter_tree(lambda _: False, example_node1), []) - self.assertListEqual(ex6_recursion.filter_tree(lambda _: True, None), []) - self.assertListEqual(ex6_recursion.filter_tree(lambda x: x % 2 == 0, example_node5), [2]) - self.assertListEqual(ex6_recursion.filter_tree(lambda x: x < 0, example_node4), []) - self.assertListEqual(ex6_recursion.filter_tree(lambda x: True, example_node3), []) - self.assertTrue(check_no_sideeffects(ex6_recursion.filter_tree, lambda x: len(x) == 3, example_node1)) - - @no_input_test - def test6_mirror_tree(self): - global output - output = "" - with Timeout(2): - import ex6_recursion - Node = ex6_recursion.Node - example_node1 = Node("zero", Node("one"), Node("two", Node("three"), Node("four"))) - example_node2 = Node(0, Node(5, Node(3)), Node(1)) - example_node3 = None - example_node4 = Node(0) - example_node5 = Node(1, Node(2), Node(3)) - self.assertIsNone(ex6_recursion.mirror_tree(example_node1)) - example_node1 = Node("zero", Node("one"), Node("two", Node("three"), Node("four"))) - - ex6_recursion.mirror_tree(example_node2) - self.assertEqual(example_node2, Node(0, Node(1), Node(5, None, Node(3)))) - ex6_recursion.mirror_tree(example_node2) - self.assertEqual(example_node2, Node(0, Node(5, Node(3)), Node(1))) - ex6_recursion.mirror_tree(example_node1) - self.assertEqual(example_node1, Node("zero", Node("two", Node("four"), Node("three")), Node("one"))) - x = None - ex6_recursion.mirror_tree(x) - self.assertIsNone(x) - - # mirror twice - ex6_recursion.mirror_tree(example_node5) - ex6_recursion.mirror_tree(example_node5) - self.assertEqual(example_node5, Node(1, Node(2), Node(3))) - - -class Ex7_generators(unittest.TestCase): - def test7_global_statements(self): - check_global_statements(self, "ex7_generators") - - @no_input_test - def test7_annot(self): - global output - output = "" - with Timeout(2): - import ex7_generators - type_params = ex7_generators.drop.__type_params__ - self.assertEqual(len(type_params), 1) - T = type_params[0] - self.assertTrue(check_annot(ex7_generators.drop, [Iterable[T], int], Iterator[T])) - - type_params = ex7_generators.split.__type_params__ - self.assertEqual(len(type_params), 1) - T = type_params[0] - self.assertTrue(check_annot(ex7_generators.split, [Iterable[T], T], Iterator[list[T]])) - - type_params = ex7_generators.apply_pairs.__type_params__ - self.assertEqual(len(type_params), 2) - T, R = type_params - self.assertTrue(check_annot(ex7_generators.apply_pairs, [Iterable[T], Callable[[T, T], R]], Iterator[R])) - - @no_input_test - def test7_drop(self): - global output - output = "" - with Timeout(2): - import ex7_generators - self.assertTrue(check_function_not_used(ex7_generators.drop, "list")) - self.assertTrue(check_gen(ex7_generators.drop(IterablePure([2, 4, 6, 8, 10, 12]), 3), [8, 10, 12])) - self.assertTrue(check_gen(ex7_generators.drop(IterablePure([True, False, False]), 0), [True, False, False])) - self.assertTrue(check_gen(ex7_generators.drop(IterablePure(["a", "b", "c", "d"]), 8), [])) - self.assertTrue(check_gen(ex7_generators.drop(IterablePure(range(1, 4)), 1), [2, 3])) - self.assertTrue(check_gen(ex7_generators.drop(IterablePure([1, 1, 1, 1]), 0), [1, 1, 1, 1])) - self.assertTrue(check_gen(ex7_generators.drop(IterablePure([]), 0), [])) - self.assertTrue(check_gen(ex7_generators.drop(IterablePure(), 178), range(178, 1000), False, 1000)) - - @no_input_test - def test7_split(self): - global output - output = "" - with Timeout(2): - import ex7_generators - g1 = ex7_generators.split(IterablePure([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 5) - self.assertTrue(check_gen(g1, [list(range(1, 5)), list(range(6, 11))])) - g2 = ex7_generators.split(IterablePure("abcdefghij", True), "e") - self.assertTrue(check_gen(g2, [list("abcd")] + [list("fghijabcd")] * 999, False, 1000)) - g3 = ex7_generators.split(IterablePure("mississippiXXX", False), "i") - self.assertTrue(check_gen(g3, [list("m"), list("ss"), list("ss"), list("pp"), list("XXX")])) - g4 = ex7_generators.split(IterablePure(("Hallo", "Welt", "asdfa", "?", "asdfa", "wsgd", "Nöö", "asdfa", "Nix da"), False), "asdfa") - self.assertTrue(check_gen(g4, [["Hallo", "Welt"], ["?"], ["wsgd", "Nöö"], ["Nix da"]])) - g5 = ex7_generators.split(IterablePure("abcdefghij", True), "a") - self.assertTrue(check_gen(g5, [[]] + [list("bcdefghij")] * 999, False, 1000)) - - g1 = ex7_generators.split(IterablePure([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 10) - self.assertTrue(check_gen(g1, [list(range(1, 10)), []])) - g2 = ex7_generators.split(IterablePure("abcdjjefjjghi"), "j") - self.assertTrue(check_gen(g2, [list("abcd"), [], list("ef"), [], list("ghi")])) - - @no_input_test - def test7_apply_pairs(self): - global output - output = "" - with Timeout(2): - import ex7_generators - check_function_not_used(ex7_generators.apply_pairs, "list") - - sub = lambda x, y: x - y - eq = lambda x, y: x == y - add_1 = lambda x, y: x + y + 1 - - g1 = ex7_generators.apply_pairs(IterablePure([5, 2, 7, 9, 1]), sub) - self.assertTrue(check_gen(g1, [3, -5, -2, 8])) - g2 = ex7_generators.apply_pairs(IterablePure("abaabbc", repeat=True), eq) - self.assertTrue(check_gen(g2, [False, False, True, False, True, False, False] * 100, False, 700)) - g3 = ex7_generators.apply_pairs(IterablePure([1]), sub) - self.assertTrue(check_gen(g3, [])) - g4 = ex7_generators.apply_pairs(IterablePure([None, None, "", None]), eq) - self.assertTrue(check_gen(g4, [True, False, False])) - g5 = ex7_generators.apply_pairs(IterablePure([0, 1, 1, 0, 1]), lambda x, y: (x + y) % 2) - self.assertTrue(check_gen(g5, [1, 0, 1, 1])) - g6 = ex7_generators.apply_pairs(IterablePure([0, 1, 1, 0, 1]), lambda x, y: x * y) - self.assertTrue(check_gen(g6, [0, 1, 0, 0])) - g7 = ex7_generators.apply_pairs(IterablePure([1, 0]), sub) - self.assertTrue(check_gen(g7, [1])) - g8 = ex7_generators.apply_pairs(IterablePure([1, 0], True), add_1) - self.assertTrue(check_gen(g8, [2, 2] * 1000, False, 1000)) - - -class Ex8_fp(unittest.TestCase): - def test8_global_statements(self): - check_global_statements(self, "ex8_fp") - - @no_input_test - def test8_annot(self): - global output - output = "" - with Timeout(2): - import ex8_fp - if ex8_fp.sum_0.__name__ == '': - self.assertTrue(check_lambda_annotations(ex8_fp, 'sum_0', [list[Callable[[float], float]]], float)) - else: - self.assertTrue(check_annot(ex8_fp.sum_0, [list[Callable[[float], float]]], float)) - - type_params = ex8_fp.extensionally_equal.__type_params__ - self.assertEqual(len(type_params), 2) - T, R = type_params - self.assertTrue(check_annot(ex8_fp.extensionally_equal, [Callable[[T], R], Callable[[T], R], list[T]], bool)) - - if ex8_fp.map_matrix.__name__ == '': - self.assertTrue(check_lambda_annotations(ex8_fp, 'map_matrix', [Callable[[float], float], list[list[float]]], list[list[float]])) - else: - self.assertTrue(check_annot(ex8_fp.map_matrix, [Callable[[float], float], list[list[float]]], list[list[float]])) - - @no_input_test - def test8_sum_0(self): - global output - output = "" - with Timeout(2): - import ex8_fp - self.assertTrue(check_only_one_line(ex8_fp.sum_0)) - f = lambda x: x + 1 - self.assertEqual(ex8_fp.sum_0([f]), 1) - self.assertEqual(ex8_fp.sum_0([f, f, f]), 3) - - f = lambda x: x + 1 - g = lambda x: x**2 - self.assertEqual(ex8_fp.sum_0([f, g]), 1) - - f = lambda x: x % 12 - g = lambda x: x**x - h = lambda _: 1337.333 - self.assertTrue(math.isclose(ex8_fp.sum_0([f, g, h]), 1338.333)) - - self.assertEqual(ex8_fp.sum_0([]), 0) - - @no_input_test - def test8_extensionally_equal(self): - global output - output = "" - with Timeout(2): - import ex8_fp - self.assertTrue(check_only_one_line(ex8_fp.extensionally_equal)) - f = lambda x: x * 2 - 1 - g = lambda y: y * (4 / 2) - 1 - self.assertTrue(ex8_fp.extensionally_equal(f, g, [1, 2, 3, 4, 5])) - h = lambda x: x ** 2 - 4 - k = lambda x: (x - 2) * (x + 2) - self.assertTrue(ex8_fp.extensionally_equal(h, k, [1, 5, 3, 7, 3])) - l = lambda y: y < 4 - le = lambda x: x <= 4 - self.assertTrue(ex8_fp.extensionally_equal(l, le, [1, 2, 3])) - - g = lambda y: y * (4 / 2) - 1 - k = lambda x: (x - 2) * (x + 2) - self.assertFalse(ex8_fp.extensionally_equal(g, k, [1])) - self.assertFalse(ex8_fp.extensionally_equal(g, k, [0])) - self.assertFalse(ex8_fp.extensionally_equal(g, k, [1, 3])) - l = lambda y: y < 4 - le = lambda x: x <= 4 - self.assertFalse(ex8_fp.extensionally_equal(l, le, [1, 2, 3, 4])) - - @no_input_test - def test8_map_matrix(self): - global output - output = "" - with Timeout(2): - import ex8_fp - self.assertTrue(check_only_one_line(ex8_fp.map_matrix)) - self.assertFalse(check_comprehension_used(ex8_fp.map_matrix)) - self.assertTrue(check_function_used(ex8_fp.map_matrix, "map")) - self.assertTrue(check_function_used(ex8_fp.map_matrix, "list")) - - example = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]] - self.assertListEqual(ex8_fp.map_matrix(lambda x: x * 2, example), [[2, 4, 6], [8, 10, 12]]) - example2 = [[2, 5], [5, 6]] - self.assertListEqual(ex8_fp.map_matrix(lambda x: x ** 2, example2), [[4, 25], [25, 36]]) - self.assertListEqual(ex8_fp.map_matrix(lambda y: y < 5, example2), [[True, False], [False, False]]) - self.assertListEqual(ex8_fp.map_matrix(lambda x: x, []), []) - - -def get_results(): - result_data = {} - output = io.StringIO("") - test_classes = [(name, cls) for name, cls in inspect.getmembers(sys.modules[__name__], lambda x: inspect.isclass( - x) and (x.__module__ == __name__)) if unittest.TestCase in cls.__mro__] - suite = unittest.TestLoader().loadTestsFromTestCase(test_classes[0][1]) - for other in test_classes[1:]: - suite.addTest(unittest.TestLoader().loadTestsFromTestCase(other[1])) - testResult = unittest.TextTestRunner(stream=output, verbosity=2).run(suite) - result_data["tests"] = [str(test_method) for test in test_classes - for test_method in unittest.TestLoader().loadTestsFromTestCase(test[1])] - result_data["failure"] = not testResult.wasSuccessful() - result_data["tests_run"] = testResult.testsRun - result_data["failures"] = [(str(func), str(err)) - for func, err in testResult.failures] - result_data["errors"] = [(str(func), str(err)) - for func, err in testResult.errors] - return result_data - - -if __name__ == "__main__": - docker_run = True - try: - with open("/output/output.json", "w", encoding="utf-8") as f: - result = get_results() - json.dump(result, f) - except FileNotFoundError: - docker_run = False - if not docker_run: - unittest.main() diff --git a/test_ex6.py b/test_ex6.py new file mode 100644 index 0000000..417c0f6 --- /dev/null +++ b/test_ex6.py @@ -0,0 +1,425 @@ +from dataclasses import dataclass, field, fields, is_dataclass, InitVar +from types import NoneType, FunctionType, GenericAlias +from typing import get_args, Callable, Iterable, Iterator, Type, Optional, TypeAliasType, ForwardRef, Any, Literal +from enum import Enum +import unittest +from unittest import mock +from unittest.mock import patch +from functools import wraps +import signal +import io +import importlib +import importlib.util +import sys +import subprocess +import os +import shutil +import re +import math +import inspect +import json +import ast +import copy +import functools +import itertools + +output = "" + + +class TimeoutException(Exception): + pass + + +class Timeout: + def __init__(self, seconds, msg=None) -> None: + if msg is None: + msg = f"Test timed out after {seconds} seconds." + self.seconds = seconds + self.msg = msg + + def handle_timeout(self, signal, frame): + raise TimeoutException(self.msg) + + def __enter__(self): + signal.signal(signal.SIGALRM, self.handle_timeout) + signal.alarm(self.seconds) + + def __exit__(self, exc_type, exc_val, exc_tb): + signal.alarm(0) + + +class IterablePure: + def __init__(self, i: Optional[Iterable] = None, repeat: bool = False): + if i is not None: + self.i = i + else: + def endless(): + c = 0 + while True: + yield c + c += 1 + self.i = endless() + + self.repeat = repeat + + def __iter__(self): + if self.repeat: + while True: + yield from self.i + else: + yield from self.i + + +def resolve_type_alias(t: Type) -> Type: + while isinstance(t, TypeAliasType | GenericAlias) and hasattr(t, "__value__"): + if isinstance(t, TypeAliasType): + t = t.__value__ + elif isinstance(t, GenericAlias): + t = t.__value__[*t.__args__] + return t + + +def check_annot(function: Callable, expected: list[Type], ret=None): + actual = function.__annotations__ + if "self" in actual: + del actual["self"] + return_type = actual["return"] if "return" in actual else None + actual = list(actual.values())[ + :-1] if "return" in actual else list(actual.values()) + for a, e in zip(actual, expected): + a = resolve_type_alias(a) + e = resolve_type_alias(e) + if a != e: + return False + return resolve_type_alias(ret) == resolve_type_alias(return_type) + + +def check_lambda_annotations(module, f_name: str, param_types: list[object], return_type: object = None) -> bool: + annotation = module.__annotations__[f_name] + if annotation.__name__ != 'Callable': + return False + + all_types = annotation.__args__ + types, ret_type = all_types[:-1], all_types[-1] + if ret_type != return_type: + return False + + for param, param_type in zip(types, param_types): + while isinstance(param, TypeAliasType): + param = param.__value__ + while isinstance(param_type, TypeAliasType): + param_type = param_type.__value__ + if param != param_type: + return False + return True + + +def check_attributes(o: object, inherited_attr: list[str], new_attr: list[str]) -> bool: + attr = [f.name for f in fields(o)] + if attr != inherited_attr + new_attr: + return False + + for attr in inherited_attr: + if attr in o.__annotations__: + return False + return True + + +def check_no_if_used(f: Callable) -> bool: + tree = ast.parse(inspect.getsource(f)) + for node in ast.walk(tree): + if isinstance(node, ast.If): + return False + return True + + +def check_comprehension_used(f: Callable) -> bool: + source = inspect.getsource(f) + tree = ast.parse(source) + + for node in ast.walk(tree): + if isinstance(node, (ast.ListComp, ast.SetComp, ast.DictComp, ast.GeneratorExp)): + return True + return False + + +def check_no_sideeffects(f: Callable, *args) -> bool: + args_copy = tuple(copy.deepcopy(a) for a in args) + f(*args) + return args == args_copy + + +def check_function_used(f: Callable, name: str) -> bool: + source = inspect.getsource(f) + if source.startswith(4 * " "): + source = "\n".join(l[4:] for l in source.split("\n")) + return name in [c.func.id for c in ast.walk(ast.parse(source)) if isinstance(c, ast.Call) and isinstance(c.func, ast.Name)] + + +def check_function_not_used(f: Callable, name: str) -> bool: + source = inspect.getsource(f) + if source.startswith(4 * " "): + source = "\n".join(l[4:] for l in source.split("\n")) + return name not in [c.func.id for c in ast.walk(ast.parse(source)) if isinstance(c, ast.Call) and isinstance(c.func, ast.Name)] + + +def check_pattern_matching_used(f: Callable) -> bool: + source = inspect.getsource(f) + tree = ast.parse(source) + return any(isinstance(node, ast.Match) for node in ast.walk(tree)) + + +def check_gen(g: Iterator, res: Iterable, check_end: bool = True, max: int = 100) -> bool: + if not isinstance(g, Iterator): + return False + for i, x in enumerate(res): + el = next(g) + if el != x: + return False + if i >= max: + break + if check_end: + try: + next(g) + except StopIteration: + return True + return False + return True + + +def check_only_one_line(f: Callable) -> bool: + if f.__name__ == '': + return True + inspect.getsource(f) + lines = [line for line in inspect.getsource(f).split( + '\n') if not line.strip().startswith('#') and line.strip() != ''] + return len(lines) == 2 + + +def mock_print(*args, sep=None, end=None, **_kwargs): + global output + if sep is None: + sep = " " + if end is None: + end = "\n" + args = [str(a) for a in args] + output += str(sep.join(args)) + end + + +def mock_input(*arg: str): + inputs = [s for s in arg] + + def input_function(arg=None): + if arg is None: + arg = "" + nonlocal inputs + if len(inputs) == 0: + raise Exception("More input asked than given") + p = inputs[0] + inputs = inputs[1:] + mock_print(arg, p, sep="") + return p + return input_function + + +def input_error(*arg): + raise Exception("Input is not allowed here") + + +@patch("builtins.print", mock_print) +@patch("builtins.input", input_error) +def check_global_statements(unit: unittest.TestCase, module_name: str): + global output + output = "" + with Timeout(2): + if module_name in sys.modules.keys(): + del sys.modules[module_name] + module = importlib.import_module(module_name) + unit.assertEqual(output, "") + has_global_asserts = any(isinstance(node, ast.Assert) + for node in ast.iter_child_nodes(ast.parse(inspect.getsource(module)))) + unit.assertFalse(has_global_asserts) + + +def no_input_test(test_function: Callable) -> Callable: + @wraps(test_function) + @patch("builtins.print", mock_print) + @patch("builtins.input", input_error) + def inner(self): + global output + output = "" + with Timeout(2): + test_function(self) + return inner + + +class Ex6a_sum_of_subtree(unittest.TestCase): + def test6_global_statements(self): + check_global_statements(self, "ex6_recursion") + + def test6_test_pattern_matching_sum_of_subtree(self): + with Timeout(2): + import ex6_recursion + check_pattern_matching_used(ex6_recursion.cut_at) + + @no_input_test + def test6_annot_sum_of_subtree(self): + global output + output = "" + with Timeout(2): + import ex6_recursion + self.assertTrue(check_annot(ex6_recursion.sum_of_subtree, [ + ex6_recursion.BTree[int]], int)) + + @no_input_test + def test6_sum_of_subtree_basisfall(self): + global output + output = "" + with Timeout(2): + import ex6_recursion + sum_of_subtree = ex6_recursion.sum_of_subtree + tree = None + self.assertEqual(sum_of_subtree(tree), 0) + self.assertIsNone(tree) + + @no_input_test + def test6_sum_of_subtree(self): + global output + output = "" + with Timeout(2): + import ex6_recursion + sum_of_subtree = ex6_recursion.sum_of_subtree + Node = ex6_recursion.Node + tree = Node(1, Node(2, Node(3), Node(4)), Node(5, Node(6))) + self.assertEqual(sum_of_subtree(tree), 21) + self.assertEqual(tree, Node(mark=21, left=Node(mark=9, left=Node(mark=3, left=None, right=None), right=Node( + mark=4, left=None, right=None)), right=Node(mark=11, left=Node(mark=6, left=None, right=None), right=None))) + single_node_tree = Node(5) + self.assertEqual(sum_of_subtree(single_node_tree), 5) + simple_tree = Node(1, Node(2), Node(3)) + total_sum = sum_of_subtree(simple_tree) + self.assertEqual(total_sum, 6) # 1 + 2 + 3 = 6 + self.assertEqual(simple_tree.mark, 6) + self.assertEqual(simple_tree.left.mark, 2) + self.assertEqual(simple_tree.right.mark, 3) + complex_tree = Node(1, Node(2, Node(3), Node(4)), Node(5, Node(6))) + total_sum = sum_of_subtree(complex_tree) + self.assertEqual(total_sum, 21) # 1 + 2 + 3 + 4 + 5 + 6 = 21 + self.assertEqual(complex_tree.mark, 21) + self.assertEqual(complex_tree.left.mark, 9) # 2 + 3 + 4 = 9 + self.assertEqual(complex_tree.right.mark, 11) # 5 + 6 = 11 + self.assertEqual(complex_tree.left.left.mark, 3) + self.assertEqual(complex_tree.left.right.mark, 4) + self.assertEqual(complex_tree.right.left.mark, 6) + + +class Ex6b_cut_at(unittest.TestCase): + def test6_global_statements(self): + check_global_statements(self, "ex6_recursion") + + def test6_test_pattern_matching_cut_at(self): + with Timeout(2): + import ex6_recursion + check_pattern_matching_used(ex6_recursion.cut_at) + + def test6_test_no_side_effects_cut_at(self): + with Timeout(2): + import ex6_recursion + Node = ex6_recursion.Node + tree = Node(1, Node(2, Node(3), Node(4)), + Node(3, Node(3), Node(6))) + check_no_sideeffects(ex6_recursion.cut_at, tree, 3) + + @no_input_test + def test6_annot_cut_at(self): + global output + output = "" + with Timeout(2): + import ex6_recursion + self.assertTrue(check_annot(ex6_recursion.sum_of_subtree, [ + ex6_recursion.BTree[int]], int)) + type_params = ex6_recursion.cut_at.__type_params__ + self.assertEqual(len(type_params), 1) + T = type_params[0] + self.assertTrue(check_annot(ex6_recursion.cut_at, [ + ex6_recursion.BTree[T]], ex6_recursion.BTree[T])) + + @no_input_test + def test6_cut_at_basisfall(self): + global output + output = "" + with Timeout(2): + import ex6_recursion + cut_at = ex6_recursion.cut_at + tree = None + self.assertIsNone(cut_at(tree, 42)) + + @no_input_test + def test6_cut_at(self): + global output + output = "" + with Timeout(2): + import ex6_recursion + Node = ex6_recursion.Node + cut_at = ex6_recursion.cut_at + tree = Node(1, Node(2, Node(3), Node(4)), + Node(3, Node(3), Node(6))) + self.assertIsNone(cut_at(tree, 1)) + self.assertEqual(cut_at(tree, 3), Node(mark=1, left=Node( + mark=2, left=None, right=Node(mark=4, left=None, right=None)), right=None)) + single_node_tree = Node(5) + new_tree = cut_at(single_node_tree, 10) + self.assertIsNotNone(new_tree) + self.assertEqual(new_tree.mark, 5) + self.assertIsNone(new_tree.left) + self.assertIsNone(new_tree.right) + tree_without_cut = Node( + 1, Node(2, Node(3), Node(4)), Node(5, Node(6))) + new_tree = cut_at(tree_without_cut, 42) # No node has value 42 + self.assertIsNotNone(new_tree) + self.assertEqual(new_tree.mark, 1) + self.assertIsNotNone(new_tree.left) + self.assertIsNotNone(new_tree.right) + tree_with_cut = Node( + 1, Node(2, Node(3), Node(4)), Node(3, Node(3), Node(6))) + new_tree = cut_at(tree_with_cut, 3) # Cut all nodes with value 3 + self.assertIsNotNone(new_tree) + self.assertEqual(new_tree.mark, 1) + self.assertEqual(new_tree.left.mark, 2) + self.assertIsNone(new_tree.right) # The right subtree is removed + self.assertIsNone(new_tree.left.left) # Node(3) is removed + self.assertIsNotNone(new_tree.left.right) + self.assertEqual(new_tree.left.right.mark, 4) # Node(4) remains + + +def get_results(): + result_data = {} + output = io.StringIO("") + test_classes = [(name, cls) for name, cls in inspect.getmembers(sys.modules[__name__], lambda x: inspect.isclass( + x) and (x.__module__ == __name__)) if unittest.TestCase in cls.__mro__] + suite = unittest.TestLoader().loadTestsFromTestCase(test_classes[0][1]) + for other in test_classes[1:]: + suite.addTest(unittest.TestLoader().loadTestsFromTestCase(other[1])) + testResult = unittest.TextTestRunner(stream=output, verbosity=2).run(suite) + result_data["tests"] = [str(test_method) for test in test_classes + for test_method in unittest.TestLoader() + .loadTestsFromTestCase(test[1])] + result_data["failure"] = not testResult.wasSuccessful() + result_data["tests_run"] = testResult.testsRun + result_data["failures"] = [(str(func), str(err)) + for func, err in testResult.failures] + result_data["errors"] = [(str(func), str(err)) + for func, err in testResult.errors] + return result_data + + +if __name__ == "__main__": + docker_run = True + try: + with open("/output/output.json", "w", encoding="utf-8") as f: + result = get_results() + json.dump(result, f) + except FileNotFoundError: + docker_run = False + if not docker_run: + unittest.main()