Rules of Merging

When coverage for source file variants is merged, different rules are applied hierarchically for functions and their content. For coverage counters, the union of identified and not identified counters is built per function and coverage measures are calculated based on this unification.

Starting point for merging is the presence of two or more variants for the same source file, variant (1), …, (n). The merged version of this source file contains all functions present in at least one variant.

For all kind of items like coverage counters, statements and lines, one fundamental decisions is made: Is the item considered equal with its counterparts in other variants?

  • If yes, coverage counters are added up or execution information is combined.
  • If no, different items are, virtually or visible, put side-by-side in the union of different variants.

This is the basic idea of merging, with different identification rules applied.

Example: Three variants of conditionally compiled source code

For the following rule descriptions, these three variants are used for explanation. The code was compiled with MY_CONST set to 1, 2 and 3. For each variant, foo(1) was executed.

Rules

Functions

Functions are identified across variants if they are defined at the same line with the same name. In the example, foo at line 5 is identified for variant (1) and (2). Variant (3) has an independent function foo defined at line 17.

Only for identified variants of a function, the following identification process for probes and their counters, statements and lines inside functions is applied.

Decision counters
Coverage probes used for decision coverage are considered equal if they match regarding
  • line number
  • probe type, like if, for, while, … or return, break, …
  • probe description like a > 99, recorded by ctc
In this case, the true, false or hit counters are added up. All probes not identified are taken over to the merged version and keep their respective counters.

In the example, the if-probe in line 6 differs in its probe description, as it is x == 1 for variant (1) and x == 2 for variant (2). Hence it is not considered equal, and two pairs of counters are taken over for the merged version.

Multicondition and condition counters
These higher coverage counters are equal and added up if their determining decision probes are considered equal.
MC/DC
With the added result for multicondition counters (T-F-combinations), the MC/DC criterium is rechecked for each possible MC/DC pair and subsequently for each condition.
Statements
Statements are processed and counted blockwise in Testwell CTC++, not individually. A block of statements belongs to one instrumentation probe (if itself is counted as one statement) or arises between different probes. Statement blocks are considered equal if
  • the number of statements is equal and
  • the line range recorded by ctc is equal and
  • no difference is recorded for this line range whether these lines have been active or inactive in compilation.
Statements from an identified block are counted as exectued if the block is executed in at least one variant. Statement blocks not identified are taken into account separately. Due to the blockwise processing, this can artificially increase the number of statements.
In the example, function foo from variant (1) and (2) has three blocks with statement.
  • The if-statement itself is identified.
  • The statement block inside the true-branch is also considered equal. Its one statement in line 7 is counted as executed in merged version of foo.
  • For the false-branch, the statement block from variant (1) contains two statements and the statement block from variant (2) contains one statement. Hence they are not considered equal, but two not executed statements and one executed statement are included in the calculation.
In total, statement coverage for the merged version of foo is 3 / 5.
Lines inside functions

For a line, the independent information if it was active, executed or justified in one of the relevant variants is transferred to the merged version of this line. Relevant are all variants contributing to the same identified function.

In the example, lines 11 and 13 are both active in merged result. Line 11 is not executed, line 13 is executed. For both lines, variant (3) is not relevant as these lines do not belong to foo in this variant.

Lines outside functions
For a line not belonging to a function in all variants, the active / inactive information is transferred as described for lines in functions.
Marked as "different in variants"

Lines are marked as different in variants if they are present both active and inactive in different compilations. Probes are "different in variants" if not identified among all variants, but put side-by-side in the result.

A function is marked, in consequence, if it contains these kind of probes or lines, or if it contains statement blocks not identified. This marker is escalated from functions to files and from files to directories.

In the example, probes belonging to line 6 are marked as they are reported side-by-side. Line 11 is marked as it is inactive in variant (2). Function foo at line 5 is marked in consequence. Function foo at line 17 is only present in one variant, so no differences can occur here.

Note:

Merged functions cannot be interpreted from a control flow perspective, as counters may root in different variants. The rules described above are independent for different aspects of coverage - merged statement coverage may contradict merged line painting results, for example. Logical conclusions like "100% decision coverage implies 100% statement coverage" do not hold anymore.

The purpose of merging is a convenient, joint impression for small preprocessing differences. As these differences may be arbitrarily large, merging results can become completely useless.

Merging as a feature is built for differences from preprocessing the same source file differently. It is not intended to be used for changed source code.