log_test_helpers.c (6248B)
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 setup_full_capture_of_logs(int new_level) 40 { 41 setup_capture_of_logs(new_level); 42 echo_to_real_logs = 0; 43 } 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 setup_capture_of_logs(int new_level) 54 { 55 if (saved_log_level == 0) { 56 saved_log_level = log_global_min_severity_; 57 } else { 58 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 if (log_global_min_severity_ < new_level) 66 log_global_min_severity_ = new_level; 67 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; 73 } 74 75 /** 76 * Undo setup_capture_of_logs(). 77 * 78 * This function is safe to call more than once. 79 */ 80 void 81 teardown_capture_of_logs(void) 82 { 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(); 88 } 89 90 /** 91 * Clear all messages in mock_saved_logs() 92 */ 93 void 94 mock_clean_saved_logs(void) 95 { 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; 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 mock_saved_logs(void) 111 { 112 return saved_logs; 113 } 114 115 int 116 mock_saved_log_n_entries(void) 117 { 118 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 mock_saved_log_has_message(const char *msg) 127 { 128 if (saved_logs) { 129 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 mock_saved_log_has_message_containing(const char *msg) 147 { 148 if (saved_logs) { 149 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 mock_saved_log_has_message_not_containing(const char *msg) 167 { 168 if (saved_logs) { 169 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 mock_saved_log_has_severity(int severity) 184 { 185 int has_sev = 0; 186 if (saved_logs) { 187 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 return has_sev; 196 } 197 198 /** Return true iff the the saved logs have at lease one message */ 199 int 200 mock_saved_log_has_entry(void) 201 { 202 if (saved_logs) { 203 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 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 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'; 222 223 if (echo_to_real_logs) { 224 tor_log(severity, domain|LD_NO_MOCK, "%s", buf); 225 } 226 227 if (severity > record_logs_at_level) { 228 tor_free(buf); 229 return; 230 } 231 232 if (!saved_logs) 233 saved_logs = smartlist_new(); 234 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); 242 243 smartlist_add(saved_logs, e); 244 } 245 246 void 247 mock_dump_saved_logs(void) 248 { 249 if (saved_logs == NULL) { 250 puts(" Captured logs: NULL"); 251 return; 252 } 253 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); 260 }