Skip to content

test(compiler-cli): migrate compliance test expectations to full-file goldens - #71343

Open
JeanMeche wants to merge 1 commit into
angular:mainfrom
JeanMeche:test/full-compliance-goldens
Open

JeanMeche wants to merge 1 commit into
angular:mainfrom
JeanMeche:test/full-compliance-goldens

Conversation

@JeanMeche

@JeanMeche JeanMeche commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

Summary

Migrates the ngtsc compliance test suite from snippet-based golden files to full-file golden files and removes the duplicate normalizeJsForComparison helpers from packages/compiler-cli/preprocessor/test/compliance_spec.ts.

Changes

  • Full-file Golden Files: Expanded macro calls (__AttributeMarker, __SelectorFlags, __QueryFlags, __i18nMsg__) and $...$ identifier placeholders in compliance golden files to full-file ngtsc output, using … only where partial-linker output legitimately diverges (r3_view_compiler_deferred/deferred_with_duplicate_external_dep_template.js and r3_view_compiler_i18n/line_ending_normalization/*_template_non_legacy_*.js). Kept source_mapping/ golden files (// SOURCE: comments) intact.
  • Consolidated Expectations: Merged the 29 multi-snippet test cases in TEST_CASES.json (preserving all extraChecks) into single full-file expectations per generated .js file and removed the 78 redundant snippet files. Un-shared the 3 shared snippet files in r3_view_compiler_i18n/line_ending_normalization/TEST_CASES.json so each test case has its own full-file golden.
  • True Full-File & Explicitly Scoped expectEmit Assertions:
    • Updated expectEmit to anchor full-file goldens at both the start and end of the generated file (unless allowPartial is set for source_mapping snippets or … is used), sub-chunk large token arrays and string literals so full-file goldens stay within V8's RegExp size limit, and ignore // inside string/template literals in stripLineComments.
    • Explicitly scoped partial-linker normalizations (ɵsetClassDebugInfo / ɵɵsetNgModuleScope elision, empty class-element semicolon elision, and linked MSG_... constant names) behind options.isLinkedCompilation so full compile and local compile (ngtsc) assert the full unstripped output.
    • Explicitly scoped NGP emit normalizations behind isNgpTestMode() || options?.isNgpCompilation.
  • Consolidated NGP Emit Normalizations: Consolidated NGP-specific cosmetic normalizations into normalizeNgpEmitForComparison (packages/compiler-cli/src/ngtsc/testing/src/ngp_emit_normalizer.ts) using AST-scoped ɵsetClassMetadata normalization and dynamic @angular/core import inspection, and removed the duplicate normalizeJsForComparison helpers from packages/compiler-cli/preprocessor/test/compliance_spec.ts.

@angular-robot angular-robot Bot added the area: compiler Issues related to `ngc`, Angular's template compiler label Oct 14, 2026
@ngbot ngbot Bot added this to the Backlog milestone Oct 14, 2026
@JeanMeche
JeanMeche force-pushed the test/full-compliance-goldens branch 5 times, most recently from f0084fe to b5ce000 Compare October 16, 2026 06:02
@JeanMeche

Copy link
Copy Markdown
Member Author

Self-Review & Trade-offs of Full-File Compliance Goldens

Here is a critical self-review of this change, highlighting the design trade-offs, subtle edge cases, and areas for reviewer scrutiny.

Summary of Changes

  • Full-file goldens: Replaces partial snippet expectations (and macro calls like __AttributeMarker, __SelectorFlags, __QueryFlags, __i18nMsg__, and $...$ placeholders) with full-file .js goldens across 609 compliance test files.
  • Prettier formatting (printWidth: 100): Both ngtsc (check_expectations.ts), ngp (compliance_spec.ts), and the .js golden files are formatted with Prettier (parser: 'babel', printWidth: 100, singleQuote: false, trailingComma: 'none', arrowParens: 'avoid', quoteProps: 'preserve'). Source-map assertions (stripAndCheckMappings) still run against the raw unformatted compiler output before Prettier.
  • Full-file bounds: expectEmit now anchors at the start of the file (via sticky y regexes) and asserts no unexpected trailing content at EOF (except for // SOURCE: partial source-map files).
  • NGP metadata parity & normalizer cleanup: Aligns ngp (HybridCompiler) DI/metadata emission with ngtsc and removes the AST parenthesis normalizer and quote canonicalizer from ngp_emit_normalizer.ts.

Trade-offs & Challenges to Consider

1. Golden churn vs. full-file coverage

  • Downside: Snippet goldens previously isolated only the fragment under test (e.g. just consts or template). Full-file goldens include imports, class declarations, ɵfac, ɵcmp, ɵsetClassMetadata, and ɵsetClassDebugInfo. Any future change to boilerplate emission (e.g. changing ɵsetClassMetadata or ɵsetClassDebugInfo structure) will now touch hundreds of golden files instead of only the class_metadata tests.
  • Upside: Snippet goldens could silently miss regressions outside the snippet (duplicate imports, broken ɵfac, missing /*@__PURE__*/ comments, or stray statements). Prettier formatting keeps the full-file goldens readable and line-diff friendly.
  • Follow-up gap: Unlike GOLDEN_PARTIAL.js (which has bazel run //packages/compiler-cli/test/compliance/test_cases:<path>.golden), there is not yet an automated .golden update script/target for full-file .js goldens. Adding one in a follow-up would make future compiler-wide updates much easier.

2. Loss of symbolic enum macro names

  • Downside: Goldens previously used readable macros like __AttributeMarker.Bindings__, __SelectorFlags.NOT__, and __QueryFlags.descendants__. Full-file goldens now contain raw numeric values (e.g. [3, "someProp"]) and concrete variable names (_r1, i0).
  • Trade-off: Numeric flags are less self-documenting at a glance, and temp variable renumbering (_r1 -> _r2) will surface in golden diffs. On the other hand, the goldens now reflect the exact JavaScript that Angular ships and executes.

3. Running Prettier on generated output in checkExpectations

  • What it masks: Formatting generated with Prettier normalizes whitespace, semicolons, string quotes, trailing commas, and operator-precedence parentheses. If ngtsc ever emitted malformed whitespace or extra parens, Prettier would normalize them away (though expectEmit's tokenizer already ignored whitespace).
  • Silent parse-error fallback: Both check_expectations.ts and compliance_spec.ts wrap prettier.format in try { ... } catch { /* keep raw */ }. If a compiler bug ever emitted syntactically invalid JS, Prettier would silently fall back to raw output and fail later in expectEmit as a token mismatch rather than reporting a syntax error directly.
  • Test runtime: Formatting every compliance test output with Prettier adds Babel parse/print work to each test case, though sharded Bazel test times remain fast (~7s for local, ~27s for linked, ~53s for full).

4. expectEmit regex chunking (MAX_CHUNK_PIECES = 500)

  • Why: V8 has a maximum RegExp size limit, which full-file goldens exceed when compiled into a single regex. expectEmit now splits token streams into chunks of 500 tokens (and string literals > 4000 chars), matching contiguous chunks with sticky (y) regexes.
  • Edge case: Within a chunk, $...$ identifier placeholders use regex backreferences; across chunk boundaries, they are substituted from extractedGroups. If an … skip ever matched ambiguously at the start of chunk N and only failed in chunk N + 1, sticky matching would not backtrack across the 500-token chunk boundary (in practice, 500 tokens of context after … is always unambiguous).

5. Partial linker (isLinkedCompilation) normalizations

  • Why: The linked compliance suite now checks against the same full-file .js goldens as full compile. However, partial declaration linking intentionally omits ɵsetClassDebugInfo and ɵɵsetNgModuleScope, and names Closure i18n constants from the linked .js filename (MSG_..._JS_0) rather than the original .ts path + external hash.
  • Fragility: expectEmit strips ɵsetClassDebugInfo and ɵɵsetNgModuleScope IIFEs from expected via regex when isLinkedCompilation is true, and linked_compile_spec.ts normalizes Babel's this && this.__decorate helper emit. If the shape of ɵsetClassDebugInfo or ɵɵsetNgModuleScope changes, those stripping regexes in expectEmit will need to be updated in lockstep.

6. complianceMode in ngp (buildDeps & buildCtorParametersExpr)

  • What it does: In compiler-utils.ts and processor.ts, imported DI tokens from non-@angular/core modules emit namespace-qualified i1.SomeService when complianceMode is true (matching ngtsc), whereas default ngp emit (complianceMode = false) keeps the bare local identifier SomeService to avoid unused-import diagnostics on preserved user imports in .ng.ts output.
  • Caveat: While SomeService and i1.SomeService are equivalent at runtime inside lazy ɵfac / ctorParameters: () => [...] closures, gating this on complianceMode means compliance tests exercise slightly different import-reference codegen than ngp's default .ng.ts mode.

7. Regex check for explicit descendants on contentChild signal queries

  • Location: buildPropDecoratorsExpr in packages/compiler-cli/preprocessor/src/processor.ts checks /\bdescendants\s*:/.test(s.original.slice(query.propertySpan.start, query.propertySpan.end)) so contentChild signal queries only emit descendants: true in ɵsetClassMetadata when descendants was explicitly written in the options object (matching ngtsc).
  • Caveat: Scanning the raw source slice with a regex could false-positive if "descendants:" appeared inside a comment or string literal inside the contentChild(...) initializer. Ideally, ng-analyze (Rust) should record hasExplicitDescendants on QueryMetadata from the AST.

8. Remaining regexes in ngp_emit_normalizer.ts

  • Prettier eliminated the 190-line TS AST paren stripper and quote canonicalizer (ngp_emit_normalizer.ts is down from 343 lines to 150 lines), but a small set of regex normalizations remains for known ngp + ts.transpileModule differences:
    • Pruning unused named imports retained by ngtsc only because emitDecoratorMetadata runs before Angular decorators are stripped.
    • Stripping TS 5.0+ decorator class alias temporaries (static { Class_1 = this; }).
    • Normalizing { ...{ descendants: true }, isSignal: true } vs { isSignal: true, descendants: true } on decorator queries with isSignal: true.
    • Normalizing { type: Pipe, args: [null] } vs { type: Pipe } for @Pipe(null!) where ng-analyze drops non-object decorator arguments.

@JeanMeche
JeanMeche force-pushed the test/full-compliance-goldens branch from b5ce000 to 90a2aea Compare October 16, 2026 06:17
@JeanMeche
JeanMeche requested a review from mattrbeck October 16, 2026 17:52
@JeanMeche
JeanMeche marked this pull request as ready for review October 16, 2026 17:52
@JeanMeche
JeanMeche force-pushed the test/full-compliance-goldens branch 2 times, most recently from 4281f6b to 344a10d Compare October 17, 2026 15:27
@JeanMeche
JeanMeche force-pushed the test/full-compliance-goldens branch from 344a10d to 1caccba Compare October 18, 2026 05:27
… goldens

Migrate the ngtsc compliance test suite from snippet-based golden files to full-file golden files:
- Expand macro calls (__AttributeMarker, __SelectorFlags, __QueryFlags, __i18nMsg__) and $...$ identifier placeholders in compliance golden files to full-file ngtsc output.
- Format full-file compliance golden files and generated compliance outputs (`ngtsc` and `ngp`) with Prettier (`printWidth: 100`).
- Consolidate multi-snippet expectations in TEST_CASES.json into single full-file expectations and un-share shared line-ending normalization expectations so each test case has its own full-file golden, using `…` for partial-linker divergent fragments.
- Enforce start-of-file and end-of-file bounds in `expectEmit` for full-file goldens, sub-chunk large token arrays and string literals, and explicitly scope partial-linker normalizations (`isLinkedCompilation`) and NGP emit normalizations (`isNgpCompilation` / `isNgpTestMode()`) to their respective test runners.
- Align `ngp` (`HybridCompiler`) DI and class metadata emission with `ngtsc` and consolidate remaining NGP cosmetic emit normalizations into `normalizeNgpEmitForComparison`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: compiler Issues related to `ngc`, Angular's template compiler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant