]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test: add unit test for new sd-json functionality 42136/head
authorLennart Poettering <lennart@amutable.com>
Sun, 17 May 2026 06:46:59 +0000 (08:46 +0200)
committerLuca Boccassi <luca.boccassi@gmail.com>
Mon, 18 May 2026 09:39:04 +0000 (10:39 +0100)
src/libsystemd/sd-json/test-json.c

index 3be4b09660b140157c2dbd1cab326abf819d89b7..c9aa5d2f9016918c287f263e4c35ce0ea1d9c1a7 100644 (file)
@@ -13,6 +13,7 @@
 #include "fd-util.h"
 #include "format-util.h"
 #include "fileio.h"
+#include "io-util.h"
 #include "iovec-util.h"
 #include "json-internal.h"
 #include "json-util.h"
@@ -518,6 +519,68 @@ TEST(source) {
         printf("--- pretty end ---\n");
 }
 
+TEST(parse_fd) {
+        static const char data[] = "{ \"foo\" : \"bar\", \"baz\" : 4711 }";
+
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+        _cleanup_close_ int fd = -EBADF;
+
+        ASSERT_OK(fd = open_tmpfile_unlinkable(NULL, O_RDWR));
+        ASSERT_OK(loop_write(fd, data, strlen(data)));
+
+        /* By default the fd is internally duplicated, the caller's fd stays open and the JSON text is
+         * read starting at the current file offset. */
+        ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET));
+        ASSERT_OK(sd_json_parse_fd("tmpfile", fd, /* flags= */ 0, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+        ASSERT_OK(fd_validate(fd));     /* still open, we only got a duplicate */
+        ASSERT_STREQ(sd_json_variant_string(sd_json_variant_by_key(v, "foo")), "bar");
+        ASSERT_EQ(sd_json_variant_unsigned(sd_json_variant_by_key(v, "baz")), UINT64_C(4711));
+        v = sd_json_variant_unref(v);
+
+        /* Without SD_JSON_PARSE_SEEK0 and with the offset left at EOF there is nothing to read. */
+        ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_END));
+        ASSERT_ERROR(sd_json_parse_fd("tmpfile", fd, /* flags= */ 0, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL), ENODATA);
+        ASSERT_NULL(v);
+        ASSERT_OK(fd_validate(fd));
+
+        /* SD_JSON_PARSE_SEEK0 rewinds to the beginning first, so the stale offset no longer matters. */
+        ASSERT_OK(sd_json_parse_fd("tmpfile", fd, SD_JSON_PARSE_SEEK0, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+        ASSERT_OK(fd_validate(fd));
+        ASSERT_STREQ(sd_json_variant_string(sd_json_variant_by_key(v, "foo")), "bar");
+        v = sd_json_variant_unref(v);
+
+        /* SD_JSON_PARSE_REOPEN_FD reopens the fd internally (starting at offset 0), the caller's fd and
+         * its offset are left untouched. */
+        ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_END));
+        ASSERT_OK(sd_json_parse_fd("tmpfile", fd, SD_JSON_PARSE_REOPEN_FD, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+        ASSERT_OK(fd_validate(fd));
+        ASSERT_STREQ(sd_json_variant_string(sd_json_variant_by_key(v, "foo")), "bar");
+        v = sd_json_variant_unref(v);
+
+        /* SD_JSON_PARSE_REOPEN_FD and SD_JSON_PARSE_DONATE_FD are mutually exclusive. */
+        ASSERT_RETURN_EXPECTED_SE(sd_json_parse_fd("tmpfile", fd, SD_JSON_PARSE_REOPEN_FD|SD_JSON_PARSE_DONATE_FD, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL) == -EINVAL);
+        ASSERT_OK(fd_validate(fd));     /* not consumed on the -EINVAL path */
+
+        /* SD_JSON_PARSE_DONATE_FD passes ownership into the call: the fd is consumed and closed even on
+         * success. */
+        ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET));
+        ASSERT_OK(sd_json_parse_fd("tmpfile", fd, SD_JSON_PARSE_DONATE_FD, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+        ASSERT_ERROR(fd_validate(fd), EBADF);
+        TAKE_FD(fd);                    /* already closed by the call, don't double-close */
+        ASSERT_STREQ(sd_json_variant_string(sd_json_variant_by_key(v, "foo")), "bar");
+        v = sd_json_variant_unref(v);
+
+        /* SD_JSON_PARSE_DONATE_FD also consumes the fd when parsing fails. */
+        _cleanup_close_ int fd2 = -EBADF;
+        ASSERT_OK(fd2 = open_tmpfile_unlinkable(NULL, O_RDWR));
+        ASSERT_OK(loop_write(fd2, "kookoo", strlen("kookoo")));
+        ASSERT_OK_ERRNO(lseek(fd2, 0, SEEK_SET));
+        ASSERT_ERROR(sd_json_parse_fd("tmpfile", fd2, SD_JSON_PARSE_DONATE_FD, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+        ASSERT_ERROR(fd_validate(fd2), EBADF);
+        TAKE_FD(fd2);
+        ASSERT_NULL(v);
+}
+
 TEST(depth) {
         _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
         int r;