File: log_formatter_test.c

package info (click to toggle)
aws-crt-python 0.20.4%2Bdfsg-1~bpo12%2B1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-backports
  • size: 72,656 kB
  • sloc: ansic: 381,805; python: 23,008; makefile: 6,251; sh: 4,536; cpp: 699; ruby: 208; java: 77; perl: 73; javascript: 46; xml: 11
file content (237 lines) | stat: -rw-r--r-- 8,572 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/**
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

#include <aws/common/log_formatter.h>

#include <aws/common/string.h>
#include <aws/common/thread.h>
#include <aws/testing/aws_test_harness.h>

#include <inttypes.h>
#include <stdarg.h>

#define TEST_FORMATTER_MAX_BUFFER_SIZE 4096

typedef int(log_formatter_test_fn)(struct aws_log_formatter *formatter, struct aws_string **output);

int do_default_log_formatter_test(
    struct aws_allocator *allocator,
    log_formatter_test_fn *test_fn,
    const char *expected_user_output,
    enum aws_log_level log_level,
    enum aws_date_format date_format) {
    /* Initialize a default formatter*/
    struct aws_log_formatter_standard_options options = {.date_format = date_format};

    struct aws_log_formatter formatter;
    aws_log_formatter_init_default(&formatter, allocator, &options);

    struct aws_date_time test_time;
    aws_date_time_init_now(&test_time);

    /* Output something via the callback */
    struct aws_string *output = NULL;
    int result = (*test_fn)(&formatter, &output);

    aws_log_formatter_clean_up(&formatter);

    char buffer[TEST_FORMATTER_MAX_BUFFER_SIZE];
    snprintf(buffer, TEST_FORMATTER_MAX_BUFFER_SIZE, "%s", aws_string_c_str(output));

    aws_string_destroy(output);

    /* Check that the format call was successful */
    ASSERT_TRUE(result == AWS_OP_SUCCESS, "Formatting operation failed");

    /*
     * Make sure there's an endline as the final character(s).
     * Move the end-of-string marker on top of the endline.
     * Otherwise our failure outputs include the endline and become confusing.
     */
    size_t line_length = strlen(buffer);
    ASSERT_TRUE(line_length >= 2, "Log line \"%s\" is too short", buffer);

    ASSERT_TRUE(buffer[line_length - 1] == '\n', "Log line did not end with a newline");
    buffer[line_length - 1] = 0;

    /*
     * Check that the log level appears properly
     */
    const char *log_level_start = strstr(buffer, "[");
    ASSERT_TRUE(log_level_start != NULL, "Could not find start of log level in output line \"%s\"", buffer);

    const char *level_string = NULL;
    ASSERT_SUCCESS(
        aws_log_level_to_string(log_level, &level_string), "Failed to convert log level %d to string", (int)log_level);
    ASSERT_TRUE(
        strncmp(log_level_start + 1, level_string, strlen(level_string)) == 0,
        "Incorrect value for log level in output line \"%s\"",
        buffer);

    /**
     * Find the timestamp substring.
     */
    const char *time_start = strstr(log_level_start + 1, "[");
    ASSERT_TRUE(time_start != NULL, "Could not find start of timestamp in output line \"%s\"", buffer);
    time_start += 1;

    const char *time_end = strstr(time_start, "]");
    ASSERT_TRUE(time_end != NULL, "Could not find end of timestamp in output line \"%s\"", buffer);

    size_t time_length = time_end - time_start;

    /*
     * Fake a buffer pointing to the logged timestamp string; convert it to a date time
     */
    struct aws_byte_buf timestamp_buffer;
    timestamp_buffer.allocator = formatter.allocator;
    timestamp_buffer.buffer = (uint8_t *)time_start;
    timestamp_buffer.capacity = time_length;
    timestamp_buffer.len = time_length;

    struct aws_date_time log_time;
    ASSERT_SUCCESS(
        aws_date_time_init_from_str(&log_time, &timestamp_buffer, date_format),
        "Could not parse timestamp value starting at \"%s\"",
        time_start);

    /*
     * Check that the timestamp, when converted back, is close to the current time.
     */
    time_t time_diff = aws_date_time_diff(&log_time, &test_time);
    ASSERT_TRUE(time_diff <= 1, "Log timestamp deviated too far from test timestamp: %d seconds", (int)time_diff);

    /*
     * Find the thread id substring
     */
    const char *thread_id_start = strstr(time_end + 1, "[");
    ASSERT_TRUE(thread_id_start != NULL, "Could not find start of thread id in output line \"%s\"", buffer);
    thread_id_start += 1;

    char *thread_id_end = strstr(thread_id_start, "]");
    ASSERT_TRUE(thread_id_end != NULL, "Could not find end of thread id in output line \"%s\"", buffer);

    ASSERT_TRUE((thread_id_end - thread_id_start + 1) == AWS_THREAD_ID_T_REPR_BUFSZ, "Unexpected thread id length");
    aws_thread_id_t current_thread_id = aws_thread_current_thread_id();
    char repr[AWS_THREAD_ID_T_REPR_BUFSZ];
    ASSERT_SUCCESS(
        aws_thread_id_t_to_string(current_thread_id, repr, AWS_THREAD_ID_T_REPR_BUFSZ),
        "Could not convert aws_thread_id_t to string repr");
    char logged_id[AWS_THREAD_ID_T_REPR_BUFSZ];
    memcpy(logged_id, thread_id_start, AWS_THREAD_ID_T_REPR_BUFSZ - 1);
    logged_id[AWS_THREAD_ID_T_REPR_BUFSZ - 1] = '\0';
    ASSERT_SUCCESS(
        strncmp(repr, logged_id, AWS_THREAD_ID_T_REPR_BUFSZ),
        "Expected logged thread id to be \"%s\" but it was actually \"%s\"",
        repr,
        logged_id);

    /*
     * Check that the user content is what was expected
     */
    const char *separator = strstr(thread_id_end, " - ");
    ASSERT_TRUE(separator != NULL, "");

    const char *user_content = separator + 3;
    size_t expected_user_content_length = strlen(expected_user_output);
    ASSERT_SUCCESS(
        strncmp(user_content, expected_user_output, expected_user_content_length),
        "Expected user content \"%s\" but received \"%s\"",
        expected_user_output,
        user_content);

    return AWS_OP_SUCCESS;
}

