test: add test cases for sd_journal_seek_realtime_usec() and sd_journal_seek_monotonic_usec()

This commit is contained in:
Yu Watanabe
2024-08-15 04:43:32 +09:00
parent 9c04f67884
commit 12dcb12df3
@@ -877,6 +877,449 @@ TEST(generic_array_bisect) {
test_generic_array_bisect_one(100, 40);
}
typedef struct TestEntry {
uint64_t seqnum;
sd_id128_t seqnum_id;
sd_id128_t boot_id;
dual_timestamp ts;
unsigned number;
unsigned data;
} TestEntry;
static bool find_entry_monotonic_one(
const TestEntry *e,
bool next,
sd_id128_t boot_id,
usec_t usec,
unsigned data,
bool *boot_found) {
assert(e);
assert(boot_found);
if (sd_id128_equal(boot_id, e->boot_id))
*boot_found = true;
if (data != 0 && data != e->data)
return false;
if (sd_id128_equal(boot_id, e->boot_id))
return next ? usec <= e->ts.monotonic : usec >= e->ts.monotonic;
return *boot_found;
}
static size_t find_entry_monotonic(
const TestEntry *entries,
size_t n_entries,
bool next,
sd_id128_t boot_id,
usec_t usec,
unsigned data) {
bool boot_found = false;
assert(entries || n_entries == 0);
for (size_t i = 0; i < n_entries; i++) {
size_t j = next ? i : n_entries - i - 1;
const TestEntry *e = &entries[j];
if (find_entry_monotonic_one(e, next, boot_id, usec, data, &boot_found))
return j;
}
return SIZE_MAX;
}
static size_t find_entry_realtime(
const TestEntry *entries,
size_t n_entries,
bool next,
usec_t usec,
unsigned data) {
assert(entries || n_entries == 0);
for (size_t i = 0; i < n_entries; i++) {
size_t j = next ? i : n_entries - i - 1;
const TestEntry *e = &entries[j];
if (data != 0 && data != e->data)
continue;
if (next ? usec <= e->ts.realtime : usec >= e->ts.realtime)
return j;
}
return SIZE_MAX;
}
static size_t next_entry(
const TestEntry *entries,
size_t n_entries,
bool next,
size_t prev,
unsigned data) {
assert(entries || n_entries == 0);
if (next)
for (size_t i = prev + 1; i < n_entries; i++) {
const TestEntry *e = &entries[i];
if (data != 0 && data != e->data)
continue;
return i;
}
else
for (size_t i = prev; i > 0; i--) {
const TestEntry *e = &entries[i-1];
if (data != 0 && data != e->data)
continue;
return i-1;
}
return SIZE_MAX;
}
static void verify_entry(sd_journal *j, const TestEntry *entry) {
_cleanup_free_ char *s = NULL, *e = NULL;
sd_id128_t id;
usec_t t;
const void *d;
size_t l;
assert(j);
assert(entry);
ASSERT_OK(sd_journal_get_monotonic_usec(j, &t, &id));
ASSERT_STREQ(SD_ID128_TO_STRING(id), SD_ID128_TO_STRING(entry->boot_id));
ASSERT_EQ(t, entry->ts.monotonic);
ASSERT_OK(sd_journal_get_realtime_usec(j, &t));
ASSERT_EQ(t, entry->ts.realtime);
ASSERT_OK(sd_journal_get_data(j, "NUMBER", &d, &l));
ASSERT_NOT_NULL(s = strndup(d, l));
ASSERT_OK(asprintf(&e, "NUMBER=%u", entry->number));
ASSERT_STREQ(s, e);
s = mfree(s);
e = mfree(e);
ASSERT_OK(sd_journal_get_data(j, "DATA", &d, &l));
ASSERT_NOT_NULL(s = strndup(d, l));
ASSERT_OK(asprintf(&e, "DATA=%u", entry->data));
ASSERT_STREQ(s, e);
}
static void test_sd_journal_seek_monotonic_usec(
sd_journal *j,
const TestEntry *entries,
size_t n_entries,
bool next,
sd_id128_t boot_id,
usec_t usec,
unsigned data) {
assert(j);
assert(entries || n_entries == 0);
log_debug("/* %s(next=%s, boot_id=%s, usec="USEC_FMT") */",
__func__, yes_no(next), SD_ID128_TO_STRING(boot_id), usec);
ASSERT_OK(sd_journal_seek_monotonic_usec(j, boot_id, usec));
for (size_t i = find_entry_monotonic(entries, n_entries, next, boot_id, usec, data);
i != SIZE_MAX;
i = next_entry(entries, n_entries, next, i, data)) {
if (next)
ASSERT_OK_POSITIVE(sd_journal_next(j));
else
ASSERT_OK_POSITIVE(sd_journal_previous(j));
verify_entry(j, &entries[i]);
}
if (next)
ASSERT_OK_ZERO(sd_journal_next(j));
else
ASSERT_OK_ZERO(sd_journal_previous(j));
}
static void test_sd_journal_seek_realtime_usec(
sd_journal *j,
const TestEntry *entries,
size_t n_entries,
bool next,
usec_t usec,
unsigned data) {
assert(j);
assert(entries || n_entries == 0);
log_debug("/* %s(next=%s, usec="USEC_FMT") */",
__func__, yes_no(next), usec);
ASSERT_OK(sd_journal_seek_realtime_usec(j, usec));
for (size_t i = find_entry_realtime(entries, n_entries, next, usec, data);
i != SIZE_MAX;
i = next_entry(entries, n_entries, next, i, data)) {
if (next)
ASSERT_OK_POSITIVE(sd_journal_next(j));
else
ASSERT_OK_POSITIVE(sd_journal_previous(j));
verify_entry(j, &entries[i]);
}
if (next)
ASSERT_OK_ZERO(sd_journal_next(j));
else
ASSERT_OK_ZERO(sd_journal_previous(j));
}
static void append_test_entry_full(
JournalFile **f,
MMapCache *m,
TestEntry **entries,
size_t *n_entries,
uint64_t *seqnum,
sd_id128_t *seqnum_id,
const sd_id128_t *boot_id,
const dual_timestamp *ts,
unsigned *number,
unsigned data,
bool expect_rotate) {
struct iovec iovec[3];
size_t n_iovec = 0;
assert(f);
assert(*f);
assert(entries);
assert(n_entries);
assert(*entries || *n_entries == 0);
assert(seqnum);
assert(seqnum_id);
assert(boot_id);
assert(ts);
assert(number);
(*number)++;
const char *q = strjoina("_BOOT_ID=", SD_ID128_TO_STRING(*boot_id));
iovec[n_iovec++] = IOVEC_MAKE_STRING(q);
_cleanup_free_ char *n = NULL;
ASSERT_OK(asprintf(&n, "NUMBER=%u", *number));
iovec[n_iovec++] = IOVEC_MAKE_STRING(n);
_cleanup_free_ char *d = NULL;
ASSERT_OK(asprintf(&d, "DATA=%u", data));
iovec[n_iovec++] = IOVEC_MAKE_STRING(d);
if (expect_rotate) {
ASSERT_ERROR(journal_file_append_entry(
*f,
ts,
boot_id,
iovec, n_iovec,
seqnum,
seqnum_id,
/* ret_object = */ NULL,
/* ret_offset = */ NULL), EREMCHG);
ASSERT_OK(journal_file_rotate(
f,
m,
/* file_flags = */ JOURNAL_STRICT_ORDER,
/* compress_threshold_bytes = */ UINT64_MAX,
/* deferred_closes = */ NULL));
}
ASSERT_OK(journal_file_append_entry(
*f,
ts,
boot_id,
iovec, n_iovec,
seqnum,
seqnum_id,
/* ret_object = */ NULL,
/* ret_offset = */ NULL));
ASSERT_NOT_NULL(GREEDY_REALLOC(*entries, *n_entries + 1));
(*entries)[(*n_entries)++] = (TestEntry) {
.seqnum = *seqnum,
.seqnum_id = *seqnum_id,
.boot_id = *boot_id,
.ts = *ts,
.number = *number,
.data = data,
};
}
static void append_test_entry(
JournalFile *f,
TestEntry **entries,
size_t *n_entries,
uint64_t *seqnum,
sd_id128_t *seqnum_id,
const sd_id128_t *boot_id,
const dual_timestamp *ts,
unsigned *number,
unsigned data) {
append_test_entry_full(&f, NULL, entries, n_entries, seqnum, seqnum_id, boot_id, ts, number, data, /* expect_rotate = */ false);
}
TEST(seek_time) {
_cleanup_(test_donep) char *t = NULL;
_cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
_cleanup_free_ TestEntry *entries = NULL;
size_t n_entries = 0;
JournalFile *f;
mkdtemp_chdir_chattr("/var/tmp/journal-seek-time-XXXXXX", &t);
ASSERT_NOT_NULL(m = mmap_cache_new());
ASSERT_OK(journal_file_open(
-EBADF,
"test.journal",
O_RDWR|O_CREAT,
JOURNAL_STRICT_ORDER,
0644,
/* compress_threshold_bytes = */ UINT64_MAX,
/* metrics = */ NULL,
m,
/* template = */ NULL,
&f));
uint64_t seqnum = 1;
sd_id128_t seqnum_id, boot_id;
ASSERT_OK(sd_id128_randomize(&seqnum_id));
ASSERT_OK(sd_id128_randomize(&boot_id));
dual_timestamp base, ts;
dual_timestamp_now(&base);
unsigned n = 0;
ts = base;
append_test_entry(f, &entries, &n_entries, &seqnum, &seqnum_id, &boot_id, &ts, &n, 100);
ts.realtime += 10;
ts.monotonic += 10;
append_test_entry(f, &entries, &n_entries, &seqnum, &seqnum_id, &boot_id, &ts, &n, 100);
ts.realtime += 10;
ts.monotonic += 10;
append_test_entry(f, &entries, &n_entries, &seqnum, &seqnum_id, &boot_id, &ts, &n, 200);
/* realtime goes to backward */
ts.realtime -= 100;
ts.monotonic += 10;
append_test_entry_full(&f, m, &entries, &n_entries, &seqnum, &seqnum_id, &boot_id, &ts, &n, 200, /* expect_rotate = */ true);
ts.realtime += 10;
ts.monotonic += 10;
append_test_entry(f, &entries, &n_entries, &seqnum, &seqnum_id, &boot_id, &ts, &n, 200);
ts.realtime += 10;
ts.monotonic += 10;
append_test_entry(f, &entries, &n_entries, &seqnum, &seqnum_id, &boot_id, &ts, &n, 100);
/* realtime goes to forward */
ts.realtime += 100;
ts.monotonic += 10;
append_test_entry(f, &entries, &n_entries, &seqnum, &seqnum_id, &boot_id, &ts, &n, 100);
ts.realtime += 10;
ts.monotonic += 10;
append_test_entry(f, &entries, &n_entries, &seqnum, &seqnum_id, &boot_id, &ts, &n, 200);
ts.realtime += 10;
ts.monotonic += 10;
append_test_entry(f, &entries, &n_entries, &seqnum, &seqnum_id, &boot_id, &ts, &n, 100);
/* reboot */
ASSERT_OK(sd_id128_randomize(&boot_id));
ts.realtime += 10;
ts.monotonic -= 1000;
append_test_entry(f, &entries, &n_entries, &seqnum, &seqnum_id, &boot_id, &ts, &n, 100);
ts.realtime += 10;
ts.monotonic += 10;
append_test_entry(f, &entries, &n_entries, &seqnum, &seqnum_id, &boot_id, &ts, &n, 100);
ts.realtime += 10;
ts.monotonic += 10;
append_test_entry(f, &entries, &n_entries, &seqnum, &seqnum_id, &boot_id, &ts, &n, 200);
ts.realtime += 10;
ts.monotonic += 10;
append_test_entry(f, &entries, &n_entries, &seqnum, &seqnum_id, &boot_id, &ts, &n, 200);
/* reboot */
ASSERT_OK(sd_id128_randomize(&boot_id));
ts.realtime += 10;
ts.monotonic -= 2000;
append_test_entry(f, &entries, &n_entries, &seqnum, &seqnum_id, &boot_id, &ts, &n, 100);
journal_file_offline_close(f);
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
ASSERT_OK(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
log_debug("Testing sequential read");
ASSERT_OK(sd_journal_seek_head(j));
ASSERT_OK_POSITIVE(sd_journal_next(j));
test_check_numbers_down(j, n);
ASSERT_OK(sd_journal_seek_tail(j));
ASSERT_OK_POSITIVE(sd_journal_previous(j));
test_check_numbers_up(j, n);
unsigned data;
FOREACH_ARGUMENT(data, 0, 100, 200, 300) {
sd_journal_flush_matches(j);
if (data == 0)
log_info("no match");
else {
log_info("match: DATA=%u", data);
_cleanup_free_ char *match_str = NULL;
ASSERT_OK(asprintf(&match_str, "DATA=%u", data));
ASSERT_OK(sd_journal_add_match(j, match_str, SIZE_MAX));
}
FOREACH_ARRAY(e, entries, n_entries) {
test_sd_journal_seek_monotonic_usec(j, entries, n_entries, /* next = */ true, e->boot_id, e->ts.monotonic - 1, data);
test_sd_journal_seek_monotonic_usec(j, entries, n_entries, /* next = */ true, e->boot_id, e->ts.monotonic, data);
test_sd_journal_seek_monotonic_usec(j, entries, n_entries, /* next = */ true, e->boot_id, e->ts.monotonic + 1, data);
test_sd_journal_seek_monotonic_usec(j, entries, n_entries, /* next = */ false, e->boot_id, e->ts.monotonic - 1, data);
test_sd_journal_seek_monotonic_usec(j, entries, n_entries, /* next = */ false, e->boot_id, e->ts.monotonic, data);
test_sd_journal_seek_monotonic_usec(j, entries, n_entries, /* next = */ false, e->boot_id, e->ts.monotonic + 1, data);
test_sd_journal_seek_realtime_usec(j, entries, n_entries, /* next = */ true, e->ts.monotonic - 1, data);
test_sd_journal_seek_realtime_usec(j, entries, n_entries, /* next = */ true, e->ts.monotonic, data);
test_sd_journal_seek_realtime_usec(j, entries, n_entries, /* next = */ true, e->ts.monotonic + 1, data);
test_sd_journal_seek_realtime_usec(j, entries, n_entries, /* next = */ false, e->ts.monotonic - 1, data);
test_sd_journal_seek_realtime_usec(j, entries, n_entries, /* next = */ false, e->ts.monotonic, data);
test_sd_journal_seek_realtime_usec(j, entries, n_entries, /* next = */ false, e->ts.monotonic + 1, data);
}
}
}
static int intro(void) {
/* journal_file_open() requires a valid machine id */
if (access("/etc/machine-id", F_OK) != 0)