2010-07-22 9 views
18

Bis jetzt habe ich eine improvisierte Unit-Test-Prozedur verwendet - im Grunde genommen werden eine ganze Menge Unit-Test-Programme automatisch von einer Batch-Datei ausgeführt. Obwohl viele von ihnen explizit ihre Ergebnisse überprüfen, viel mehr Cheat - sie geben Ergebnisse an Textdateien aus, die versioniert sind. Jede Änderung der Testergebnisse wird durch Subversion gekennzeichnet und ich kann leicht erkennen, was die Änderung war. Viele der Tests geben Punktdateien oder eine andere Form aus, die es mir ermöglicht, eine visuelle Darstellung der Ausgabe zu erhalten.Wie kann ich meine Komponententests an cmake und ctest anpassen?

Das Problem ist, dass ich zur Verwendung von cmake wechseln. Mit dem cmake-Fluss zu arbeiten bedeutet, Out-of-Source-Builds zu verwenden, was bedeutet, dass die Bequemlichkeit des Dumpings in einem gemeinsam genutzten Quell-/Build-Ordner resultiert und deren Versionsverwaltung zusammen mit der Quelle nicht wirklich funktioniert.

Als Ersatz, was ich würde wie zu tun ist, um die Einheit Test-Tool, wo Dateien der erwarteten Ergebnisse zu finden (im Quellbaum) und bekommen Sie es, um den Vergleich zu tun. Bei einem Fehler sollte es die tatsächlichen Ergebnisse und Diff-Listings bereitstellen.

Ist das möglich, oder sollte ich einen ganz anderen Ansatz wählen?

Offensichtlich kann ich Ctest ignorieren und nur anpassen, was ich immer zu Out-of-Source-Builds getan habe. Ich könnte zum Beispiel meinen Ordner "where-all-the-builds-live" (mit liberaler Verwendung von "Ignorieren" natürlich) versionieren. Ist das vernünftig? Wahrscheinlich nicht, da jeder Build mit einer separaten Kopie der erwarteten Ergebnisse enden würde.

Auch, alle Ratschläge auf die empfohlene Art und Weise zu Unit-Tests mit cmake/ctest ratfuly erhalten. Ich habe viel Zeit mit CMake verschwendet, nicht weil es schlecht ist, sondern weil ich nicht verstanden habe, wie ich damit am besten arbeiten kann.

EDIT

Am Ende entschied ich die Cmake/ctest Seite des Modultests so einfach wie möglich zu halten. Um zu testen, tatsächlich gegen die erwarteten Ergebnisse, fand ich ein Haus für die folgende Funktion in meiner Bibliothek ...

bool Check_Results (std::ostream    &p_Stream , 
        const char    *p_Title , 
        const char    **p_Expected, 
        const std::ostringstream &p_Actual ) 
{ 
    std::ostringstream l_Expected_Stream; 

    while (*p_Expected != 0) 
    { 
    l_Expected_Stream << (*p_Expected) << std::endl; 
    p_Expected++; 
    } 

    std::string l_Expected (l_Expected_Stream.str()); 
    std::string l_Actual (p_Actual.str()); 

    bool l_Pass = (l_Actual == l_Expected); 

    p_Stream << "Test: " << p_Title << " : "; 

    if (l_Pass) 
    { 
    p_Stream << "Pass" << std::endl; 
    } 
    else 
    { 
    p_Stream << "*** FAIL ***" << std::endl; 
    p_Stream << "===============================================================================" << std::endl; 
    p_Stream << "Expected Results For: " << p_Title << std::endl; 
    p_Stream << "-------------------------------------------------------------------------------" << std::endl; 
    p_Stream << l_Expected; 
    p_Stream << "===============================================================================" << std::endl; 
    p_Stream << "Actual Results For: " << p_Title << std::endl; 
    p_Stream << "-------------------------------------------------------------------------------" << std::endl; 
    p_Stream << l_Actual; 
    p_Stream << "===============================================================================" << std::endl; 
    } 

    return l_Pass; 
} 

Eine typische Einheit Test sieht nun so etwas wie ...

bool Test0001() 
{ 
    std::ostringstream l_Actual; 

    const char* l_Expected [] = 
    { 
    "Some", 
    "Expected", 
    "Results", 
    0 
    }; 

    l_Actual << "Some" << std::endl 
      << "Actual" << std::endl 
      << "Results" << std::endl; 

    return Check_Results (std::cout, "0001 - not a sane test", l_Expected, l_Actual); 
} 

Wo ich brauche eine wiederverwendbare Daten-Dumping-Funktion, es dauert ein Parameter des Typs std::ostream&, so dass es zu einem tatsächlichen Ergebnis-Stream ausgeben kann.

+0

Sie sollten stattdessen Ihre Edit als Antwort hinzufügen, da es Ihre eigene Frage beantwortet – Joakim

Antwort

16

Ich würde CMake Standalone-Scripting-Modus verwenden, um die Tests auszuführen und die Ausgaben zu vergleichen. Normalerweise würden Sie für ein Komponententestprogramm add_test(testname testexecutable) schreiben, aber Sie können einen beliebigen Befehl als Test ausführen.

