Reputation: 21
I try to unittest a terminal output function. The function streams data to stdout, so my idea was to get control of the stdout buffer and to check if the function properly writes the correct data into the buffer.
setvbuf seemed to me to be the ideal thing to achieve this.
I used setvbuf to redirect stdout to use my own specified buffer.
I used streaming mode _IOFBF so that theoretically an internal flush only happens if the buffer is full. BUFSIZ on my system has a size of 8192 bytes ... which is more than big enough for the output of the function under test, without internal flush during invocation.
My source currently looks like :
char buffer [BUFSIZ] = {0};
/* any outputs before redirecting */
printf("0\n");
/* output any remaining data */
fflush(stdout);
/* redirect to my buffer with full buffering */
setvbuf(stdout,buffer,_IOFBF,BUFSIZ);
/* testcode ... output of the function under test */
printf("1\n");
printf("2\n");
/* output any remaining data */
fflush(stdout);
/* restore internal buffering with line buffering */
setvbuf(stdout,NULL,_IOLBF,BUFSIZ);
/* let us see what is in our buffer */
printf("%s",buffer);
Debugging this code shows that "2\n" overwrites "1\n" in the buffer during the testcode section (gcc 5.4.0 x64 GNU libc version: 2.23)
Output is :
0
1
2
2
But I expected :
0
1
2
1
2
I would be appreciated for any hint.
Upvotes: 1
Views: 1237
Reputation: 21
After messing around with closing and opening stdout, "dup"-ing, writing to file and reading from file and other weird solutions I found one that fits my needs, but isn't that portable but does not need another file to be opened :
How to buffer stdout in memory and write it from a dedicated thread
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_LEN 40
int main( int argc, char *argv[] ) {
char buffer[MAX_LEN+1] = {0};
int out_pipe[2];
int saved_stdout;
saved_stdout = dup(STDOUT_FILENO); /* save stdout for display later */
if( pipe(out_pipe) != 0 ) { /* make a pipe */
exit(1);
}
dup2(out_pipe[1], STDOUT_FILENO); /* redirect stdout to the pipe */
close(out_pipe[1]);
/* anything sent to printf should now go down the pipe */
printf("ceci n'est pas une pipe");
fflush(stdout);
read(out_pipe[0], buffer, MAX_LEN); /* read from pipe into buffer */
dup2(saved_stdout, STDOUT_FILENO); /* reconnect stdout for testing */
printf("read: %s\n", buffer);
return 0;
}
After rewriting it to make an own module "stdout_redirect.c" this results in :
#define BIGENOUGH 1000
#define PIPE_READ 0
#define PIPE_WRITE 1
char stdout_buffer [BIGENOUGH];
static int stdout_save;
static int stdout_pipe[2];
bool stdout_redirect()
{
stdout_save = dup(STDOUT_FILENO); /* save stdout for display later */
if( pipe(stdout_pipe) != 0 ) { /* make a pipe */
return false;
}
dup2(stdout_pipe[PIPE_WRITE], STDOUT_FILENO); /* redirect stdout to the pipe */
close(stdout_pipe[PIPE_WRITE]);
return true;
}
ssize_t stdout_restore()
{
ssize_t size;
fflush(stdout); /* flush if not flushed before */
size = read(stdout_pipe[PIPE_READ], stdout_buffer, sizeof(stdout_buffer)); /* read from pipe into buffer */
close(stdout_pipe[PIPE_READ]);
dup2(stdout_save, STDOUT_FILENO); /* reconnect stdout */
return size;
}
Upvotes: 1