test: add test cases for sd_journal_seek_realtime_usec() and sd_journal_seek_monotonic_usec()
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user