2

I am trying to create a tester using googletest. the problem is that the function that I am testing is returning void and printing a result instead. I want to get the last string printed into the console so I can test the output. the string may include \n.

so I have the function itself:

void f_sequence(char sequenceStr[])
{
   //logic...
    if(condotion1)
        printf("somthing1");
    else if(condotion2)
        printf("somthing2")
(...)
}

and then the tester:

TEST(TesterGroup, TesterName)
{
    f_sequence("input");
    EXPECT_EQ("somthing1", /*how do i get the output?*/);
}

Is it possible?

The functions I test are in c, while the Test function itself (the tester) is in c++. the output is printed using printf. I cannot change the function itself. I am using CLion latest version.

avivgood2
  • 227
  • 3
  • 19
  • 4
    Can you change the function so it accepts a `std::ostream&` parameter instead of just printing to stdout/stderr? – jtbandes Feb 19 '20 at 19:13
  • c or c++ ? They are different languages and solutions will differ substantially. (and there is no language called C/C++, thats a unfortunately commonly used misnomer) – 463035818_is_not_an_ai Feb 19 '20 at 19:14
  • @idclev463035818 The functions I test are in c, while the Test function itself (the tester) is in c++ – avivgood2 Feb 19 '20 at 19:16
  • 1
    @avivgood2 -- post some (minimal) code that demonstrates the problem. – Happy Green Kid Naps Feb 19 '20 at 19:16
  • @jtbandes It is printed using printf and I can't change it, just write the testers – avivgood2 Feb 19 '20 at 19:17
  • not my topic to help with, but still you should provide a [mcve]. Details do matter – 463035818_is_not_an_ai Feb 19 '20 at 19:21
  • 1
    You can [redirect stdout to a file](https://stackoverflow.com/q/29154056/673852) and then read from that file when your C function returns. – Ruslan Feb 19 '20 at 19:24
  • 1
    This function isn't testable as is. I would recommend @jtbandes suggestion. – andre Feb 19 '20 at 19:46
  • There is source code [HERE](https://codereview.stackexchange.com/questions/188630/send-command-and-get-response-from-windows-cmd-prompt-silently-follow-up) (for Windows applications only) that creates an API for sending a cmd and reading `stdout` into a dynamically sizing buffer. I have tested it up to 2GBytes when reading a recursive directory call to Windows. ( [Here is the original](https://codereview.stackexchange.com/questions/162546/send-command-and-get-response-from-windows-cmd-prompt-silently), before review. ) – ryyker Feb 19 '20 at 19:53

5 Answers5

2

Redirect the standard output to a buffer.

Live on Coliru

#include <stdio.h>
#include <unistd.h>

#define BUFFER_SIZE 1024
int stdoutSave;
char outputBuffer[BUFFER_SIZE];

void replaceStdout()
{
    fflush(stdout); //clean everything first
    stdoutSave = dup(STDOUT_FILENO); //save the stdout state
    freopen("NUL", "a", stdout); //redirect stdout to null pointer
    setvbuf(stdout, outputBuffer, _IOFBF, 1024); //set buffer to stdout
}

void restoreStdout()
{
    freopen("NUL", "a", stdout); //redirect stdout to null again
    dup2(stdoutSave, STDOUT_FILENO); //restore the previous state of stdout
    setvbuf(stdout, NULL, _IONBF, BUFFER_SIZE); //disable buffer to print to screen instantly
}

void printHelloWorld()
{
    printf("hello\n");
    printf("world");
}

int main()
{
    replaceStdout();
    printHelloWorld();
    restoreStdout();
    // Use outputBuffer to test EXPECT_EQ("somthing1", outputBuffer);
    printf("Fetched output: (%s)", outputBuffer);
    return 0;
}

References: http://kaskavalci.com/redirecting-stdout-to-array-and-restoring-it-back-in-c/

LWolf
  • 188
  • 1
  • 2
  • 7
0

I don't know if it's possible to get what was last printed, but if you control the environment before your test function is called, you can redirect where standard output goes, which lets you write it to a file, which you can then check.

See this old answer which IMO was neglected. The example from it is modified here:

FILE *fp_old = stdout;  // preserve the original stdout
stdout = fopen("/path/to/file/you/want.txt","w");  // redirect stdout to anywhere you can open
// CALL YOUR FUNCTION UNDER TEST HERE
fclose(stdout);  // Close the file with the output contents
stdout=fp_old;  // restore stdout to normal

// Re-open the file from above, and read it to make sure it contains what you expect.
Kevin Anderson
  • 6,850
  • 4
  • 32
  • 54
0

Two ways:

If you're on a POSIX compatibile system, you can redirect the output of the program to a file using >, then read from the file later to confirm that the output is correct.

The other way is something like this:

freopen("output.txt", "w", stdout);
f_sequence();
freopen("/dev/tty", "w", stdout);

for POSIX compatible systems. On other platforms you'd need to change the "/dev/tty" to something else. I'm not aware of a completely portable way to do this.

And then read from output.txt. What the above snippet does is change what stdout is, so that it prints to a file instead of the normal stdout.

CoffeeTableEspresso
  • 2,614
  • 1
  • 12
  • 30
0

One solution: You can write a separate program that executes the function, and in the unit test, you can execute that program as a sub process and inspect the output. This can be done with std::system, but be very careful to not pass any non-constant input to it. You don't want shell injection vulnerability even in a unit test. System specific functions exist that avoid the use of shell in the subprocess.

Another solution, which is possible at least on POSIX: Replace the the standard out / err streams with file descriptors, and read the files afterwards.

Googletest specific: There seems to be testing::internal::CaptureStdout, which appears to implement the idea of replacing standard streams. But as the namespace implies, this is not official API, so may change in future.

eerorika
  • 232,697
  • 12
  • 197
  • 326
0

There is a solution ( in C ) for an API call ( cmd_rsp ) with source code here, that when called in your program, creates a separate process, and pipes to both stdin and stdout, from which it accepts a command and returns the response via an auto sizing buffer. Similar in concept to popen(...).

A simple use case:

char *buf = NULL;

/// test cmd_rsp
buf = calloc(BUF_SIZE, 1);
if(!buf)return 0;
if (!cmd_rsp("dir /s", &buf, BUF_SIZE))//note the first argument can be any legal command that 
                                       //can be sent via the CMD prompt in windows, 
                                       //including a custom executable
{
    printf("%s", buf);
}
else
{
    printf("failed to send command.\n");
}
free(buf);
ryyker
  • 22,849
  • 3
  • 43
  • 87