Skip to content

Testing ​

推論の品質は user-facing な入力と出力で固定し、実装の細部は必要な範囲だけ unit / integration test で守る。テストの説明は現在のルールだけを置き、追加履歴は残さない。

テスト層 ​

層守るもの
scenarioRuby / RBS / RBI / project fixture から期待 RBS への user-facing 契約
unit型演算、名前解決、merge、diagnostic 判定、上限などの局所不変条件
CLI integration出力形式、file selection、diagnostics、debug、coverage option
LSP integrationprotocol、snapshot、cache、refresh、incremental change
MCP integration実バイナリの起動、stdio protocol、tool discovery / call、推論結果
robustness壊れた入力での panic / hang がないこと
performance速度・メモリの基準値と大規模 workspace の有界性

通常のゲート ​

bash
./scripts/check.sh
git diff --check

check.sh はテストターゲット(lib / bins / 各 integration test / doc)を個別に実行し、 途中で失敗してもすべてのターゲットを走らせたうえで失敗一覧を末尾にまとめて表示する (fail-fast で後続ターゲットの失敗が隠れないようにするため)。

tests/mcp.rs は tyda mcp を子プロセスとして起動し、MCP client で stdio handshake、tool 一覧、 infer_type_at_position の呼び出しと構造化された推論結果を確認する。GitHub Actions の integration shard で実行する。

通常の Ruby scenario は analysis::analyze_source_for_display を入口とする完全解決の snapshot 経路を使う。この経路は LSP、playground、詳細 CLI の表示結果と共通であり、推論結果の差分を 入口ごとの実装で吸収しない。RBS / RBI / project fixture など、入力や解析 profile が異なる scenario はその条件を維持したうえで、同じ core の解析 backend を利用する。

通常の Ruby の表示 parity は analysis の unit test で tests/scenarios/ruby/ を in-process に走査し、 Playground の RBS / CodeLens / hover を canonical snapshot の projection と比較する。Rails / Sorbet / RBS / RBI / project fixture / 構文エラーを含むケースはこの対象から除外し、ブラウザや WASM を起動せずに 表示入口の差分を検出する。シナリオが増えたときも、テストはファイル単位で並列実行する。

入力途中の表示契約 ​

tests/incremental_hover.rs は代表的な class / module / constant / method / parameter / local / ivar / literal の入力について、完成形へ向かうすべての UTF-8 prefix を解析する。型がその時点で 確定する位置以降は期待する hover を厳密に比較し、入力中の未完成 token や value-corrupting syntax error は untyped または抑制を許容する。後続の壊れた method が、先に確定した領域の hover を壊さ ないことも固定する。

LSP の lsp_hover_tracks_incremental_content_updates は同じ入力列を didChange で差し替え、 各状態の hover が古いキャッシュを返さないことを確認する。Playground と LSP の token hover は 同じ精密な snapshot 型を表示する。未注釈 method の call-site 引数は、public callable signature と メソッド本体の parameter、initialize から伝播する ivar で基底型へ widen する。一方、#: / sig / 外部 RBS・RBI で明示された parameter 型は保持する。 長い型の省略は hover 本文の表示だけに適用し、推論結果と RBS 出力は変更しない。 全シナリオの mutation test は引き続き panic / hang のないことを確認する層であり、意味的な期待値は このような明示的な incremental contract で追加する。

対象を絞るときは次を使う。

bash
TYDA_SCENARIO_FILTER=<substring> cargo test -q --test scenario_runner -- --test-threads=1
cargo test --test <name>
cargo test <module>::<test>

GitHub Actions では Test、Performance、Analysis compatibility、pages、release-vscode、Workflow lint をPRゲートとする。required checkはmatrix個別jobではなく、常設の test、performance、analysis-compatibility、pages、release-vscode、actionlint gateとする。各gateは対象scopeでは依存する全jobのsuccessを要求し、対象外scopeでは分類成功と依存jobの全skipを確認した場合だけsuccessとする。Test はLinuxとWindowsの Rust build / clippy / test(Windowsも全test targetを実行し、unit、軽量integration、mutation、pathological、docのshardを独立したrunnerで並列実行し、windows gateでformat / clippyと併せて集約する)、Performance は pinned な Ruby / Rails OSS subject ごとの base/head 比較、release workflowはgem / VSIXのLinux x86_64・Windows x64・Intel macOS・ ARM macOS package smoke testも確認する。Linux ARM64はrunnerの利用条件が整い次第追加する。

pages / e2e-test は npm run format:check、npm run typecheck、npm run lint を実行してからVitePressのドキュメント、Playgroundのwasm buildとE2Eを行う。E2EはPages artifactを /tyda/ 配下で配信し、Top・Docs・Playgroundの遷移、編集内容のhash付きreload、ページ間およびPlayground内のBack/Forward、壊れたhashからの復帰を確認する。pages gateが e2e-test の結果をrequired checkとして集約する。

Analysis compatibility は base commit と head commitをそれぞれのGemfile.lockから生成したRBSと組み合わせ、sample・pinned OSS subjectのCLI RBS出力とdiagnosticsをbyte単位で比較する。coverageも同じ組み合わせで比較し、差分があるPRはデフォルトで失敗する。意図した推論・coverage変更はJob SummaryのdiffをレビューしたMaintainer/Adminだけが approved-analysis-change ラベルで許可できる。ラベルはPerformance matrixのcoverage差分にも適用するが、実行時間・メモリの回帰は許可しない。新しいcommitではラベルを自動削除するため、古い承認を再利用できない。

