log_test_helpers.c 6.09 KB
Newer Older
1
/* Copyright (c) 2015-2018, The Tor Project, Inc. */
2
/* See LICENSE for licensing information */
Ola Bini's avatar
Ola Bini committed
3
4
5
6
#define LOG_PRIVATE
#include "torlog.h"
#include "log_test_helpers.h"

7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * \file log_test_helpers.c
 * \brief Code to check for expected log messages during testing.
 */

static void mock_saving_logv(int severity, log_domain_mask_t domain,
                             const char *funcname, const char *suffix,
                             const char *format, va_list ap)
  CHECK_PRINTF(5, 0);

/**
 * Smartlist of all the logs we've received since we last set up
 * log capture.
 */
Ola Bini's avatar
Ola Bini committed
21
22
static smartlist_t *saved_logs = NULL;

23
/** Boolean: should we also send messages to the test-runner? */
24
25
static int echo_to_real_logs = 1;

26
27
28
/** Record logs at this level or more severe */
static int record_logs_at_level = LOG_ERR;

29
30
static int saved_log_level = 0;

31
32
33
34
35
36
37
/**
 * As setup_capture_of_logs, but do not relay log messages into the main
 * logging system.
 *
 * Avoid using this function; use setup_capture_of_logs() instead if you
 * can. If you must use this function, then make sure you detect any
 * unexpected log messages, and treat them as test failures. */
38
void
39
40
setup_full_capture_of_logs(int new_level)
{
41
  setup_capture_of_logs(new_level);
42
43
44
  echo_to_real_logs = 0;
}

45
46
/**
 * Temporarily capture all the messages logged at severity <b>new_level</b> or
47
 * higher.
48
49
50
51
 *
 * This function does not prevent messages from being sent to the main
 * logging system.
 */
52
void
Ola Bini's avatar
Ola Bini committed
53
54
setup_capture_of_logs(int new_level)
{
55
56
57
58
59
  if (saved_log_level == 0) {
    saved_log_level = log_global_min_severity_;
  } else {
    tor_assert(0);
  }
60
61
62
63
64
65
66
67
68

  /* Only change the log_global_min_severity_ if we're making things _more_
   * verbose.  Otherwise we could prevent real log messages that the test-
   * runner wanted.
   */
  if (log_global_min_severity_ < new_level)
    log_global_min_severity_ = new_level;

  record_logs_at_level = new_level;
Ola Bini's avatar
Ola Bini committed
69
  mock_clean_saved_logs();
70
  saved_logs = smartlist_new();
Ola Bini's avatar
Ola Bini committed
71
  MOCK(logv, mock_saving_logv);
72
  echo_to_real_logs = 1;
Ola Bini's avatar
Ola Bini committed
73
74
}

75
76
/**
 * Undo setup_capture_of_logs().
77
78
 *
 * This function is safe to call more than once.
79
 */
Ola Bini's avatar
Ola Bini committed
80
void
81
teardown_capture_of_logs(void)
Ola Bini's avatar
Ola Bini committed
82
83
{
  UNMOCK(logv);
84
85
86
  if (saved_log_level)
    log_global_min_severity_ = saved_log_level;
  saved_log_level = 0;
Ola Bini's avatar
Ola Bini committed
87
88
89
  mock_clean_saved_logs();
}

90
91
92
/**
 * Clear all messages in mock_saved_logs()
 */
Ola Bini's avatar
Ola Bini committed
93
94
95
96
97
98
99
100
101
102
103
void
mock_clean_saved_logs(void)
{
  if (!saved_logs)
    return;
  SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
                    { tor_free(m->generated_msg); tor_free(m); });
  smartlist_free(saved_logs);
  saved_logs = NULL;
}

104
105
106
107
108
/**
 * Return a list of all the messages captured since the last
 * setup_[full_]capture_of_logs() call. Each log call is recorded as a
 * mock_saved_log_entry_t.
 */
Ola Bini's avatar
Ola Bini committed
109
110
111
112
113
114
const smartlist_t *
mock_saved_logs(void)
{
  return saved_logs;
}

