@@ -17,6 +17,8 @@ from searx.search.models import SearchQuery, EngineRef
17from searx.search.processors import EngineProcessor
17from searx.search.processors import EngineProcessor
20+logger = logger.getChild('searx.search.checker')
20HTML_TAGS = [
22HTML_TAGS = [
21 'embed', 'iframe', 'object', 'param', 'picture', 'source', 'svg', 'math', 'canvas', 'noscript', 'script',
23 'embed', 'iframe', 'object', 'param', 'picture', 'source', 'svg', 'math', 'canvas', 'noscript', 'script',
22 'del', 'ins', 'area', 'audio', 'img', 'map', 'track', 'video', 'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite',
24 'del', 'ins', 'area', 'audio', 'img', 'map', 'track', 'video', 'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite',
@@ -121,20 +123,25 @@ def _search_query_diff(sq1: SearchQuery, sq2: SearchQuery)\
122class TestResults:
124class TestResults:
124− __slots__ = 'errors', 'broken_urls'
126+ __slots__ = 'errors', 'logs', 'languages'
126 def __init__(self):
128 def __init__(self):
127 self.errors: typing.Dict[str, typing.List[str]] = {}
129 self.errors: typing.Dict[str, typing.List[str]] = {}
128− self.broken_urls = []
130+ self.logs: typing.Dict[str, typing.List[typing.Any]] = {}
131+ self.languages: typing.Set[str] = set()
130− def add_error(self, test, message):
133+ def add_error(self, test, message, *args):
134+ # message to self.errors
131 errors_for_test = self.errors.setdefault(test, [])
135 errors_for_test = self.errors.setdefault(test, [])
132 if message not in errors_for_test:
136 if message not in errors_for_test:
133 errors_for_test.append(message)
137 errors_for_test.append(message)
138+ # (message, *args) to self.logs
139+ logs_for_test = self.logs.setdefault(test, [])
140+ if (message, *args) not in logs_for_test:
141+ logs_for_test.append((message, *args))
135− def add_broken_url(self, url):
143+ def add_language(self, language):
136− if url not in self.broken_urls:
144+ self.languages.add(language)
137− self.broken_urls.append(url)
139 @property
146 @property
140 def succesfull(self):
147 def succesfull(self):
@@ -167,20 +174,23 @@ class ResultContainerTests:
167 results = self.result_container.get_ordered_results()
174 results = self.result_container.get_ordered_results()
168 return [result['url'] for result in results]
175 return [result['url'] for result in results]
170− def _record_error(self, message: str) -> None:
177+ def _record_error(self, message: str, *args) -> None:
178+ sq = _search_query_to_dict(self.search_query)
179+ sqstr = ' '.join(['{}={!r}'.format(k, v) for k, v in sq.items()])
171− self.test_results.add_error(self.test_name, message)
180+ self.test_results.add_error(self.test_name, message, *args, '(' + sqstr + ')')
173 def _add_language(self, text: str) -> typing.Optional[str]:
182 def _add_language(self, text: str) -> typing.Optional[str]:
174 r = cld3.get_language(str(text)) # pylint: disable=E1101
183 r = cld3.get_language(str(text)) # pylint: disable=E1101
175− if r is not None and r.probability >= 0.9 and r.is_reliable:
184+ if r is not None and r.probability >= 0.98 and r.is_reliable:
176 self.languages.add(r.language)
185 self.languages.add(r.language)
186+ self.test_results.add_language(r.language)
177 return None
187 return None
179 def _check_result(self, result):
189 def _check_result(self, result):
180 if not _check_no_html(result.get('title', '')):
190 if not _check_no_html(result.get('title', '')):
181− self._record_error('HTML in title')
191+ self._record_error('HTML in title', repr(result.get('title', '')))
182 if not _check_no_html(result.get('content', '')):
192 if not _check_no_html(result.get('content', '')):
183− self._record_error('HTML in content')
193+ self._record_error('HTML in content', repr(result.get('content', '')))
185 self._add_language(result.get('title', ''))
195 self._add_language(result.get('title', ''))
186 self._add_language(result.get('content', ''))
196 self._add_language(result.get('content', ''))
@@ -198,13 +208,11 @@ class ResultContainerTests:
198 thumbnail_src = result.get('thumbnail_src')
208 thumbnail_src = result.get('thumbnail_src')
199 if thumbnail_src is not None:
209 if thumbnail_src is not None:
200 if not _is_url_image(thumbnail_src):
210 if not _is_url_image(thumbnail_src):
201− self.test_results.add_broken_url(thumbnail_src)
202− self._record_error('thumbnail_src URL is invalid')
211+ self._record_error('thumbnail_src URL is invalid', thumbnail_src)
203 elif not _is_url_image(result.get('img_src')):
212 elif not _is_url_image(result.get('img_src')):
204− self.test_results.add_broken_url(result.get('img_src'))
205− self._record_error('img_src URL is invalid')
213+ self._record_error('img_src URL is invalid', result.get('img_src'))
206 if template == 'videos.html' and not _is_url_image(result.get('thumbnail')):
214 if template == 'videos.html' and not _is_url_image(result.get('thumbnail')):
207− self._record_error('thumbnail URL is invalid')
215+ self._record_error('thumbnail URL is invalid', result.get('img_src'))
209 def _check_results(self, results: list):
217 def _check_results(self, results: list):
210 for result in results:
218 for result in results:
@@ -213,16 +221,16 @@ class ResultContainerTests:
213 def _check_answers(self, answers):
221 def _check_answers(self, answers):
214 for answer in answers:
222 for answer in answers:
215 if not _check_no_html(answer):
223 if not _check_no_html(answer):
216− self._record_error('HTML in answer')
224+ self._record_error('HTML in answer', answer)
218 def _check_infoboxes(self, infoboxes):
226 def _check_infoboxes(self, infoboxes):
219 for infobox in infoboxes:
227 for infobox in infoboxes:
220 if not _check_no_html(infobox.get('content', '')):
228 if not _check_no_html(infobox.get('content', '')):
221− self._record_error('HTML in infobox content')
229+ self._record_error('HTML in infobox content', infobox.get('content', ''))
222 self._add_language(infobox.get('content', ''))
230 self._add_language(infobox.get('content', ''))
223 for attribute in infobox.get('attributes', {}):
231 for attribute in infobox.get('attributes', {}):
224 if not _check_no_html(attribute.get('value', '')):
232 if not _check_no_html(attribute.get('value', '')):
225− self._record_error('HTML in infobox attribute value')
233+ self._record_error('HTML in infobox attribute value', attribute.get('value', ''))
227 def check_basic(self):
235 def check_basic(self):
228 if len(self.result_container.unresponsive_engines) > 0:
236 if len(self.result_container.unresponsive_engines) > 0: