Project Testing in Unix

The topics covered on this page includes:

Getting started

In order to test your code on ecelinux.uwaterloo.ca, you require a machine with an FTP client and a Secure Shell client. You can read how to transfer your files and to use the remote login at the tutorial on using these facilities.

For example, in Project 1, you are required to write and test a Single_list class. First, log onto the machine ecelinux.uwaterloo.ca using the SSh client. You will use your UW User ID together with your Nexus password. In your home directory, you should create a subdirectory appropriate for this course:

$ mkdir ece250
$ mkdir ece250/lab1
$ cd ece250/lab1

Recall that in Linux and other Unix systems, the directory divider is / and not \ as in Microsoft MS DOS and Windows.

Using the FTP client, you can now navigate to this directory and copy your code here. You can list the contents of the directory in the SSh client and you should see a minimum of the following files:

$ ls
double.in   Exception.h  Single_list_driver.cpp         Single_node.h
double.out  int.in       Single_list.h                  Single_node_tester.h
ece250.h    int.out      Single_list_tester.cpp         Tester.h

You are now ready to compile and test your code:

$ g++ Single_list_driver.cpp

Command completion

Recall that you can use command completion on ecelinux. For example, if you start with

$ g++ STab

the shell will complete your command to greatest common part:

$ g++ Single_

Typing l followed by Tab, again, takes us from

$ g++ Single_lTab

to

$ g++ Single_list

Typing _i followed by Tab, again, takes us from

$ g++ Single_list_iTab

to

$ g++ Single_list_driver.cpp

Testing your code manually

At this point, if you hit Enter, it will compile. If all was successful, you will see another prompt. If there is a compilation error, error messages will be printed out.

We will assume at this point that compilation was success and later we will discuss dealing with compilation errors. If you list the directory contents, you will see a new file, a.out. We will want to execute this file, but Unix will only run executables in specific locations dictated by the PATH environment variable. To run the a.out in this directory, you must say the a.out executable in this directory, as follows:

$ ./a.out int
1 %

You are greeted by a prompt. This is a testing environment, so to begin, we must create a new instance of your single list class:

$ ./a.out int
1 % new
Okay

You can continue using commands that you can find at the top of your Single_list_tester.h file. For example,

$ ./a.out int
1 % new
Okay
2 % empty 1
Okay
3 % push_front 4
Okay
4 % empty 0
Okay
5 % front 4
Okay

Suppose your implementation is wrong: perhaps you forgot to correctly implement size(). In this case, you will either get a warning or an error:

6 % size 1
Failure in size(): expecting the value '1' but got '0'

The warning is very specific: the function that was being called, the expected value as specified by the command, and the actual value that was received when the member function size() was called on the argument.

We can conclude this testing session by first deleting the object we created, then checking that all memory was correctly cleaned up, and then actually editing.

7 % delete
Okay
8 % summary
Memory allocated minus memory deallocated: 0
9 % exit
Okay
Finishing Test Run
$

Note, if your destructor or some other member function did not correctly deallocate memory, the number at the end of the summary command will be non-zero. You can use the details command to see the exact memory that was allocated and which was correctly deallocated and which was not.

Using the testing input files

The tester file for Single_list_int_driver.cpp is the text file int.in. If your code is working correctly, you will see something like:

$ g++ Single_list_driver.cpp
$ ./a.out int < int.in
Starting Test Run
1 % Okay
2 % Okay
3 % Okay
4 % Okay
5 % Okay
6 % Okay
7 % Okay
8 % Okay
9 % Okay
10 % Okay
11 % Okay
12 % Okay
13 % Okay
14 % Okay
15 % Okay
16 % Okay
17 % Okay
18 % Okay
19 % Okay
20 % Okay
21 % Okay
22 % Okay
23 % Okay
24 % Okay
25 % Okay
26 % Okay
27 % Okay
28 % Okay
29 % Okay
30 % Okay
31 % Memory allocated minus memory deallocated: 0
32 % Okay
Finishing Test Run
$

The redirect < int.in indicates to the shell that rather than getting input from the user, it should take the contents of the file int.in as the input to the command.

You should compare the output with the content of int.out by using the cat (concatenate) command to print the file to the screen:

