Ground Truth

What DO-178C and MC/DC Actually Require, With a Worked Example

Mostafa DhouibMostafa Dhouib·5 min read
The short answer

Branch coverage on D = A and B is satisfied by two tests, and a bug that ignores B passes both. MC/DC requires each condition to independently flip the outcome, needing three tests, and it catches that bug. It is the cleanest illustration of covered against proven.

What DO-178C and MC/DC Actually Require, With a Worked Example

The short answer. Branch coverage on D = A and B is satisfied by two tests, and a bug that implements D = A passes both. MC/DC requires each condition to be shown to independently affect the outcome, which needs three tests and catches the bug. Covered is not the same as proven.

MC/DC gets described as "the strict coverage criterion for aviation," which makes it sound like more of the same thing. It is not more coverage. It is a different question, and the worked example makes the difference obvious in about ninety seconds.

What the standard requires, by level

Design Assurance Levels and their failure conditions: A catastrophic, B hazardous, C major, D minor, E no effect.

Structural coverage obligations scale with the level:

Level A: statement, decision, and Modified Condition Decision Coverage. Level B: statement and decision coverage. Level C: statement coverage. Levels D and E: no structural coverage objective at all.

There is also an object-code caveat at Level A that gets forgotten: source level structural coverage alone is insufficient, and additional source-to-object-code traceability analysis is required where the compiler introduces code not directly traceable to source.

The procedural point almost every summary drops

DO-178C requires requirements based testing first. Structural coverage is then measured to reveal code that no requirements based test exercised.

That ordering is the whole design. Coverage is used as a gap finder pointed at the tests, never as a quality target in itself. You do not write tests to raise coverage; you write tests against requirements, then use coverage to find code your requirements did not reach, which is usually either dead code or a missing requirement.

Which is precisely what Inozemtseva and Holmes concluded from an empirical study in 2014, arrived at independently by a certification body decades earlier. When the research and the regulator agree about how to use a metric, and industry uses it the other way round, that is worth noticing.

The standard also requires demonstrating the tests themselves are adequate, traceable to requirements, with independence of verification at Levels A and B. That independence requirement is the same principle a technical diligence engineer applies when they check whether somebody other than the author can walk the system: the person who wrote a thing is the worst available judge of whether its checks work.

The worked example

Take the decision D = A and B.

Branch coverage, test 1A=T B=T gives T
Branch coverage, test 2A=F B=F gives Fboth outcomes exercised, so 100 percent
A and B moved togetherneither shown to mattera bug implementing D = A passes both
MC/DC adds a third testA=T B=F gives Fhold A, flip B, the outcome flips
And a fourth pairingA=F B=T gives Fhold B, flip A, the outcome flips
The D = A bugnow caughtit returns T where correct behaviour is F
n + 1 tests for n conditions, against 2 to the n for every combination. Linear cost, and each condition proven to matter.
FigureThe cleanest technical illustration in this pillar. Both suites report one hundred percent of their own criterion on D = A and B, and only one of them can see a decision that ignores B entirely.

Branch coverage is satisfied by two tests:

  • A true, B true, giving D true
  • A false, B false, giving D false

Both outcomes of D are exercised, so branch coverage reads 100 percent. And it is genuinely 100 percent; the tool is not lying.

But A and B changed together between those two tests. Neither was shown to independently drive the result. So a bug that ignores B entirely, implementing D = A, passes both tests. Branch coverage is structurally blind to it, and running that suite a thousand more times will not help.

MC/DC requires each condition to be shown by execution to independently and correctly affect the outcome. That needs n plus 1 tests, so three here:

  • A true, B true, giving true
  • A true, B false, giving false
  • A false, B true, giving false

Independence of A: compare test 1 against test 3. Hold B true, flip A from true to false, and the outcome flips. A is proven to matter.

Independence of B: compare test 1 against test 2. Hold A true, flip B from true to false, and the outcome flips. B is proven to matter.

And the D = A bug now dies, on test 2, where A is true, B is false, and correct behaviour is false.

Why MC/DC rather than every combination

Full multiple condition coverage tests every combination, which is 2 to the n. Four tests here, and intractable by the time you have eight conditions.

MC/DC is linear in the number of conditions and still proves each one's independent effect. That is the entire engineering argument for it: the efficient middle path between a criterion that proves nothing about individual conditions and one that costs exponentially.

The caveat that belongs here

Inozemtseva and Holmes, in the same paper this pillar leans on elsewhere, explicitly note that a standard like DO-178B's MC/DC requirement may increase expenses without necessarily increasing quality.

That is a real finding from the people whose work most supports the rest of this argument, and it should not be buried because it is inconvenient.

The honest position: MC/DC is the clearest available illustration of the difference between exercising a condition and proving it does anything, and it is evidence that the highest assurance domain on earth encodes that distinction as law. Whether mandating it is cost effective in a domain without a regulator is a separate question, and the answer is not obviously yes.

To generate the minimal test set for a decision from your own code, with the independence pair printed for each condition so you can check it rather than trust it, use the MC/DC generator. To see how many injected faults each suite actually catches while both report 100 percent, use coverage against mutation. The general argument is in a test that has never failed is a decoration.

FAQ

What is MC/DC coverage? Modified Condition Decision Coverage requires each condition within a decision to be shown by execution to independently and correctly affect the outcome of that decision, which takes a pair of tests differing in exactly that condition with differing outcomes.

How is MC/DC different from branch coverage? Branch coverage only requires the decision to come out true once and false once, which two tests satisfy however many conditions there are. Those tests can move several conditions together, so a decision that ignores one entirely still passes.

Which DO-178C levels require MC/DC? Level A requires statement, decision and MC/DC. Level B requires statement and decision. Level C requires statement. Levels D and E have no structural coverage objective.

How many tests does MC/DC need? n plus 1 for n conditions, against 2 to the n for full multiple condition coverage. That linear cost is the reason the criterion exists.

Is MC/DC worth it outside aviation? Inozemtseva and Holmes note that a mandate like it may increase expenses without necessarily increasing quality. Its value in this pillar is as the clearest illustration of covered against proven, rather than as a recommendation to adopt the obligation everywhere.

Carrying a program like this one?

Tell us the system, the stakes, and the date that matters. You get a straight technical reply from the person who would lead the work, within 24 hours.

Bring us the program