如何创建由简单字符数组支持的文件描述符?
How do I create a file descriptor backed by a simple char array?
我有一个接收描述符的函数(你知道,open()
and socket()
吐出的那些东西之一),从中读取并对数据做一些事情:
int do_something(int fd);
我想测试这个功能。为了便于调试,输入数据最好紧挨着测试断言。 (因此,应避免实际读取文件。)在我看来,理想情况应该是
unsigned char test_input[] = { 1, 2, 3 };
int fd = char_array_to_fd(test_input);
ck_assert(do_something(fd) == 1234);
(ck_assert
来自 Check framework。它只是一个典型的单元测试断言。)
有没有办法实现char_array_to_fd()
?我不介意我是否需要以 NULL 终止数组或发送长度。
我想象我可以为自己打开一个套接字并在一端写入,以便测试函数在另一端接收数据。我只是不想写一些笨拙的东西,然后发现 Unix 一直以来都有一些不那么做作的东西。解决方案应该是任何 Unix 友好的。
(基本上,我要求的是 ByteArrayInputStream
的 C 等效项。)
或者:我是否应该考虑以其他方式解决这个问题?
在 Linux 上,您可以使用 memfd_create()
创建内存支持的临时文件:
unsigned char test_input[] = ...;
int fd = memfd_create( "test_input", 0 );
// write test data to the the "file"
write( fd, test_input, sizeof( test_input );
// reset file descriptor to the start of the "file"
lseek( fd, 0, SEEK_SET );
请注意,完全没有错误检查。
您可以使用 mkstemp
创建一个临时文件并写入或读取:
int fd = mkstemp("tempXXXXXX");
如果你想要更动态的东西,你可以使用socketpair
创建一对连接的套接字。
int pair[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, pair);
然后您可以派生进程或线程来与您的测试程序进行交互。
@ChrisDodd 的回答已被接受,但为了完整起见,我想添加我开发的管道解决方案(感谢@Someprogrammerdude 的评论):
struct writer_args {
int fd;
unsigned char *buffer;
size_t size;
};
/* Pipe writer thread function */
void *write_buffer(void *_args)
{
struct writer_args *args = _args;
/*
* error handling omitted.
* Should probably also be thrown into a loop, in case it writes less
* than args->size.
*/
write(args->fd, args->buffer, args->size);
close(args->fd);
return NULL;
}
/*
* Wrapper for quick & easy testing of the do_something() function.
* Replaces the fd parameter for a char array and its size.
*/
static int __do_something(unsigned char *input, size_t size)
{
pthread_t writer_thread;
struct writer_args args;
int fd[2];
int result;
pipe(fd); /* error handling omitted */
/* fd[0] is for reading, fd[1] is for writing */
/*
* We want one thread for reading, another one for writing.
* This is because pipes have a nonstandardized maximum buffer capacity.
* If we write too much without reading, it will block forever.
*/
args.fd = fd[1];
args.buffer = input;
args.size = size;
/* error handling omitted */
pthread_create(&writer_thread, NULL, write_buffer, &args);
result = do_something(fd[0]);
close(fd[0]);
pthread_join(writer_thread, NULL); /* error handling omitted */
return result;
}
然后,我可以继续测试 do_something
,想测试多少就测试多少:
ret = __do_something(input1, input1_size);
if (ret != 1234)
fprintf(stderr, "Fail. Expected:1234 Actual:%d\n", ret);
ret = __do_something(input2, input2_size);
if (ret != 0)
fprintf(stderr, "Fail. Expected:0 Actual:%d\n", ret);
ret = __do_something(input3, input3_size);
if (ret != 555)
fprintf(stderr, "Fail. Expected:555 Actual:%d\n", ret);
...
我有一个接收描述符的函数(你知道,open()
and socket()
吐出的那些东西之一),从中读取并对数据做一些事情:
int do_something(int fd);
我想测试这个功能。为了便于调试,输入数据最好紧挨着测试断言。 (因此,应避免实际读取文件。)在我看来,理想情况应该是
unsigned char test_input[] = { 1, 2, 3 };
int fd = char_array_to_fd(test_input);
ck_assert(do_something(fd) == 1234);
(ck_assert
来自 Check framework。它只是一个典型的单元测试断言。)
有没有办法实现char_array_to_fd()
?我不介意我是否需要以 NULL 终止数组或发送长度。
我想象我可以为自己打开一个套接字并在一端写入,以便测试函数在另一端接收数据。我只是不想写一些笨拙的东西,然后发现 Unix 一直以来都有一些不那么做作的东西。解决方案应该是任何 Unix 友好的。
(基本上,我要求的是 ByteArrayInputStream
的 C 等效项。)
或者:我是否应该考虑以其他方式解决这个问题?
在 Linux 上,您可以使用 memfd_create()
创建内存支持的临时文件:
unsigned char test_input[] = ...;
int fd = memfd_create( "test_input", 0 );
// write test data to the the "file"
write( fd, test_input, sizeof( test_input );
// reset file descriptor to the start of the "file"
lseek( fd, 0, SEEK_SET );
请注意,完全没有错误检查。
您可以使用 mkstemp
创建一个临时文件并写入或读取:
int fd = mkstemp("tempXXXXXX");
如果你想要更动态的东西,你可以使用socketpair
创建一对连接的套接字。
int pair[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, pair);
然后您可以派生进程或线程来与您的测试程序进行交互。
@ChrisDodd 的回答已被接受,但为了完整起见,我想添加我开发的管道解决方案(感谢@Someprogrammerdude 的评论):
struct writer_args {
int fd;
unsigned char *buffer;
size_t size;
};
/* Pipe writer thread function */
void *write_buffer(void *_args)
{
struct writer_args *args = _args;
/*
* error handling omitted.
* Should probably also be thrown into a loop, in case it writes less
* than args->size.
*/
write(args->fd, args->buffer, args->size);
close(args->fd);
return NULL;
}
/*
* Wrapper for quick & easy testing of the do_something() function.
* Replaces the fd parameter for a char array and its size.
*/
static int __do_something(unsigned char *input, size_t size)
{
pthread_t writer_thread;
struct writer_args args;
int fd[2];
int result;
pipe(fd); /* error handling omitted */
/* fd[0] is for reading, fd[1] is for writing */
/*
* We want one thread for reading, another one for writing.
* This is because pipes have a nonstandardized maximum buffer capacity.
* If we write too much without reading, it will block forever.
*/
args.fd = fd[1];
args.buffer = input;
args.size = size;
/* error handling omitted */
pthread_create(&writer_thread, NULL, write_buffer, &args);
result = do_something(fd[0]);
close(fd[0]);
pthread_join(writer_thread, NULL); /* error handling omitted */
return result;
}
然后,我可以继续测试 do_something
,想测试多少就测试多少:
ret = __do_something(input1, input1_size);
if (ret != 1234)
fprintf(stderr, "Fail. Expected:1234 Actual:%d\n", ret);
ret = __do_something(input2, input2_size);
if (ret != 0)
fprintf(stderr, "Fail. Expected:0 Actual:%d\n", ret);
ret = __do_something(input3, input3_size);
if (ret != 555)
fprintf(stderr, "Fail. Expected:555 Actual:%d\n", ret);
...