テスト基盤
INFO
テスト基盤の改善アイデアを共有するための記事です。ご意見は Discord までどうぞ。
Oxc では正しさと信頼性を非常に重視しています。
下流ツールに問題が波及しないよう、テスト基盤の強化に多くの時間を使っています。
Parser(パーサ)
適合性テスト
Test262、Babel、TypeScript のパーサテストで、JavaScript・TypeScript・JSX 構文を検証します。
Test262 では stage 4 と正規表現テストをすべて含めています。
適合結果はスナップショットに保存し、変更を追跡します。
構文エラーもこれらのスナップショットに書き出し、差分で変更を確認します。
ファジング
ランダムデータでパーサがパニックしないように、三種類のファザーを使います。
- cargo fuzz でランダムバイトをパーサへ送る
- bakkot 氏の shift-fuzzer-js で、ランダムだが妥当な AST を生成
- qarmin 氏の Automated-Fuzzer がクラッシュを積極的に報告
メモリ安全性
Oxc は AST などのメモリ確保に bumpalo ベースのアリーナアロケータを使います。AST ノード型に Drop 実装はありません。
Oxc のアロケータがコンパイル時に強制し、アリーナ上に Drop 型を割り当てようとするとコンパイルエラーになります。ヒープ所有データを持つ型をアリーナに置けないことで、静的にメモリリークを防ぎます。
unsafe コード
パフォーマンスのために unsafe を使います。unsafe は自己完結したデータ構造内に閉じ込め、外からは安全な API を保つことを目指します。該当クレートには毎 PR で Miri を実行しています。
Linter(リンター)
診断のスナップショット
すべてのリント診断をスナップショットファイルに書き、劣化と照合します。
例:
⚠ typescript-eslint(adjacent-overload-signatures): All "foo" signatures should be adjacent.
╭─[adjacent_overload_signatures.tsx:3:18]
2 │ function foo(s: string);
3 │ function foo(n: number);
· ───
4 │ type bar = number;
5 │ function foo(sn: string | number) {}
· ───
6 │ }
╰────エコシステム CI
oxc-ecosystem-ci が大規模リポジトリに対して oxlint を走らせ、偽陽性・劣化・パニックを検出します。対象の例:
- rolldown/rolldown
- napi-rs/napi-rs
- toeverything/affine
- preactjs/preact
- microsoft/vscode
- bbc/simorgh
- elastic/kibana
- DefinitelyTyped/DefinitelyTyped
冪等性
すべてのツールについて、統合テストと E2E で冪等性テストを使います。
手順は次のとおりです。
let sourceText = "foo";
let printed = tool(sourceText);
let printed2 = tool(printed);
assert(printed == printed2);例えばコードの冪等なミニファイは、同じ結果になるべきです。
パーサ、トランスフォーマ、ミニファイなどすべてのツールが、Test262・Babel・TypeScript のテストファイルで冪等性検証されます。
統合テスト
単体テストより統合テストを優先します。
codecov が現状 の行カバレッジを報告しています。
エンドツーエンド
monitor-oxc リポジトリが、npm-high-impact の上位 3000 npm パッケージに対して E2E テストを行います。
package.json に 3000 件の依存があります。
"devDependencies": {
"@aashutoshrathi/word-wrap": "latest",
"@actions/http-client": "latest",
"@adobe/css-tools": "latest",
"@alloc/quick-lru": "latest",
...
"zip-stream": "latest",
"zod": "latest",
"zone.js": "latest",
"zustand": "latest"
}各パッケージをインポートして検証するテストファイル:
src/dynamic.test.mjs
import test from "node:test";
import assert from "node:assert";
test("@aashutoshrathi/word-wrap", () => import("@aashutoshrathi/word-wrap").then(assert.ok));
test("@actions/http-client", () => import("@actions/http-client").then(assert.ok));
test("@adobe/css-tools", () => import("@adobe/css-tools").then(assert.ok));
test("@alloc/quick-lru", () => import("@alloc/quick-lru").then(assert.ok));
...
test("zod", () => import("zod").then(assert.ok));
test("zone.js", () => import("zone.js").then(assert.ok));
test("zustand", () => import("zustand").then(assert.ok));
test("zwitch", () => import("zwitch").then(assert.ok));コード生成、トランスフォーマ、ミニファイなど各ツールが node_modules 内のファイルを書き換えたあとにこのテストを実行します。
パッケージは毎日最新版に更新されます。
この構成で、適合テストスイートが見逃していた珍しいバグを多数捕捉してきました。
テスト基盤の改善アイデアがあれば、Discord までご連絡ください。