scenario の期待 RBS は whitespace を正規化して比較するが、意味のない出力変更を許容する ための仕組みではない。出力を変えたときは意図を scenario / CLI test に残す。

Scenario の形式 ​

1 ファイルは関連する 1 カテゴリ、1 case は 1 つの振る舞いにする。case 名は短い英語で書く。

markdown
# Ruby / method / example

## Infer a literal return

### update

```ruby
def answer = 42
```

### result

```rbs
def answer: -> 42
```

case heading の直後には optional な YAML config を置ける。

yaml
ruby_version: "3.3"
rails_version: "7.1"
include_synthetic_dsl_methods: false
known_issue: false

update section では次を使える。

block / marker用途
rubycurrent file。複数 block は project file として扱う
### file: path次の Ruby / SQL block の project path
rbs外部 RBS input
rbi外部 RBI input
routesconfig/routes.rb fixture
schema / sqlRails schema fixture
result の rbs期待する RBS

同じ case に複数の ### update を置く場合、各 update は累積差分ではなく、その時点の完全な入力状態を 表す。宣言を段階的に追加・削除するケースでは、各状態の期待 RBS を並べて優先順位と fallback を固定する。

project-backed case は config/、db/、app/models/ 以下の最小 fixture だけで構成する。 実在プロジェクトをそのまま fixture にせず、挙動が分かる最小コードへ縮める。

Scenario のカテゴリ ​

カテゴリ例
ruby/classnamespace、継承、mixin、singleton、visibility
ruby/controlnarrowing、branch、rescue、loop
ruby/literalcollection shape、補間、complexity cap
ruby/methodreturn、param、block、recursion、dispatch
ruby/runtimeThread、Fiber、Queue、runtime fact
ruby/variablelocal、ivar、constant、pattern scope
ruby/rbs_comment / rbs_inputinline / external RBS
sorbetcomment、sig、RBI、T::Struct
rails/DSL、routes、schema、framework plugin

対応範囲の要約は capability-matrix.md に置く。未対応の user-facing 制約だけを roadmap に書き、テスト一覧を roadmap の代わりにしない。

外部コードを素材にする方針 ​

外部プロジェクトやツールは、型結果との parity を測るためではなく、Ruby のコードパターンを 見つけるために使う。推論できるパターンは scenario にし、まだ扱えないパターンは安全な 縮退結果を確認するか、対応範囲として記録する。

解析対象の Ruby は必要性と信頼境界を見て扱う。挙動確認には必要な小さなコード片を隔離して実行する。 大規模 OSS はプログラムとして起動せず、静的解析・ドキュメント・最小再現例で調べる。性能計測では OSS を解析対象として扱う。

素材収集するパターン
Ruby / Prism構文、AST 境界、壊れた入力、version gate
Ruby LSPworkspace、document change、definition、completion
Tapioca / RBSgenerated RBI、schema、relation、宣言の合成
Steep / Sorbet / TypeProfcomment、signature、generic、LSP の入口
Rigor-type / Method-Ray / Type-Guessr実コードの型推定が難しい idiom
Redmine / Mastodon / GitLabRails DSL、巨大 workspace、in-tree extension

Redmine の runtime DSL 登録は対象外とし、必要なら RBS / RBI fixture で表す。 GitLab の static extension は tests/scenarios/rails/dsl/gitlab_presenter.md、 Redmine の縮退契約は tests/scenarios/rails/dsl/redmine.md に置く。

LSP と diagnostics ​

  • LSP test は protocol の形だけでなく、CLI と同じ query backend の結果を確認する。
  • didOpen / didChange 後の Hover、CodeLens、diagnostics が最新 snapshot を見ることを固定する。
  • workspace scan 完了前の誤診断抑制、refresh の coalesce、full / incremental change を確認する。
  • diagnostics は真陽性を先に固定し、Unknown を沈黙させる変更では false positive も確認する。

Robustness / performance ​

bash
cargo test --release --test pathological_inputs
cargo test --test mutation_robustness
./scripts/benchmark.sh <path> [runs]
./scripts/benchmark_lsp.sh [runs] [subject] [release|debug]

mutation test は壊れた入力の graceful degradation、pathological test は深い再帰・巨大 union・ 循環・自己参照 shape の有界性を確認する。基準値と計測ルールは performance.md に置く。

追加時のルール ​

  • まず最小 scenario を追加し、同じ入力を複数のテスト層で重複させない。
  • 期待 RBS は user-facing な契約だけを含め、内部用 synthetic method を無闇に表示しない。
  • まだ推論できない入力も panic / hang しない結果を固定する。
  • 正しい Ruby だが未対応の期待値は tests/scenarios/known-issues/ に置くか、case config に known_issue: true を付ける。最小の Ruby 例と Ruby semantics / RBS に基づく暫定期待値を残し、 推論が一致したら通常 scenario へ昇格する。
  • 大きな推論変更は最小例から始め、対象 scenario、全 suite、OSS subject、benchmark へ段階的に広げる。
  • flaky test は原因を再現・修正し、retry や skip で隠さない。
  • Ruby version / Rails version による意味の差だけを version case にする。
  • DSL は gem / framework ごとに分け、1 case に複数の無関係な機能を詰め込まない。
  • source 固有の名前は一般化し、API 名や DSL 名など意味に必要な固有名だけ残す。
  • scenario の意図が分からない自由文や作業履歴を追加しない。