| # 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.text_formatting""" |
| |
| from datetime import datetime |
| import logging |
| import unittest |
| |
| from parameterized import parameterized # type: ignore |
| |
| from pw_console.console_prefs import ConsolePrefs |
| from pw_console.log_line import LogLine |
| from pw_console.widgets.table import TableView |
| |
| _TIMESTAMP_FORMAT = '%Y%m%d %H:%M:%S' |
| _TIMESTAMP_SAMPLE = datetime(2021, 6, 30, 16, 10, 37, 818901) |
| _TIMESTAMP_SAMPLE_STRING = _TIMESTAMP_SAMPLE.strftime(_TIMESTAMP_FORMAT) |
| |
| _TABLE_PADDING = ' ' |
| _TABLE_PADDING_FRAGMENT = ('', _TABLE_PADDING) |
| |
| formatter = logging.Formatter( |
| '\x1b[30m\x1b[47m' |
| '%(asctime)s' |
| '\x1b[0m' |
| ' ' |
| '\x1b[33m\x1b[1m' |
| '%(levelname)s' |
| '\x1b[0m' |
| ' ' |
| '%(message)s', _TIMESTAMP_FORMAT) |
| |
| |
| def make_log(**kwargs): |
| """Create a LogLine instance.""" |
| # Construct a LogRecord |
| attributes = dict(name='testlogger', |
| levelno=logging.INFO, |
| levelname='INF', |
| msg='[%s] %.3f %s', |
| args=('MOD1', 3.14159, 'Real message here'), |
| created=_TIMESTAMP_SAMPLE.timestamp(), |
| filename='test.py', |
| lineno=42, |
| pathname='/home/user/test.py') |
| # Override any above attrs that are passed in. |
| attributes.update(kwargs) |
| # Create the record |
| record = logging.makeLogRecord(dict(attributes)) |
| # Format |
| formatted_message = formatter.format(record) |
| log_line = LogLine(record=record, |
| formatted_log=formatted_message, |
| ansi_stripped_log='') |
| log_line.update_metadata() |
| return log_line |
| |
| |
| class TestTableView(unittest.TestCase): |
| """Tests for rendering log lines into tables.""" |
| def setUp(self): |
| # Show large diffs |
| self.maxDiff = None # pylint: disable=invalid-name |
| self.prefs = ConsolePrefs(project_file=False, |
| project_user_file=False, |
| user_file=False) |
| self.prefs.reset_config() |
| |
| @parameterized.expand([ |
| ( |
| 'Correct column widths with all fields set', |
| [ |
| make_log( |
| args=('M1', 1.2345, 'Something happened'), |
| extra_metadata_fields=dict(module='M1', anumber=12)), |
| |
| make_log( |
| args=('MD2', 567.5, 'Another cool event'), |
| extra_metadata_fields=dict(module='MD2', anumber=123)), |
| ], |
| dict(module=len('MD2'), anumber=len('123')), |
| ), |
| ( |
| 'Missing metadata fields on some rows', |
| [ |
| make_log( |
| args=('M1', 54321.2, 'Something happened'), |
| extra_metadata_fields=dict(module='M1', anumber=54321.2)), |
| |
| make_log( |
| args=('MOD2', 567.5, 'Another cool event'), |
| extra_metadata_fields=dict(module='MOD2')), |
| ], |
| dict(module=len('MOD2'), anumber=len('54321.200')), |
| ), |
| ]) # yapf: disable |
| def test_column_widths(self, _name, logs, expected_widths) -> None: |
| """Test colum widths calculation.""" |
| table = TableView(self.prefs) |
| for log in logs: |
| table.update_metadata_column_widths(log) |
| metadata_fields = { |
| k: v |
| for k, v in log.metadata.fields.items() |
| if k not in ['py_file', 'py_logger'] |
| } |
| # update_metadata_column_widths shoulp populate self.metadata.fields |
| self.assertEqual(metadata_fields, log.record.extra_metadata_fields) |
| # Check expected column widths |
| results = { |
| k: v |
| for k, v in dict(table.column_widths).items() |
| if k not in ['time', 'level', 'py_file', 'py_logger'] |
| } |
| self.assertCountEqual(expected_widths, results) |
| |
| @parameterized.expand([ |
| ( |
| 'Build header adding fields incrementally', |
| [ |
| make_log( |
| args=('MODULE2', 567.5, 'Another cool event'), |
| extra_metadata_fields=dict( |
| # timestamp missing |
| module='MODULE2')), |
| |
| make_log( |
| args=('MODULE1', 54321.2, 'Something happened'), |
| extra_metadata_fields=dict( |
| # timestamp added in |
| module='MODULE1', timestamp=54321.2)), |
| ], |
| [ |
| [('bold', 'Time '), |
| _TABLE_PADDING_FRAGMENT, |
| ('bold', 'Lev'), |
| _TABLE_PADDING_FRAGMENT, |
| ('bold', 'Module '), |
| _TABLE_PADDING_FRAGMENT, |
| ('bold', 'Message')], |
| |
| [('bold', 'Time '), |
| _TABLE_PADDING_FRAGMENT, |
| ('bold', 'Lev'), |
| _TABLE_PADDING_FRAGMENT, |
| ('bold', 'Module '), |
| _TABLE_PADDING_FRAGMENT, |
| # timestamp added in |
| ('bold', 'Timestamp'), |
| _TABLE_PADDING_FRAGMENT, |
| ('bold', 'Message')], |
| ], |
| ), |
| ]) # yapf: disable |
| def test_formatted_header(self, _name, logs, expected_headers) -> None: |
| """Test colum widths calculation.""" |
| table = TableView(self.prefs) |
| |
| for log, header in zip(logs, expected_headers): |
| table.update_metadata_column_widths(log) |
| self.assertEqual(table.formatted_header(), header) |
| |
| @parameterized.expand([ |
| ( |
| 'Build rows adding fields incrementally', |
| [ |
| make_log( |
| args=('MODULE2', 567.5, 'Another cool event'), |
| extra_metadata_fields=dict( |
| # timestamp missing |
| module='MODULE2', |
| msg='Another cool event')), |
| |
| make_log( |
| args=('MODULE2', 567.5, 'Another cool event'), |
| extra_metadata_fields=dict( |
| # timestamp and msg missing |
| module='MODULE2')), |
| |
| make_log( |
| args=('MODULE1', 54321.2, 'Something happened'), |
| extra_metadata_fields=dict( |
| # timestamp added in |
| module='MODULE1', timestamp=54321.2, |
| msg='Something happened')), |
| ], |
| [ |
| [ |
| ('class:log-time', _TIMESTAMP_SAMPLE_STRING), |
| _TABLE_PADDING_FRAGMENT, |
| ('class:log-level-20', 'INF'), |
| _TABLE_PADDING_FRAGMENT, |
| ('class:log-table-column-0', 'MODULE2'), |
| _TABLE_PADDING_FRAGMENT, |
| ('', 'Another cool event'), |
| ('', '\n') |
| ], |
| |
| [ |
| ('class:log-time', _TIMESTAMP_SAMPLE_STRING), |
| _TABLE_PADDING_FRAGMENT, |
| ('class:log-level-20', 'INF'), |
| _TABLE_PADDING_FRAGMENT, |
| ('class:log-table-column-0', 'MODULE2'), |
| _TABLE_PADDING_FRAGMENT, |
| ('', '[MODULE2] 567.500 Another cool event'), |
| ('', '\n') |
| ], |
| |
| [ |
| ('class:log-time', _TIMESTAMP_SAMPLE_STRING), |
| _TABLE_PADDING_FRAGMENT, |
| ('class:log-level-20', 'INF'), |
| _TABLE_PADDING_FRAGMENT, |
| ('class:log-table-column-0', 'MODULE1'), |
| _TABLE_PADDING_FRAGMENT, |
| ('class:log-table-column-1', '54321.200'), |
| _TABLE_PADDING_FRAGMENT, |
| ('', 'Something happened'), |
| ('', '\n') |
| ], |
| ], |
| ), |
| ]) # yapf: disable |
| def test_formatted_rows(self, _name, logs, expected_log_format) -> None: |
| """Test colum widths calculation.""" |
| table = TableView(self.prefs) |
| # Check each row meets expected formats incrementally. |
| for log, formatted_log in zip(logs, expected_log_format): |
| table.update_metadata_column_widths(log) |
| self.assertEqual(formatted_log, table.formatted_row(log)) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |