Defined and Then Defined Again Error
Holly had reached the historic period and level of maturity to cover the emotional nuances of Thomas Wolfe's exclamation "you can't get home once again," only in her case it was fifty-fifty more poignant because there was no dwelling house to return to: her parents had separated, sold the business firm, euthanized Bowser, and disowned Holly for dropping out of high school to marry that 43-yr-one-time managing director of Trailer Boondocks in Idaho—and even their trailer wasn't a place she could call home because it was only a summer sublet.
— Eileen Ostrow Feldman
Introduction
About every programmer has fabricated this blazon of fault:
main() { int x; if (x==42){ ...} }
The mistake is referencing the value of a variable without outset assigning a value to it. Naive developers unconsciously assume that the language compiler or run-time arrangement volition set all variables to zero, blanks, TRUE, 42, or any they require later in the plan. A simple C program that illustrates this defect is:
#include main() { int ten; printf ("%d",ten); }
The value printed will exist any value was "left over" in the memory location to which 10 has been assigned, non necessarily what the developer wanted or expected.
Data flow testing is a powerful tool to detect merely such errors. Rapps and Weyuker, popularizers of this approach, wrote, "Information technology is our belief that, but as one would non experience confident about a plan without executing every statement in information technology every bit part of some examination, 1 should not feel confident nearly a plan without having seen the effect of using the value produced by each and every computation."
Key Signal | Data flow testing is a powerful tool to detect improper use of data values due to coding errors. |
Technique
Variables that contain information values have a defined life cycle. They are created, they are used, and they are killed (destroyed). In some programming languages (FORTRAN and BASIC, for example) creation and destruction are automatic. A variable is created the get-go time it is assigned a value and destroyed when the program exits.
In other languages (similar C, C++, and Java) the creation is formal. Variables are declared past statements such as:
int x; // x is created as an integer string y; // y is created as a cord
These declarations generally occur within a block of code beginning with an opening brace { and ending with a closing caryatid }. Variables divers within a block are created when their definitions are executed and are automatically destroyed at the end of a block. This is chosen the "scope" of the variable. For example:
{ // begin outer block int x; // x is defined every bit an integer within this outer block ...; // x tin can exist accessed here { // begin inner block int y; // y is defined within this inner block ...; // both 10 and y can be accessed here } // y is automatically destroyed at the terminate of // this block ...; // ten tin still be accessed, but y is gone } // ten is automatically destroyed
Variables can be used in computation (a=b+1). They can also be used in conditionals (if (a>42)). In both uses it is as important that the variable has been assigned a value before it is used.
Three possibilities exist for the first occurrence of a variable through a program path:
1. | ~d | the variable does not exist (indicated by the ~), then information technology is divers (d) |
2. | ~u | the variable does not exist, then it is used (u) |
3. | ~thou | the variable does not be, then it is killed or destroyed (1000) |
The get-go is correct. The variable does not exist and then it is defined. The second is incorrect. A variable must not be used before it is defined. The 3rd is probably wrong. Destroying a variable earlier it is created is indicative of a programming error.
Now consider the following fourth dimension-sequenced pairs of divers (d), used (u), and killed (yard):
dd | Defined and divers again—non invalid but suspicious. Probably a programming mistake. |
du | Defined and used—perfectly right. The normal example. |
dk | Defined and and so killed—not invalid but probably a programming error. |
ud | Used and defined—adequate. |
uu | Used and used again—acceptable. |
great britain | Used and killed—acceptable. |
kd | Killed and defined—acceptable. A variable is killed and so redefined. |
ku | Killed and used—a serious defect. Using a variable that does not exist or is undefined is always an error. |
kk | Killed and killed—probably a programming error. |
Key Point | Examine time-sequenced pairs of divers, used, and killed variable references. |
A data flow graph is similar to a control flow graph in that it shows the processing period through a module. In improver, it details the definition, utilize, and destruction of each of the module's variables. We volition construct these diagrams and verify that the define-use-kill patterns are advisable. First, nosotros will perform a static test of the diagram. By "static" we mean we examine the diagram (formally through inspections or informally through look-sees). 2nd, we perform dynamic tests on the module. By "dynamic" we mean we construct and execute test cases. Let's begin with the static testing.
Static Data Catamenia Testing
The following control menstruum diagram has been annotated with define-apply-kill data for each of the variables used in the module.
Figure 11-1: The control flow diagram annotated with ascertain-utilise-impale information for each of the module'due south variables.
For each variable within the module we volition examine ascertain-use-kill patterns along the control flow paths. Consider variable x as we traverse the left and then the right path:
Effigy 11-two: The control period diagram annotated with ascertain-use-kill information for the x variable.
The define-use-kill patterns for x (taken in pairs every bit we follow the paths) are:
~ascertain | correct, the normal case |
define-define | suspicious, peradventure a programming error |
define-utilise | correct, the normal instance |
Now for variable y. Note that the outset co-operative in the module has no impact on the y variable.
Figure 11-3: The control flow diagram annotated with ascertain-use-kill information for the y variable.
The define-use-kill patterns for y (taken in pairs as we follow the paths) are:
~use | major blunder |
use-define | acceptable |
define-employ | correct, the normal case |
use-kill | acceptable |
ascertain-kill | probable programming error |
At present for variable z.
Effigy 11-4: The control flow diagram annotated with define-use-kill information for the z variable.
The define-use-kill patterns (taken in pairs as we follow the paths) are:
~kill | programming error |
impale-use | major blunder |
use-use | correct, the normal example |
apply-define | acceptable |
kill-kill | probably a programming fault |
impale-define | acceptable |
define-apply | correct, the normal case |
In performing a static analysis on this data menstruum model the post-obit problems accept been discovered:
10: define-define y: ~employ y: define-impale z: ~impale z: impale-utilise z: kill-kill
Unfortunately, while static testing can observe many data flow errors, it cannot find them all. Consider the following situations:
- Arrays are collections of data elements that share the same name and type. For example
int stuff[100];
defines an assortment named stuff consisting of 100 integer elements. In C, C++, and Coffee the private elements are named stuff[0], stuff[1], stuff[2], etc. Arrays are defined and destroyed every bit a unit but specific elements of the array are used individually. Ofttimes programmers refer to stuff[j] where j changes dynamically every bit the program executes. In the general case, static analysis cannot determine whether the define-use-kill rules have been followed properly unless each element is considered individually.
- In circuitous control flows it is possible that a certain path can never be executed. In this case an improper define-use-impale combination might exist simply will never be executed and so is not truly improper.
- In systems that procedure interrupts, some of the define-use-kill actions may occur at the interrupt level while other define-use-kill deportment occur at the main processing level. In addition, if the system uses multiple levels of execution priorities, static analysis of the myriad of possible interactions is but also difficult to perform manually.
For this reason, we now turn to dynamic data menstruum testing.
Dynamic Information Flow Testing
Considering data catamenia testing is based on a module's command flow, it assumes that the control menstruum is basically correct. The data flow testing process is to cull plenty examination cases then that:
- Every "define" is traced to each of its "uses"
- Every "utilise" is traced from its corresponding "define"
To do this, enumerate the paths through the module. This is washed using the same arroyo as in control flow testing: Brainstorm at the module'southward entry point, take the leftmost path through the module to its exit. Return to the outset and vary the first branching condition. Follow that path to the exit. Return to the beginning and vary the second branching condition, then the third, and so on until all the paths are listed. And so, for every variable, create at least one test instance to cover every define-utilize pair.
Applicability and Limitations
Data flow testing builds on and expands control flow testing techniques. Every bit with control catamenia testing, it should be used for all modules of code that cannot be tested sufficiently through reviews and inspections. Its limitations are that the tester must have sufficient programming skill to understand the code, its control period, and its variables. Like command flow testing, data flow testing can exist very fourth dimension consuming because of all the modules, paths, and variables that comprise a arrangement.
Summary
- A mutual programming fault is referencing the value of a variable without first assigning a value to it.
- A data flow graph is similar to a control menstruum graph in that it shows the processing flow through a module. In add-on, information technology details the definition, use, and destruction of each of the module's variables. We will utilize these diagrams to verify that the define-use-kill patterns are appropriate.
- Enumerate the paths through the module. And so, for every variable, create at least ane test example to cover every define-use pair.
Practice
- The post-obit module of code computes north! (n factorial) given a value for northward. Create data menses test cases covering each variable in this module. Remember, a unmarried test case can encompass a number of variables.
int factorial (int n) { int reply, counter; answer = one; counter = ane; loop: if (counter > due north) return answer; answer = answer * counter; counter = counter + 1; goto loop; }
- Diagram the control flow paths and derive the data menstruum examination cases for the following module:
int module( int selector) { int foo, bar; switch selector { case SELECT-i: foo = calc_foo_method_1(); break; instance SELECT-two: foo = calc_foo_method_2(); break; case SELECT-3: foo = calc_foo_method_3(); suspension; } switch foo { case FOO-1: bar = calc_bar_method_1(); suspension; instance FOO-2: bar = calc_bar_method_2(); pause; } return foo/bar; }
Do you take whatsoever concerns with this code? How would y'all deal with them?
References
Beizer, Boris (1990). Software Testing Techniques. Van Nostrand Reinhold.
Binder, Robert 5. (2000). Testing Object-Oriented Systems: Models, Patterns, and Tools. Addison-Wesley.
Marick, Brian (1995). The Arts and crafts of Software Testing: Subsystem Testing Including Object-Based and Object-Oriented Testing. Prentice-Hall.
Rapps, Sandra and Elaine J. Weyuker. "Information Period Analysis Techniques for Exam Information Selection." Sixth International Conference on Software Engineering, Tokyo, Japan, September 13–16, 1982.
reyesarinalwas1948.blogspot.com
Source: https://flylib.com/books/en/2.156.1/data_flow_testing.html
0 Response to "Defined and Then Defined Again Error"
Post a Comment