Chapter 8.5 - White-Box Testing

Updated 4 Oct 2026

White-Box Testing Part I

Definition of White-Box Testing

  • Testing based on analysis of internal logic (design, code, etc.)
  • Expected results still come from requirements (not internal logic)
  • Also known as structural testing
  • White-box testing concerns techniques for designing tests — it is not a level of testing
  • Applies primarily to lower levels of testing (e.g., unit and component)

Analogy: เหมือนการตรวจสอบเครื่องยนต์รถโดยเปิดฝากระโปรงดูข้างใน (white-box) แทนที่จะแค่ลองขับดูว่าวิ่งได้ไหม (black-box)


White-Box Testing Techniques

  • Logic coverage
  • Data-flow coverage
  • Path conditions and symbolic evaluation
  • Other white-box testing strategies (e.g., "fault-based testing")

Types of Logic Coverage

Coverage TypeDescription
StatementEach statement executed at least once
BranchEach branch traversed (and every entry point taken) at least once
ConditionEach condition True ≥ once AND False ≥ once
Branch/ConditionBoth Branch AND Condition coverage achieved
Compound ConditionAll combinations of condition values at every branch statement covered
PathAll program paths traversed at least once

Pseudo-Code and Control Flow Graphs

  • A Control Flow Graph (CFG) represents a program as nodes and edges
    • Nodes = statements or conditions
    • Edges = control flow between statements (branches)

Example Program:

1. input(Y)
2. if (Y <= 0)
3.   then Y := -Y
4. end_if
5. while (Y > 0) do
6.   input(X)
7.   Y := Y - 1
8. end_while

Analogy: CFG คือแผนที่ของโปรแกรม — node คือจุดแวะพัก, edge คือถนนที่เชื่อมระหว่างจุด


Statement Coverage

  • Requires that each statement will have been executed at least once
  • Simplest form of logic coverage
  • Also known as Node Coverage

Analogy: คล้ายการเดินสำรวจบ้าน โดยต้องเหยียบเข้าไปทุกห้องอย่างน้อยหนึ่งครั้ง

For the example program above:

  • น่าจะ 2 นะ!

  • 1 test case with Y = -1 is enough:

    • Executes input(Y) → if (Y<=0) → Y := -Y → while (Y>0) → input(X) → Y := Y-1

Branch Coverage

  • Requires that each branch will have been traversed, and every program entry point taken, at least once
  • Also known as Edge Coverage

Analogy: ต้องขับรถผ่านทุกสาขาของถนน ทั้งทางซ้ายและทางขวาที่ทางแยก — ไม่พอแค่ผ่านจุดหนึ่งจุดใด

Relationship Between Statement and Branch Coverage

  • Does Statement ⇒ Branch? NO
    • You can cover all statements with 1 test but miss a branch
  • Does Branch ⇒ Statement? YES (normally)
    • If you traverse every branch, you pass through every statement
    • Exception: "Dead Code" — code not reachable via any executable path

Dead Code: โค้ดที่ไม่มีทางเดินถึงได้เลย ไม่ว่า input จะเป็นอะไร เช่น if (false) { ... }

Branch Coverage⇒Statement Coverage (assuming no dead code)\boxed{\text{Branch Coverage} \Rightarrow \text{Statement Coverage (assuming no dead code)}}


Condition Coverage

  • A branch predicate may have more than one condition

Example:

input(X, Y)
if (Y <= 0) or (X = 0) then
  Y := -Y
end_if
while (Y > 0) and (not EOF) do
  input(X)
  Y := Y - 1
end_while
  • Condition Coverage requires each individual condition to be True at least once and False at least once
  • Normally, achieving condition coverage also achieves branch coverage

Condition Coverage⇒Branch Coverage\boxed{\text{Condition Coverage} \Rightarrow \text{Branch Coverage}}

Condition Table Example (if A or B):

TestABBranch Taken
test 1TFtrue
test 2FTtrue
test 3TTtrue
test 4FFfalse

Analogy: Branch coverage ถามว่า "ประตูนี้เคยเปิดและปิดไหม?" แต่ Condition coverage ถามว่า "สวิตช์แต่ละตัวที่ควบคุมประตูนี้เคยอยู่ในทุกสถานะไหม?"


Branch/Condition Coverage

  • Requires that both Branch AND Condition Coverage are achieved
  • Subsumes both Branch Coverage and Condition Coverage

