Line data Source code
1 : /* Copyright (c) 2015-2021, The Tor Project, Inc. */
2 : /* See LICENSE for licensing information */
3 : #define LOG_PRIVATE
4 : #include "lib/log/log.h"
5 : #include "test/log_test_helpers.h"
6 :
7 : /**
8 : * \file log_test_helpers.c
9 : * \brief Code to check for expected log messages during testing.
10 : */
11 :
12 : static void mock_saving_logv(int severity, log_domain_mask_t domain,
13 : const char *funcname, const char *suffix,
14 : const char *format, va_list ap)
15 : CHECK_PRINTF(5, 0);
16 :
17 : /**
18 : * Smartlist of all the logs we've received since we last set up
19 : * log capture.
20 : */
21 : static smartlist_t *saved_logs = NULL;
22 :
23 : /** Boolean: should we also send messages to the test-runner? */
24 : static int echo_to_real_logs = 1;
25 :
26 : /** Record logs at this level or more severe */
27 : static int record_logs_at_level = LOG_ERR;
28 :
29 : static int saved_log_level = 0;
30 :
31 : /**
32 : * As setup_capture_of_logs, but do not relay log messages into the main
33 : * logging system.
34 : *
35 : * Avoid using this function; use setup_capture_of_logs() instead if you
36 : * can. If you must use this function, then make sure you detect any
37 : * unexpected log messages, and treat them as test failures. */
38 : void
39 184 : setup_full_capture_of_logs(int new_level)
40 : {
41 184 : setup_capture_of_logs(new_level);
42 184 : echo_to_real_logs = 0;
43 184 : }
44 :
45 : /**
46 : * Temporarily capture all the messages logged at severity <b>new_level</b> or
47 : * higher.
48 : *
49 : * This function does not prevent messages from being sent to the main
50 : * logging system.
51 : */
52 : void
53 370 : setup_capture_of_logs(int new_level)
54 : {
55 370 : if (saved_log_level == 0) {
56 370 : saved_log_level = log_global_min_severity_;
57 : } else {
58 0 : tor_assert(0);
59 : }
60 :
61 : /* Only change the log_global_min_severity_ if we're making things _more_
62 : * verbose. Otherwise we could prevent real log messages that the test-
63 : * runner wanted.
64 : */
65 370 : if (log_global_min_severity_ < new_level)
66 163 : log_global_min_severity_ = new_level;
67 :
68 370 : record_logs_at_level = new_level;
69 370 : mock_clean_saved_logs();
70 370 : saved_logs = smartlist_new();
71 370 : MOCK(logv, mock_saving_logv);
72 370 : echo_to_real_logs = 1;
73 370 : }
74 :
75 : /**
76 : * Undo setup_capture_of_logs().
77 : *
78 : * This function is safe to call more than once.
79 : */
80 : void
81 382 : teardown_capture_of_logs(void)
82 : {
83 382 : UNMOCK(logv);
84 382 : if (saved_log_level)
85 366 : log_global_min_severity_ = saved_log_level;
86 382 : saved_log_level = 0;
87 382 : mock_clean_saved_logs();
88 382 : }
89 :
90 : /**
91 : * Clear all messages in mock_saved_logs()
92 : */
93 : void
94 1017 : mock_clean_saved_logs(void)
95 : {
96 1017 : if (!saved_logs)
97 : return;
98 1526 : SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
99 : { tor_free(m->generated_msg); tor_free(m); });
100 540 : smartlist_free(saved_logs);
101 540 : saved_logs = NULL;
102 : }
103 :
104 : /**
105 : * Return a list of all the messages captured since the last
106 : * setup_[full_]capture_of_logs() call. Each log call is recorded as a
107 : * mock_saved_log_entry_t.
108 : */
109 : const smartlist_t *
110 14 : mock_saved_logs(void)
111 : {
112 14 : return saved_logs;
113 : }
114 :
115 : int
116 113 : mock_saved_log_n_entries(void)
117 : {
118 113 : return saved_logs ? smartlist_len(saved_logs) : 0;
119 : }
120 :
121 : /**
122 : * Return true iff there is a message recorded by log capture
123 : * that is exactly equal to <b>msg</b>
124 : */
125 : int
126 126 : mock_saved_log_has_message(const char *msg)
127 : {
128 126 : if (saved_logs) {
129 161 : SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
130 : {
131 : if (msg && m->generated_msg &&
132 : !strcmp(msg, m->generated_msg)) {
133 : return 1;
134 : }
135 : });
136 : }
137 :
138 : return 0;
139 : }
140 :
141 : /**
142 : * Return true iff there is a message recorded by log capture
143 : * that contains <b>msg</b> as a substring.
144 : */
145 : int
146 357 : mock_saved_log_has_message_containing(const char *msg)
147 : {
148 357 : if (saved_logs) {
149 621 : SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
150 : {
151 : if (msg && m->generated_msg &&
152 : strstr(m->generated_msg, msg)) {
153 : return 1;
154 : }
155 : });
156 : }
157 :
158 : return 0;
159 : }
160 :
161 : /**
162 : * Return true iff there is not a message recorded by log capture
163 : * that contains <b>msg</b> as a substring.
164 : */
165 : int
166 5 : mock_saved_log_has_message_not_containing(const char *msg)
167 : {
168 5 : if (saved_logs) {
169 6 : SMARTLIST_FOREACH(
170 : saved_logs, mock_saved_log_entry_t *, m,
171 : {
172 : if (msg && m->generated_msg && strstr(m->generated_msg, msg))
173 : return 0;
174 : }
175 : );
176 : }
177 :
178 : return 1;
179 : }
180 :
181 : /** Return true iff the saved logs have any messages with <b>severity</b> */
182 : int
183 13 : mock_saved_log_has_severity(int severity)
184 : {
185 13 : int has_sev = 0;
186 13 : if (saved_logs) {
187 26 : SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
188 : {
189 : if (m->severity == severity) {
190 : has_sev = 1;
191 : }
192 : });
193 : }
194 :
195 13 : return has_sev;
196 : }
197 :
198 : /** Return true iff the the saved logs have at lease one message */
199 : int
200 29 : mock_saved_log_has_entry(void)
201 : {
202 29 : if (saved_logs) {
203 24 : return smartlist_len(saved_logs) > 0;
204 : }
205 : return 0;
206 : }
207 :
208 : /* Replacement for logv: record the log message, and (maybe) send it
209 : * into the logging system again.
210 : */
211 : static void
212 1015 : mock_saving_logv(int severity, log_domain_mask_t domain,
213 : const char *funcname, const char *suffix,
214 : const char *format, va_list ap)
215 : {
216 1015 : char *buf = tor_malloc_zero(10240);
217 1015 : int n;
218 1015 : n = tor_vsnprintf(buf,10240,format,ap);
219 1015 : tor_assert(n < 10240-1);
220 1015 : buf[n]='\n';
221 1015 : buf[n+1]='\0';
222 :
223 1015 : if (echo_to_real_logs) {
224 619 : tor_log(severity, domain|LD_NO_MOCK, "%s", buf);
225 : }
226 :
227 1015 : if (severity > record_logs_at_level) {
228 2 : tor_free(buf);
229 2 : return;
230 : }
231 :
232 1013 : if (!saved_logs)
233 174 : saved_logs = smartlist_new();
234 :
235 1013 : mock_saved_log_entry_t *e = tor_malloc_zero(sizeof(mock_saved_log_entry_t));
236 1013 : e->severity = severity;
237 1013 : e->funcname = funcname;
238 1013 : e->suffix = suffix;
239 1013 : e->format = format;
240 1013 : e->generated_msg = tor_strdup(buf);
241 1013 : tor_free(buf);
242 :
243 1013 : smartlist_add(saved_logs, e);
244 : }
245 :
246 : void
247 0 : mock_dump_saved_logs(void)
248 : {
249 0 : if (saved_logs == NULL) {
250 0 : puts(" Captured logs: NULL");
251 0 : return;
252 : }
253 :
254 0 : puts(" Captured logs:");
255 0 : SMARTLIST_FOREACH_BEGIN(saved_logs, const mock_saved_log_entry_t *, m) {
256 0 : printf("% 5d. %s: %s\n", m_sl_idx + 1,
257 0 : log_level_to_string(m->severity),
258 0 : escaped(m->generated_msg));
259 0 : } SMARTLIST_FOREACH_END(m);
260 : }
261 :
|