この記事は何
C++ のコードの gcovr を使った coverage test を gitlab CI で自動実行して、結果を gitlab pages で公開するまでの流れ
(2022/11/14 現在)
初めに
みなさん、テストちゃんと書いてますか?書いてます?
でもどこまでテスト書いたか不安になりません?なりますよね?
そんなあなたにオススメなのが、coverage test!
これを使えばあなたのコードがどの程度テスト済みかが簡単に分かっちゃいます!
更に、その情報をメインページでお知らせしてあげれば、あなたのコードの安全性を皆さんにお知らせすることだってできちゃうんです!
でも一々テストをするのがめんどくさい?
大丈夫です!GitLab CIを使って全部自動で済ませちゃいましょう!
そんな便利な機能が今ならタダ!これはもう使うしかないでしょう!
(深夜テンション)
いつの間にかgitlabの仕様が変わっていた恨みを込めて
0. 対象コード
簡単のため
#include<cstdio>
int main(int argc, char** argv){
if(argv[1][0] == '1'){
printf("head = 1\n");
}else{
printf("head != 1\n");
}
return 0;
}
を対象にします。最初のコマンドライン引数の最初の文字が1か1じゃないかを判定するコードですね。引数がない時にバグるって?いいんだよ細かいことは!!!(現在深夜31時)
1. GitLabで自動実行しよう
とにもかくにもsample.cpp
をGitLab CI上で自動実行するために、.gitlab-ci.yml
ファイルを書きます。
stage:
- test
sample-test:
image: ubuntu:22.04
stage: test
before_script:
- export DEBIAN_FRONTEND=noninteractive
- apt-get update
- apt-get install -y build-essential gcc-11 g++-11
script:
- g++ sample.cpp -o sample
- ./sample 1
.gitlab-ci.ymlについての詳しい解説はしません。GitLabのページを見てください。
sample.cpp
と一緒にgitlabにpushします。CI/CD->JobsからCIの実行状況が確認できます。成功していると、Statusの欄がpassedになり、そこをクリックして飛んだ先に
のように表示されるはずです。
2. gcovr で coverage test をしよう
gcovrを使ってc++のコードのcoverage testをしてみます。gcovrはapt-getで落として来れるので便利です。正確にはgcovというcoverage testを行うツールの結果をまとめるのがgcovrのお仕事なのですが、gcovrのコマンドを打つとgcovも自動実行されます。ただし、gcovはgccと一緒に落ちてくるツールでgcovrとは異なるため、バージョンが違うと痛い目を見ることがあります(見ました)。気を付けましょう。
stage:
- test
sample-test:
image: ubuntu:22.04
stage: test
before_script:
- export DEBIAN_FRONTEND=noninteractive
- apt-get update
- apt-get install -y build-essential gcovr gcc-11 g++-11
script:
- g++ sample.cpp -o sample -fprofile-arcs -ftest-coverage -fPIC
- ./sample 1
- gcovr -v -r .
コンパイル時にコマンドを追加し、プログラム実行後にgcovrを実行します。詳しくはgcovrのサイトを見てください。
正しく実行されると、CIの結果が次のようになります。
sample.cpp
のelseの中身が実行されないので、coverが100%にはならず、80%になっています。
3. 結果をgitlab pagesで表示しよう
コンソール上の結果だけだと分かりにくいので、htmlで分かりやすく表示するために、次のように.gitlab-ci.yml
を変更します。(このテストだけだとjson出力を介する必要はありませんが、後のために一旦jsonで出力しています。)
stages:
- test
- deploy
sample-test:
image: ubuntu:22.04
stage: test
before_script:
- export DEBIAN_FRONTEND=noninteractive
- apt-get update
- apt-get install -y build-essential gcovr gcc-11 g++-11
script:
- g++ sample.cpp -o sample -fprofile-arcs -ftest-coverage -fPIC
- ./sample 1
- mkdir -p public
- gcovr -v -r . --json coverage.json
- cp *.json public/
artifacts:
paths:
- public
pages:
image: ubuntu:22.04
stage: deploy
needs:
- sample-test
before_script:
- export DEBIAN_FRONTEND=noninteractive
- apt-get update
- apt-get install -y gcovr
script:
- cd public
- gcovr -v --add-tracefile "coverage.json" -r ../. --html-details -o index.html
artifacts:
paths:
- public
CIが終わった後で、GitLabのSettings->Pages->Access pages内のYour pages are served under: とある部分のリンクを踏むと、coverage testの結果を見ることが出来ます。
sample.cppをクリックすると、このコード内のより分かりやすい結果を見ることが出来ます。
このように、else内のテストが流れていないことが一目で分かるようになります。
4. gitlabのホームにcoverageのバッジを付けよう
ページを訪れた人にどの程度coverage testが行われているかを知らせるために、coverageのバッジを付けます。
stages:
- test
- deploy
sample-test:
image: ubuntu:22.04
stage: test
before_script:
- export DEBIAN_FRONTEND=noninteractive
- apt-get update
- apt-get install -y build-essential gcovr gcc-11 g++-11
script:
- g++ sample.cpp -o sample -fprofile-arcs -ftest-coverage -fPIC
- ./sample 1
- mkdir -p public
- gcovr -v -r . --json coverage.json
- cp *.json public/
artifacts:
paths:
- public
pages:
image: ubuntu:22.04
stage: deploy
needs:
- sample-test
before_script:
- export DEBIAN_FRONTEND=noninteractive
- apt-get update
- apt-get install -y gcovr
script:
- cd public
- gcovr -v --add-tracefile "coverage.json" -r ../. --html-details -o index.html
- gcovr -v --add-tracefile "coverage.json" -r ../. --xml-pretty --exclude-unreachable-branches --print-summary -o coverage.xml
coverage: /^\s*lines:\s*\d+.\d+\%/
artifacts:
reports:
junit: public/coverage.xml
coverage_report:
coverage_format: cobertura
path: public/coverage.xml
paths:
- public
GitLabのページへ行けば、gcovr以外のツールを使った場合の書き方を見ることが出来ます。
Settings->General->Badgesをクリックしてバッジを作りましょう。
Nameの欄は適当で大丈夫です。
Linkの欄は、3で確認したgitlab pagesのurlでも入れておきましょう。
Badge image URL には https://gitlab.com/%{project_path}/badges/%{default_branch}/coverage.svg
と入力します。(default_branchは何も弄ってなければmainです。main以外のbranchで遊んでる場合は適宜変えてください。)
すると、下のBadge image previewのところにバッジが表示されます。
クリックすると先ほどのサイトに飛べることも確認しておくとよいでしょう。
Add badgeを押すとメインページにバッジが付きます!
5. 別々のCIのテスト結果をまとめよう
さて、別々のCIでテストを実行して、その結果をまとめることを考えます。
例えばdockerコンテナ依存の条件分岐がある場合は、それぞれ対応するコンテナを使っているCIを通すでしょう。
その場合、それぞれの結果をまとめて出力することが出来ます。
次の例では、入力1の結果をcoverage-1.jsonに、入力2の結果をcoverage-2.jsonに出力し、それらをまとめてcoverage testを行っています。
stages:
- test
- deploy
.sample-test:
image: ubuntu:22.04
stage: test
before_script:
- export DEBIAN_FRONTEND=noninteractive
- apt-get update
- apt-get install -y build-essential gcovr gcc-11 g++-11
script:
- g++ sample.cpp -o sample -fprofile-arcs -ftest-coverage -fPIC
- ./sample ${INPUT}
- mkdir -p public
- gcovr -v -r . --json ${OUTPUT}
- cp *.json public/
artifacts:
paths:
- public
sample-test-1:
extends:
- .sample-test
variables:
INPUT: 1
OUTPUT: coverage-1.json
sample-test-2:
extends:
- .sample-test
variables:
INPUT: 2
OUTPUT: coverage-2.json
pages:
image: ubuntu:22.04
stage: deploy
needs:
- sample-test-1
- sample-test-2
before_script:
- export DEBIAN_FRONTEND=noninteractive
- apt-get update
- apt-get install -y gcovr
script:
- cd public
- gcovr -v --add-tracefile "coverage-*.json" -r ../. --html-details -o index.html
- gcovr -v --add-tracefile "coverage-*.json" -r ../. --xml-pretty --exclude-unreachable-branches --print-summary -o coverage.xml
coverage: /^\s*lines:\s*\d+.\d+\%/
artifacts:
reports:
junit: public/coverage.xml
coverage_report:
coverage_format: cobertura
path: public/coverage.xml
paths:
- public
この場合、gcovrの結果は次のようになります。
ちゃんとバッジも100%になって気持ちが良いですね。
最後に
一旦寝惚けて記事の半分ぐらいが消えて書き直しました。悲しいです。