$ cat int.out
Starting Test Run
1 % Okay
2 % Okay
3 % Okay
4 % Okay
5 % Okay
6 % Okay
7 % Okay
8 % Okay
9 % Okay
10 % Okay
11 % Okay
12 % Okay
13 % Okay
14 % Okay
15 % Okay
16 % Okay
17 % Okay
18 % Okay
19 % Okay
20 % Okay
21 % Okay
22 % Okay
23 % Okay
24 % Okay
25 % Okay
26 % Okay
27 % Okay
28 % Okay
29 % Okay
30 % Okay
31 % Memory allocated minus memory deallocated: 0
32 % Okay
Finishing Test Run
$

Marking is as follows: if your output matches, you get 1/1, if it does not match, you get 0/1.

Repeating commands

If you want to repeat the previous command, use

$ !!

More useful, if you want to repeat the compilation command, you can use either

$ !g

or

$ !g+

if you happened to have used a command starting with g (this author uses gvim as an editor, so he will use !g+ to execute the last compilation command and !gv to execute the last command to edit a file.

Rather than always typing ./a.out int or ./a.out int < int.in, you can use

$ !.

If you want to see a full history of your commands, type

$ history
    46  13:15   g++ Single_list_driver.cpp
    47  13:15   ./a.out int < int.in
    48  13:15   pico Single_list.h
    49  13:15   ./a.out int
    50  13:15   history
$

The command history lists and numbers all the commands you made and you can re-execute one of those commands by using !n. For example, in this case, if you type !., it will execute ./a.out int; however, you may wish to execute the 47th command:

$ !47
./a.out int < int.in
Starting Test Run
1 % Okay
2 % Okay
3 % Okay
4 % Okay
...

Writing your own testing files

Suppose you want to write your own input files instead of having to type commands over and over again. For example, suppose you create a singly linked list containing the entries 5, 3, 7, 7, 4, in that order. If you call erase( 7 ) on that singly linked list, the return value should be two (2) as two nodes containing 7 should have been removed. Following this, the size of the linked list should be 3. You can then write a test file using the text editor:

$ nano test1.in

and you can type the commands as you would expect them to execute:

new
push_front 4
push_front 7
push_front 7
push_front 3
push_front 5
size 5
erase 7 2
size 3
delete
summary
exit

Because this file contains what you would consider to be a valid test for a functioning singly linked list, the output should all be Okay:

$ ./a.out int < test1.in
Starting Test Run
1 % Okay
2 % Okay
3 % Okay
4 % Okay
5 % Okay
6 % Okay
7 % Okay
8 % Okay
9 % Okay
10 % Okay
11 % Memory allocated minus memory deallocated: 0
12 % Okay
Finishing Test Run
$

You are welcome to create such tests and to pass them to your classmates.

If you get something else, you should ask yourself: "Is the test file correctly testing a linked list, or is there a bug in my code?"

Dealing with compilation errors

If your file does not compile, the compiler tries very hard to give you information as to where it failed. For example, if the signature of the member functions do not match, you might get an error like:

$ g++ Single_list_driver.cpp
Single_list.h:154: error: prototype for 'Type Single_list::front(int) const' does not match any in class 'Single_list'
Single_list.h:41: error: candidate is: Type Single_list::front() const
Single_list.h:154: error: template definition of non-template 'Type Single_list::front(int) const'

It tells you which line the error is on, and it tries to describe the problem. Suppose you forgot a semicolon:

g++ Single_list_driver.cpp
Single_list.h: In member function 'int Single_list::count(const Type&) const'
Single_list.h:198: error: expected `;' before '}' token

In this case, a line immediately before a closing brace did not have a semicolon.

Suppose you included an end-of-line comment but instead of having two slashes, you only had one. This will be a little different because / is the division operator, so it will attempt to parse the file as if it was a division operator:

g++ Single_list_driver.cpp
Single_list.h: In member function 'Type Single_list::pop_front()'
Single_list.h:270: error: expected primary-expression before '/' token
Single_list.h:270: error: 'Access' not declared in this scope
Single_list.h:270: error: expected ';' before 'the'

You will note that there are three errors reported:

  1. It was expecting something before the division operator,
  2. It did not recognize the variable Access, and
  3. It does not expect two variable names in a row: there has to be a semicolon between Access and the.