libei/test/iotest.c
Peter Hutterer dabd48c028 util: add fd passing to the iobufs
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2020-08-21 14:51:53 +10:00

246 lines
7 KiB
C

/*
* Copyright © 2020 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "util-io.h"
#include "util-munit.h"
MUNIT_TEST(test_iobuf_new)
{
/* 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;
}
MUNIT_TEST(test_iobuf_cleanup)
{
/* 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;
}
MUNIT_TEST(test_iobuf_append)
{
/* 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;
}
MUNIT_TEST(test_iobuf_append_short)
{
_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;
}
MUNIT_TEST(test_iobuf_append_fd)
{
_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;
}
MUNIT_TEST(test_iobuf_recv_fd)
{
int fds[2];
int rc = socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, fds);
munit_assert_int(rc, ==, 0);
_cleanup_close_ int left = fds[0];
_cleanup_close_ int right = fds[1];
_cleanup_fclose_ FILE *fp = tmpfile();
/* actual message data to be sent */
char data[] = "some data\n";
/* Send the fd from left to right */
int sendfds[2] = { fileno(fp), -1 };
int sendrc = xsend_with_fd(left, data, sizeof(data), sendfds);
munit_assert_int(sendrc, ==, sizeof(data));
_cleanup_iobuf_ struct iobuf *buf = iobuf_new(64);
rc = iobuf_recv_from_fd(buf, right);
munit_assert_int(rc, ==, sizeof(data));
_cleanup_close_ int fd = iobuf_take_fd(buf);
munit_assert_int(fd, !=, -1);
return MUNIT_OK;
}
MUNIT_TEST(test_pass_fd)
{
int fds[2];
int rc = socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, fds);
munit_assert_int(rc, ==, 0);
_cleanup_close_ int left = fds[0];
_cleanup_close_ int right = fds[1];
_cleanup_fclose_ FILE *fp = tmpfile();
/* actual message data to be sent */
char data[] = "some data\n";
/* Send the fd from left to right */
int sendfds[2] = { fileno(fp), -1 };
int sendrc = xsend_with_fd(left, data, sizeof(data), sendfds);
munit_assert_int(sendrc, ==, sizeof(data));
/* Write some data to the file on it's real fd */
char buf[] = "foo\n";
fwrite(buf, sizeof(buf), 1, fp);
fflush(fp);
/* Receive the fd on the right */
_cleanup_free_ int *recvfds = NULL;
char recvbuf[sizeof(data)];
int recvrc = xread_with_fds(right, recvbuf, sizeof(recvbuf), &recvfds);
munit_assert_int(recvrc, ==, sizeof(data));
munit_assert_string_equal(recvbuf, data);
munit_assert_ptr_not_null(recvfds);
munit_assert_int(recvfds[0], !=, -1);
munit_assert_int(recvfds[1], ==, -1);
/* Now check that we can read "foo" from the passed fd */
_cleanup_close_ int passed_fd = recvfds[0];
off_t off = lseek(passed_fd, 0, SEEK_SET);
munit_assert_int(off, ==, 0);
char readbuf[64];
int readrc = xread(passed_fd, readbuf, sizeof(readbuf));
munit_assert_int(readrc, ==, sizeof(buf));
munit_assert_string_equal(readbuf, buf);
return MUNIT_OK;
}
int
main(int argc, char* argv[MUNIT_ARRAY_PARAM(argc + 1)])
{
return munit_tests_run(argc, argv);
}