1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
# ############################################################################ #
# #
# ::: :::::::: #
# suite.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2020/07/15 18:24:29 by charles #+# #+# #
# Updated: 2021/03/06 15:57:52 by cacharle ### ########.fr #
# #
# ############################################################################ #
from typing import List, Tuple, Optional, Callable
from minishell_test.config import Config
from minishell_test.test import Test
from minishell_test import colors
class SuiteException(Exception):
""" Base exception for suite """
pass
class SuiteExitFirstException(SuiteException):
pass
# class AmbiguousNameException(SuiteException):
# def __init__(self, name: str, matches: List[str]):
# self._name = name
# self._matches = matches
#
# def __str__(self) -> str:
# return (f"Ambiguous name `{self._name}` match the following suites"
# f"\n\t{', '.join(self._matches)}\n"
# "See the --list option to list the _available test suites")
class NoMatchException(SuiteException):
def __init__(self, name: str):
self._name = name
def __str__(self) -> str:
return (f"Name `{self._name}` doesn't match any suite/group name\n\t"
"Try to run with -l to see the available suites")
class Suite:
_available: List['Suite'] = []
@classmethod
def run(cls, asked_names: List[str]) -> None:
"""Run all _available suites"""
""" Remove not asked suite from _available suites
Tries to autocomplete the asked names
"""
asked_suites = cls._asked_suites(asked_names)
for suite in asked_suites:
suite._register()
for suite in asked_suites:
try:
suite._run()
except SuiteExitFirstException:
break
@classmethod
def _asked_suites(cls, asked_names: [str]) -> ['Suite']:
suites = cls._available
if not Config.bonus:
suites = [suite for suite in cls._available if not suite._bonus]
if len(asked_names) == 0:
asked_names = [suite._name for suite in suites]
names = []
for name in asked_names:
matches = [suite._name for suite in suites if suite._name.startswith(name) or suite._group.startswith(name)]
if len(matches) == 0:
raise NoMatchException(name)
names.extend(matches)
suites = list(set(
[suite for suite in suites if suite._name in names] +
[suite for suite in suites if suite._group in names]
))
return sorted(suites, key=lambda suite: suite._name)
@classmethod
def list(cls) -> str:
max_name_width = max(len(suite._name + suite._group) for suite in cls._available) + 5
out = ""
for suite in cls._available:
prefixed_name = f"{suite._group}/{suite._name} "
out += f"{prefixed_name:.<{max_name_width}} {suite._description}\n"
return out
def __init__(
self,
origin,
name: str,
group: str,
bonus: bool = False,
description: str = "no description",
):
"""Suite class
name: suite id
groups: list of suite groups
bonus: is this suite bonus
"""
self._name = name
self._group = group
self._description = description
self._bonus = bonus
self._origin = origin
self._tests: List[Test] = []
self._results: List[Result] = []
def _run(self) -> None:
"""Run all test in the suite"""
title = ' ' + self._name + ' '
print(colors.blue(f"{title:#^{Config.term_cols}}"))
if Config.range is not None:
self._tests = self._tests[Config.range[0] : Config.range[1] + 1]
for i, test in enumerate(self._tests):
result = test.run()
self._results.append(result)
print(result.summarize(i))
if Config.exit_first and result.failed:
raise SuiteExitFirstException()
def _register(self) -> None:
def test(*args, **kwargs):
self._tests.append(Test(*args, **kwargs))
self._origin(test)
@classmethod
def summarize(cls):
"""Print a summary of all runned suites"""
full_pass_count = sum(suite._pass_count for suite in suites)
full_fail_count = sum(suite._fail_count for suite in suites)
lines = ["Summary:"]
for suite in cls._available:
lines.append(Suite._stat_summary(suite._name, suite._pass_count, suite._fail_count))
lines.append(Suite._stat_summary("TOTAL", full_pass_count, full_fail_count))
return "\n".join(lines) + "\n"
@property
def _pass_count(self) -> int:
count = 0
for result in self._results:
if result.passed:
count += 1
return count
@property
def _fail_count(self) -> int:
return len(self._results) - self._pass_count
@staticmethod
def _stat_summary(self, name: str, pass_count: int, fail_count: int) -> str:
prefix = f"{name + ' ':.<{Config.term_cols - 24}}"
pass_str = colors.green("{pass_count:4} [PASS]")
fail_str = colors.red("{fail_count:4} [FAIL]")
return f"{prefix} {pass_str} {fail_str}"
@classmethod
def save(cls):
"""Save the result of all suites to a file"""
colors.disable()
with open(Config.log_path, "w") as file:
for suite in suites:
for result in suite._results:
if result.failed:
file.write(result)
|