| # 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() |