I have the following functions as part of a college assignment:
int readMenuOption()
{
/* local declarations */
char option[2];
/* read in 1 char from stdin plus 1 char for string termination character */
readStdin(1 + 1, option);
return (int)option[0] <= ASCII_OFFSET ? 0 : (int)option[0] - ASCII_OFFSET;
}
int readStdin(int limit, char *buffer)
{
char c;
int i = 0;
int read = FALSE;
while ((c = fgetc(stdin)) != '\n') {
/* if the input string buffer has already reached it maximum
limit, then abandon any other excess characters. */
if (i <= limit) {
*(buffer + i) = c;
i++;
read = TRUE;
}
}
/* clear the remaining elements of the input buffer with a null character. */
for (i = i; i < strlen(buffer); i++) {
*(buffer + i) = '\0';
}
return read;
}
It works perfectly for what I need it to do (take in input from keyboard). I've had to do it using stdin (like I have) because of a number of requirements outlined by my professor.
I want to write a series of "unit tests" for the assignment, but I dont know how to get my testing functions to call readMenuOption()
and pass in input to it (without having to do it at run-time).
Is this possible, and if so, how can I do it? (i.e., is it possible to write to stdin)?
One thing you could do is to simply modify readStdin
to allow it to either get data from the real standard input, or from a helper function, something like:
char *fakeStdIn = "";
int myfgetc (FILE *fin) {
if (*fakeStdIn == '\0')
return fgetc (fin);
return *fakeStdIn++;
}
int readStdin(int limit, char *buffer)
{
char c;
int i = 0;
int read = FALSE;
while ((c = myfgetc(stdin)) != '\n') {
/* if the input string buffer has already reached it maximum
limit, then abandon any other excess characters. */
if (i <= limit) {
*(buffer + i) = c;
i++;
read = TRUE;
}
}
/* clear the remaining elements of the input buffer with a null character. */
for (i = i; i < strlen(buffer); i++) {
*(buffer + i) = '\0';
}
return read;
}
Then, to call it from your unit test, you can do:
fakeStdIn = "1\npaxdiablo\nnice guy\n";
// Call your top-level input functions like readMenuOption().
By putting a hook at the lower levels, you can inject your own character sequence instead of using standard input. And if, at any point, the fake standard input is exhausted, it reverts to the real one.
Obviously, this is using characters so, if you want to inject EOF events, you'll need an array of integers instead, but that would be a minor modification to the scheme.
Lookup the nonstandard but extremely useful function forkpty
. Then do something like:
int ptyfd;
pid = forkpty(&ptyfd, 0, 0, 0);
if (pid<0) perror("forkpty"), exit(1);
if (!pid) {
/* call your function to be tested */
_exit(1);
} else {
/* write to ptyfd here to generate input for the function */
}
Note that this will allow you to test your function exactly as if it were reading from an interactive terminal. If you don't need that level of testing, you could use a simple pipe instead.
Why can't you use redirection? Something like:
./a.out < input.txt
Where "input.txt" will contain whatever input you want to give to the program.