Skip to content

テスト基盤

INFO

テスト基盤の改善アイデアを共有するための記事です。ご意見は Discord までどうぞ。

Oxc では正しさと信頼性を非常に重視しています。

下流ツールに問題が波及しないよう、テスト基盤の強化に多くの時間を使っています。

Parser(パーサ)

適合性テスト

Test262BabelTypeScript のパーサテストで、JavaScript・TypeScript・JSX 構文を検証します。

Test262 では stage 4 と正規表現テストをすべて含めています。

適合結果はスナップショットに保存し、変更を追跡します。

構文エラーもこれらのスナップショットに書き出し、差分で変更を確認します。

ファジング

ランダムデータでパーサがパニックしないように、三種類のファザーを使います。

  1. cargo fuzzランダムバイトをパーサへ送る
  2. bakkot 氏の shift-fuzzer-js で、ランダムだが妥当な AST を生成
  3. qarmin 氏の Automated-Fuzzer がクラッシュを積極的に報告

メモリ安全性

Oxc は AST などのメモリ確保に bumpalo ベースのアリーナアロケータを使います。AST ノード型に Drop 実装はありません。

Oxc のアロケータがコンパイル時に強制し、アリーナ上に Drop 型を割り当てようとするとコンパイルエラーになります。ヒープ所有データを持つ型をアリーナに置けないことで、静的にメモリリークを防ぎます。

unsafe コード

パフォーマンスのために unsafe を使います。unsafe は自己完結したデータ構造内に閉じ込め、外からは安全な API を保つことを目指します。該当クレートには毎 PR で Miri を実行しています。

Linter(リンター)

診断のスナップショット

すべてのリント診断をスナップショットファイルに書き、劣化と照合します。

例:

javascript
 ⚠ typescript-eslint(adjacent-overload-signatures): All "foo" signatures should be adjacent.
  ╭─[adjacent_overload_signatures.tsx:3:18]
2function foo(s: string);
3function foo(n: number);
  ·                  ───
4type bar = number;
5function foo(sn: string | number) {}
  ·                  ───
6 │       }
  ╰────

エコシステム CI

oxc-ecosystem-ci が大規模リポジトリに対して oxlint を走らせ、偽陽性・劣化・パニックを検出します。対象の例:

冪等性

すべてのツールについて、統合テストと E2E で冪等性テストを使います。

手順は次のとおりです。

javascript
let sourceText = "foo";
let printed = tool(sourceText);
let printed2 = tool(printed);
assert(printed == printed2);

例えばコードの冪等なミニファイは、同じ結果になるべきです。

パーサ、トランスフォーマ、ミニファイなどすべてのツールが、Test262・Babel・TypeScript のテストファイルで冪等性検証されます。

統合テスト

単体テストより統合テストを優先します。

codecov が現状 Code Coverage の行カバレッジを報告しています。

エンドツーエンド

monitor-oxc リポジトリが、npm-high-impact の上位 3000 npm パッケージに対して E2E テストを行います。

package.json に 3000 件の依存があります。

json
"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

javascript
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 までご連絡ください。