blob: d885560c00328d858dc20d60bada582027f497f9 [file] [log] [blame]
# Copyright 2021 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""Tests for pw_console.log_view"""
import logging
import re
import unittest
from parameterized import parameterized # type: ignore
from prompt_toolkit.document import Document
from prompt_toolkit.validation import ValidationError
from pw_console.log_line import LogLine
from pw_console.log_filter import (
LogFilter,
RegexValidator,
SearchMatcher,
preprocess_search_regex,
)
class TestLogFilter(unittest.TestCase):
"""Tests for LogFilter."""
def setUp(self):
self.maxDiff = None # pylint: disable=invalid-name
# pylint: disable=anomalous-backslash-in-string
@parameterized.expand([
(
'raw string',
SearchMatcher.STRING,
'f(x)',
'f\(x\)',
re.IGNORECASE,
),
(
'simple regex',
SearchMatcher.REGEX,
'f(x)',
'f(x)',
re.IGNORECASE,
),
(
'regex with case sensitivity',
SearchMatcher.REGEX,
'f(X)',
'f(X)',
re.RegexFlag(0),
),
(
'regex with error',
SearchMatcher.REGEX,
'f of (x', # Un-terminated open paren
'f of (x',
re.IGNORECASE,
True, # fails_validation
),
(
'simple fuzzy',
SearchMatcher.FUZZY,
'f x y',
'(f)(.*?)(x)(.*?)(y)',
re.IGNORECASE,
),
(
'fuzzy with case sensitivity',
SearchMatcher.FUZZY,
'f X y',
'(f)(.*?)(X)(.*?)(y)',
re.RegexFlag(0),
),
]) # yapf: disable
def test_preprocess_search_regex(
self,
_name,
input_matcher,
input_text,
expected_regex,
expected_re_flag,
should_fail_validation=False,
) -> None:
"""Test preprocess_search_regex returns the expected regex settings."""
result_text, re_flag = preprocess_search_regex(input_text,
input_matcher)
self.assertEqual(expected_regex, result_text)
self.assertEqual(expected_re_flag, re_flag)
if should_fail_validation:
document = Document(text=input_text)
with self.assertRaisesRegex(ValidationError,
r'Regex Error.*at position [0-9]+'):
RegexValidator().validate(document)
def _create_logs(self, log_messages):
test_log = logging.getLogger('log_filter.test')
with self.assertLogs(test_log, level='DEBUG') as log_context:
for log, extra_arg in log_messages:
test_log.debug('%s', log, extra=extra_arg)
return log_context
@parameterized.expand([
(
'simple fuzzy',
SearchMatcher.FUZZY,
'log item',
[
('Log some item', {'planet': 'Jupiter'}),
('Log another item', {'planet': 'Earth'}),
('Some exception', {'planet': 'Earth'}),
],
[
'Log some item',
'Log another item',
],
None, # field
False, # invert
),
(
'simple fuzzy inverted',
SearchMatcher.FUZZY,
'log item',
[
('Log some item', dict()),
('Log another item', dict()),
('Some exception', dict()),
],
[
'Some exception',
],
None, # field
True, # invert
),
(
'regex with field',
SearchMatcher.REGEX,
'earth',
[
('Log some item',
dict(extra_metadata_fields={'planet': 'Jupiter'})),
('Log another item',
dict(extra_metadata_fields={'planet': 'Earth'})),
('Some exception',
dict(extra_metadata_fields={'planet': 'Earth'})),
],
[
'Log another item',
'Some exception',
],
'planet', # field
False, # invert
),
(
'regex with field inverted',
SearchMatcher.REGEX,
'earth',
[
('Log some item',
dict(extra_metadata_fields={'planet': 'Jupiter'})),
('Log another item',
dict(extra_metadata_fields={'planet': 'Earth'})),
('Some exception',
dict(extra_metadata_fields={'planet': 'Earth'})),
],
[
'Log some item',
],
'planet', # field
True, # invert
),
]) # yapf: disable
def test_log_filter_matches(
self,
_name,
input_matcher,
input_text,
input_lines,
expected_matched_lines,
field=None,
invert=False,
) -> None:
"""Test log filter matches expected lines."""
result_text, re_flag = preprocess_search_regex(input_text,
input_matcher)
log_filter = LogFilter(
regex=re.compile(result_text, re_flag),
input_text=input_text,
invert=invert,
field=field,
)
matched_lines = []
logs = self._create_logs(input_lines)
for record in logs.records:
if log_filter.matches(
LogLine(record, record.message, record.message)):
matched_lines.append(record.message)
self.assertEqual(expected_matched_lines, matched_lines)
if __name__ == '__main__':
unittest.main()