Commit | Line | Data |
---|---|---|
c71df627 CH |
1 | #!/usr/bin/env python3 |
2 | ||
3 | """ | |
4 | Test log_helpers. | |
5 | ||
6 | .. codeauthor:: Intra2net AG <info@intra2net.com> | |
7 | """ | |
8 | ||
c71df627 CH |
9 | import unittest |
10 | import logging | |
11 | from tempfile import mkstemp | |
12 | import os | |
13 | from os.path import isfile, basename | |
14 | import re | |
15 | ||
16 | from src import log_helpers | |
17 | ||
18 | ||
19 | class LogHelperTest(unittest.TestCase): | |
20 | ||
21 | def setUp(self): | |
22 | """Create temp file to write log to.""" | |
23 | descriptor, self.temp_name = mkstemp( | |
24 | prefix='pyi2ncommon-test-log-helpers-', suffix='.log', text=True) | |
25 | self.temp_handle = os.fdopen(descriptor, 'at') | |
26 | ||
27 | def tearDown(self): | |
28 | """Remove temp log file.""" | |
29 | self.temp_handle.close() | |
30 | if isfile(self.temp_name): | |
31 | os.unlink(self.temp_name) | |
32 | ||
33 | def get_logger_name(self): | |
34 | """Return a unique name for a logger.""" | |
35 | return 'logtest-' + basename(self.temp_name) | |
36 | ||
37 | def check_expected_contents(self, expected_contents): | |
38 | """ | |
39 | Helper for other tests: open temp file, compare contents. | |
40 | ||
41 | Ignores timestamp and line numbers | |
42 | """ | |
43 | self.temp_handle.close() | |
44 | with open(self.temp_name, 'rt') as log_reader: | |
45 | for line_no, (actual_line, expect_line) in \ | |
46 | enumerate(zip(log_reader, expected_contents.splitlines())): | |
47 | if re.match(r'\d{1,2}:\d{2}:\d{2}:\d{3}\s+', actual_line): | |
48 | cmp_line = actual_line[13:].rstrip() | |
49 | elif re.match(r'\s*File ".+", line \d+, in .+', actual_line): | |
50 | cmp_line = ' TRACEBACK LINE REPLACED' | |
51 | else: | |
52 | cmp_line = actual_line.rstrip() | |
53 | self.assertEqual(cmp_line, expect_line) | |
54 | ||
55 | def test_short_level_format(self): | |
56 | """Tests that calls to :py:class:`ShortLevelFormatter` do not fail.""" | |
57 | logger = logging.getLogger(self.get_logger_name()) | |
58 | logger.setLevel(logging.DEBUG) | |
59 | handler = logging.StreamHandler(self.temp_handle) | |
60 | handler.setLevel(logging.DEBUG) | |
61 | formatter = log_helpers.ShortLevelFormatter( | |
62 | '%(asctime)s:%(msecs)03d %(shortlevel)s| %(msg)s' | |
63 | ' [regular levelname=%(levelname)s]', | |
64 | datefmt='%H:%M:%S') | |
65 | handler.setFormatter(formatter) | |
66 | logger.addHandler(handler) | |
67 | ||
68 | # 'application' code | |
69 | logger.debug('debug message') | |
70 | logger.info('info message') | |
71 | logger.warning('warn message') | |
72 | logger.error('error message') | |
73 | logger.critical('critical message') | |
74 | logger.log(15, 'unknown level') | |
75 | logger.log(logging.NOTSET, 'level not set') | |
76 | ||
77 | # add notice level | |
78 | notice = (logging.INFO + logging.WARNING)//2 | |
79 | formatter.add_level(notice, 'note') | |
80 | logger.log(notice, 'more important than info but no warning nor error') | |
81 | ||
82 | # try if exception formatting still works: | |
83 | try: | |
84 | logger.info('this is what an exception looks like:') | |
85 | impossible_result = 1/0 | |
86 | logger.critical('just divided 1/0! The result is {0}' | |
87 | .format(impossible_result)) | |
88 | except ZeroDivisionError: | |
89 | logger.exception('1/0 still does not work!', exc_info=True) | |
90 | ||
91 | # done | |
92 | logger.info('done testing') | |
93 | ||
94 | # now check the file | |
95 | expected_contents = \ | |
96 | 'dbug| debug message [regular levelname=DEBUG]\n' \ | |
97 | 'info| info message [regular levelname=INFO]\n' \ | |
98 | 'warn| warn message [regular levelname=WARNING]\n' \ | |
99 | 'err | error message [regular levelname=ERROR]\n' \ | |
100 | 'crit| critical message [regular levelname=CRITICAL]\n' \ | |
101 | '????| unknown level [regular levelname=Level 15]\n' \ | |
102 | 'note| more important than info but no warning nor error [regular levelname=NOTICE]\n' \ | |
103 | 'info| this is what an exception looks like: [regular levelname=INFO]\n' \ | |
104 | 'err | 1/0 still does not work! [regular levelname=ERROR]\n' \ | |
105 | 'Traceback (most recent call last):\n' \ | |
106 | ' TRACEBACK LINE REPLACED\n' \ | |
107 | ' impossible_result = 1/0\n' \ | |
108 | 'ZeroDivisionError: division by zero\n' \ | |
109 | 'info| done testing [regular levelname=INFO]\n' | |
110 | self.check_expected_contents(expected_contents) | |
111 | ||
112 | def test_get_logger(self): | |
113 | log = log_helpers.get_logger('logger_test') | |
114 | log2 = log_helpers.get_logger('logger_test') | |
115 | self.assertEqual(log, log2) | |
116 | ||
117 | def test_interesting_lines(self): | |
118 | log = log_helpers.get_logger(self.get_logger_name(), | |
119 | streams=self.temp_handle) | |
120 | for idx in range(20): | |
121 | log.log_count_if_interesting(idx) | |
122 | ||
123 | expected_contents = 'info| Counter is at 1\n' \ | |
124 | 'info| Counter is at 2\n' \ | |
125 | 'info| Counter is at 3\n' \ | |
126 | 'info| Counter is at 6\n' \ | |
127 | 'info| Counter is at 10\n' | |
128 | self.check_expected_contents(expected_contents) | |
129 | ||
130 | def test_error_in_formatting(self): | |
131 | """Ensure that forgotten args to not raise err but issue warning.""" | |
132 | self.check_expected_contents(expected_contents) | |
133 | ||
134 | def test_error_in_formatting(self): | |
135 | """Ensure that forgotten args to not raise err but issue warning.""" | |
136 | log = log_helpers.get_logger(self.get_logger_name(), | |
137 | streams=self.temp_handle) | |
138 | log.warn('forgot to add argument {} and {1} and {cnf} to format string') | |
139 | log.warn('wrong number {} of arguments {}', 1) | |
140 | ||
141 | expected_contents = \ | |
142 | 'warn| [LOG ARGS!] forgot to add argument {} and {1} and {cnf} ' \ | |
143 | 'to format string\n' \ | |
144 | 'warn| [LOG ARGS!] wrong number {} of arguments {}\n' | |
145 | self.check_expected_contents(expected_contents) | |
146 | ||
147 | ||
148 | if __name__ == '__main__': | |
149 | unittest.main() |