Branch/Condition Coverage⇒Branch Coverage AND Condition Coverage\boxed{\text{Branch/Condition Coverage} \Rightarrow \text{Branch Coverage AND Condition Coverage}}

Compound Condition Coverage

  • Problem: Some compilers use short-circuit evaluation
    • e.g., if (A) or (y/x = 5) — if A is true, y/x = 5 is never evaluated
  • Compound Condition Coverage (also: Multiple Condition Coverage) requires:
    • All combinations of condition values at every branch statement covered
    • Every entry point taken at least once
  • Subsumes Branch/Condition Coverage regardless of evaluation order

Compound Condition Coverage⇒Branch/Condition Coverage\boxed{\text{Compound Condition Coverage} \Rightarrow \text{Branch/Condition Coverage}}

Example combinations for if (Y <= 0) or (X = 0):

Y <= 0X = 0
TT
TF
FT
FF

Analogy: Compound condition เหมือนการทดสอบรีโมทคอนโทรลทุกปุ่มในทุกๆ combination — ไม่ใช่แค่กดแต่ละปุ่มแยกกัน


Path Coverage

  • Requires that all program paths will have been traversed at least once
  • All Path Coverage may be impractical when loops are present

Why loops are a problem:

  • A for loop that repeats 30 times with 3 paths per iteration = 3303^{30} paths

V(paths)=∏i=1n(paths per iterationi)\boxed{V(\text{paths}) = \prod_{i=1}^{n} (\text{paths per iteration}_i)}

Analogy: เหมือนการเดินทางที่มีทางแยก 30 ครั้ง แต่ละครั้งมี 3 เส้นทาง — การเดินให้ครบทุก combination เป็นไปไม่ได้จริงๆ


Strategies when full Path Coverage is impractical:

  • Loop Coverage
  • Basis Paths Coverage
  • Data-flow Coverage

Loop Coverage

  • Requires that the body of loops be executed 0, 1, 2, t, max, and max+1 times (where possible)
IterationsRationale
0Is some action needed in the body also needed when body is skipped?
1Check lower bound on execution
2Check loop re-initialisation
tCheck typical number of iterations
maxCheck upper (valid) bound
max+1What behavior results if maximum is exceeded?

Analogy: เหมือนทดสอบลิฟต์ — ดูว่าทำงานถูกเมื่อกด 0 ครั้ง, 1 ครั้ง, ครั้งทั่วไป, ครั้งสูงสุด, และเกินกว่าที่รองรับ


Basis Paths Coverage

  • A white-box technique for designing test cases to examine all possible paths at least once
  • Achieves 100% statement coverage and 100% branch coverage
  • Uses McCabe's Cyclomatic Complexity to determine the number of linearly independent paths
  • Then generates test cases for each independent path

Note: In a simple program path:

  • while loop bodies are executed at most once
  • repeat-until loop bodies are executed at most twice

Basis Paths Coverage⇒Branch Coverage⇒Statement Coverage\boxed{\text{Basis Paths Coverage} \Rightarrow \text{Branch Coverage} \Rightarrow \text{Statement Coverage}}


Cyclomatic Complexity

  • A software metric indicating the complexity of a program
  • Measures the number of linearly independent paths through source code
  • Developed by Thomas J. McCabe, Sr. in 1976

Three ways to compute V(G)V(G):

V(G)=P+1\boxed{V(G) = P + 1} Where PP = number of predicate nodes (decision nodes)
V(G)=Number of Regions in the Flow Graph\boxed{V(G) = \text{Number of Regions in the Flow Graph}}
V(G)=E−N+2\boxed{V(G) = E - N + 2} Where:

  • EE = number of edges
  • NN = number of nodes

Analogy: Cyclomatic Complexity คือการนับว่าโปรแกรมนี้มี "ทางแยก" กี่จุด — ยิ่งมาก ยิ่งซับซ้อน ยิ่งต้องการ test case มากขึ้น

Example (from lecture):

MetricValue
Number of Nodes (NN)8
Number of Edges (EE)10
Number of Predicate Nodes (PP)3
Number of Regions (RR)4

V(G)=R=4V(G) = R = 4 V(G)=P+1=3+1=4V(G) = P + 1 = 3 + 1 = 4 V(G)=E−N+2=10−8+2=4V(G) = E - N + 2 = 10 - 8 + 2 = 4

