#include "coverage.h" #define TRUE 1 #define FALSE 0 #define CTRL_IDX 0xa #define FUNCTION_LIKE_MACRO(idx__, val__) do { \ (array[(idx__)] != 0xffu && array[(idx__) + 1u] != 0xffu) ? \ (((idx__) == (val__)) ? SetFalse(&(val__)) : SetTrue(&(val__))) : \ SetFalse(&(val__)); \ } while (0) struct Compound { int a; int b; int c; int d; int e; int f; }; enum Color { RED = 0, YELLOW = -1, BLUE = 1, ORANGE = -2, GREEN = 2, }; static volatile unsigned char array[5] = { 0xff, 0xff, 0xa, 0xb, 0 }; static int Identity(int const a) { return a; } static void SetTrue(int* value) { value[0] = TRUE; } static void SetFalse(int* value) { value[0] = FALSE; } static int TestFunctionLikeMacro(void) { int result = -1; FUNCTION_LIKE_MACRO(0, result); return result; } static unsigned ComplexBooleanParameter(int const a, int const b, int const c, int const d) { unsigned outcome = FALSE; if (Identity((!a || !(b > -36)) && (!Identity(c) && !(Identity(d) > 2)))) { outcome = TRUE; } else { outcome = FALSE; } return outcome; } static void TestComplexBooleanParameter(void) { ComplexBooleanParameter(-4768, 5003, 8031, 5240); ComplexBooleanParameter(-4768, -36, 6858, 5240); ComplexBooleanParameter(-4768, -36, 0, 3335); ComplexBooleanParameter(0, -102, 0, 2); ComplexBooleanParameter(-4768, -36, 0, 2); } static unsigned ComplexFor(int const a, int const b, int const c, int const d) { unsigned num_cycles = 0u; for (; (((a || b) && (c > -23)) || (Identity(d) < 5)); num_cycles++) { if (num_cycles > 1u) { break; } } return num_cycles; } static void TestComplexFor(void) { /* Set of test vectors for MC/DC */ ComplexFor(0, 0, -22, 5); ComplexFor(0, 0, -23, 5); ComplexFor(0, 1, -23, 5); ComplexFor(0, 0, -23, 4); ComplexFor(0, 1, -22, 5); ComplexFor(1, 0, -22, 5); } static unsigned ComplexDoWhile(int const a, int const b, int const c, int const d) { unsigned num_cycles = 0u; do { if (num_cycles > 1u) { break; } num_cycles++; } while (((!(Identity(a) >= -45) && Identity(b)) && Identity(c)) || d); return num_cycles; } static void TestComplexDoWhile(void) { /* Set of test vectors for MC/DC */ ComplexDoWhile(-45, 1, 1, 0); ComplexDoWhile(-46, 0, 0, 0); ComplexDoWhile(-46, 0, 1, 0); ComplexDoWhile(-46, 1, 0, 0); ComplexDoWhile(-46, 0, 0, 1); ComplexDoWhile(-46, 1, 1, 0); } static unsigned ComplexWhile(int const a, int const b, int const c, int const d) { unsigned num_cycles = 0u; while ((!(a > -70) && !(Identity(b) == 39)) || !(c <= -13) || (Identity(d) < 39)) { if (num_cycles > 1u) { break; } num_cycles++; } return num_cycles; } static void TestComplexWhile(void) { /* Set of test vectors for MC/DC */ ComplexWhile(-69, 40, -13, 39); ComplexWhile(-70, 39, -13, 39); ComplexWhile(-70, 39, -12, 39); ComplexWhile(-70, 39, -13, 38); ComplexWhile(-70, 40, -13, 39); } static unsigned ComplexIf(int const a, int const b, int const c, int const d) { unsigned outcome = FALSE; if (a && !(b > -100 || !(c > 42)) && Identity(d) < 36) { outcome = TRUE; } else { outcome = FALSE; } return outcome; } static void TestComplexIf(void) { /* Set of test vectors for MC/DC */ ComplexIf(0, -100, 42, 36); ComplexIf(1, -99, 42, 36); ComplexIf(1, -100, 42, 36); ComplexIf(1, -100, 43, 36); ComplexIf(1, -100, 43, 35); } static int SwitchCase(enum Color const color) { int offset = 0; switch (color) { case RED: offset = 10; break; case BLUE: offset = 8; break; case ORANGE: offset = 6; break; case YELLOW: case GREEN: offset = 2; break; default: offset = -1; break; } return offset; } static void TestSwitchCase(int const tic) { /* Switch-case expression * * There are several options for compilers to model switch-case * expressions. Representations containing jump or value tables are not * suitable for a trace-based measurement of code coverage. It is possible * for most compilers to suppress the creation of jump tables via compiler * flags. The result is a binary search incorporating conditional branches * that suitable for monitoring the status of each condition. */ if (tic) { /* Set of test vectors for MC/DC */ SwitchCase(RED); SwitchCase(BLUE); SwitchCase(ORANGE); SwitchCase(YELLOW); SwitchCase(GREEN); SwitchCase(3); /* Lower than expected input range */ SwitchCase(-3); /* Higher than expected input range */ } else { /* Expected input range */ SwitchCase(RED); SwitchCase(BLUE); SwitchCase(ORANGE); SwitchCase(YELLOW); SwitchCase(GREEN); } } static unsigned MultiLine(struct Compound *compound) { if ( ( compound->a == TRUE || compound->b == TRUE || compound->c == TRUE) && ( compound->d == TRUE || compound->e == TRUE || compound->f == TRUE)) { return TRUE; } return FALSE; } static void TestMultiline(void) { /* Multi-line multi-operator decision * * Each operator resides on a distinct line, so compilers may choose * to create additional entries in the debug symbol information per * line. However, even though this may improve debugging it also * conceals the direct relationship of these lines. A semantic analysis * is required to restore this relationship and reason about the decision * as a whole. */ /* Set of test vectors for decision coverage */ struct Compound compound = {0}; compound.a = FALSE; compound.b = FALSE; compound.c = FALSE; compound.d = TRUE; compound.e = TRUE; compound.f = TRUE; if (MultiLine(&compound) == TRUE) { return; } compound.b = TRUE; MultiLine(&compound); } static unsigned NestedExprTrans(int a, int b, float c) { /* Equivalence transformation for decision with nested Boolean expression * * Equivalent expression after transformation. The nested Boolean * expression is extracted and put into a branching context. Compilers * typically choose to use conditional branches for modelling this type of * structure. */ int tmp = 0; if ((float) b < c) { tmp = 1; } if (a > (b + tmp)) { return TRUE; } return FALSE; } static unsigned NestedExpr(int a, int b, float c) { /* Decision with nested Boolean expression * * Expression showing a nested Boolean expression. Compilers may choose to * model nested expressions with conditional or unconditional instructions * instead of conditional branches that are not suitable for the trace- * based measurement of code coverage. */ return (a > (b + ((float) b < c))); } static void TestExprNesting(void) { /* Decision with nested Boolean expression * * The decision contains a nested Boolean expression. Both decision and * nested expression must be analyzed as two distinct decisions. Compilers * may choose to model nested expressions with conditional or unconditional * instructions instead of conditional branches that are not suitable for * the trace-based measurement of code coverage. Transforming the * expression into a branching context may force the use of conditional * instructions. */ /* Set of test vectors for MC/DC */ NestedExpr(1, 1, 3.0f); /* top: T, nested: T */ NestedExpr(6, 4, 3.0f); /* top: F, nested: F */ /* Transformed expression */ NestedExprTrans(1, 1, 3.0f); /* top: T, nested: T */ NestedExprTrans(6, 4, 3.0f); /* top: F, nested: F */ } static float TernaryExprTrans(float const a, float const b) { /* Equivalence transformation for ternary expression * * Equivalent expression after transformation. The decision appears in a * branching context once more. Compilers typically choose to use * conditional branches for modelling this type of expression. */ if (a >= b) { return a; } else { return b; } } static float TernaryExpr(float const a, float const b) { /* Ternary expression as decision * * Expression showing a decision with ternary expression. Compilers may * choose to model ternanry expressions with conditional or unconditional * instructions instead of conditional branches that are not suitable for * the trace-based measurement of code coverage. */ return (a >= b) ? a : b; } static void TestTernaryExpr(void) { /* Test for ternary expression as decision * * Compilers may choose to model ternanry expressions with conditional or * unconditional instructions instead of conditional branches that are not * suitable for the trace-based measurement of code coverage. Transforming * the expression into a branching context may force the use of conditional * instructions. */ /* Set of test vectors for both MC/DC and OBC */ TernaryExpr(0.0f, 0.0f); TernaryExpr(0.0f, 1.0f); /* Transformed expression */ TernaryExprTrans(0.0f, 0.0f); TernaryExprTrans(0.0f, 1.0f); } static int SimpleIfFunctionCall(int const a) { /* If-then block controlled by nested function call * * The expression shows an expression in branching context. The outcome of * the decision is controlled by the Boolean return value of a simple * function. */ if (Identity(a)) { return 1; } return 0; } static void TestSimpleIfFunctionCall(void) { /* Test for if-then construct with nested function call * * Tests whether the compiler uses conditional branches to model this * simple expression type. */ SimpleIfFunctionCall(0); SimpleIfFunctionCall(1); } static int BooleanAssignmentRelExprTrans(int const a, int const b) { /* Equivalence transformation for relational expression * * Equivalent expression after transformation. The decision appears in a * branching context once more. Compilers typically choose to use * conditional branches for modelling this type of expression. */ if (a == b) { return TRUE; } return FALSE; } static int BooleanAssignmentRelExpr(int const a, int const b) { /* Relational expression as decision * * Expression showing a decision in non-branching context. Compilers may * choose to model Boolean assignments with conditional or unconditional * instructions instead of conditional branches that are not suitable for * the trace-based measurement of code coverage. */ return a == b; } static void TestNoBranchCtxRelExpr(void) { /* Test for relational expression as decision * * Relational operators are no Boolean operators. Hence, the decision * has a single condition. * Compilers may choose to model Boolean assignments with conditional or * unconditional instructions instead of conditional branches that are not * suitable for the trace-based measurement of code coverage. Transforming * the expression into a branching context may force the use of conditional * instructions. */ /* Set of test vectors for both MC/DC and OBC */ BooleanAssignmentRelExpr(0, 0); BooleanAssignmentRelExpr(0, 1); /* Transformed expression */ BooleanAssignmentRelExprTrans(0, 0); BooleanAssignmentRelExprTrans(0, 1); } static int BooleanAssignmentNotOp(int const a, int const b, int const c) { /* Decision in assignment * * Boolan expression that does not occur in branching context. * * Structure: Truth table: Test vectors for MC/DC: * * a----> F a b c | outcome a: 1. + 2. * \ -------+--------- b: 2. + 3. * b----> F 1. F x x | F c: 2. + 4. * |\ 2. T F F | T * | c----> F 3. T T x | F * v | 4. T F T | F * T v * T */ return a && !(b || c); } static void TestNoBranchCtxNotOp(void) { /* Test for decision in assignment * * Boolan expression that does not occur in branching context. * * Decision: a && !(b || c) * * Structure: Truth table: Test vectors for MC/DC: * * a----> F a b c | outcome a: 1. + 2. * \ -------+--------- b: 2. + 3. * b----> F 1. F x x | F c: 2. + 4. * |\ 2. T F F | T * | c----> F 3. T T x | F * v | 4. T F T | F * T v * T */ /* Set of test vectors for both MC/DC and OBC */ BooleanAssignmentNotOp(0, 1, 1); /* 1. */ BooleanAssignmentNotOp(1, 0, 0); /* 2. */ BooleanAssignmentNotOp(1, 1, 1); /* 3. */ BooleanAssignmentNotOp(1, 0, 1); /* 4. */ } static int BooleanExprCoupledTerms(int const a, int const b) { /* Decision with strongly coupled terms * * Each condition that appears more than once is treated like * a distinct condition. Masking of subexpressions is used to find * independence pairs that are suitable even though more than one value * is changing at the same time. * * Structure: Truth table: Test vectors for MC/DC: * * a---+ a b | outcome a(1): 1. + 2. * \ | -----+--------- b(1): 2. + 4. * b-+--> F 1. F F | F a(2): 3. + 4. * |\v 2. T F | T b(2): 1. + 3. * | a----> F 3. F T | T * | \ 4. T T | F * v b----> F * T | * v * T */ unsigned outcome = FALSE; if ((a && !b) || (!a && b)) { outcome = TRUE; } else { outcome = FALSE; } return outcome; } static void TestMaskingMcdc(void) { /* Test for decision with strongly coupled terms * * Each condition that appears more than once is treated like * a distinct condition. Masking of subexpressions is used to find * independence pairs that are suitable even though more than one value * is changing at the same time. * * Decision: (a && !b) || (!a && b) * * Structure: Truth table: Test vectors for MC/DC: * * a---+ a b | outcome a(1): 1. + 2. * \ | -----+--------- b(1): 2. + 4. * b-+--> F 1. F F | F a(2): 3. + 4. * |\v 2. T F | T b(2): 1. + 3. * | a----> F 3. F T | T * | \ 4. T T | F * v b----> F * T | * v * T */ /* Set of test vectors for MC/DC */ BooleanExprCoupledTerms(0, 0); /* 1. */ BooleanExprCoupledTerms(1, 0); /* 2. */ BooleanExprCoupledTerms(0, 1); /* 3. */ BooleanExprCoupledTerms(1, 1); /* 4. */ } static unsigned BooleanExprMixedOps(int const a, int const b, int const c) { /* Deviation of MC/DC and OBC * * The structure of the decision results in the deviation of MC/DC and * Object Code Branch Coverage. Full coverage of all conditional branches * at the object code level alone is not sufficient to achieve MC/DC. * * Structure: Truth table: Test vectors for MC/DC: * * a----+ a b c | outcome a: 1. + 5. * | | -------+--------- b: 3. + 5. * v | 1. T x T | T c: 1. + 2. * b | 2. T x F | F * |\ v 3. F T T | T Test vectors for OBC: * | +->c---->T 4. F T F | F 2. + 3. + 5. * | | 5. F F x | F * v v * F F */ unsigned outcome = FALSE; if ((a || b) && c) { outcome = TRUE; } else { outcome = FALSE; } return outcome; } static void TestObcDiffersMcdc(int const tic) { /* Test for deviation of MC/DC and OBC * * The structure of the decision results in the deviation of MC/DC and * Object Code Branch Coverage. Full coverage of all conditional branches * at the object code level alone is not sufficient to achieve MC/DC. * * Decision: (a || b) && c * * Structure: Truth table: Test vectors for MC/DC: * * a----+ a b c | outcome a: 1. + 5. * | | -------+--------- b: 3. + 5. * v | 1. T x T | T c: 1. + 2. * b | 2. T x F | F * |\ v 3. F T T | T Test vectors for OBC: * | +->c---->T 4. F T F | F 2. + 3. + 5. * | | 5. F F x | F * v v * F F */ if (tic) { /* Set of test vectors for MC/DC */ BooleanExprMixedOps(1, 0, 1); /* 1. */ BooleanExprMixedOps(1, 0, 0); /* 2. */ BooleanExprMixedOps(0, 1, 1); /* 3. */ BooleanExprMixedOps(0, 0, 0); /* 5. */ } else { /* Set of test vectors for OBC */ BooleanExprMixedOps(1, 0, 0); /* 2. */ BooleanExprMixedOps(0, 1, 1); /* 3. */ BooleanExprMixedOps(0, 0, 0); /* 5. */ } } static unsigned BooleanExprSameOps(int const a, int const b, int const c) { /* Equivalence of MC/DC and OBC * * The structure of the decision results in the equivalence of MC/DC * and Object Code Branch Coverage. Full coverage of all conditional * branches at the object code level is sufficient to achieve MC/DC. * * Structure: Truth table: Test vectors for MC/DC: * * a----> T a b c | outcome a: 1. + 4. * \ -------+--------- b: 2. + 4. * b----> T 1. T x x | T c: 3. + 4. * \ 2. F T x | T * c----> T 3. F F T | T * | 4. F F F | F * v * F */ unsigned outcome = FALSE; if (a || b || c) { outcome = TRUE; } else { outcome = FALSE; } return outcome; } static void TestObcEqualsMcdc(void) { /* Test for equivalence of MC/DC and OBC * * The structure of the decision results in the equivalence of MC/DC * and Object Code Branch Coverage. Full coverage of all conditional * branches at the object code level is sufficient to achieve MC/DC. * * Decision: a || b || c * * Structure: Truth table: Test vectors for MC/DC: * * a----> T a b c | outcome a: 1. + 4. * \ -------+--------- b: 2. + 4. * b----> T 1. T x x | T c: 3. + 4. * \ 2. F T x | T * c----> T 3. F F T | T * | 4. F F F | F * v * F */ /* Set of test vectors for both MC/DC and OBC */ BooleanExprSameOps(1, 0, 0); /* 1. */ BooleanExprSameOps(0, 1, 0); /* 2. */ BooleanExprSameOps(0, 0, 1); /* 3. */ BooleanExprSameOps(0, 0, 0); /* 4. */ } void RunCoverageDemo(void) { static unsigned tic = 1u; while (TRUE) { tic = !tic; TestObcEqualsMcdc(); TestObcDiffersMcdc(tic); TestMaskingMcdc(); TestNoBranchCtxNotOp(); TestNoBranchCtxRelExpr(); TestSimpleIfFunctionCall(); TestTernaryExpr(); TestExprNesting(); TestMultiline(); TestSwitchCase(tic); TestComplexIf(); TestComplexFor(); TestComplexWhile(); TestComplexDoWhile(); TestComplexBooleanParameter(); TestFunctionLikeMacro(); } }