115
116
117
118
119
120
int
mock_saved_log_n_entries(void)
{
  return saved_logs ? smartlist_len(saved_logs) : 0;
}

121
122
123
124
/**
 * Return true iff there is a message recorded by log capture
 * that is exactly equal to <b>msg</b>
 */
125
126
127
128
129
130
131
132
int
mock_saved_log_has_message(const char *msg)
{
  if (saved_logs) {
    SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
                      {
                        if (msg && m->generated_msg &&
                            !strcmp(msg, m->generated_msg)) {
133
                          return 1;
134
135
136
137
                        }
                      });
  }

138
  return 0;
139
140
}

141
142
143
144
/**
 * Return true iff there is a message recorded by log capture
 * that contains <b>msg</b> as a substring.
 */
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
int
mock_saved_log_has_message_containing(const char *msg)
{
  if (saved_logs) {
    SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
                      {
                        if (msg && m->generated_msg &&
                            strstr(m->generated_msg, msg)) {
                          return 1;
                        }
                      });
  }

  return 0;
}

161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/**
 * Return true iff there is not a message recorded by log capture
 * that contains <b>msg</b> as a substring.
 */
int
mock_saved_log_has_message_not_containing(const char *msg)
{
  if (saved_logs) {
    SMARTLIST_FOREACH(
      saved_logs, mock_saved_log_entry_t *, m,
      {
        if (msg && m->generated_msg && strstr(m->generated_msg, msg))
          return 0;
      }
    );
  }

  return 1;
}

181
/** Return true iff the saved logs have any messages with <b>severity</b> */
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
int
mock_saved_log_has_severity(int severity)
{
  int has_sev = 0;
  if (saved_logs) {
    SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
                      {
                        if (m->severity == severity) {
                          has_sev = 1;
                        }
                      });
  }

  return has_sev;
}

198
/** Return true iff the the saved logs have at lease one message */
199
200
201
202
203
204
205
206
207
int
mock_saved_log_has_entry(void)
{
  if (saved_logs) {
    return smartlist_len(saved_logs) > 0;
  }
  return 0;
}

208
209
210
211
/* Replacement for logv: record the log message, and (maybe) send it
 * into the logging system again.
 */
static void
212
213
214
mock_saving_logv(int severity, log_domain_mask_t domain,
                 const char *funcname, const char *suffix,
                 const char *format, va_list ap)
Ola Bini's avatar
Ola Bini committed
215
216
217
218
{
  char *buf = tor_malloc_zero(10240);
  int n;
  n = tor_vsnprintf(buf,10240,format,ap);
219
  tor_assert(n < 10240-1);
Ola Bini's avatar
Ola Bini committed
220
221
222
  buf[n]='\n';
  buf[n+1]='\0';

223
224
225
226
227
228
229
230
231
232
233
234
  if (echo_to_real_logs) {
    tor_log(severity, domain|LD_NO_MOCK, "%s", buf);
  }

  if (severity > record_logs_at_level) {
    tor_free(buf);
    return;
  }

  if (!saved_logs)
    saved_logs = smartlist_new();

Ola Bini's avatar
Ola Bini committed
235
236
237
238
239
  mock_saved_log_entry_t *e = tor_malloc_zero(sizeof(mock_saved_log_entry_t));
  e->severity = severity;
  e->funcname = funcname;
  e->suffix = suffix;
  e->format = format;
240
241
  e->generated_msg = tor_strdup(buf);
  tor_free(buf);
Ola Bini's avatar
Ola Bini committed
242
243
244

  smartlist_add(saved_logs, e);
}
Andrea Shepard's avatar
Andrea Shepard committed
245

246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
void
mock_dump_saved_logs(void)
{
  if (saved_logs == NULL) {
    puts("  Captured logs: NULL");
    return;
  }

  puts("  Captured logs:");
  SMARTLIST_FOREACH_BEGIN(saved_logs, const mock_saved_log_entry_t *, m) {
    printf("% 5d. %s: %s\n", m_sl_idx + 1,
           log_level_to_string(m->severity),
           escaped(m->generated_msg));
  } SMARTLIST_FOREACH_END(m);
}
Nick Mathewson's avatar
Nick Mathewson committed
261