トップ 最新 追記

日々の流転


2011-12-28 [長年日記]

λ. Haskell Program Coverage ツールキットを試してみる

この記事は、Haskell Advent Calendar 2011 の15日目の記事として書かれました。

色々書きたいことはあったはずなのだけど、いざ書こうと思うと、ちょうど良いネタはなかなかなく悩んでしまい、大幅に遅延してしまいました。 最初は、category-extrasの紹介でもしようかと思ったのだけれど、どうも圏論の知識をある程度前提にしないと面白い部分が書けなさそうだし。 悩んだんだけど、そういえば ICFP Programming Contest 2009 に参加した際 にカバレッジ機能を使ってみたくなったのを思い出し、GHCのカバレッジ測定機能について試して、簡単に紹介してみることに。

カバレッジとは

コードカバレッジ(Code Coverage)とは、プログラムのテスト時に使われる指標で、プログラムがどれだけ網羅的にテストされたのかの度合いを表すものです。 コードカバレッジには幾つか種類があり、メジャーなものでは、命令網羅(statement coverage)、分岐網羅(branch coverage)、決定網羅(decision coverage)、パス網羅(path coverage)といったものがあります。 例えば、命令網羅であれば、プログラムの全行数中、テストによって実行(カバー)された行数の割合を現してます。 コードカバレッジが低いということは、プログラムをちゃんとテストできていないということであり、コードカバレッジの高さはテストの品質の必要条件になります。

Haskell Program Coverage (HPC) ツールキット

gccにはgcovが付属していてカバレッジの測定ができますが、GHC にも Haskell Program Coverage (HPC) ツールキットというのが付属していて、GHCのドキュメントでも使い方が解説されてます。

今回はこれを参考にHPCを少しだけ試してみることにします。

ターゲット

今回は拙作のPTQパッケージで試してみます。 これは英語の平叙文を受け取って一種の論理式へと変換するツールで、様々な文法や変換規則を扱う部分がそれぞれあるため、色々な種類の入力を与えてみないとプログラムの全体は駆動できないはずです。

コンパイル

ドキュメントによると-fhpc オプションを指定して「ghc -fhpc Recip.hs --make」などとコンパイルすれば、カバレッジ測定機能を有効にしてコンパイルできるようだ。 そこで、tarballを展開して「cabal configure --ghc-options=-fhpc && cabal build」としてコンパイルしてみる。

コンパイルの様子は普通の場合と特に変わらないですが、.hpcディレクトリに*.mixファイルというのが沢山できてました。 ドキュメントによるとこれはHPCにとっての.hiファイルのようなものだそう。

% ls .hpc
CGI.mix         Main.mix        Report.mix      URLEncoding.mix
IL.mix          P.mix           ReportHTML.mix  Version.mix
MP.mix          Parser.mix      Translation.mix

実行

で、実行してみる。

% dist/build/ptq/ptq
PTQ version 0.0.5

Type :quit to quit

PTQ> Every man loves a woman such that she loves him.
Parsed:
  F10 0 (F2 every man) (F4 (He 0) (F5 love (F2 a (F3 1 woman (F4 (He 1) (F5 love (He 0)))))))
  : t
(中略)
Translation (MP applied):
  forall x0 : e. man x0 -> (exists x1 : e. woman x1 && love* x0 x1 && love* x1 x0)
  : t
(中略)
PTQ> :quit
% ls -l *.tix
-rw-r--r--  1 sakai  staff  7991 12 29 00:28 ptq.tix

ptq.tix というファイルが出来ていた。

レポート機能

測定できているはずなので、測定結果をもとにレポートを生成する機能を試してみる。

% hpc report ptq.tix 
hpc: can not find IL in ["./.hpc"]

あれれ、怒られてしまった。 ILというのは実際には使っていないモジュールだったので、そのせいかも……

気をとりなおして、モジュール名を指定して試す。

% hpc report ptq.tix Parser
 73% expressions used (858/1171)
 80% boolean coverage (4/5)
     100% guards (0/0)
      66% 'if' conditions (2/3), 1 always False
     100% qualifiers (2/2)
 52% alternatives used (32/61)
 85% local declarations used (17/20)
 89% top-level declarations used (105/117)
% hpc report ptq.tix Translation
 22% expressions used (143/623)
100% boolean coverage (0/0)
     100% guards (0/0)
     100% 'if' conditions (0/0)
     100% qualifiers (0/0)
 26% alternatives used (13/50)
 16% local declarations used (5/30)
 80% top-level declarations used (4/5)

なんか、 色々出てきたけれど、項目の意味は大体以下のようなものでしょうか。

expression used
ソース中のどれくらいの割合の式が評価されたかというもので、命令型言語での命令カバレッジに相当。
boolean coverage
ガード、if式の条件、リスト内包表記のガード(qualifier)が、真に評価される場合と、偽に評価される場合の両方が実行されたか。
alternatives used
複数の節からなる定義や、case式、if式などで各分岐が実行されたか。 古典的な命令型言語での分岐カバレッジ(branch coverage)に近い?
local declarations used
局所定義のどのくらいが使われたか。 古典的な命令型言語での関数カバレッジ(Function coverage)に近い?
top-level declarations used
トップレベル定義のどのくらいが使われたか。 古典的な命令型言語での関数カバレッジ(Function coverage)に近い?

1文を翻訳させただけなので、カバレッジは非常に低いのではないかと思っていたけど、パーサの方は案外高かったのでビックリ。 これは翻訳機の方は構文木にあう部分しか実行されないのに対して、パーサの方はマッチするかどうか色々な構文を試行しているためでしょうか。

マークアップ機能

hpc markup コマンドを使うと、表のHTMLと、ソースファイルをマークアップして色付きHTMLを作ってくれます。 ここでは hpc markup ptq Parser Translation としたところ、以下のファイルが生成されました。

表の情報はレポートと同じ。 ソースファイルのマークアップの方は、実行されなかった箇所が黄色でマークアップされ、条件式等でTrueにしか評価されなかった箇所は緑、Falseにしか評価されなった箇所は赤で表示されている。 これは結構分かりやすそうです。

まとめ

GHC にはHaskell Program Coverage (HPC) ツールキットが付属していて、コードカバレッジを非常に簡単に測れて、測定結果も見やすく表示することができます。

日本のHaskellコミュニティではHPCの事を聞いたことはこれまであまりなかったけれど、簡単に使えるので、「テストがちゃんと出来ているかどうか」の確認の一助に使ってみるとか、あるいは「プログラムをミスって実行されるべき箇所が実行されていない」なんてことが起こっていないか確かめるとかに使ってみると良いと思います。

ただし、HPCでサポートされているカバレッジは、命令型言語でいう命令カバレッジや分岐カバレッジに相当する緩い基準であり、高い信頼性が要求される分野で用いられているModified condition/decision coverage (MC/DC)といった基準に相当するものではありません。 なので、HPCでのカバレッジが高いからといって十分テストされていると過信するのは危険です。

あと、ちょっと気になったのは、HPCはGHCiでは使えないので、HUnitやQuickCheckをGHCi上で使う際にはカバレッジの測定ができないこと。 コンパイルして実行すれば良いのだけど、そうなるとテスト自動化のためのちゃんとした仕組みが欲しくなるね。

追記

shelarcyさんから以下の補足をもらいました。

リンク

Tags: haskell