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