Skip to content

Commit c3218ec

Browse files
committed
Add comprehensive test suite and CI integration
This commit introduces a test infrastructure using cmocka to improve code reliability and prevent regressions. The test suite includes: - Unit tests for RFC 3339 timestamp formatting - Unit tests for protocol, log format, facility, and level string table conversions - CI integration to automatically run tests on every build Test Coverage: - test-protocol: Tests RFC 3339 timestamp formatting with various inputs including specific timestamps, NULL values, and structure validation - test-string-tables: Tests all string table lookup functions for protocols (UDP/TCP/TLS/DTLS), log formats (RFC 5424/5425/3339), syslog facilities, and syslog levels Build System: - Added tests/meson.build for test compilation - Tests are optional and only built when cmocka is available - Updated main meson.build to include tests subdirectory CI Improvements: - Added libcmocka-dev to CI dependencies - Added "run tests" step to execute tests after build - Tests run with verbose output for better debugging All tests passing (7 total: 3 protocol + 4 string-tables).
1 parent 34031cc commit c3218ec

5 files changed

Lines changed: 211 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ jobs:
1616
- name: update
1717
run: sudo apt-get update
1818
- name: install build essentials
19-
run: sudo apt-get install -y python3-sphinx ninja-build meson glib-2.0-dev libudev-dev libsystemd-dev clang gperf libcap-dev build-essential
19+
run: sudo apt-get install -y python3-sphinx ninja-build meson glib-2.0-dev libudev-dev libsystemd-dev clang gperf libcap-dev build-essential libcmocka-dev
2020
- name: build
2121
run: make
22+
- name: run tests
23+
run: meson test -C build -v
2224
- name: install
2325
run: sudo make install
2426
- name: add systemd-journal-netlog user

meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ includes = include_directories('src/share',
140140

141141
subdir('units')
142142
subdir('doc')
143+
subdir('tests')
143144

144145
############################################################
145146

tests/meson.build

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
cmocka = dependency('cmocka', required: false)
2+
test_libsystemd = dependency('libsystemd', version : '>= 230')
3+
test_libopenssl = dependency('openssl', version : '>= 1.1.0', required : false)
4+
5+
test_libcap = dependency('libcap', required : false)
6+
if not test_libcap.found()
7+
# Compat with Ubuntu 14.04 which ships libcap w/o .pc file
8+
test_libcap = cc.find_library('cap')
9+
endif
10+
11+
if cmocka.found()
12+
test_protocol = executable(
13+
'test-protocol',
14+
'test-protocol.c',
15+
'../src/netlog/netlog-protocol.c',
16+
'../src/netlog/netlog-network.c',
17+
'../src/netlog/netlog-manager.c',
18+
'../src/netlog/netlog-tls.c',
19+
'../src/netlog/netlog-dtls.c',
20+
'../src/netlog/netlog-ssl.c',
21+
include_directories : includes,
22+
link_with : libshared,
23+
dependencies : [cmocka, test_libsystemd, test_libopenssl, test_libcap],
24+
)
25+
26+
test_string_tables = executable(
27+
'test-string-tables',
28+
'test-string-tables.c',
29+
'../src/netlog/netlog-manager.c',
30+
'../src/netlog/netlog-network.c',
31+
'../src/netlog/netlog-tls.c',
32+
'../src/netlog/netlog-dtls.c',
33+
'../src/netlog/netlog-ssl.c',
34+
'../src/netlog/netlog-protocol.c',
35+
include_directories : includes,
36+
link_with : libshared,
37+
dependencies : [cmocka, test_libsystemd, test_libcap, test_libopenssl],
38+
)
39+
40+
test('protocol', test_protocol)
41+
test('string-tables', test_string_tables)
42+
else
43+
warning('cmocka not found, tests will not be built')
44+
endif

tests/test-protocol.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
3+
#include <stdarg.h>
4+
#include <stddef.h>
5+
#include <setjmp.h>
6+
#include <cmocka.h>
7+
#include <string.h>
8+
#include <sys/time.h>
9+
#include <time.h>
10+
11+
#include "macro.h"
12+
#include "time-util.h"
13+
14+
#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */
15+
16+
/* Forward declaration from netlog-protocol.c */
17+
void format_rfc3339_timestamp(const struct timeval *tv, char *header_time, size_t header_size);
18+
19+
/* Test RFC 3339 timestamp formatting */
20+
static void test_format_rfc3339_timestamp(void **state) {
21+
char buf[FORMAT_TIMESTAMP_MAX];
22+
struct timeval tv = {
23+
.tv_sec = 1234567890,
24+
.tv_usec = 123456,
25+
};
26+
27+
format_rfc3339_timestamp(&tv, buf, sizeof(buf));
28+
29+
/* Should contain year 2009 (timestamp 1234567890) */
30+
assert_non_null(strstr(buf, "2009"));
31+
/* Should contain microseconds */
32+
assert_non_null(strstr(buf, ".123456"));
33+
/* Should contain timezone offset in RFC3339 format (colon separated) */
34+
assert_true(strchr(buf, ':') != NULL);
35+
}
36+
37+
/* Test RFC 3339 timestamp with NULL tv (current time) */
38+
static void test_format_rfc3339_timestamp_null(void **state) {
39+
char buf[FORMAT_TIMESTAMP_MAX];
40+
41+
format_rfc3339_timestamp(NULL, buf, sizeof(buf));
42+
43+
/* Should contain a valid timestamp */
44+
assert_int_not_equal(strlen(buf), 0);
45+
/* Should contain year 20xx */
46+
assert_non_null(strstr(buf, "20"));
47+
}
48+
49+
/* Test RFC 3339 timestamp format structure */
50+
static void test_rfc3339_timestamp_structure(void **state) {
51+
char buf[FORMAT_TIMESTAMP_MAX];
52+
struct timeval tv = {
53+
.tv_sec = 1609459200, /* 2021-01-01 00:00:00 UTC */
54+
.tv_usec = 500000,
55+
};
56+
57+
format_rfc3339_timestamp(&tv, buf, sizeof(buf));
58+
59+
/* Check for T separator */
60+
assert_non_null(strchr(buf, 'T'));
61+
/* Check for microseconds with dot */
62+
assert_non_null(strstr(buf, ".500000"));
63+
}
64+
65+
int main(void) {
66+
const struct CMUnitTest tests[] = {
67+
cmocka_unit_test(test_format_rfc3339_timestamp),
68+
cmocka_unit_test(test_format_rfc3339_timestamp_null),
69+
cmocka_unit_test(test_rfc3339_timestamp_structure),
70+
};
71+
72+
return cmocka_run_group_tests(tests, NULL, NULL);
73+
}

tests/test-string-tables.c

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
3+
#include <stdarg.h>
4+
#include <stddef.h>
5+
#include <setjmp.h>
6+
#include <cmocka.h>
7+
#include <string.h>
8+
9+
#include "netlog-manager.h"
10+
11+
/* Test protocol string table conversions */
12+
static void test_protocol_string_table(void **state) {
13+
assert_string_equal(protocol_to_string(SYSLOG_TRANSMISSION_PROTOCOL_UDP), "udp");
14+
assert_string_equal(protocol_to_string(SYSLOG_TRANSMISSION_PROTOCOL_TCP), "tcp");
15+
assert_string_equal(protocol_to_string(SYSLOG_TRANSMISSION_PROTOCOL_DTLS), "dtls");
16+
assert_string_equal(protocol_to_string(SYSLOG_TRANSMISSION_PROTOCOL_TLS), "tls");
17+
18+
assert_int_equal(protocol_from_string("udp"), SYSLOG_TRANSMISSION_PROTOCOL_UDP);
19+
assert_int_equal(protocol_from_string("tcp"), SYSLOG_TRANSMISSION_PROTOCOL_TCP);
20+
assert_int_equal(protocol_from_string("dtls"), SYSLOG_TRANSMISSION_PROTOCOL_DTLS);
21+
assert_int_equal(protocol_from_string("tls"), SYSLOG_TRANSMISSION_PROTOCOL_TLS);
22+
23+
/* Test invalid protocol - returns -1 when not found */
24+
assert_true(protocol_from_string("invalid") < 0);
25+
assert_null(protocol_to_string(999));
26+
}
27+
28+
/* Test log format string table conversions */
29+
static void test_log_format_string_table(void **state) {
30+
assert_string_equal(log_format_to_string(SYSLOG_TRANSMISSION_LOG_FORMAT_RFC_5424), "rfc5424");
31+
assert_string_equal(log_format_to_string(SYSLOG_TRANSMISSION_LOG_FORMAT_RFC_3339), "rfc3339");
32+
assert_string_equal(log_format_to_string(SYSLOG_TRANSMISSION_LOG_FORMAT_RFC_5425), "rfc5425");
33+
34+
assert_int_equal(log_format_from_string("rfc5424"), SYSLOG_TRANSMISSION_LOG_FORMAT_RFC_5424);
35+
assert_int_equal(log_format_from_string("rfc3339"), SYSLOG_TRANSMISSION_LOG_FORMAT_RFC_3339);
36+
assert_int_equal(log_format_from_string("rfc5425"), SYSLOG_TRANSMISSION_LOG_FORMAT_RFC_5425);
37+
38+
/* Test invalid format - returns -1 when not found */
39+
assert_true(log_format_from_string("invalid") < 0);
40+
assert_null(log_format_to_string(999));
41+
}
42+
43+
/* Test syslog facility string table conversions */
44+
static void test_syslog_facility_string_table(void **state) {
45+
assert_string_equal(syslog_facility_to_string(SYSLOG_FACILITY_KERN), "kern");
46+
assert_string_equal(syslog_facility_to_string(SYSLOG_FACILITY_USER), "user");
47+
assert_string_equal(syslog_facility_to_string(SYSLOG_FACILITY_MAIL), "mail");
48+
assert_string_equal(syslog_facility_to_string(SYSLOG_FACILITY_DAEMON), "daemon");
49+
assert_string_equal(syslog_facility_to_string(SYSLOG_FACILITY_AUTH), "auth");
50+
assert_string_equal(syslog_facility_to_string(SYSLOG_FACILITY_LOCAL0), "local0");
51+
assert_string_equal(syslog_facility_to_string(SYSLOG_FACILITY_LOCAL7), "local7");
52+
53+
assert_int_equal(syslog_facility_from_string("kern"), SYSLOG_FACILITY_KERN);
54+
assert_int_equal(syslog_facility_from_string("user"), SYSLOG_FACILITY_USER);
55+
assert_int_equal(syslog_facility_from_string("local0"), SYSLOG_FACILITY_LOCAL0);
56+
57+
/* Test invalid facility - returns -1 when not found */
58+
assert_true(syslog_facility_from_string("invalid") < 0);
59+
assert_null(syslog_facility_to_string(999));
60+
}
61+
62+
/* Test syslog level string table conversions */
63+
static void test_syslog_level_string_table(void **state) {
64+
assert_string_equal(syslog_level_to_string(SYSLOG_LEVEL_EMERGENCY), "emerg");
65+
assert_string_equal(syslog_level_to_string(SYSLOG_LEVEL_ALERT), "alert");
66+
assert_string_equal(syslog_level_to_string(SYSLOG_LEVEL_CRITICAL), "crit");
67+
assert_string_equal(syslog_level_to_string(SYSLOG_LEVEL_ERROR), "err");
68+
assert_string_equal(syslog_level_to_string(SYSLOG_LEVEL_WARNING), "warning");
69+
assert_string_equal(syslog_level_to_string(SYSLOG_LEVEL_NOTICE), "notice");
70+
assert_string_equal(syslog_level_to_string(SYSLOG_LEVEL_INFORMATIONAL), "info");
71+
assert_string_equal(syslog_level_to_string(SYSLOG_LEVEL_DEBUG), "debug");
72+
73+
assert_int_equal(syslog_level_from_string("emerg"), SYSLOG_LEVEL_EMERGENCY);
74+
assert_int_equal(syslog_level_from_string("debug"), SYSLOG_LEVEL_DEBUG);
75+
76+
/* Test invalid level - returns -1 when not found */
77+
assert_true(syslog_level_from_string("invalid") < 0);
78+
assert_null(syslog_level_to_string(999));
79+
}
80+
81+
int main(void) {
82+
const struct CMUnitTest tests[] = {
83+
cmocka_unit_test(test_protocol_string_table),
84+
cmocka_unit_test(test_log_format_string_table),
85+
cmocka_unit_test(test_syslog_facility_string_table),
86+
cmocka_unit_test(test_syslog_level_string_table),
87+
};
88+
89+
return cmocka_run_group_tests(tests, NULL, NULL);
90+
}

0 commit comments

Comments
 (0)