Basis Paths (4 paths):

  • Path 1: 1-2-4-7-8
  • Path 2: 1-2-3-5-7-8
  • Path 3: 1-2-3-6-7-8
  • Path 4: 1-2-4-7-2-4-...-7-8 (loop)

Exercise: Cyclomatic Complexity

ดูให้เป็น เหมือนจะไม่ยากเลยนะ นับให้ได้มีกี่ path (path = condition node + 1 มั้ง), นับ region ให้ได้ สุดได้เดี๋ยวได้คำตอบเอง


N = 7
E = 8
P = 2
R = 3

P + 1 = 3
E - N + 2 = 3
R = 3

IF A = 10 THEN
  IF B > C THEN
    A = B
  ELSE
    A = C
  ENDIF
ENDIF
Print A
Print B
Print C

Cyclomatic complexity is ________

Hint: Count predicate nodes → A=10 and B>C → P=2P = 2 → V(G)=P+1=3V(G) = P + 1 = 3


Summary of White-Box Coverage Relationships

        Compound Condition          Path
               |                  /    \
               ↓                 ↓      ↓
          Branch/Condition   Basis    Loop
            /        \      Paths
           ↓          ↓       ↓
       Condition     Branch ←←←
                       ↓
                   Statement

Subsumption chain (strongest → weakest):
Compound Condition⇒Branch/Condition⇒Branch⇒Statement\text{Compound Condition} \Rightarrow \text{Branch/Condition} \Rightarrow \text{Branch} \Rightarrow \text{Statement}
Path⇒Basis Paths⇒Branch⇒Statement\text{Path} \Rightarrow \text{Basis Paths} \Rightarrow \text{Branch} \Rightarrow \text{Statement}
Path⇒Loop\text{Path} \Rightarrow \text{Loop}

Analogy: เหมือน coverage ที่สูงกว่า "ครอบคลุม" coverage ที่ต่ำกว่าเสมอ — ถ้าทำ Compound Condition ได้ แปลว่าทำ Branch ได้ด้วยโดยอัตโนมัติ


Swift Code Example & Swift Testing

สมมติว่ามีฟังก์ชัน Swift ที่สอดคล้องกับ pseudo-code ในบทเรียน:

func processValue(y: Int, x: Int) -> Int {
    var result = y
 
    // if (Y <= 0) then Y := -Y
    if result <= 0 {
        result = -result
    }
 
    // while (Y > 0) do Y := Y - 1
    var count = 0
    while result > 0 {
        _ = x  // input(X) simulation
        result -= 1
        count += 1
    }
 
    return count
}

Swift Testing — Coverage Examples

import Testing
 
@Suite("processValue Tests — White-Box Coverage")
struct ProcessValueTests {
 
    // MARK: - Statement Coverage (1 test covers all statements)
    @Test("Statement Coverage — Y negative triggers negation and enters loop")
    func statementCoverage() {
        // Y = -2 → becomes 2 → loop runs 2 times
        let result = processValue(y: -2, x: 1)
        #expect(result == 2)
    }
 
    // MARK: - Branch Coverage (covers both branches of if)
    @Test("Branch Coverage — Y positive (skip if-branch)")
    func branchCoverageSkipIf() {
        // Y = 3 → if-branch NOT taken → loop runs 3 times
        let result = processValue(y: 3, x: 0)
        #expect(result == 3)
    }
 
    @Test("Branch Coverage — Y zero (take if-branch, skip loop)")
    func branchCoverageTakeIf() {
        // Y = 0 → if-branch taken → Y = 0 → loop NOT entered
        let result = processValue(y: 0, x: 0)
        #expect(result == 0)
    }
 
    // MARK: - Loop Coverage
    @Test("Loop Coverage — 0 iterations")
    func loopZeroIterations() {
        // Y = 0 → after if: still 0 → loop body never runs
        let result = processValue(y: 0, x: 0)
        #expect(result == 0)
    }
 
    @Test("Loop Coverage — 1 iteration")
    func loopOneIteration() {
        let result = processValue(y: 1, x: 0)
        #expect(result == 1)
    }
 
    @Test("Loop Coverage — typical (t) iterations")
    func loopTypicalIterations() {
        let result = processValue(y: 5, x: 0)
        #expect(result == 5)
    }
}

Note: In real white-box testing, you'd also compute Cyclomatic Complexity of processValue:

  • 2 predicate nodes (if result <= 0 and while result > 0)
  • V(G)=P+1=2+1=3V(G) = P + 1 = 2 + 1 = 3
  • So 3 independent test paths are needed for Basis Paths coverage

