2017-04-19 5 views
1

Ich verwende gcov/lcov für Coverage-Analyse von googletest-Unit-Tests.Coverage-Analyse einiger googletest-Makros zeigt unvollständige Abdeckung, wenn über mehrere Zeilen verteilt - warum?

Ein wiederkehrendes Problem ist, dass der Coverage-Bericht nicht aufgedeckte Zeilen im Testcode für einige googletest-Makros zeigt, wenn das Makro über mehrere Zeilen verteilt ist.

Ich weiß, dass gov/lcov nicht genauer als eine einzige Zeile sein kann, aber ich bin verwirrt über das Verhalten, das ich sehe. Kann jemand das erklären? Minimal Beispiel:

#include <gtest/gtest.h> 

TEST(coverage,incomplete) 
{ 
    // Every second line in every invocation here will show up as uncovered: 
    EXPECT_NO_THROW(40 + 
        2); 
    EXPECT_NO_THROW(40 + 2 
       ); 
    EXPECT_NO_THROW(40 + 2) 
    ; 
} 

TEST(coverage,complete) 
{ 
    // This test does not show uncovered lines 
    EXPECT_NO_THROW(40 + 2); 
    EXPECT_EQ(40 
      + 
      2 
      , // even though this is spread over several lines 
      42 
      ) 
    ; 
} 

Wie Coverage-Analyse durchgeführt wurde:

g++-4.8 -Igtest/googletest/include/ --coverage -o coverage_macropp coverage_macropp.cpp gtest/googletest/make/gtest_main.a -pthread 
./coverage_macropp 
lcov --capture --directory . --output-file coverage.info 
genhtml --demangle-cpp coverage.info --output-directory coverage 

Die Coverage-Analyse im Web-Browser dann 7 Zeilen zeigen, 9 und 11 als aufgedeckt:

 Line data Source code 

    1    : #include <gtest/gtest.h> 
    2    : 
    3   5 : TEST(coverage,incomplete) 
    4    : { 
    5    : // Every second line in every invocation here will show up as uncovered: 
    6   1 : EXPECT_NO_THROW(40 + 
    7   0 :     2); 
    8   1 : EXPECT_NO_THROW(40 + 2 
    9   0 :     ); 
    10   1 : EXPECT_NO_THROW(40 + 2) 
    11   0 :  ; 
    12   1 : } 
    13    : 
    14   5 : TEST(coverage,complete) 
    15    : { 
    16    : // This test does not show uncovered lines 
    17   1 : EXPECT_NO_THROW(40 + 2); 
    18   1 : EXPECT_EQ(40 
    19    :    + 
    20    :    2 
    21    :    , // even though this is spread over several lines 
    22    :    42 
    23    :   ) 
    24   1 :  ; 
    25   4 : } 

Warum? Und warum ist das EXPECT_EQ-Makro nicht betroffen?

+0

Es hängt davon ab, wie gcov seine Eingaben analysiert und möglicherweise wie die Makros definiert sind (zum Beispiel, ob sie Token einfügen oder nicht). Das sind also zwei Orte, an denen man nach Erklärungen suchen kann. – Peter

Antwort

1

Dies war eine faszinierende Untersuchung. Mit Blick auf diese Makros habe ich Dinge über Switch/Case und Goto gelernt, von denen ich nicht wusste, dass sie möglich sind. Der Grund für die Verhaltensunterschiede kommt aber von if/else-Konstrukten, die gemeinsam haben, dass die else-Verzweigungen nie ausgeführt werden, die sich aber dadurch unterscheiden, ob der Compiler bereits weiß, dass diese else-Verzweigungen nie ausgeführt werden .

Zuerst im Quellcode das Makro über mehrere Zeilen verbreitet hat den Präprozessor produzieren (soweit es für diese Frage wichtig ist) den folgenden Code für den Compiler und seine Abdeckung Analysator:

if (condition) statement1; else statement2 


    ; 

Und anscheinend Der Coverage-Analysator gcov zählt die Zeile mit dem einsamen Semikolon als Codezeile und betrachtet sie als ausgeführt, wenn das Statement2 aus dem else-Zweig ausgeführt wird.

, nun die Coverage-Analyse Unterschied zu reproduzieren, wie in der Frage festgestellt, betrachten Sie dieses Beispiel-Programm:

#include <stdio.h> 
#include <time.h> 

int main(int, char*[]) { 
    const bool true_1 = true; 
    const bool true_2 = time(NULL) != 0; 

    if (true_1) 42; else printf("hello\n") 
        ; 
    if (true_2) 42; else printf("hello\n") 
        ; 
    return 0; 
} 

true_1 und true_2 immer wahr sind (in meinem Leben, und wenn ich nicht mit Zohan an Computern (Uhren), aber im Fall von true_1, der Compiler weiß es, während für True_2 kann es nicht wissen. (Ja, ich könnte wahrscheinlich einen sichereren Initialisierer für true_2 finden. Dies wird für jetzt tun.)

Beachten Sie auch, dass die Anweisungen in den if-Zweigen nichts tun, aber die Anweisungen in den else-Zweigen würden Nebenwirkungen haben.

Die gcov/lcov-Coverage-Analyse für dieses Programm sieht wie folgt aus:

 Line data Source code 

    1    : #include <stdio.h> 
    2    : #include <time.h> 
    3    : 
    4   1 : int main(int, char*[]) { 
    5   1 : const bool true_1 = true; 
    6   1 : const bool true_2 = time(NULL) != 0; 
    7    : 
    8    : if (true_1) 42; else printf("hello\n") 
    9    :      ; 
    10   1 : if (true_2) 42; else printf("hello\n") 
    11   0 :      ; 
    12   1 : return 0; 
    13    : } 

Da der Compiler bereits weiß, dass true_1 wahr ist, die else-Zweig, und deshalb Linie 9 wird nicht für die Berichterstattung Analyse berücksichtigt . Dies gilt sogar für die Kompilierung mit -O0.

Zeile 11 in diesem Programm wird jedoch für die Coverage-Analyse berücksichtigt, da true_2 nur zur Laufzeit bekannt ist.Als Randnotiz: wenn ich eine andere Dummy-Anweisung auch im else-Zweig verwendet hätte, oder sogar einen Funktionsaufruf, von dem bekannt ist, dass er keine Nebeneffekte hat, wie sin (7) anstelle von printf ("hallo") , dann würde diese Zeile auch nicht in der GCOV-Coverage-Analyse zählen.

Bisher sieht es so aus, dass ich zur vollständigen formalen Abdeckung Googletest-Makros auf eine einzige Quellzeile beschränken muss, wenn das Testergebnis zum Zeitpunkt der Kompilierung noch nicht bekannt ist.

+0

Guter Fang! Und sehr gut formuliert. Ich denke, du solltest deine eigene Antwort akzeptieren. – Jonas

Verwandte Themen