Thursday, February 08, 2007

Using PIPEs to TEST your application

Today, i will talk about pipes and how to use them. A pipe is a data flow from one direction to another direction and vice versa. Imagine that you have to write an application and this application takes some input parameters from another application which runs at the same time. There are several ways of IPC. One way is that, one application prints its results to a file, and the other application reads from this file and use it as an input provider!
However, today i introduce a different way of speaking. Pipe is a data flow mechanism. We can create pipes between 2 process so that one writes its data to pipe and another reads from that pipe. I give my example for unix systems in C.

Now the story is;
At the lowel level, main process defines an array with two elements:

int descriptors[2];

This array holds our file descriptors. So we must say to kernel that this array is used as a pipe:

pipe( descriptors );

After this call we can use descriptor[0] for reading from the pipe, and descriptor[1] for writing to that pipe. So, we write to one end, read from the other end of the pipe ( a flow is created ).

Using a pipe in a process is meanless unless this process try to communicate with outside. So i create a second process with a fork system call.

fork();

This call returns the pid of the child to the parent process, and returns 0 to the child process. I may explain the fork call at a later time but for now it is enough to know that calling a fork from a process creates a child process. So now we have 2 processes at our memory, these are the main process and its child. Now we pass some arguments from parent ( main ) process to child by using the pipe that we created just before.

In the parent process code, we simply write:

write( descriptors[1] , buff , strlen( buff) );

So look at the first parameter. It is the pipe that we created. We write the buffer to pipe's writable end.

In the child process code, we simply write:

read( descriptors[0] , buff , 22 );

Now we read from the pipe ( descriptors[0] ) and store the buffer in buff.

I hope you understand what the pipe is:) Here i give you complete code then i write a code for testing a program automatically by using pipe.

#include stdio.h
#include unistd.h


int main(){

int isParent;
char buff[255];

int descriptors[2];

if ( pipe( descriptors ) < 0 )
return 1;

isParent = fork();

if( isParent ) {
//parent process

strcpy( buff , "Hi, this is emreknlk!");

write( descriptors[1] , buff , strlen( buff) );

printf("Hi this is parent\n");

}else{
//child process
sleep( 1 );

read( descriptors[0] , buff , 22 );

printf("Hi this is child, buffer is: %s\n",buff);

}

close( descriptors[0] );
close( descriptors[1] );

return 0;

}


The output of this program:
-bash-2.05b$ ./pipe
Hi this is parent
-bash-2.05b$ Hi this is child, buffer is: Hi, this is emreknlk!

-bash-2.05b$


Now are you ready for making some differences!
Suppose that you have to test 100 different code which are written by 100 different person. What would you do? if this number is smaller, lets say 10, you can test it by manually by executing each of them and providing the neccessary inputs.
When the number of codes that must be tested become larger, then a code can be written for testing them automatically.

Assume that, we want to test this simple application:

#include stdio.h

int main(){

int number, i, fact = 1;

scanf("%d",&number); //take the number for calculating factorial

//calculate factorial
for( i = 2; i <= number ; i++)
fact *= i;

//print result to screen
printf("%d\n",fact);

return 0;
}


We will write a code for testing this code. tester should provide input to application and control the result which is calculated by application.
For this purpose it is clear that, in our test code we must create two pipe for writing input parameter from test code to application (i.e number variable ) and writing result from application to test code (i.e fact ).

After we compile the above code we rename it as app. Our test code for testing this factorial code is:

#include stdio.h
#include string.h
#include unistd.h
#include fcntl.h

int main(){

int isParent,n;
char buff[255];

int des1[2];
int des2[2];

if ( pipe( des1 ) < 0 || pipe( des2 ) < 0 )
return 1;

isParent = fork();

if( isParent ) {
//parent process

write( des1[1], "5\n" , 3 ); //send value to our application

do{
n =read( des2[0], buff , 255 ); //get the result!
}while( n<= 0);

if( strncmp(buff,"120\n",n) == 0 )
printf("Test Succeed!\n");
else
printf("Test Failed %s\n",buff);

}else{
//child process
close( STDIN_FILENO );
close( STDOUT_FILENO );
dup2( des1[0] , STDIN_FILENO ); //change des1[0] to stdin of child
dup2( des2[1], STDOUT_FILENO ); //change des2[1] to stdout of the child

close( des1[0] );
close( des1[1] );
close( des2[0] );
close( des2[1] );

if ( execlp("./app","./app",(char*)NULL) < 0 )
exit(1);
}

return 0;

}


In my computer this test passes and Test Succeed message appears on my screen.
Here i give a brief explanation about test code.

For child process: We replace its stdout and stdin with the pipe descriptors so when it wants to write to stdout, the test code can reach it from the pipe by reading it and when it wants to read an input from stdin, it can read the value from the pipe.
execlp command is used for replacing the context of the child process with the our factorial application. Here app is the name of our factorial application.

For parent process: It is the code which tests the factorial application. It simply send an input value ( i.e 5 ) to the factorial application and controls the return value if it is 120.

By using this automatic tool we can test lots of application which take one parameter , calculate the factorial and print the result. So some conventions must be exist. For example if the factorial application prints the result by this way:
"Factorial is 120"
Then our test code will be fail because it expects only the result ( i.e 120 )

I try to give the simplest example for writing a test tool. I plan to write about IO methods and IO multiplexing for writing much detail about this topic. See you later :)

1 comment:

Anonymous said...

Nice dispatch and this enter helped me alot in my college assignement. Gratefulness you as your information.