White-Box Testing Part II

White-Box Testing Topics (Overview)

  • Logic coverage ← covered in Part I
  • Data-flow coverage ← this lecture
  • Path conditions and symbolic execution ← Part III
  • Other white-box testing strategies (e.g., fault-based testing)

Data-flow Coverage

  • Basic idea: Program paths along which variables are defined and then used should be covered
  • A family of path selection criteria has been defined, each providing a different degree of coverage
  • CASE tool support is very desirable (hard to do manually at scale)

Analogy: เหมือนการติดตามเงิน — ถ้าเราสร้างเงิน (define) แล้วต้องมั่นใจว่ามีเส้นทางที่เงินนั้นถูกใช้ (use) จริงๆ และไม่ถูกแทนที่ระหว่างทาง


Variable Definition (DEF)

A program variable is DEFINED when:

  • It appears on the left-hand side of an assignment statement
    • e.g., x = 17, x = y + 1
  • It is used in an input statement
    • e.g., read(x), input(x)
  • It is used as a call-by-reference parameter in a subroutine call
    • e.g., update(&x, y)

Analogy: DEF คือการ "เกิดของค่า" — ตอนที่ตัวแปรได้รับค่าใหม่เข้ามา เหมือนการเติมน้ำลงในถัง


Variable Use (USE)

A program variable is USED when:

  • It appears on the right-hand side of an assignment statement
    • e.g., y = x + 17
  • It is used as a call-by-value parameter in a subroutine or function call
    • e.g., y = sqrt(x), update(x, y)
  • It is used in a predicate of a branch statement
    • e.g., if (x > 0) { ... }

Analogy: USE คือการ "ดึงน้ำออกจากถัง" มาใช้งาน


Variable Use: p-use and c-use

  • p-use (predicate-use): use in a branch predicate/condition
  • c-use (computation-use): any other use (assignment RHS, function call parameter, output)

Example:

if ( x > 0 ) {
    print(y);
}
  • x → p-use (used in the if condition)
  • y → c-use (used in print, a computation context)

Analogy:

  • p-use คือการใช้ตัวแปรเพื่อ "ตัดสินใจ" ว่าจะเดินทางไปทิศไหน (branch)
  • c-use คือการใช้ตัวแปรเพื่อ "คำนวณ" ค่าอื่นต่อ

Variable Use and Re-definition

A variable can be used AND re-defined in a single statement when:

  • It appears on both sides of an assignment:
    • e.g., y = y + x → y is both used (RHS) and re-defined (LHS)
  • It is used as a call-by-reference parameter:
    • e.g., increment(&y) → y is read and potentially overwritten

More Data-flow Terms and Definitions

Def-Clear Path

  • A path is definition clear ("def-clear") with respect to variable vv if it has no re-definition of vv along the path
  • In other words: the value defined at the start of the path reaches the end without being overwritten

Complete Path

  • A complete path is a path whose:
    • Initial node = start node of the program
    • Final node = exit node of the program

