テスト実行に時間がかかった順(遅かった順)で表示するsbt pluginを作った

以下のsbt plugin作った話をします。

github.com

数日前のScalaMatsuri 2024の自分の発表の中で以下のような記述がありましたが

https://speakerdeck.com/xuwei_k/scalapuroziekutono-birudosu-du-gai-shan?slide=67

個々のtest時間を集計してみる

  • 例えばscalatestだと -oD のようなオプションがあります
  • それをさらにいい感じに集計しないと・・・
  • これだけでボトルネックわかるか?というと

ここに関して、真面目にしっかり計測というか集計したことがありませんでした。

正確には、sbtに関連する仕組みが存在しそうだったので、昔試したことがあったのですが、うまくいかなくてやめたことはありました。

https://github.com/sbt/sbt/blob/586e0a752cd5d0f0335deaa910c4355e0e0a0e56/main/src/main/scala/sbt/Keys.scala#L326

testListeners というkeyなど?

結論からいうと、sbt自体かscalatest側がバグっている気がする(おそらくsbt?)(forkしてる場合のみ?)のですが、まだ詳細追ってません。

sbt本体にもbug報告はしてないし、これ書いてる時点でかるく調べた限りは、他の人によるbug報告も存在しなそうです。

sbtのものは、テストが全部終わってからstartのeventなどが呼ばれて、実行時間が正しく取れない気がします。 sbtが吐き出すjunit reportにも実行時間があったはずですが、そちらも同様の問題があった気がします(うろ覚え)

実は他の手法でsbt単体で取得可能な仕組みが存在していたら、教えてください。

さて、他の方の 飛び入りカンファレンス で、以下のような発表がありました。

github.com

cobalt-lupin-e48.notion.site

OpenTelemetryの存在は知っていますが、それほど詳しくないのと、そもそも個人的には

「OpenTelemetry使うほど大掛かりでなくていいので、とりあえず遅い順に表示するだけとか、毎回のテストごとの変化は全自動で計測できなくてもいいから、1回のテスト実行で、相対的にどれが遅いのか?だけ知りたい」

と思ったので、OpenTelemetryの方ですごくしっかりやれば、そういう機能を含めて上位互換で綺麗に作れる可能性は普通にあるかもしれませんが、 一旦アイデアというか、scalatestのReporterの使い方だけ少し参考にさせてもらい、完全独自に作ってpublishしてみました。

実際のメインのファイルはreporterとsbt pluginのそれぞれ以下のファイルのみ

以下、作った際の感想やポイントや解説です

  • Chrome Traceか、GitHubで使えるmermaidのガントチャートで例のアレと同じように表示する機能も作っても良かったかもしれないが、一旦本当に最低限テキストで上位から表示する機能だけでリリースした
  • このblog書いた時点の対応versionなど
    • Java 8以上
    • Scalaは2.12と2.13と3
      • scalatestのreporterを使っている都合上、reporterはScalaでcross buildする必要がある
      • 2.11以前はさすがに最初からやめた
      • 動的に毎回sbt plugin側でsourceGeneratorsでコード生成、という手法もあり得なくもないが・・・一旦そこまで頑張る必要性を感じなかったので
    • scalatestのreporterを使ってる都合上、現状では他のテストライブラリでは無理
      • 気が向いたら?他のテストライブラリにも同じような仕組みがあるなら?対応するかも
  • sbtとは別にscalatestそのものにもjunit reportを出す仕組みがあって、もしかしたらそちらのxmlを独自にparseして集計をすれば、独自reporterは必要ない可能性はあるが、junit report若干size大きいし、細かいこと考えると独自に作ってしまった方が柔軟というか、大した手間ではない?ので作ってしまった
    • xmlを独自にparseするコード書くのも、そこまで手間ではないとはいえ、結局書かないといけないし
  • multi projectで全てを集計したい、reporterの依存をいい感じに自動で設定したい、などを考えると、それほど大したことはしてないですが、一応sbt pluginという形にしました
    • OpenTelemetryでやる場合は、そちらで集計その他全部やってくれるでしょうけど(よくわかってない)、これは単純にsub projectごとにファイルに出して、その後にsbt pluginのtaskで集計、ということ
  • scalatestの依存がユーザー側で追加されているどうか?の判断が、すごく無理やりになってしまったけど、もっといい案ないですか?
  • taskひとつ実行するだけで、最近blogで何度か書いてるように GITHUB_STEP_SUMMARY に直接書き込む機能もつけました
  • 単体で別ファイルに書き出す機能もあるので、GitHub Actions以外の任意のCIやローカルでも使用は可能です
    • ただし、出力の形式や、情報の種類がかなり限定的で固定ですが・・・(今後、どの程度柔軟に拡張するか?の詳細は未定)