diff --git a/meson.build b/meson.build index 6b25904..4319ad4 100644 --- a/meson.build +++ b/meson.build @@ -84,4 +84,27 @@ executable('ei-socket-client', 'tools/ei-socket-client.c', dependencies: [dep_libei]) +# tests +subproject('munit') + +munit = dependency('munit', fallback: ['munit', 'munit_dep']) + +test('iotest', + executable('iotest', + 'test/iotest.c', + include_directories: 'src', + dependencies: munit)) + +valgrind = find_program('valgrind', required : false) +if valgrind.found() + add_test_setup('valgrind', + exe_wrapper : [ valgrind, + '--leak-check=full', + '--gen-suppressions=all', + '--error-exitcode=3' ], + timeout_multiplier : 100) +else + message('valgrind not found, disabling valgrind test suite') +endif + configure_file(output: 'config.h', configuration: config_h) diff --git a/subprojects/munit.wrap b/subprojects/munit.wrap new file mode 100644 index 0000000..c53b275 --- /dev/null +++ b/subprojects/munit.wrap @@ -0,0 +1,4 @@ +[wrap-git] +directory=munit +url=https://github.com/nemequ/munit/ +revision=head diff --git a/test/iotest.c b/test/iotest.c new file mode 100644 index 0000000..2455a36 --- /dev/null +++ b/test/iotest.c @@ -0,0 +1,171 @@ +#include "config.h" + +#include +#include +#include + +#include "util-io.h" + + +static MunitResult +test_iobuf_new(const MunitParameter params[], void *user_data) +{ + /* test allocation and freeing a buffer */ + struct iobuf *buf = iobuf_new(10); + munit_assert_size(buf->sz, ==, 10); + + munit_assert_size(buf->len, ==, 0); + munit_assert_size(iobuf_len(buf), ==, 0); + + buf = iobuf_free(buf); + munit_assert_null(buf); + + return MUNIT_OK; +} + +static MunitResult +test_iobuf_cleanup(const MunitParameter params[], void *user_data) +{ + /* Test the attribute(cleanup) define. This test needs to run in + * valgrind --leak-check=full to be really useful */ + _cleanup_iobuf_ struct iobuf *buf = iobuf_new(10); + _cleanup_iobuf_ struct iobuf *nullbuf = NULL; + + return MUNIT_OK; +} + +static MunitResult +test_iobuf_append(const MunitParameter params[], void *user_data) +{ + /* Test appending data */ + _cleanup_iobuf_ struct iobuf *buf = iobuf_new(10); + + /* append data without a resize */ + const char data[] = "foo"; + iobuf_append(buf, data, 3); + + munit_assert_size(buf->len, ==, 3); + munit_assert_size(iobuf_len(buf), ==, 3); + munit_assert_size(buf->sz, ==, 10); + + /* we don't have a trailing \0 */ + const char *bufdata = iobuf_data(buf); + munit_assert_char(bufdata[0], ==, 'f'); + munit_assert_char(bufdata[1], ==, 'o'); + munit_assert_char(bufdata[2], ==, 'o'); + + /* Now append enough data to force a buffer resize */ + + const char data2[] = "data forcing resize"; + iobuf_append(buf, data2, sizeof(data2)); /* includes \0 */ + + size_t expected = strlen(data) + strlen(data2) + 1; + munit_assert_size(iobuf_len(buf), ==, expected); + munit_assert_size(buf->sz, ==, expected); + /* now we have a trailing \0 */ + munit_assert_string_equal(iobuf_data(buf), "foodata forcing resize"); + + return MUNIT_OK; +} + +static MunitResult +test_iobuf_append_short(const MunitParameter params[], void *user_data) +{ + _cleanup_iobuf_ struct iobuf *buf = iobuf_new(10); + + /* Append only the first few bytes out of a larger data field, i.e. + * make sure we honor the lenght parameter */ + const char data[] = "foobar"; + const char nullbyte = '\0'; + iobuf_append(buf, data, 3); + iobuf_append(buf, &nullbyte, 1); + + munit_assert_size(buf->len, ==, 4); + munit_assert_size(iobuf_len(buf), ==, 4); + munit_assert_size(buf->sz, ==, 10); + munit_assert_string_equal(iobuf_data(buf), + "foo"); + + return MUNIT_OK; +} + +static MunitResult +test_iobuf_append_fd(const MunitParameter params[], void *user_data) +{ + _cleanup_iobuf_ struct iobuf *buf = iobuf_new(10); + int fds[2]; + + int rc = socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, fds); + munit_assert_int(rc, ==, 0); + + int wr = fds[0], + rd = fds[1]; + + /* write some data */ + const char data[] = "foobar"; + int wlen = xwrite(wr, data, 4); + munit_assert_int(wlen, ==, 4); + + /* read that data */ + int rlen = iobuf_append_from_fd(buf, rd); + munit_assert_int(rlen, ==, 4); + + munit_assert_size(iobuf_len(buf), ==, 4); + + /* so we can do strcmp */ + const char nullbyte = '\0'; + iobuf_append(buf, &nullbyte, 1); + munit_assert_string_equal(iobuf_data(buf), "foob"); + + /* read when there's nothing waiting */ + int blocking_read = iobuf_append_from_fd(buf, rd); + munit_assert_int(blocking_read, ==, -EAGAIN); + + const char largebuffer[2048] = {0xaa}; + + /* read data exactly our internal buffer size */ + wlen = xwrite(wr, largebuffer, 1024); + munit_assert_int(wlen, ==, 1024); + int read_1024 = iobuf_append_from_fd(buf, rd); + munit_assert_int(read_1024, ==, 1024); + + /* read data exactly our internal buffer size + 1*/ + wlen = xwrite(wr, largebuffer, 1025); + munit_assert_int(wlen, ==, 1025); + int read_1025 = iobuf_append_from_fd(buf, rd); + munit_assert_int(read_1025, ==, 1025); + + /* close write side, read nothing */ + xclose(wr); + int read_none = iobuf_append_from_fd(buf, rd); + munit_assert_int(read_none, ==, 0); + + /* close read side, expect error */ + xclose(rd); + int read_fail = iobuf_append_from_fd(buf, rd); + munit_assert_int(read_fail, ==, -EBADF); + + return MUNIT_OK; +} + +static MunitTest iotest_tests[] = { + { "iobuf/new", test_iobuf_new, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { "iobuf/cleanup", test_iobuf_cleanup, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { "iobuf/append", test_iobuf_append, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { "iobuf/append_short", test_iobuf_append_short, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { "iobuf/append_fd", test_iobuf_append_fd, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, +}; + +static const MunitSuite iotest_suite = { + "iotest", + iotest_tests, + NULL, + 1, + MUNIT_SUITE_OPTION_NONE, +}; + +int +main(int argc, char* argv[MUNIT_ARRAY_PARAM(argc + 1)]) +{ + return munit_suite_main(&iotest_suite, (void*) "io/", argc, argv); +}