Example (from CFG with read(x,v); if(x>0) ...):

  • Path p1p_1 is def-clear for v (no redefinition of v on p1p_1)
  • Path p2p_2 is NOT def-clear for v (v is redefined along p2p_2)
  • Paths p1p_1, p2p_2 are not complete (don't span start→exit)
  • Path p3p_3 is complete

Analogy: Def-clear path คือเส้นทางที่ "ไม่มีใครเปลี่ยนมือ" ตลอดทาง — เงินก้อนที่ถูก define ยังเป็นเงินก้อนเดิมเมื่อถึงปลายทาง


Definition-Use Pair (DU-Pair)

(d,u) is a DU-pair for variable v  ⟺  d defines v,;u uses v, and ∃ def-clear path from d to u\boxed{(d, u) \text{ is a DU-pair for variable } v \iff d \text{ defines } v, ; u \text{ uses } v, \text{ and } \exists \text{ def-clear path from } d \text{ to } u}
More precisely:

  • dd = node where vv is defined
  • uu = node or edge where vv is used
  • There exists a def-clear path with respect to vv from dd to uu

⚠️ Important: The definition of a DU-pair does NOT require the existence of a feasible def-clear path from dd to uu — the path just has to structurally exist in the CFG (it might be infeasible to actually execute)


Infeasible DU-Pairs

  • Some DU-pairs may be structurally valid (def-clear path exists in CFG) but infeasible in practice (no input can actually cause execution to follow that path)
  • These are marked as infeasible! in analysis

Analogy: เหมือนแผนที่บอกว่ามีถนนเชื่อมสองเมือง แต่จริงๆ ถนนนั้นถูกปิดกั้นด้วยหินถล่ม — structurally มันมี แต่ใช้ไม่ได้จริง

du-pair ให้ iterate เริ่มทุก d node (define) อย่างข้างบนเป็น 1, แล้วไป 5 ไง
แต่ต้องทำสำหรับ Variable ใดซักอันนึงเท่านั้นด้วยนะ เท่าที่โจทย์จะสั่ง

พอมีตารางแบบนี้แล้ว เราก็สามารถมี test case ทั้งหมดที่จะเป็นไปได้แล้ว


Example 1 — Program for DU-Pair Analysis

1. input(A, B)
   if (B > 1) {
2.   A = A + 7
   }
3. if (A > 10) {
4.   B = A + B
   }
5. output(A, B)

Control Flow Graph:

  • Node 1: input(A, B) → branches: B>1 (→ node 2), B≤1 (→ node 3)
  • Node 2: A := A+7 → node 3
  • Node 3: if (A>10) → branches: A>10 (→ node 4), A≤10 (→ node 5)
  • Node 4: B := A+B → node 5
  • Node 5: output(A, B) → exit


Identifying DU-Pairs — Variable A

For variable A, definitions occur at:

  • Node 1 (input(A,B) — A is defined by input)
  • Node 2 (A := A+7 — A is re-defined)

Uses of A occur at:

  • Node 2 (A := A+7 — A used on RHS → c-use)
  • Node 3 (if A>10 — A used in predicate → p-use)
  • Node 4 (B := A+B — A used on RHS → c-use)
  • Node 5 (output(A,B) — A used in output → c-use)

Complete DU-Pair Table for Variable A:

DU-PairPath(s)
(1, 2)<1,2>
(1, 4)<1,3,4>
(1, 5)<1,3,4,5> and <1,3,5>
(1, <3,4>)<1,3,4>
(1, <3,5>)<1,3,5>
(2, 4)<2,3,4>
(2, 5)<2,3,4,5> and <2,3,5>
(2, <3,4>)<2,3,4>
(2, <3,5>)<2,3,5>

Note: Pairs with angle brackets like (1, <3,4>) represent p-use pairs where the "use" is an edge (the branch from node 3 to node 4), not a node. Regular pairs like (1, 4) represent c-use pairs where the use is at a node.

![DU-Pair identification slides with highlighted paths for each du-pair on the CFG]


Dataflow Test Coverage Criteria

All-Defs Coverage

All-Defs: for every variable v, at least one def-clear path from every definition of v to at least one c-use or p-use must be covered\boxed{\text{All-Defs: for every variable } v, \text{ at least one def-clear path from every definition of } v \text{ to at least one c-use or p-use must be covered}}

  • Every definition of every variable must reach at least one use
  • Weakest dataflow criterion

Analogy: ตรวจสอบว่าทุกแหล่งผลิต (def) มีเส้นทางส่งออกไปให้ผู้ใช้ (use) อย่างน้อยหนึ่งคน — ไม่ต้องครบทุกคน

All-Uses Coverage

All-Uses: for every variable v, at least one def-clear path from every definition of v to every c-use AND every p-use must be covered\boxed{\text{All-Uses: for every variable } v, \text{ at least one def-clear path from every definition of } v \text{ to every c-use AND every p-use must be covered}}

  • Every definition of every variable must reach every use (c-use and p-use)
  • Stronger than All-Defs

All-Uses⇒All-Defs\boxed{\text{All-Uses} \Rightarrow \text{All-Defs}}

Analogy: All-Defs คือ "ตรวจสอบว่าโรงงานทุกแห่งส่งสินค้าออกได้บ้าง" แต่ All-Uses คือ "ตรวจสอบว่าทุกโรงงานส่งสินค้าถึงลูกค้าทุกคน"


Checking All-Defs: Test Case <1,2,3,4,5>

For path <1,2,3,4,5>, the def-clear paths subsumed (covered) for variable A are:

DU-PairPath Subsumed?
(1, 2)<1,2> ✓
(1, 4)<1,3,4> ✗ (path goes 1→2→3, not 1→3)
(1, 5)<1,3,4,5> ✗
(1, <3,4>)✗
(1, <3,5>)✗
(2, 4)<2,3,4> ✓
(2, 5)<2,3,4,5> ✓
(2, <3,4>)<2,3,4> ✓
(2, <3,5>)✗
  • Definition at node 1 → covered by (1,2) ✓
  • Definition at node 2 → covered by (2,4), (2,5) ✓
  • All-Defs is achieved by <1,2,3,4,5> alone ✓

Checking All-Uses: Test Cases <1,2,3,4,5>, <1,3,4,5>, <1,2,3,5>

Adding paths <1,3,4,5> and <1,2,3,5>:

After all three test cases, the DU-pair (1, <3,5>) via path <1,3,5> is still NOT covered

  • <1,2,3,4,5>: doesn't take the B≤1 branch directly to node 3 with A≤10 to node 5
  • <1,3,4,5>: covers (1,<3,4>) ✓ but not (1,<3,5>)
  • <1,2,3,5>: covers (2,<3,5>) ✓ but not (1,<3,5>)

⚠️ Conclusion: Based on three test cases, All-Defs is achieved, but All-Uses Coverage is NOT achieved because DU-pair (1, <3,5>) for variable A is never covered!

To cover (1, <3,5>), we need a test case executing path <1,3,5> — i.e., B ≤ 1 (skip node 2, so A is NOT redefined) AND A ≤ 10(skip node 4, go straight to output).


All-du-paths Coverage

All-du-paths: for every variable v, ALL def-clear paths from every definition of v to every use must be covered\boxed{\text{All-du-paths: for every variable } v, \text{ ALL def-clear paths from every definition of } v \text{ to every use must be covered}}

  • Strongest dataflow criterion
  • Subsumes All-Uses

All-du-paths⇒All-Uses⇒All-Defs\boxed{\text{All-du-paths} \Rightarrow \text{All-Uses} \Rightarrow \text{All-Defs}}


Complete White-Box Coverage Subsumption Relationships

                    Path
                   /    \
                  ↓      ↓
   Compound    All-du-paths    Loop
   Condition      ↓
       ↓       All-Uses
   Branch/    /    ↓
   Condition ↓    All-Defs
    /      \  ↓
   ↓   Basis Paths
Condition      ↓
            Branch
               ↓
           Statement

Full hierarchy (strongest → weakest):

Path⇒All-du-paths⇒All-Uses⇒All-Defs\text{Path} \Rightarrow \text{All-du-paths} \Rightarrow \text{All-Uses} \Rightarrow \text{All-Defs}

Path⇒Basis Paths⇒Branch⇒Statement\text{Path} \Rightarrow \text{Basis Paths} \Rightarrow \text{Branch} \Rightarrow \text{Statement}

All-Uses⇒Branch\text{All-Uses} \Rightarrow \text{Branch}

Compound Condition⇒Branch/Condition⇒Branch\text{Compound Condition} \Rightarrow \text{Branch/Condition} \Rightarrow \text{Branch}

Path⇒Loop\text{Path} \Rightarrow \text{Loop}

![White-Box Coverage Subsumption Relationships diagram]

Analogy: ลองนึกภาพ hierarchy นี้เหมือนระดับความละเอียดของการตรวจร่างกาย — Statement coverage แค่ตรวจว่าทุกอวัยวะยังอยู่ครบ, Branch ตรวจว่าแต่ละอวัยวะทำงานได้, All-Uses ตรวจว่าการส่งสัญญาณระหว่างอวัยวะทำงานถูกต้อง, Path ตรวจทุก scenario ที่เป็นไปได้ทั้งหมด


Exercises

Exercise 1: Identifying DU-Pairs — Variable B

Using the same program and CFG from Example 1:

1. input(A, B)
   if (B > 1) {
2.   A = A + 7
   }
3. if (A > 10) {
4.   B = A + B
   }
5. output(A, B)

Task: Fill in the DU-Pairs table for variable B

DU-PairsPath

Hint for solving:

  • Where is B defined? → Node 1 (input(A,B)) and Node 4 (B := A+B)
  • Where is B used?
    • Node 1 predicate: B > 1 → p-use
    • Node 4: B := A+B → c-use (B on RHS)
    • Node 5: output(A,B) → c-use
  • Now trace def-clear paths for each (def, use) pair

Exercise 2: Checking Coverage for Variable B

Given these three test cases:

  • <1,2,3,4,5>
  • <1,3,4,5>
  • <1,2,3,5>

Question 2.1: Check whether all these paths are def-clear paths for variable B.

Question 2.2: Check whether All-Defs and All-Uses coverages are achieved.

Question 2.3: If the three paths cannot cover All-Defs or All-Uses, identify additional test cases needed.

Hint for solving:

  • Definition at node 4 (B := A+B): after node 4, B is redefined → any path continuing from node 4 to node 5 that uses B is a def-clear path from def@4
  • Check if there's any use of B that is never reached by a def-clear path from any definition

Swift Code Example & Swift Testing

Swift equivalent of Example 1's program:

func processAB(a: Int, b: Int) -> (Int, Int) {
    var a = a
    var b = b
 
    // Node 1: input(A, B) — handled by parameters
    // Branch: B > 1
    if b > 1 {
        // Node 2: A = A + 7  [DEF of A, USE of A (c-use)]
        a = a + 7
    }
 
    // Node 3: if (A > 10)  [USE of A (p-use)]
    if a > 10 {
        // Node 4: B = A + B  [DEF of B, USE of A (c-use), USE of B (c-use)]
        b = a + b
    }
 
    // Node 5: output(A, B)  [USE of A (c-use), USE of B (c-use)]
    return (a, b)
}

Variable Analysis Summary:

// DEF of A: parameter 'a' (node 1), 'a = a + 7' (node 2)
// USE of A: 'a + 7' RHS (node 2, c-use), 'a > 10' predicate (node 3, p-use),
//           'a + b' RHS (node 4, c-use), 'return (a, b)' (node 5, c-use)
 
// DEF of B: parameter 'b' (node 1), 'b = a + b' (node 4)
// USE of B: 'b > 1' predicate (node 1, p-use), 'a + b' RHS (node 4, c-use),
//           'return (a, b)' (node 5, c-use)

Swift Testing — Dataflow Coverage

import Testing
 
@Suite("processAB — Data-flow Coverage Tests")
struct DataflowTests {
 
    // MARK: - Path <1,2,3,4,5>: b>1 (take node 2) AND a>10 (take node 4)
    // Covers DU-pairs: (1,2), (2,4), (2,5), (2,<3,4>) for A
    @Test("Path 1-2-3-4-5: B>1 and A>10 after increment")
    func path_1_2_3_4_5() {
        // a=5, b=2 → b>1 so a becomes 12 → a>10 so b = 12+2 = 14
        let (a, b) = processAB(a: 5, b: 2)
        #expect(a == 12)
        #expect(b == 14)
    }
 
    // MARK: - Path <1,3,4,5>: b≤1 (skip node 2) AND a>10
    // Covers DU-pair: (1,4), (1,<3,4>) for A
    @Test("Path 1-3-4-5: B≤1 and A>10 already")
    func path_1_3_4_5() {
        // a=11, b=1 → b≤1 skip node2 → a still 11 > 10 → b = 11+1 = 12
        let (a, b) = processAB(a: 11, b: 1)
        #expect(a == 11)
        #expect(b == 12)
    }
 
    // MARK: - Path <1,2,3,5>: b>1 AND a≤10 after increment
    // Covers DU-pair: (2,5), (2,<3,5>) for A
    @Test("Path 1-2-3-5: B>1 but A≤10 after increment")
    func path_1_2_3_5() {
        // a=1, b=5 → b>1 so a = 1+7 = 8 → a≤10 skip node4 → output(8, 5)
        let (a, b) = processAB(a: 1, b: 5)
        #expect(a == 8)
        #expect(b == 5)
    }
 
    // MARK: - Path <1,3,5>: b≤1 AND a≤10
    // Covers DU-pair: (1,<3,5>) for A — NEEDED for All-Uses!
    @Test("Path 1-3-5: B≤1 and A≤10 — covers missing du-pair (1,<3,5>)")
    func path_1_3_5() {
        // a=5, b=0 → b≤1 skip node2 → a=5 ≤10 skip node4 → output(5, 0)
        let (a, b) = processAB(a: 5, b: 0)
        #expect(a == 5)
        #expect(b == 0)
    }
}

Note: The 4th test case (path_1_3_5) is exactly the missing test case needed to achieve All-Uses coverage for variable A! Without it, DU-pair (1, <3,5>) is never covered.