1 | from contextlib import contextmanager |
||
2 | import queue |
||
3 | from tempfile import NamedTemporaryFile |
||
4 | import unittest |
||
5 | |||
6 | from coalib.bears.LocalBear import LocalBear |
||
7 | from coalib.results.Result import Result |
||
8 | from coalib.settings.Section import Section |
||
9 | from coalib.settings.Setting import Setting |
||
10 | from bears.tests.BearTestHelper import generate_skip_decorator |
||
11 | |||
12 | |||
13 | @contextmanager |
||
14 | def prepare_file(lines, filename, force_linebreaks, create_tempfile): |
||
15 | """ |
||
16 | Can creates a temporary file (if filename is None) with the lines. |
||
17 | Can also add a trailing newline to each line specified if needed. |
||
18 | |||
19 | :param lines: The lines from the file. (list of strings) |
||
20 | :param filename: The filename to be prepared. |
||
21 | - If it is None, A new tempfile will be created |
||
22 | (if create_tempfile is True). |
||
23 | - If it is a string, use it as the filename. |
||
24 | - If it is a dictionary, it is passed as kwargs |
||
25 | to NamedTemporaryFile. |
||
26 | :param force_linebreaks: Whether to append newlines at each line if needed. |
||
27 | :param create_tempfile: Whether to save lines in tempfile if needed. |
||
28 | """ |
||
29 | if force_linebreaks: |
||
30 | for i, line in enumerate(lines): |
||
31 | lines[i] = line if line.endswith("\n") else line + "\n" |
||
32 | |||
33 | if not create_tempfile and filename is None: |
||
34 | filename = "dummy_file_name" |
||
35 | |||
36 | if not isinstance(filename, str) and create_tempfile: |
||
37 | tempfile_kwargs = {} if filename is None else filename |
||
38 | with NamedTemporaryFile(**tempfile_kwargs) as file: |
||
39 | file.write(bytes("".join(lines), 'UTF-8')) |
||
40 | yield lines, file.name |
||
41 | else: |
||
42 | yield lines, filename |
||
43 | |||
44 | |||
45 | class LocalBearTestHelper(unittest.TestCase): # pragma: no cover |
||
46 | """ |
||
47 | This is a helper class for simplification of testing of local bears. |
||
48 | |||
49 | Please note that all abstraction will prepare the lines so you don't need |
||
50 | to do that if you use them. |
||
51 | |||
52 | If you miss some methods, get in contact with us, we'll be happy to help! |
||
53 | """ |
||
54 | |||
55 | def check_validity(self, |
||
56 | local_bear, |
||
57 | lines, |
||
58 | filename="default", |
||
59 | valid=True, |
||
60 | force_linebreaks=True, |
||
61 | create_tempfile=True): |
||
62 | """ |
||
63 | Asserts that a check of the given lines with the given local bear |
||
64 | either yields or does not yield any results. |
||
65 | |||
66 | :param local_bear: The local bear to check with. |
||
67 | :param lines: The lines to check. (string if single line |
||
68 | or List of strings) |
||
69 | :param filename: The filename, if it matters. |
||
70 | :param valid: Whether the lines are valid or not. |
||
71 | :param force_linebreaks: Whether to append newlines at each line |
||
72 | if needed. (Bears expect a \\n for every line) |
||
73 | :param create_tempfile: Whether to save lines in tempfile if needed. |
||
74 | """ |
||
75 | if isinstance(lines, str): |
||
76 | lines = [lines] |
||
77 | |||
78 | assert isinstance(self, unittest.TestCase) |
||
79 | self.assertIsInstance(local_bear, |
||
80 | LocalBear, |
||
81 | msg="The given bear is not a local bear.") |
||
82 | self.assertIsInstance(lines, |
||
83 | list, |
||
84 | msg="The given lines are not a list.") |
||
85 | |||
86 | with prepare_file(lines, filename, force_linebreaks, create_tempfile) \ |
||
87 | as (lines, filename): |
||
88 | |||
89 | bear_output = self._execute_bear(local_bear, filename, lines) |
||
90 | if valid: |
||
91 | msg = ("The local bear '{}' yields a result although it " |
||
92 | "shouldn't.".format(local_bear.__class__.__name__)) |
||
93 | self.assertEqual(bear_output, [], msg=msg) |
||
94 | else: |
||
95 | msg = ("The local bear '{}' yields no result although it " |
||
96 | "should.".format(local_bear.__class__.__name__)) |
||
97 | self.assertNotEqual(len(bear_output), 0, msg=msg) |
||
98 | |||
99 | def check_results(self, |
||
100 | local_bear, |
||
101 | lines, |
||
102 | results, |
||
103 | filename="default", |
||
104 | check_order=False, |
||
105 | force_linebreaks=True, |
||
106 | create_tempfile=True): |
||
107 | """ |
||
108 | Asserts that a check of the given lines with the given local bear does |
||
109 | yield exactly the given results. |
||
110 | |||
111 | :param local_bear: The local bear to check with. |
||
112 | :param lines: The lines to check. (string if single line |
||
113 | or List of strings) |
||
114 | :param results: The expected result or list of results. |
||
115 | :param filename: The filename, if it matters. |
||
116 | :param force_linebreaks: Whether to append newlines at each line |
||
117 | if needed. (Bears expect a \\n for every line) |
||
118 | :param create_tempfile: Whether to save lines in tempfile if needed. |
||
119 | """ |
||
120 | if isinstance(lines, str): |
||
121 | lines = [lines] |
||
122 | if isinstance(results, Result): |
||
123 | results = [results] |
||
124 | |||
125 | assert isinstance(self, unittest.TestCase) |
||
126 | self.assertIsInstance(local_bear, |
||
127 | LocalBear, |
||
128 | msg="The given bear is not a local bear.") |
||
129 | self.assertIsInstance(lines, |
||
130 | list, |
||
131 | msg="The given lines are not a list.") |
||
132 | self.assertIsInstance(results, |
||
133 | list, |
||
134 | msg="The given results are not a list.") |
||
135 | |||
136 | with prepare_file(lines, filename, force_linebreaks, create_tempfile) \ |
||
137 | as (lines, filename): |
||
138 | |||
139 | bear_output = self._execute_bear(local_bear, filename, lines) |
||
140 | msg = ("The local bear '{}' doesn't yield the right results. Or the" |
||
141 | " order may be wrong.".format(local_bear.__class__.__name__)) |
||
142 | if not check_order: |
||
143 | self.assertEqual(sorted(bear_output), sorted(results), msg=msg) |
||
144 | else: |
||
145 | self.assertEqual(bear_output, results, msg=msg) |
||
146 | |||
147 | def _execute_bear(self, bear, *args, **kwargs): |
||
0 ignored issues
–
show
|
|||
148 | try: |
||
149 | bear_output_generator = bear.execute(*args, **kwargs) |
||
150 | assert bear_output_generator is not None |
||
151 | except AssertionError: |
||
152 | msg = [] |
||
153 | while not bear.message_queue.empty(): |
||
154 | msg.append(bear.message_queue.get().message) |
||
155 | raise AssertionError("Bear returned None on execution \n" + |
||
156 | "\n".join(msg)) |
||
157 | return list(bear_output_generator) |
||
158 | |||
159 | def verify_local_bear(bear, |
||
160 | valid_files, |
||
161 | invalid_files, |
||
162 | filename='default', |
||
163 | settings={}, |
||
164 | force_linebreaks=True, |
||
165 | create_tempfile=True): |
||
166 | """ |
||
167 | Generates a test for a local bear by checking the given valid and invalid |
||
168 | file contents. Simply use it on your module level like: |
||
169 | |||
170 | YourTestName = verify_local_bear(YourBear, (['valid line'],), |
||
171 | (['invalid line'],)) |
||
172 | |||
173 | :param bear: The Bear class to test. |
||
174 | :param valid_files: An iterable of files as a string list that won't |
||
175 | yield results. |
||
176 | :param invalid_files: An iterable of files as a string list that must |
||
177 | yield results. |
||
178 | :param filename: The filename to use for valid and invalid files. |
||
179 | :param settings: A dictionary of keys and values (both string) from |
||
180 | which settings will be created that will be made |
||
181 | available for the tested bear. |
||
182 | :param force_linebreaks: Whether to append newlines at each line |
||
183 | if needed. (Bears expect a \\n for every line) |
||
184 | :param create_tempfile: Whether to save lines in tempfile if needed. |
||
185 | :return: A unittest.TestCase object. |
||
186 | """ |
||
187 | @generate_skip_decorator(bear) |
||
188 | class LocalBearTest(LocalBearTestHelper): |
||
189 | |||
190 | def setUp(self): |
||
191 | self.section = Section('name') |
||
192 | self.uut = bear(self.section, |
||
193 | queue.Queue()) |
||
194 | for name, value in settings.items(): |
||
195 | self.section.append(Setting(name, value)) |
||
196 | |||
197 | def test_valid_files(self): |
||
198 | for file in valid_files: |
||
199 | self.check_validity(self.uut, |
||
200 | file, |
||
201 | filename, |
||
202 | valid=True, |
||
203 | force_linebreaks=force_linebreaks, |
||
204 | create_tempfile=create_tempfile) |
||
205 | |||
206 | def test_invalid_files(self): |
||
207 | for file in invalid_files: |
||
208 | self.check_validity(self.uut, |
||
209 | file, |
||
210 | filename, |
||
211 | valid=False, |
||
212 | force_linebreaks=force_linebreaks, |
||
213 | create_tempfile=create_tempfile) |
||
214 | |||
215 | return LocalBearTest |
||
216 |
If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example
could be written as