Skip to content

テストインフラ

INFO

この記事は、テストインフラの改善に関するアイデアを共有する招待です。 Discord で気軽に連絡してください。

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

問題が下流のツールに影響を与えないようにするため、テストインフラの強化に多くの時間を割いています。

パーサー

コンプライアンス

Test262BabelTypeScript からのパーサーテストを使用して、JavaScript、TypeScript、JSX の構文を検証しています。

Test262については、ステージ4および正規表現のテストすべてが含まれています。

すべてのコンプライアンス結果は、変更の追跡のためにスナップショットファイルに保存されています:

すべての構文エラーは、これらのスナップショットファイルに記録され、差分の確認に利用されます。

ファズィング

ランダムなデータに遭遇してもパーサーがクラッシュしないことを保証するため、3つのファズァーを使用しています:

  1. cargo fuzz により、ランダムなバイト列 をパーサーに送信。
  2. bakkot による shift-fuzzer-js により、ランダムだが有効なASTを生成。
  3. qarmin による Automated-Fuzzer は、積極的にクラッシュを報告 しています。

メモリセキュリティ

Oxcは、bumpalo を基盤としたアリーナアロケータを、その抽象構文木(AST)および他のデータのメモリアロケータとして使用しています。アリーナ内のすべてのASTノード型には Drop の実装がありません。これは、アリーナに Drop を持つ型を割り当てようとするコードが存在するとコンパイル時にエラーを発生させるOxcのアロケータによって、コンパイル時に強制されます。これにより、ヒープに割り当てられたデータを所有する型がアリーナに格納されることを静的に行うことで、メモリリークを防止します。

安全でないコード

パフォーマンス最適化のために、Oxcは unsafe コードを使用しています。私たちの目標は、unsafe を自己完結したデータ構造内に限定し、外部から安全なAPIを提供することです。これらの構造を含むクレートに対して、毎回のプルリクエストで Miriが実行 されています。

ライナー

スナップショット診断

すべてのライナー診断は、レグレッションのテスト用に スナップショットファイル に書き込まれています。

例えば:

javascript
 ⚠ typescript-eslint(adjacent-overload-signatures): すべての "foo" シグネチャは隣接している必要があります。
  ╭─[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 を実行し、誤検出、レグレッション、クラッシュの有無を確認します。テスト対象となるリポジトリには以下のものがあります:

同値性(イデムポテンシー)

同値性テストは、すべてのツールにおける統合テストおよびエンドツーエンドテストに使用されます。

同値性テストは以下の手順に従います:

javascript
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パッケージに対してエンドツーエンドテストを実行しています。

その 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 で気軽に連絡してください。