フレーキーなテスト、タイムアウト、最小化
1 回の実行で読み取った kill が常に kill とは限らない。Shi, Bell and Marinov (ISSTA 2019) は、 同一の再実行の間でミューテーションスコアが 4 ポイント変動し、ミュータント × テストの組の 9% が 不安定であることを測定した。テストごとの kill 行列はスコアよりも影響を受けやすい。フレーキーな kill が 1 つあれば、あるテストを essential と判定したり、別のテストを redundant と判定したりするのに 十分である。2 つのフラグが再実行によって確信度を高める。
run -confirm-kills N は、ミュータントを kill したテストを、それぞれが N 回失敗するまで再実行する。
再実行で再現しない失敗は killed_by ではなく suspicious_by に記録される (mutmut の SUSPICIOUS)。
したがってそれは kill でも要件でもない。ミュータントを kill したテストがすべて suspicious になると、
そのミュータントは LIVED になる。再実行するのは kill したテストだけなので、コストはスイートではなく
kill の数に比例する。
run -confirm-baseline N は、トレース時に各テストを 1 回ではなく N 回実行する。そのうち一部の実行で
失敗し他で成功したテストは、実行を失敗させる代わりに tests[] で "flaky": true とマークされる。
毎回失敗するテストは、失敗するベースラインが常にそうであるように、依然としてエラーである。
フレーキーなテストのミュータント下での失敗は決して kill に数えず、minimize はそのテストを行列から
完全に除外する。選択されることも redundant とされることもなく、代わりに flaky_tests に列挙される。
その観測はどちらの判定の根拠にもならないからである。totals.suspicious は未確認の kill を 1 つ以上
持つミュータントの数であり、実行のどれだけを疑うべきかを示す。
go run ./cmd/mutrim run -test-bin pkg.test -mutants mutants.json \ -confirm-baseline 3 -confirm-kills 3 -out report.json各ミュータントのタイムアウトは、それに到達するテストに従う。-timeout-factor (3) × それらの
トレースされた所要時間 + -timeout-const (2s) で、下限は -min-timeout (10s)、上限は
-timeout-factor × ベースライン実行である。したがって、1 つの速いテストが到達するホットな関数で
無限ループするミュータントは、スイート全体分よりずっと早く打ち切られる。各結果は timeout_ms を
記録する。-timeout は代わりにすべてのミュータントに 1 つのタイムアウトを設定する。ループする
ミュータントはすべて下限まで待つので、速いユニットテストの実行ではこれが支配的になる。
-min-timeout 1s にすると、mutrim 自身の minimize パッケージは同じ判定のまま 41s ではなく 5s で
実行できた。プロセスを起動したりネットワークに触れたりするテストでは、最悪ケースがトレースされた時間
から大きく外れるので、デフォルトのままにしておく。
トレースとミュータントの実行は -jobs 個のテストプロセスを同時に実行する (デフォルトは GOMAXPROCS)。
実行の終了順にかかわらず、結果は mutants.json の順序を保つ。
minimize はその行列を構成し (到達したサイトの重み 1、kill の重み 5、テスト時間 1 ミリ秒あたり。
-w-site / -w-kill)、貪欲な集合被覆を実行する。kill の要件は支配ミュータント
(dominator mutant) だけである (Ammann–Delamaro–Offutt; Kurtz et al.)。あるミュータントを kill する
テストがすべて別のミュータントも kill するなら後者は包含 (subsume) されて除かれ、kill するテストが
同じミュータントは 1 つの要件にまとめられるので、1 つの if の変種をすべて kill するテストが過大に
評価されない。totals は kill された (killed) ミュータント、そのうちの dominators、生き残った
(survived) ミュータントの数と dominator_score = dominators / (dominators + survived) を示す。その後、他の選択済みテストの組み合わせで要件が満たされる
選択済みテストをすべて取り除く。selected は保持されたテストをゲインとともに、essential なものから
順に列挙する。スイート全体で他のどのテストも満たさない要件を満たすテストは essential であり
(Harrold–Gupta–Soffa; Chen & Lau)、それらの要件のラベル (unique) が付く。したがってリストは上から
「必ず保持」→「当面は保持」と読める。redundant は残りのテストを、それぞれを包含する選択済みテストと、
その要件を共有する他のテストの数 (shared_with。1 なら削除 1 回で essential になる) とともに列挙し、
weak_spots (-mutants 指定時) はミュータントが生き残る関数を列挙する。-keep
(デフォルト ^TestRegression_) に一致するテスト、または doc コメントで //mutrim:keep タグが
付いたテスト (-srcs は走査する _test.go ファイルまたはディレクトリを指定する) は常に保持される。
-keep はサブテスト行の完全な TestX/case 名を見て、親のタグはそのすべてのサブテストを保持する。
修飾された行では、どちらもパッケージ内の名前に一致する。-matrix は構成したテスト × 要件行列を
厳密ソルバー向けに JSON でエクスポートし、-raw-matrix を付けると支配ミュータントではなく kill された
すべてのミュータントを含める。1 つのパッケージのシャードはまとめて渡せ、複数の
パッケージのレポートもまとめて渡せる。そのとき修飾されていない各テスト名はレポートのパッケージで
修飾されるので、テストはどこに現れても 1 つの行になる。-extra-test で pkg のミュータントに対して
実行され、かつ app 自身のミュータントに対しても実行された app のテストは、両方のサイトと kill を
持つ。