#define DEFINE_LOG_FORMATTER_TEST(test_function, log_level, date_format, expected_user_string)                         \
    static int s_log_formatter_##test_function(struct aws_allocator *allocator, void *ctx) {                           \
        (void)ctx;                                                                                                     \
        return do_default_log_formatter_test(allocator, test_function, expected_user_string, log_level, date_format);  \
    }                                                                                                                  \
    AWS_TEST_CASE(test_log_formatter_##test_function, s_log_formatter_##test_function);

static int invoke_formatter(
    struct aws_log_formatter *formatter,
    struct aws_string **output,
    enum aws_log_level log_level,
    const char *format,
    ...) {
    va_list args;
    va_start(args, format);

    int result = formatter->vtable->format(formatter, output, log_level, AWS_LS_COMMON_GENERAL, format, args);

    va_end(args);

    return result;
}

/*
 * Tests
 */

/*
 * Empty string case
 */
static int s_formatter_empty_case(struct aws_log_formatter *formatter, struct aws_string **output) {
    return invoke_formatter(formatter, output, AWS_LL_WARN, "");
}

DEFINE_LOG_FORMATTER_TEST(s_formatter_empty_case, AWS_LL_WARN, AWS_DATE_FORMAT_RFC822, "")

/*
 * Simple string
 */
static int s_formatter_simple_case(struct aws_log_formatter *formatter, struct aws_string **output) {
    return invoke_formatter(formatter, output, AWS_LL_DEBUG, "Sample log output");
}

DEFINE_LOG_FORMATTER_TEST(s_formatter_simple_case, AWS_LL_DEBUG, AWS_DATE_FORMAT_ISO_8601, "Sample log output")

/*
 * Format string with numbers
 */
static int s_formatter_number_case(struct aws_log_formatter *formatter, struct aws_string **output) {
    return invoke_formatter(
        formatter, output, AWS_LL_FATAL, "%d bottles of milk on the wall. Take %.4f bottles down.", 99, .9999f);
}

DEFINE_LOG_FORMATTER_TEST(
    s_formatter_number_case,
    AWS_LL_FATAL,
    AWS_DATE_FORMAT_RFC822,
    "99 bottles of milk on the wall. Take 0.9999 bottles down.")

/*
 * Format string with strings
 */
static int s_formatter_string_case(struct aws_log_formatter *formatter, struct aws_string **output) {
    return invoke_formatter(
        formatter,
        output,
        AWS_LL_INFO,
        "Once there was, if %s there was, and just perhaps there %s was",
        "ever",
        "never");
}

DEFINE_LOG_FORMATTER_TEST(
    s_formatter_string_case,
    AWS_LL_INFO,
    AWS_DATE_FORMAT_ISO_8601,
    "Once there was, if ever there was, and just perhaps there never was")

/*
 * Format string with newlines
 */
static int s_formatter_newline_case(struct aws_log_formatter *formatter, struct aws_string **output) {
    return invoke_formatter(formatter, output, AWS_LL_TRACE, "\nMaking sure \nnewlines don't mess things\nup");
}

DEFINE_LOG_FORMATTER_TEST(
    s_formatter_newline_case,
    AWS_LL_TRACE,
    AWS_DATE_FORMAT_RFC822,
    "\nMaking sure \nnewlines don't mess things\nup")