GITLAB mit Tests und Code Coverage
« | 27 Aug 2022 | »Gitlab wird als Quellcode Verwaltungssystem in vielen Firmen eingesetzt, auch in denen für die ich gearbeitet habe und arbeite.
Gitlab ist simpel … und deshalb sehr kompliziert.
Den jeder Anwender kann sich seinen eigenen Weg durch einen Berg von
Funktionen graben und deshalb sieht jeder Tunnel dann etwas anders aus.
Und eine interessante Herausforderung ist das Sammeln von Code-Coverage Daten in C++.
CI Pipelines und Jobs
Das Continuous Integration
Feature (kurz CI) gestatt es automatisiert “Operationen” bei Bestimmten
Ereignissen auszuführen.
Der Klassiker ist, dass man bei jedem Commit in einem GIT
Repository dann eine Build-Pipeline
startet, die den neuen Code kompiliert und gegebenenfalls abtestet.
Eine Pipeline besteht dann aus einer oder mehreren “Stages” mit den größeren Teilschritten und in jeder Stage laufen dann die eigentlichen “Jobs”, z.B.: kann jeder “Job” eine Plattform wie Windows oder Linux abdecken oder für unterschiedliche Testframeworks.
Ein mir vertrauter einfacher Ablauf solcher Pipeline “Stages” ist:
- Build
Alle Sourcen werden kompiliert - Test
Unit Tests werden ausgeführt um Test und Code-Coverage Berichte zu erstellen - Deploy
Fertige Binaries werden irgendwo hin hochgeladen, wo sie direkt eingesetzt werden oder auf einen Download warten.
Report Formate
Unit test Frameworks können ihre
Ergebnisse in unterschiedlichen Formaten ausgeben, was man meist über
Commandline-Switches einstellen kann.
Zwei recht bekannte Formate sind:
JUnit Test Reports
JUnit und Cobertura entstanden ursprünglich von
Java Projekten, sie
wurde aber von vielen Sprachen genutzt um eigene Tests standardisiert
auszugeben.
Das C++ Framework BOOST
unterstützt in seinen boost/test
Klassen auch JUnit und mit eben diesem
durfte ich die letzten Jahre arbeiten.
Der Test:
führt dann zu sowas wie:
1<testsuite tests="5" skipped="0" errors="1" failures="0" id="0" name="test_my_code_module" time="0.130668"> 2 <testcase classname="my_test_suite" name="my_test_case"> 3 <error message="unexpected exception" type="system error"> 4 <![CDATA[ 5 UNCAUGHT EXCEPTION ... 6 ]]> 7 </error> 8 </testcase> 9</testsuite>
Ein Gitlab CI Job in gitlab_ci.yml
, der für einen Boost-Test in etwa so
aussieht:
1test_job: 2 script: 3 - mkdir ${CI_PROJECT_DIR}/test_results 4 - > 5 path/to/test_my_code_module 6 --report_sink=${CI_PROJECT_DIR}/test_results/my_test_results_sumary.txt 7 --logger=JUNIT,all,${CI_PROJECT_DIR}/test_results/my_test_results.xml 8 - echo "Done" 9 artifacts: 10 paths: 11 - ${CI_PROJECT_DIR}/test_results/* 12 reports: 13 junit: ${CI_PROJECT_DIR}/test_results/my_test_results.xml
Lädt die Ergebnisse dann zum Gitlab-Coordinator hoch und der kann dann die
my_test_results.xml
sofort parsen und im Web-UI schön anzeigen.
Code Coverage
Bei der Code Coverage (oder auch Test-Coverage) wird im Sourcecode mitgezählt, welche Zeilen ausgeführt wurden und welche nicht. Am Ende wissen wir, ob unsere Tests sinnvoll waren und alles abgetestet haben, oder viele Codezeilen nie angesprungen und damit ungetestet verblieben sind.
Je nach Umgebung muss man den Sourcecode speziell kompilieren und mit Hilfsprogrammen die Coverage errechnen lassen.
Beim GCC kann man die Sourcen mit --coverage
kompilieren womit bei der
Ausführung .gcno
und .gcda
automatisch erstellt werden.
Tools wie gcovr können diese Dateien dann in das
Cobertura-XML Format umwandeln.
1gcovr --xml-pretty -o coverage.xml --root path/to/source
Unter Windows kann z.B.: OpenCppCoverage
ein Debug-Binary ausführen und misst
dessen Ausführung per Debugger mit.
1OpenCppCoverage.exe --source path/to/sources --export_type cobertura:coverage.xml -- test.exe
Doch leider reicht es jetzt nicht ganz aus, dieses Ergebnis einfach zum Gitlab Coordinator hochzuladen mit:
Denn um die %
Zahl der Coverage registriert zu bekommen, nutzt Gitlab
nicht die XML-Datei (die wird nur bei Mergerequests herangezogen).
Gitlab erwartet, dass das Ergebnis auf die Konsole geschrieben wird, und
dass man es von dort per
Regular-Expression
auslesen kann.
Bei gcovr
muss man noch den Commandline-Switch --print-summary
anfügen, damit der Output so aussieht:
Mit dem coverage
Zusatz im Gitlab Job wird die lines
Zeile extrahiert:
Fazit
Am Ende hat man dann Test-Reports und Coverage-Reports, die man im Gitlab Web-UI anzeigen und per Rest-API abholen kann.
Durch Gitlab-Badges lässt sich die Coverage-Zahl auch direkt auf der Projektseite anzeigen und diese kleinen SVG-Bilder lassen sich auch hervorragend in andere Gitlab-Seiten einbauen um einen Überblick zu erhalten.