Merge branch 'maint-0.4.8' into release-0.4.8
[tor.git] / src / test / log_test_helpers.c
blob8928f396348e35016611f43e32ff14c1580eb6c8
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"
7 /**
8 * \file log_test_helpers.c
9 * \brief Code to check for expected log messages during testing.
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);
17 /**
18 * Smartlist of all the logs we've received since we last set up
19 * log capture.
21 static smartlist_t *saved_logs = NULL;
23 /** Boolean: should we also send messages to the test-runner? */
24 static int echo_to_real_logs = 1;
26 /** Record logs at this level or more severe */
27 static int record_logs_at_level = LOG_ERR;
29 static int saved_log_level = 0;
31 /**
32 * As setup_capture_of_logs, but do not relay log messages into the main
33 * logging system.
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 setup_full_capture_of_logs(int new_level)
41 setup_capture_of_logs(new_level);
42 echo_to_real_logs = 0;
45 /**
46 * Temporarily capture all the messages logged at severity <b>new_level</b> or
47 * higher.
49 * This function does not prevent messages from being sent to the main
50 * logging system.
52 void
53 setup_capture_of_logs(int new_level)
55 if (saved_log_level == 0) {
56 saved_log_level = log_global_min_severity_;
57 } else {
58 tor_assert(0);
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.
65 if (log_global_min_severity_ < new_level)
66 log_global_min_severity_ = new_level;
68 record_logs_at_level = new_level;
69 mock_clean_saved_logs();
70 saved_logs = smartlist_new();
71 MOCK(logv, mock_saving_logv);
72 echo_to_real_logs = 1;
75 /**
76 * Undo setup_capture_of_logs().
78 * This function is safe to call more than once.
80 void
81 teardown_capture_of_logs(void)
83 UNMOCK(logv);
84 if (saved_log_level)
85 log_global_min_severity_ = saved_log_level;
86 saved_log_level = 0;
87 mock_clean_saved_logs();
90 /**
91 * Clear all messages in mock_saved_logs()
93 void
94 mock_clean_saved_logs(void)
96 if (!saved_logs)
97 return;
98 SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
99 { tor_free(m->generated_msg); tor_free(m); });
100 smartlist_free(saved_logs);
101 saved_logs = NULL;
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.
109 const smartlist_t *
110 mock_saved_logs(void)
112 return saved_logs;
116 mock_saved_log_n_entries(void)
118 return saved_logs ? smartlist_len(saved_logs) : 0;
122 * Return true iff there is a message recorded by log capture
123 * that is exactly equal to <b>msg</b>
126 mock_saved_log_has_message(const char *msg)
128 if (saved_logs) {
129 SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
131 if (msg && m->generated_msg &&
132 !strcmp(msg, m->generated_msg)) {
133 return 1;
138 return 0;
142 * Return true iff there is a message recorded by log capture
143 * that contains <b>msg</b> as a substring.
146 mock_saved_log_has_message_containing(const char *msg)
148 if (saved_logs) {
149 SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
151 if (msg && m->generated_msg &&
152 strstr(m->generated_msg, msg)) {
153 return 1;
158 return 0;
162 * Return true iff there is not a message recorded by log capture
163 * that contains <b>msg</b> as a substring.
166 mock_saved_log_has_message_not_containing(const char *msg)
168 if (saved_logs) {
169 SMARTLIST_FOREACH(
170 saved_logs, mock_saved_log_entry_t *, m,
172 if (msg && m->generated_msg && strstr(m->generated_msg, msg))
173 return 0;
178 return 1;
181 /** Return true iff the saved logs have any messages with <b>severity</b> */
183 mock_saved_log_has_severity(int severity)
185 int has_sev = 0;
186 if (saved_logs) {
187 SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
189 if (m->severity == severity) {
190 has_sev = 1;
195 return has_sev;
198 /** Return true iff the the saved logs have at lease one message */
200 mock_saved_log_has_entry(void)
202 if (saved_logs) {
203 return smartlist_len(saved_logs) > 0;
205 return 0;
208 /* Replacement for logv: record the log message, and (maybe) send it
209 * into the logging system again.
211 static void
212 mock_saving_logv(int severity, log_domain_mask_t domain,
213 const char *funcname, const char *suffix,
214 const char *format, va_list ap)
216 char *buf = tor_malloc_zero(10240);
217 int n;
218 n = tor_vsnprintf(buf,10240,format,ap);
219 tor_assert(n < 10240-1);
220 buf[n]='\n';
221 buf[n+1]='\0';
223 if (echo_to_real_logs) {
224 tor_log(severity, domain|LD_NO_MOCK, "%s", buf);
227 if (severity > record_logs_at_level) {
228 tor_free(buf);
229 return;
232 if (!saved_logs)
233 saved_logs = smartlist_new();
235 mock_saved_log_entry_t *e = tor_malloc_zero(sizeof(mock_saved_log_entry_t));
236 e->severity = severity;
237 e->funcname = funcname;
238 e->suffix = suffix;
239 e->format = format;
240 e->generated_msg = tor_strdup(buf);
241 tor_free(buf);
243 smartlist_add(saved_logs, e);
246 void
247 mock_dump_saved_logs(void)
249 if (saved_logs == NULL) {
250 puts(" Captured logs: NULL");
251 return;
254 puts(" Captured logs:");
255 SMARTLIST_FOREACH_BEGIN(saved_logs, const mock_saved_log_entry_t *, m) {
256 printf("% 5d. %s: %s\n", m_sl_idx + 1,
257 log_level_to_string(m->severity),
258 escaped(m->generated_msg));
259 } SMARTLIST_FOREACH_END(m);