Improve proxy server support
[tinc] / test / integration / testlib / check.py
1 """Simple assertions which print the expected and received values on failure."""
2
3 import typing as T
4
5 from .log import log
6
7 Val = T.TypeVar("Val")
8 Num = T.TypeVar("Num", int, float)
9
10
11 def false(value: T.Any) -> None:
12     """Check that value is falsy."""
13     if value:
14         raise ValueError(f'expected "{value}" to be falsy')
15
16
17 def true(value: T.Any) -> None:
18     """Check that value is truthy."""
19     if not value:
20         raise ValueError(f'expected "{value}" to be truthy', value)
21
22
23 def port(value: int) -> None:
24     """Check that value resembles a port."""
25     if not isinstance(value, int) or value < 1 or value > 65535:
26         raise ValueError(f'expected "{value}" to be be a port')
27
28
29 def equals(expected: Val, actual: Val) -> None:
30     """Check that the two values are equal."""
31     if expected != actual:
32         raise ValueError(f'expected "{expected}", got "{actual}"')
33
34
35 def has_prefix(text: T.AnyStr, prefix: T.AnyStr) -> None:
36     """Check that text has prefix."""
37     if not text.startswith(prefix):
38         raise ValueError(f"expected {text!r} to start with {prefix!r}")
39
40
41 def greater(value: Num, than: Num) -> None:
42     """Check that value is greater than the other value."""
43     if value <= than:
44         raise ValueError(f"value {value} must be greater than {than}")
45
46
47 def in_range(value: Num, gte: Num, lte: Num) -> None:
48     """Check that value lies in the range [min, max]."""
49     if not gte >= value >= lte:
50         raise ValueError(f"value {value} must be between {gte} and {lte}")
51
52
53 def is_in(needle: Val, *haystacks: T.Container[Val]) -> None:
54     """Check that at least one haystack includes needle."""
55     for haystack in haystacks:
56         if needle in haystack:
57             return
58     raise ValueError(f'expected any of "{haystacks}" to include "{needle}"')
59
60
61 def not_in(needle: Val, *haystacks: T.Container[Val]) -> None:
62     """Check that all haystacks do not include needle."""
63     for haystack in haystacks:
64         if needle in haystack:
65             raise ValueError(f'expected all "{haystacks}" NOT to include "{needle}"')
66
67
68 def nodes(node, want_nodes: int) -> None:
69     """Check that node can reach exactly N nodes (including itself)."""
70     log.debug("want %d reachable nodes from tinc %s", want_nodes, node)
71     stdout, _ = node.cmd("dump", "reachable", "nodes")
72     equals(want_nodes, len(stdout.splitlines()))
73
74
75 def files_eq(path0: str, path1: str) -> None:
76     """Compare file contents, ignoring whitespace at both ends."""
77     log.debug("comparing files %s and %s", path0, path1)
78
79     def read(path: str) -> str:
80         log.debug("reading file %s", path)
81         with open(path, "r", encoding="utf-8") as f:
82             return f.read().strip()
83
84     content0 = read(path0)
85     content1 = read(path1)
86
87     if content0 != content1:
88         raise ValueError(f"expected files {path0} and {path1} to match")