Beyond Decision Coverage
In the previous lesson, you learned about statement and decision coverage. Decision coverage ensures every branch is exercised, but it does not tell you whether individual conditions within a compound decision are truly tested. Consider this code:
if sensor_active and temperature > threshold and not emergency_override:
activate_cooling_system()
Decision coverage only requires two test cases — one where the entire expression is True and one where it is False. But which condition caused the False outcome? Decision coverage does not care. For a cooling system in a nuclear plant, that distinction is critical.
This is where condition-level coverage criteria come in, culminating in MC/DC — the most rigorous practical coverage measure used in safety-critical industries.
Condition Coverage
Condition coverage requires that each individual condition in a decision takes both True and False values across the test suite.
For the decision A AND B:
| Test | A | B | Decision |
|---|---|---|---|
| 1 | T | F | F |
| 2 | F | T | F |
This achieves 100% condition coverage — both A and B take True and False values. But notice: the decision outcome is False in both cases. We never tested the True branch. Condition coverage alone can miss entire branches.
Branch/Condition Coverage
Branch/condition coverage combines both requirements: every condition must take both values AND every branch must be exercised.
For A AND B:
| Test | A | B | Decision |
|---|---|---|---|
| 1 | T | T | T |
| 2 | F | F | F |
Now we cover both branches and both conditions take True and False. But there is still a gap — we cannot tell if A independently affects the outcome, because A and B always change together.
Multiple Condition Coverage
Multiple condition coverage (MCC) requires testing every possible combination of condition values. For A AND B, that means:
| Test | A | B | Decision |
|---|---|---|---|
| 1 | T | T | T |
| 2 | T | F | F |
| 3 | F | T | F |
| 4 | F | F | F |
This is thorough but impractical for real-world code. With N conditions, you need 2^N test cases. A decision with 10 conditions requires 1,024 tests. For 20 conditions: over a million.
Modified Condition/Decision Coverage (MC/DC)
MC/DC is the practical sweet spot. It was developed by NASA and Boeing in the 1990s for avionics software and became the required coverage criterion for safety-critical aviation software under DO-178B (now DO-178C).
MC/DC requires:
- Every entry and exit point is invoked (basic block coverage)
- Every condition takes both True and False (condition coverage)
- Every decision takes both True and False (decision coverage)
- Each condition independently affects the decision outcome — for each condition, there exists a pair of test cases where only that condition changes value and the decision outcome changes
Rule 4 is what makes MC/DC special. It proves that each condition actually matters.
Deriving MC/DC Test Cases
For the decision A AND B AND C, follow this systematic process:
Step 1: Build the complete truth table.
| # | A | B | C | Result |
|---|---|---|---|---|
| 1 | T | T | T | T |
| 2 | T | T | F | F |
| 3 | T | F | T | F |
| 4 | T | F | F | F |
| 5 | F | T | T | F |
| 6 | F | T | F | F |
| 7 | F | T | F | F |
| 8 | F | F | F | F |
Step 2: Find independence pairs for each condition. An independence pair for condition X is two rows where X changes, all other conditions stay the same, and the decision outcome changes.
- Condition A: Rows 1 (T,T,T→T) and 5 (F,T,T→F) — A changes, B and C stay T, outcome changes
- Condition B: Rows 1 (T,T,T→T) and 3 (T,F,T→F) — B changes, A and C stay T, outcome changes
- Condition C: Rows 1 (T,T,T→T) and 2 (T,T,F→F) — C changes, A and B stay T, outcome changes
Step 3: Select the minimum set covering all pairs: rows {1, 2, 3, 5} = 4 test cases.
For N conditions with AND: N+1 test cases. For N conditions with OR: also N+1. This is dramatically better than the 2^N required by multiple condition coverage.
MC/DC for Mixed Operators
When a decision mixes AND and OR, the process is the same but finding independence pairs requires more care.
For (A OR B) AND C:
| # | A | B | C | A OR B | Result |
|---|---|---|---|---|---|
| 1 | T | T | T | T | T |
| 2 | T | T | F | T | F |
| 3 | T | F | T | T | T |
| 4 | T | F | F | T | F |
| 5 | F | T | T | T | T |
| 6 | F | T | F | T | F |
| 7 | F | F | T | F | F |
| 8 | F | F | F | F | F |
Independence pairs:
- A: Rows 3 (T,F,T→T) and 7 (F,F,T→F) — A changes, B=F, C=T held constant
- B: Rows 5 (F,T,T→T) and 7 (F,F,T→F) — B changes, A=F, C=T held constant
- C: Rows 1 (T,T,T→T) and 2 (T,T,F→F) — C changes, A=T, B=T held constant
Minimum set: {1, 2, 3, 5, 7} = 5 test cases. Mixed operators may require slightly more than N+1.
Where MC/DC Is Required
MC/DC is not just an academic exercise. It is mandated by real standards:
| Standard | Domain | MC/DC Required For |
|---|---|---|
| DO-178C | Aviation | Level A (catastrophic failure) software |
| ISO 26262 | Automotive | ASIL D (highest risk) software |
| IEC 61508 | Industrial | SIL 4 (highest safety integrity) |
| EN 50128 | Railway | SIL 3 and SIL 4 software |
If you work in any of these industries, MC/DC is not optional — it is a regulatory requirement.
Tools for MC/DC Analysis
Several tools support MC/DC measurement:
- VectorCAST — Industry standard for embedded and safety-critical systems
- LDRA — Widely used in aerospace and automotive
- Parasoft C/C++test — Supports MC/DC for C and C++ codebases
- gcov + custom analysis — Open-source option for basic condition tracking
- JaCoCo — Java coverage tool that supports branch coverage (not full MC/DC, but useful as approximation)
Exercise: Derive MC/DC Test Cases
Problem 1
Given the function:
def should_trigger_alert(pressure_high, temp_critical, manual_override):
return (pressure_high or temp_critical) and not manual_override
Derive the minimum MC/DC test set.
Solution
Build the truth table for (A OR B) AND NOT C where A=pressure_high, B=temp_critical, C=manual_override:
| # | A | B | C | NOT C | A OR B | Result |
|---|---|---|---|---|---|---|
| 1 | T | T | F | T | T | T |
| 2 | T | F | F | T | T | T |
| 3 | F | T | F | T | T | T |
| 4 | F | F | F | T | F | F |
| 5 | T | T | T | F | T | F |
| 6 | T | F | T | F | T | F |
| 7 | F | T | T | F | T | F |
| 8 | F | F | T | F | F | F |
Independence pairs:
- A (pressure_high): Rows 2 (T,F,F→T) and 4 (F,F,F→F)
- B (temp_critical): Rows 3 (F,T,F→T) and 4 (F,F,F→F)
- C (manual_override): Rows 2 (T,F,F→T) and 6 (T,F,T→F)
Minimum set: {2, 3, 4, 6} = 4 test cases.
| Test | pressure_high | temp_critical | manual_override | Expected |
|---|---|---|---|---|
| 1 | True | False | False | True |
| 2 | False | True | False | True |
| 3 | False | False | False | False |
| 4 | True | False | True | False |
Problem 2
A flight control system has this logic:
if (altitude_low && airspeed_low && !landing_gear_deployed) {
trigger_terrain_warning();
}
- How many test cases does MC/DC require?
- How many does multiple condition coverage require?
- Derive the MC/DC test set.
Solution
Decision: A AND B AND NOT C where A=altitude_low, B=airspeed_low, C=landing_gear_deployed.
- MC/DC requires 4 test cases (N+1 = 3+1, since NOT C is still one condition)
- Multiple condition coverage requires 8 test cases (2^3)
Truth table:
| # | A | B | C | NOT C | Result |
|---|---|---|---|---|---|
| 1 | T | T | F | T | T |
| 2 | T | T | T | F | F |
| 3 | T | F | F | T | F |
| 4 | F | T | F | T | F |
Independence pairs:
- A: Rows 1 and 4 (A changes T→F, B=T, C=F fixed, result T→F)
- B: Rows 1 and 3 (B changes T→F, A=T, C=F fixed, result T→F)
- C: Rows 1 and 2 (C changes F→T, A=T, B=T fixed, result T→F)
Minimum MC/DC set: {1, 2, 3, 4} — exactly 4 tests instead of 8.
Practical Tips for MC/DC
Simplify before analyzing. If a condition contains redundant sub-expressions, simplify using Boolean algebra first. MC/DC on a simplified expression requires fewer tests.
Coupled vs. uncoupled conditions. When two conditions share a variable (e.g., x > 5 AND x < 10), you cannot vary one independently of the other. This is called a coupled condition. Coupled conditions may require different independence pairs than uncoupled ones.
Use masking MC/DC for complex expressions. In masking MC/DC (allowed by DO-178C), a condition can be shown to independently affect the decision even if other conditions change, as long as the changes are “masked” (do not affect the outcome through the logical structure). This can reduce the required test set further.
Short-circuit evaluation matters. In languages with short-circuit evaluation (most modern languages), A AND B does not evaluate B if A is False. This means some MC/DC test cases may not actually exercise certain conditions at runtime. Consider whether your coverage tool accounts for short-circuit behavior.
Key Takeaways
- Condition coverage tests individual conditions but can miss branches entirely
- Branch/condition coverage combines both but does not prove independence
- MC/DC proves each condition independently affects the decision outcome
- MC/DC requires N+1 test cases (vs. 2^N for multiple condition coverage) — practical even for complex decisions
- MC/DC is mandated by DO-178C, ISO 26262, and IEC 61508 for safety-critical software
- Always find independence pairs systematically using truth tables
- Consider coupled conditions, short-circuit evaluation, and masking MC/DC for complex real-world decisions