GitLab CIを使ってcoverage testの結果を公開しよう！ ~C++とgcovr~

# この記事は何

C++ のコードの gcovr を使った coverage test を gitlab CI で自動実行して、結果を gitlab pages で公開するまでの流れ
(2022/11/14 現在)



# 初めに

みなさん、テストちゃんと書いてますか？書いてます？
でもどこまでテスト書いたか不安になりません？なりますよね？
そんなあなたにオススメなのが、coverage test！
これを使えばあなたのコードがどの程度テスト済みかが簡単に分かっちゃいます！
更に、その情報をメインページでお知らせしてあげれば、あなたのコードの安全性を皆さんにお知らせすることだってできちゃうんです！
でも一々テストをするのがめんどくさい？
大丈夫です！GitLab CIを使って全部自動で済ませちゃいましょう！
そんな便利な機能が今ならタダ！これはもう使うしかないでしょう！
（深夜テンション）


いつの間にかgitlabの仕様が変わっていた恨みを込めて

# 0. 対象コード

簡単のため

```c++ sample.cpp
#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`ファイルを書きます。

```yml .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のページを見てください。

https://docs.gitlab.com/ee/ci/yaml/

`sample.cpp`と一緒にgitlabにpushします。CI/CD->JobsからCIの実行状況が確認できます。成功していると、Statusの欄がpassedになり、そこをクリックして飛んだ先に

![スクリーンショット 2022-11-14 065747.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328786/a4b8e34a-f1d9-422a-5187-c1a48e1855a8.png)

のように表示されるはずです。

# 2. gcovr で coverage test をしよう

gcovrを使ってc++のコードのcoverage testをしてみます。gcovrはapt-getで落として来れるので便利です。正確にはgcovというcoverage testを行うツールの結果をまとめるのがgcovrのお仕事なのですが、gcovrのコマンドを打つとgcovも自動実行されます。ただし、gcovはgccと一緒に落ちてくるツールでgcovrとは異なるため、バージョンが違うと痛い目を見ることがあります（見ました）。気を付けましょう。

```yml .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 gcovr gcc-11 g++-11
  script:
    - g++ sample.cpp -o sample -fprofile-arcs -ftest-coverage -fPIC
    - ./sample 1
    - gcovr -v -r .
```

コンパイル時にコマンドを追加し、プログラム実行後にgcovrを実行します。詳しくはgcovrのサイトを見てください。

https://gcovr.com/en/stable/index.html

正しく実行されると、CIの結果が次のようになります。

![スクリーンショット 2022-11-14 070129.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328786/c8a49976-8c00-4b17-2ea1-d9082c31c111.png)

`sample.cpp`のelseの中身が実行されないので、coverが100%にはならず、80%になっています。

# 3. 結果をgitlab pagesで表示しよう

コンソール上の結果だけだと分かりにくいので、htmlで分かりやすく表示するために、次のように`.gitlab-ci.yml`を変更します。（このテストだけだとjson出力を介する必要はありませんが、後のために一旦jsonで出力しています。）

```yml .gitlab-ci.yml
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の結果を見ることが出来ます。

![スクリーンショット 2022-11-14 070848.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328786/55230abe-d8da-569f-06e4-6bef304a3834.png)

sample.cppをクリックすると、このコード内のより分かりやすい結果を見ることが出来ます。

![スクリーンショット 2022-11-14 070909.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328786/2f79cb86-5a94-b32b-1b6c-2adcc55f4485.png)

このように、else内のテストが流れていないことが一目で分かるようになります。

# 4. gitlabのホームにcoverageのバッジを付けよう

ページを訪れた人にどの程度coverage testが行われているかを知らせるために、coverageのバッジを付けます。

```yml .gitlab-ci.yml
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以外のツールを使った場合の書き方を見ることが出来ます。

https://docs.gitlab.com/ee/ci/testing/test_coverage_visualization.html

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のところにバッジが表示されます。

![スクリーンショット 2022-11-14 072257.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328786/d308a7c6-6732-ec81-868b-5a070aebe3f7.png)

クリックすると先ほどのサイトに飛べることも確認しておくとよいでしょう。
Add badgeを押すとメインページにバッジが付きます！

# 5. 別々のCIのテスト結果をまとめよう

さて、別々のCIでテストを実行して、その結果をまとめることを考えます。
例えばdockerコンテナ依存の条件分岐がある場合は、それぞれ対応するコンテナを使っているCIを通すでしょう。
その場合、それぞれの結果をまとめて出力することが出来ます。
次の例では、入力1の結果をcoverage-1.jsonに、入力2の結果をcoverage-2.jsonに出力し、それらをまとめてcoverage testを行っています。

```yml .gitlab-ci.yml
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の結果は次のようになります。

![スクリーンショット 2022-11-14 073321.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328786/7d96456e-9a32-156b-979f-49a629e949fa.png)

ちゃんとバッジも100%になって気持ちが良いですね。

![スクリーンショット 2022-11-14 073340.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328786/e99b4ba2-c4d2-9088-744d-493a04740ced.png)


# 最後に

一旦寝惚けて記事の半分ぐらいが消えて書き直しました。悲しいです。