]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test: add test cases for sd_journal_seek_realtime_usec() and sd_journal_seek_monotoni...
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 5 Aug 2024 20:20:05 +0000 (05:20 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 14 Aug 2024 19:43:32 +0000 (04:43 +0900)
src/libsystemd/sd-journal/test-journal-interleaving.c

index 101ca5396fa44622790f7c363bb4bdcb7ebd3c9d..ace2cb8c053206d76640853de3cf9a65b5c3bb39 100644 (file)
@@ -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)