Wenn Sie ein Skript "runtest.cmake" schreiben und Ihr Komponententestprogramm über dieses Skript ausführen, kann das Skript runtest.cmake alles tun, was es möchte - einschließlich des Dienstprogramms cmake -E compare_files. Sie wollen so etwas wie die folgenden in Ihrer CMakeLists.txt Datei:

enable_testing() 
add_executable(testprog main.c) 
add_test(NAME runtestprog 
    COMMAND ${CMAKE_COMMAND} 
    -DTEST_PROG=$<TARGET_FILE:testprog> 
    -DSOURCEDIR=${CMAKE_CURRENT_SOURCE_DIR} 
    -P ${CMAKE_CURRENT_SOURCE_DIR}/runtest.cmake) 

Dieses ein Skript ausgeführt wird (cmake -P runtest.cmake) und definiert zwei Variablen: TEST_PROG, auf den Pfad der Test ausführbar gesetzt und SOURCEDIR , auf das aktuelle Quellverzeichnis gesetzt. Sie müssen zuerst wissen, welches Programm ausgeführt wird, und als zweites wissen Sie, wo die erwarteten Testergebnisdateien zu finden sind.Der Inhalt runtest.cmake wäre:

execute_process(COMMAND ${TEST_PROG} 
       RESULT_VARIABLE HAD_ERROR) 
if(HAD_ERROR) 
    message(FATAL_ERROR "Test failed") 
endif() 

execute_process(COMMAND ${CMAKE_COMMAND} -E compare_files 
    output.txt ${SOURCEDIR}/expected.txt 
    RESULT_VARIABLE DIFFERENT) 
if(DIFFERENT) 
    message(FATAL_ERROR "Test failed - files differ") 
endif() 

Die erste execute_process läuft das Testprogramm, das „output.txt“ wird auszuschreiben. Wenn das funktioniert, dann läuft die nächste execute_process effektiv cmake -E compare_files output.txt expected.txt. Die Datei "expected.txt" ist das bekannte gute Ergebnis in Ihrem Quellbaum. Wenn es Unterschiede gibt, werden Fehler ausgegeben, sodass Sie den fehlgeschlagenen Test sehen können.

Was dies nicht tut, ist die Unterschiede auszudrucken; CMake hat keine vollständige "diff" -Implementierung in sich versteckt. Im Moment verwenden Sie Subversion, um zu sehen, welche Zeilen geändert haben, so eine offensichtliche Lösung ist, den letzten Teil zu ändern:

if(DIFFERENT) 
    configure_file(output.txt ${SOURCEDIR}/expected.txt COPYONLY) 
    execute_process(COMMAND svn diff ${SOURCEDIR}/expected.txt) 
    message(FATAL_ERROR "Test failed - files differ") 
endif() 

Dies überschreibt den Quellbaum mit der Build-Ausgabe bei einem Fehler führt dann svn diff auf sich. Das Problem ist, dass Sie den Quellbaum auf diese Weise nicht wirklich ändern sollten. Wenn Sie den Test ein zweites Mal ausführen, passiert es! Ein besserer Weg ist, ein visuelles Diff-Tool zu installieren und das auf der Ausgabe und der erwarteten Datei auszuführen.

+0

klingt ziemlich nah und sicherlich informativ - danke. BTW - das "führen Sie den Test erneut und es besteht" Problem existiert nicht wirklich. Wenn der Test fehlschlägt, bin ich nicht dämlich genug, um die aktuellen falschen Ergebnisse als neue erwartete Ergebnisse zu begehen. Der Unterschied, den ich will, ist zwischen der Arbeitskopie und der Kopfversion. Trotzdem ist es sinnvoll, die tatsächlichen Ergebnisse in der Build-Struktur zu belassen und den Vergleich mit einem anderen Programm durchzuführen, damit die Arbeitskopie nicht geändert wird. "Ich bin nicht dumm genug, um diesen Fehler zu machen" scheint ein bisschen wie verlockendes Schicksal. – Steve314

+0

Was ich in der Vergangenheit auch getan habe (obwohl es mehr Arbeit für nicht viel mehr Nutzen war) war, dass das Testprogramm seine Ergebnisse generiert und sie auch mit den bekannten guten Ergebnissen vergleicht. Ich habe 'config_file (expected.dat.in expected.dat COPYONLY)' verwendet, um das erwartete Ergebnis in den Build-Baum zu kopieren. Auf diese Weise könnte der Test sagen "eh! Sie haben ein Problem, das beim 163. Eintrag beginnt!", Anstatt ein Diff nachher auszuführen. – richq

+0

Ich arbeite an einem [CMake Framework] (http://jaws.rootdirectory.de) zum "Kickstarten" von C++ - Projekten. Das wird als [CC0] (http://creativecommons.org/publicdomain/zero/1.0/) lizenziert, während die Lizenz von StackOverflow CC by-sa ist. Wäre es mit dir in Ordnung, wenn ich deine Lösung unter CC0-Bedingungen anpassen würde, während du volle Credits gibst? – DevSolar

Verwandte Themen