nkhail hna
nkhail hna

Reputation: 51

How do I create a file descriptor backed by a simple char array?

I have a function that receives a descriptor (you know, one of those things that open() and socket() spit), reads from it and does something with the data:

int do_something(int fd);

I want to test this function. Preferably, the input data should sit right next to the test assert for the sake of easy debugging. (Therefore, actual file reading should be avoided.) In my mind, the ideal would be something like

unsigned char test_input[] = { 1, 2, 3 };
int fd = char_array_to_fd(test_input);
ck_assert(do_something(fd) == 1234);

(ck_assert is from the Check framework. It's just a typical unit test assert.)

Is there a way to implement char_array_to_fd()? I don't mind if I need to NULL-terminate the array or send the length in.

I imagine that I can open a socket to myself and write on one end so the test function receives the data on the other end. I just don't want to write something awkward and find out that Unix had something less contrived all along. The solution should be any-Unix friendly.

(Basically, I'm asking for a C equivalent of ByteArrayInputStream.)

Alternatively: Should I be thinking in some other way to solve this problem?

Upvotes: 4

Views: 3178

Answers (3)

nkhail hna
nkhail hna

Reputation: 51

@ChrisDodd's answer is already accepted, but I wanted to add the pipes solution I had developed (thanks to @Someprogrammerdude's comment) for completeness sake:

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;
}

Then, I can keep testing do_something as much as I want:

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);

...

Upvotes: 0

Chris Dodd
Chris Dodd

Reputation: 126195

You can use mkstemp to make a temporary file and write to it or read from it:

int fd = mkstemp("tempXXXXXX");

If you want something more dynamic, you can use socketpair to create a pair of connected sockets.

int pair[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, pair);

You can then fork process or thread to interact with your program under test.

Upvotes: 1

Andrew Henle
Andrew Henle

Reputation: 1

On Linux, you can use memfd_create() to create a memory-backed temporary file:

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 );

Note that completely lacks error checking.

Upvotes: 4

Related Questions