240 likes | 458 Views
ECE 453 – CS 447 – SE 465 Software Testing & Quality Assurance Lecture 13 Instructor Paulo Alencar. Overview. Structural Testing Introduction – General Concepts Flow Graph Testing Data Flow Testing Definitions Some Basic Data Flow Analysis Algorithms Define/use Testing
E N D
ECE 453 – CS 447 – SE 465 Software Testing & Quality AssuranceLecture 13InstructorPaulo Alencar
Overview • Structural Testing • Introduction – General Concepts • Flow Graph Testing • Data Flow Testing • Definitions • Some Basic Data Flow Analysis Algorithms • Define/use Testing • Slice Based Testing • Guidelines and Observations • Hybrid Methods • Retrospective on Structural Testing
Slicing • The notion of a program slice is useful in software testing, program debugging, automatic parallelization, and program integration • A slice of a program is taken with respect to a program point P and a variable x. The slice consists of all program statements that may affect the value x at program point P. The tuple <P, x> is called a slicing criterion • A Forward Slice of a program with respect to a program point P and variable x consists of all statements of the program that may be affected by the value of x at point P • One way to compute a slice is by constructing a Program Dependence Graph (PDG) and then appropriately traverse this graph [Ref:S. Horowitz, T. Reps, and D. Binkley. Interprocedural slicing using depen- dence graphs. In Proceedings of the SIGPLAN '88 Conference on Programming Language Design and Implementation, Atlanta, GA, June 1988].
Motivation Static Slicing • Program reduction technique • Useful for program comprehension, debugging, maintenance, reuse, etc. • Based on control and flow graph dependencies
Motivation <<Show word count function>>
Motivation <<Extract word count function>>
Program Dependency Graphs (1) • Directed graphs with three types of vertices: • Entry vertex • Initial-State(x) for every variable x such that, there exists a path in the CFG on which x is used before it is defined. It represents an assignment to x from the initial state • Final-Use(x) for every variable x named in P’s end statement. It represents an access to the final value of x computed by P
Program Dependence Graphs (2) • Two main types of edges: • Control Dependence edges labeled as True or False. The source is either a predicate or the entry vertex. A control dependence means that during program execution, if the label of the edge matches the execution result of the source vertex predicate then, the target vertex will be eventually executed. • Data Dependence edges between two nodes iff one is defining a variable and the other is using the variable. Two types of data dependence edges: • Flow dependence • Loop carried • Loop independent • Def-order dependence
Control Dependence Edges • A Program Dependence Graph of a program P contains a Control Dependence edge from vertex v1 to vertex v2 (v1c v2 iff one of the following holds: • Vertex v1 is the entry vertex and v2 represents a component of program P that is not nested within a loop or conditional; these edges are labeled True • Vertex v1 represents a control dependence and v2 represents a component of P immediately nested within a loop or a conditional statement whose predicate is represented by v1. If v1 is a test predicate of a loop structure the edge is labeled True. If it is a test predicate in a conditional statement the edge is labeled according to the boolean result of the predicate.
Data Dependence Edges • A Program Dependence Graph of a program P contains a Data Dependence edge from vertex v1 to vertex v2 (v1f v2 iff all of the following holds: • Vertex v1 is a vertex that defines variable x • Vertex v2 is a vertex that uses variable y • Variables x and y are either the same variable or aliases • Control can reach v2 after v1, via an execution path along which there is no other intervening definition of x or y. That is there is a path in the program’s CFG by which definition of x at v1 reaches the use of x at v2. • Data flow dependence edges can be further classified as loop carried and loop independent
Loop Carried Data Dependence • Loop carried dependencies between vertex v1 and vertex v2 denoted as v1lc(L) v2occur when in addition to the other criteria for data dependence we have: • There is an execution path that includes a back edge to the predicate of the loop L • Both vertex v1 and vertex v2 are enclosed in the loop L • If there is no back edge to the predicate the data dependence is called loop independent
Def-Order Dependence • A PDG contains a def-order dependence edge from vertex v1 to vertex v2 with witness vertex v3 denoted as v1do(v3) v2 iff all of the following hold: • Vertex v1 and v2 define variables x1 and x2 respectively • Variables x1 and x2 are either aliases or the same variable • Vertex v1 and v2 and in the same branch of any conditional statement that encloses both of them • There exists a program component v3 that v1 f v2 and v2 f v3 • Vertex v1 occurs to the left of v2 in the program’s AST (i.e., the statement related to v1 executes before the statement related to vertex v2)
Legend: Example Control dependence Loop independent data dependence Loop carried data dependence Def-order dependence program Sum sum = 0; i=1; while i < 10) do sum = sum +i; i = i+1; enddo end ENTRY T T T T T FinalUse(sum); FinalUse(i); while i < 10; sum = 0 i = 1; T T sum=sum+1; i = i +1;
Computing a Slice Using a PDG (1) • For vertex S of the Program Dependence Graphs G of a program P, the slice of G with respect to S is a graph (denoted as G/S) containing all vertices of which S has a transitive flow or control dependence (i.e. all vertices that can reach S via flow or control edges) • The edges in the slice graph G/S are data dependence (loop independent), control dependence, and def-order edges of the original graph G that have source and targets in vertices in G/S
Computing a Slice Using a PDG (2) • Therefore the vertices of a slice G/S are: V(G/S) = {w | w in V(G) and w * S} • We can extend the definition to a slice with respect to a set of vertices S = Ui Si V(G/S) = V(G/ Ui Si) = Ui (V(G/Si)) • The edges in a slice are: E(G/S) = {u f w | u w in E(G) and u, w in V(G/S)} U {u c w | u w in E(G) and u, w in V(G/S)} U {u do w | u w in E(G) and u, w in V(G/S)} c,f
Algorithm for Intra-Procedural Slice Using a PDG Program ComputeSlice(G, S) G: a program dependence graph S: a set of vertices in G WorkList: a set of vertices in G v, w: vertices in G Begin WorkList = S; while Worklist ≠ 0 do begin Select and remove vertex v from WorkList; Mark v For each unmarked vertex w such that w f v or w c v or w do v do begin Insert w in WorkList end end End
Example (1) ENTRY T T T T T FinalUse(sum); FinalUse(i); sum = 0 while i < 10; i = 1; T T sum=sum+1; i = i +1; Slice based of the criterion FinalUse(i)
Example (2) program SumSlice i=1; while i < 10) do i = i+1; enddo end program Sum sum = 0; i=1; while i < 10) do sum = sum +i; i = i+1; enddo end Slice on FinalUse(i) ENTRY T T T i = 1; while i < 10; FinalUse(i); T i = i +1;
Example: Ada Program • function P return INTEGER is • begin • X, Y: INTEGER • READ(X); READ(Y); • while (X>10) loop • X:= X – 10; • exit when X=10; • end loop; • if (Y < 20 and then X mod 2 = 0) then • Y:= Y – 20; • else • Y:= Y – 20; • end if; • return 2 * X + Y; • end P;
Slicing using Control Flow Graphs • Given a program P, and a program graph G(P) in which statements and statement fragments are numbered, and a set V of variables in P, the slice of the set of variables V at statement fragment n, written as S(V, n), is the set node numbers of all statement fragments W in P prior to n that contribute to the values of variables in V at point n • Contribute means that a statement in S(V, n) will affect the c-use or a p-use of a variable v in V at statement n. Actually, we refine here the concepts of c-use and p-use. More specifically, • P-use: used in a predicate (e.g., decision) • C-use: used in a computation (e.g., assignment) • O-use: used for output (e.g., write/print) • L-use: used for location (e.g., pointers, array indices) • I-use: iteration (loop counters, internal counters) • Also we can distinguish two definition nodes: • I-def: defined by input (e.g., read) • A-def: defined by assignment (e.g., a:=x+y)
Slices • We can consider that in a slice S(V,n), V has only one element say variable v. If n is a defining node for v then is included the slice. If n is a usage node for v it is not included in the slice • p-uses, and c-uses of other variables not in V are included as long their execution affects the value of variable v at point n • We chose to exclude O-uses, L-uses, and I-uses from the slice
Slices – Points of Interest • Do not make slices with a criterion (V, n) where variables of V do not appear in n • Consider making slices with respect of one variable not a set of variables • Consider making slices for all A-def nodes (focus is on the variables in the right hand side of an assignment) • Consider making slices for p-use nodes • Consider making slices compilable
Uses of Slices in Testing • If a slice is compilable one could use the testing techniques we have examine so far • On the other hand, in general slices do not map nicely to test cases. We can use slices: • to identify and eliminate unwanted dependencies between variables • to identify problems in mutually exclusive parts of the code by considering set complements or localizing potential problems in segments