1# Copyright 2021 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""Tests for pw_console.log_store""" 15 16import logging 17import unittest 18from unittest.mock import MagicMock 19 20from pw_console.log_store import LogStore 21from pw_console.console_prefs import ConsolePrefs 22 23 24def _create_log_store(): 25 log_store = LogStore(prefs=ConsolePrefs( 26 project_file=False, project_user_file=False, user_file=False)) 27 28 assert not log_store.table.prefs.show_python_file 29 viewer = MagicMock() 30 viewer.new_logs_arrived = MagicMock() 31 log_store.register_viewer(viewer) 32 return log_store, viewer 33 34 35class TestLogStore(unittest.TestCase): 36 """Tests for LogStore.""" 37 def setUp(self): 38 self.maxDiff = None # pylint: disable=invalid-name 39 40 def test_get_total_count(self) -> None: 41 log_store, viewer = _create_log_store() 42 test_log = logging.getLogger('log_store.test') 43 # Must use the assertLogs context manager and the addHandler call. 44 with self.assertLogs(test_log, level='DEBUG') as log_context: 45 test_log.addHandler(log_store) 46 for i in range(5): 47 test_log.debug('Test log %s', i) 48 49 # Expected log message content 50 self.assertEqual( 51 ['DEBUG:log_store.test:Test log {}'.format(i) for i in range(5)], 52 log_context.output, 53 ) 54 # LogStore state checks 55 viewer.new_logs_arrived.assert_called() 56 self.assertEqual(5, log_store.get_total_count()) 57 self.assertEqual(4, log_store.get_last_log_index()) 58 59 log_store.clear_logs() 60 self.assertEqual(0, log_store.get_total_count()) 61 62 def test_channel_counts_and_prefix_width(self) -> None: 63 """Test logger names and prefix width calculations.""" 64 log_store, _viewer = _create_log_store() 65 66 # Log some messagse on 3 separate logger instances 67 for i, logger_name in enumerate([ 68 'log_store.test', 69 'log_store.dev', 70 'log_store.production', 71 ]): 72 test_log = logging.getLogger(logger_name) 73 with self.assertLogs(test_log, level='DEBUG') as _log_context: 74 test_log.addHandler(log_store) 75 test_log.debug('Test log message') 76 for j in range(i): 77 test_log.debug('%s', j) 78 79 self.assertEqual( 80 { 81 'log_store.test': 1, 82 'log_store.dev': 2, 83 'log_store.production': 3, 84 }, 85 log_store.channel_counts, 86 ) 87 self.assertEqual( 88 'log_store.test: 1, log_store.dev: 2, log_store.production: 3', 89 log_store.get_channel_counts(), 90 ) 91 92 self.assertRegex( 93 log_store.logs[0].ansi_stripped_log, 94 r'[0-9]{8} [0-9]{2}:[0-9]{2}:[0-9]{2} DEBUG Test log message', 95 ) 96 self.assertEqual( 97 { 98 'log_store.test': 0, 99 'log_store.dev': 0, 100 'log_store.production': 0, 101 }, 102 log_store.channel_formatted_prefix_widths, 103 ) 104 105 def test_render_table_header_with_metadata(self) -> None: 106 log_store, _viewer = _create_log_store() 107 test_log = logging.getLogger('log_store.test') 108 109 # Log table with extra columns 110 with self.assertLogs(test_log, level='DEBUG') as _log_context: 111 test_log.addHandler(log_store) 112 test_log.debug('Test log %s', 113 extra=dict(extra_metadata_fields={ 114 'planet': 'Jupiter', 115 'galaxy': 'Milky Way' 116 })) 117 118 self.assertEqual( 119 [ 120 ('bold', 'Time '), 121 ('', ' '), 122 ('bold', 'Level'), 123 ('', ' '), 124 ('bold', 'Planet '), 125 ('', ' '), 126 ('bold', 'Galaxy '), 127 ('', ' '), 128 ('bold', 'Message'), 129 ], 130 log_store.render_table_header(), 131 ) 132 133 134if __name__ == '__main__': 135 unittest.main() 136