#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"
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;