Consider the case of a prime that is a sequence of three alternations:
#-- 1 # [ if C1 -> T1 # else -> F1 ] ... #-- 2 # [ if C2 -> T2 # else -> F2 ] ... #-- 3 # [ if C3 -> T3 # else -> F3 ] ...
As discussed in Section 8.2, “Trace table for alternation”, in
general there are eight different paths through this
prime. However, in practice, quite often some of the
cases contain terminal special forms like function returns or
exceptions, which reduces the number of cases overall.
In the above example, for instance, if
stops execution and
F2 also stops
execution, there are only four paths through the code:
In the general case, how can we be sure that we have a trace table for each possible path through the code? In this section, we describe a way to insure this.
We will start with a rather generic intended function for an entire script that reads some input file and produces a PDF-format report on that file under control of some command line options.
We assume that this code is connected to a written specification that details all the critical definitions: the command line options and what they do, the format of the input file, and the appearance of the output report. In accordance with the principles discussed in Section 3.2, “Design factoring and separation of concerns”, these details are not the concern of the main program logic. All we care about at this level is the overall sequence of the three major steps: processing the command line options, reading the input file, and writing the output file.
#!/usr/bin/env python # [ if (the command line options are valid) and # (the input file specified by those options is readable and # valid) -> # sys.stdout +:= a PDF report on that file using those options # else -> # sys.stdout +:= (anything) # sys.stderr +:= an error message ]
The refinement of this overall intended function
follows. We assume the use of two classes defined
elsewhere: an instance of class
represents the command line options, and an instance of
Report represents the input file and
the report to be produced from it.
#-- 1 # [ if the command line options are valid -> # args := an Args instance representing those options # else -> # sys.stderr +:= an error message # stop execution ] args = Args() #-- 2 # [ if the input file specified by (args) is readable and valid -> # report := a Report instance representing that file # processed using (args) # else -> # sys.stderr +:= an error message # stop execution ] report = Report(args) #-- 3 # [ sys.stdout +:= (report) formatted as a PDF ] report.pdf(sys.stdout)
Two conditions control the paths through this code. We'll call them C0 and C1:
C0: the command line options are valid C1: the input file is readable and valid
Here is a standard truth table for the possible
combinations of C0 and C1. In this table, “
X” means “don't care”:
In case (A), the command line options aren't valid, so we
don't care about the state of the input file. We want
only one state change in this case: an error message
In case (B), the command line options are valid, but there's some problem with the input file.
Case (C) is the successful case where we get all the way to prime .
Of the lines in the trace table above, only the last one is external to the prime, and it matches the line in the overall intended function “sys.stdout +:= a PDF report on that file using those options”.
To be sure that you have covered all routes through the code and have a trace table for each route, build a truth table as above, and make sure the truth table covers all the cases. You could even write a program to verify that your truth table is valid; this project we will leave to the interested reader. The test is that every possible combination of the states of the conditions matches exactly one line of the truth table.