[PATCH v2 3/3] tests/unit: add pread_all and preadv_all tests for io channel file

Junjie Cao posted 3 patches 2 weeks, 5 days ago
Maintainers: "Daniel P. Berrangé" <berrange@redhat.com>, Peter Xu <peterx@redhat.com>, Fabiano Rosas <farosas@suse.de>
[PATCH v2 3/3] tests/unit: add pread_all and preadv_all tests for io channel file
Posted by Junjie Cao 2 weeks, 5 days ago
Add five targeted test cases for the new qio_channel_pread{v,}_all()
and preadv_all_eof() helpers introduced earlier in this series:

  preadv_all:          read from a non-zero offset into two iovecs
  pread_all:           single-buffer wrapper smoke test
  preadv_all_eof/clean:   clean EOF (offset == file length) returns 0
  preadv_all_eof/partial: partial data then EOF returns -1
  preadv_all/eof-is-error: strict wrapper turns clean EOF into -1

All tests create a 16-byte file with known content and exercise the
read helpers against it, verifying return values, error state, and
(where applicable) buffer contents.  The tests are guarded by
CONFIG_PREADV since the underlying preadv support is optional.

Signed-off-by: Junjie Cao <junjie.cao@intel.com>
---
 tests/unit/test-io-channel-file.c | 137 ++++++++++++++++++++++++++++++
 1 file changed, 137 insertions(+)

diff --git a/tests/unit/test-io-channel-file.c b/tests/unit/test-io-channel-file.c
index 1977006ce9..97a2a5b5cb 100644
--- a/tests/unit/test-io-channel-file.c
+++ b/tests/unit/test-io-channel-file.c
@@ -102,6 +102,131 @@ static void test_io_channel_fd(void)
 }
 
 
+#ifdef CONFIG_PREADV
+#define TEST_PREAD_FILE "tests/test-io-channel-pread.txt"
+#define TEST_PREAD_PATTERN "ABCDEFGHIJKLMNOP"  /* 16 bytes */
+#define TEST_PREAD_LEN 16
+
+static QIOChannel *create_pread_test_file(void)
+{
+    int fd;
+
+    fd = open(TEST_PREAD_FILE, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+    g_assert_cmpint(fd, >=, 0);
+    g_assert_cmpint(write(fd, TEST_PREAD_PATTERN, TEST_PREAD_LEN),
+                    ==, TEST_PREAD_LEN);
+    close(fd);
+
+    return QIO_CHANNEL(qio_channel_file_new_path(
+                           TEST_PREAD_FILE, O_RDONLY, 0,
+                           &error_abort));
+}
+
+static void test_io_channel_preadv_all(void)
+{
+    QIOChannel *ioc;
+    char buf1[4], buf2[4];
+    struct iovec iov[2] = {
+        { .iov_base = buf1, .iov_len = sizeof(buf1) },
+        { .iov_base = buf2, .iov_len = sizeof(buf2) },
+    };
+    int ret;
+
+    ioc = create_pread_test_file();
+
+    /* Read 8 bytes from offset 4 into two iovecs: "EFGH" + "IJKL" */
+    ret = qio_channel_preadv_all(ioc, iov, 2, 4, &error_abort);
+    g_assert_cmpint(ret, ==, 0);
+    g_assert_cmpmem(buf1, 4, "EFGH", 4);
+    g_assert_cmpmem(buf2, 4, "IJKL", 4);
+
+    unlink(TEST_PREAD_FILE);
+    object_unref(OBJECT(ioc));
+}
+
+static void test_io_channel_pread_all(void)
+{
+    QIOChannel *ioc;
+    char buf[8];
+    int ret;
+
+    ioc = create_pread_test_file();
+
+    /* Read 8 bytes from offset 8: "IJKLMNOP" */
+    ret = qio_channel_pread_all(ioc, buf, sizeof(buf), 8, &error_abort);
+    g_assert_cmpint(ret, ==, 0);
+    g_assert_cmpmem(buf, 8, "IJKLMNOP", 8);
+
+    unlink(TEST_PREAD_FILE);
+    object_unref(OBJECT(ioc));
+}
+
+static void test_io_channel_preadv_all_eof_clean(void)
+{
+    QIOChannel *ioc;
+    Error *err = NULL;
+    char buf[8];
+    struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) };
+    int ret;
+
+    ioc = create_pread_test_file();
+
+    /* Read from offset == file length: clean EOF, expect 0 and no error */
+    ret = qio_channel_preadv_all_eof(ioc, &iov, 1, TEST_PREAD_LEN, &err);
+    g_assert_cmpint(ret, ==, 0);
+    g_assert_null(err);
+
+    unlink(TEST_PREAD_FILE);
+    object_unref(OBJECT(ioc));
+}
+
+static void test_io_channel_preadv_all_eof_partial(void)
+{
+    QIOChannel *ioc;
+    Error *err = NULL;
+    char buf[8];
+    struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) };
+    int ret;
+
+    ioc = create_pread_test_file();
+
+    /*
+     * Read 8 bytes from offset 12: only 4 bytes available before EOF.
+     * Expect -1 (partial data then EOF is an error) and err set.
+     */
+    ret = qio_channel_preadv_all_eof(ioc, &iov, 1, 12, &err);
+    g_assert_cmpint(ret, ==, -1);
+    g_assert_nonnull(err);
+    error_free(err);
+
+    unlink(TEST_PREAD_FILE);
+    object_unref(OBJECT(ioc));
+}
+
+static void test_io_channel_preadv_all_eof_is_error(void)
+{
+    QIOChannel *ioc;
+    Error *err = NULL;
+    char buf[8];
+    struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) };
+    int ret;
+
+    ioc = create_pread_test_file();
+
+    /*
+     * Clean EOF through the strict wrapper: should be translated to -1.
+     */
+    ret = qio_channel_preadv_all(ioc, &iov, 1, TEST_PREAD_LEN, &err);
+    g_assert_cmpint(ret, ==, -1);
+    g_assert_nonnull(err);
+    error_free(err);
+
+    unlink(TEST_PREAD_FILE);
+    object_unref(OBJECT(ioc));
+}
+#endif /* CONFIG_PREADV */
+
+
 #ifndef _WIN32
 static void test_io_channel_pipe(bool async)
 {
@@ -147,6 +272,18 @@ int main(int argc, char **argv)
     g_test_add_func("/io/channel/file", test_io_channel_file);
     g_test_add_func("/io/channel/file/rdwr", test_io_channel_file_rdwr);
     g_test_add_func("/io/channel/file/fd", test_io_channel_fd);
+#ifdef CONFIG_PREADV
+    g_test_add_func("/io/channel/file/preadv-all",
+                    test_io_channel_preadv_all);
+    g_test_add_func("/io/channel/file/pread-all",
+                    test_io_channel_pread_all);
+    g_test_add_func("/io/channel/file/preadv-all-eof/clean",
+                    test_io_channel_preadv_all_eof_clean);
+    g_test_add_func("/io/channel/file/preadv-all-eof/partial",
+                    test_io_channel_preadv_all_eof_partial);
+    g_test_add_func("/io/channel/file/preadv-all/eof-is-error",
+                    test_io_channel_preadv_all_eof_is_error);
+#endif
 #ifndef _WIN32
     g_test_add_func("/io/channel/pipe/sync", test_io_channel_pipe_sync);
     g_test_add_func("/io/channel/pipe/async", test_io_channel_pipe_async);
-- 
2.43.0