kernel-grammar-parity.test.ts 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. /**
  2. * Grammar-source parity gate (R1, migration plan §3.5).
  3. *
  4. * The native kernel compiles grammars from crates.io / vendored sources; the
  5. * wasm fallback loads grammars from tree-sitter-wasms / src/extraction/wasm.
  6. * If the two are built from different grammar revisions, a language's graph
  7. * would depend on WHICH path extracted it — per-language routing (and the
  8. * kernel-absent fallback) must be graph-neutral.
  9. *
  10. * Rather than trusting version metadata, this asserts the grammars are
  11. * behaviorally identical where extraction can observe them: ABI version and
  12. * the full node-kind and field tables, compared id by id.
  13. *
  14. * Runs wherever a kernel binary is staged (scripts/build-kernel.sh); skips
  15. * otherwise. CI that builds the kernel sets CODEGRAPH_KERNEL_EXPECT=1 so the
  16. * skip can't mask a missing build (asserted in kernel-scaffold.test.ts).
  17. */
  18. import { describe, it, expect, beforeAll } from 'vitest';
  19. import * as fs from 'fs';
  20. import * as path from 'path';
  21. import type { Language as WasmLanguage } from 'web-tree-sitter';
  22. import { getKernel, resetKernelForTests } from '../src/extraction/kernel';
  23. import { initGrammars, loadGrammarsForLanguages, getParser } from '../src/extraction/grammars';
  24. import type { Language } from '../src/types';
  25. const KERNEL_PATH = path.join(
  26. __dirname,
  27. '..',
  28. 'codegraph-kernel',
  29. 'prebuilds',
  30. `${process.platform}-${process.arch}`,
  31. 'codegraph-kernel.node'
  32. );
  33. const kernelBuilt = fs.existsSync(KERNEL_PATH);
  34. // Every kernel-capable language. `jsx` shares the javascript grammar on BOTH
  35. // paths (langs.rs mirrors WASM_GRAMMAR_FILES), so the distinct grammars are:
  36. const GRAMMAR_LANGUAGES: Language[] = ['typescript', 'tsx', 'javascript', 'java', 'python', 'go', 'c', 'cpp', 'rust', 'csharp', 'ruby', 'php', 'swift', 'kotlin'];
  37. describe.skipIf(!kernelBuilt)('kernel↔wasm grammar parity', () => {
  38. beforeAll(async () => {
  39. resetKernelForTests();
  40. await initGrammars();
  41. await loadGrammarsForLanguages(GRAMMAR_LANGUAGES);
  42. });
  43. it.each(GRAMMAR_LANGUAGES)('%s: node-kind and field tables are identical', (language) => {
  44. const kernel = getKernel();
  45. expect(kernel).not.toBeNull();
  46. const native = kernel!.grammarInfo(language);
  47. expect(native, `kernel has no grammar for ${language}`).not.toBeNull();
  48. const wasmLang = getParser(language)?.language as WasmLanguage | null | undefined;
  49. expect(wasmLang, `wasm grammar for ${language} not loaded`).toBeTruthy();
  50. expect(native!.abiVersion, 'grammar ABI version').toBe(wasmLang!.abiVersion);
  51. expect(native!.nodeKindCount, 'node-kind count').toBe(wasmLang!.nodeTypeCount);
  52. expect(native!.fieldCount, 'field count').toBe(wasmLang!.fieldCount);
  53. const wasmKinds: (string | null)[] = [];
  54. for (let i = 0; i < wasmLang!.nodeTypeCount; i++) wasmKinds.push(wasmLang!.nodeTypeForId(i));
  55. expect(native!.nodeKinds).toEqual(wasmKinds.map((k) => k ?? ''));
  56. // Field ids are 1-based on both sides.
  57. const wasmFields: (string | null)[] = [];
  58. for (let i = 1; i <= wasmLang!.fieldCount; i++) wasmFields.push(wasmLang!.fieldNameForId(i));
  59. expect(native!.fieldNames).toEqual(wasmFields.map((f) => f ?? ''));
  60. });
  61. });