test(aot): cover Trie<TValue> in the Native AOT smoke test - #317
Conversation
Trie was the only collection with no block in the AOT smoke test, so nothing proved its prefix operations survive a Native AOT publish. It is also the one collection whose surface mixes an allocation-free struct enumerator with compiler-generated iterators (GetByPrefix / GetKeysWithPrefix / Keys / Values), so both traversal shapes now get compiled and executed. The block exercises the indexer / Add / TryAdd / TryGetValue, the empty-string key on the root, GetByPrefix ordering and its subtree-only walk, GetKeysWithPrefix, ContainsPrefix, TryGetLongestPrefix (interior / exact / empty-key fallback), Remove with bottom-up pruning, ordered enumeration, and the IReadOnlyDictionary<string, TValue?> surface, plus a reference-type TValue instantiation at scale and Clear. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Coverage
Files below 100% line coverage
|
There was a problem hiding this comment.
Pull request overview
Adds Native AOT smoke-test coverage for Trie<TValue> so its prefix/iteration APIs are exercised under PublishAot=true, aligning it with the existing AOT checks for other collections.
Changes:
- Add a
Trie<TValue>exercise block to the Native AOT smoke-test program covering construction, lookups, prefix queries, longest-prefix match, removal/pruning, and enumeration. - Add a CHANGELOG entry noting the new AOT smoke-test coverage for
Trie<TValue>.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/Celerity.AotSmokeTest/Program.cs | Adds a new smoke-test block that executes Trie<TValue> APIs to ensure they run under Native AOT publish. |
| CHANGELOG.md | Adds an [Unreleased] bullet documenting the new AOT smoke-test coverage for the trie. |
…e change The bullet carried implementation detail (struct enumerator vs compiler- generated iterators) that CLAUDE.md keeps out of the changelog. The rationale lives in the PR body; the entry now states only what changed and that it is test-only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
CHANGELOG.md:9
- The changelog bullet says the trie's surface is "proven" to run under Native AOT. A smoke test exercises representative operations, but it’s not a proof of the entire surface; this wording can read stronger than what the change guarantees. Consider using more precise language like “now exercises” / “now covers” under Native AOT.
- **Native AOT smoke-test coverage for `Trie<TValue>`** — the trie was the one collection the AOT smoke test did not exercise, so its surface is now proven to run under a Native AOT publish. Test-only hardening; no public API change.
Benchmarks4 regressions Highlights
Collections (420)
Hashers (100)
Same-runner A/B (sharded 8-way): main ( |
Trie<TValue>(added in #286, issue #285) was the only collection with no block insrc/Celerity.AotSmokeTest/Program.cs— every sibling (Deque, DisjointSet, IndexedPriorityQueue, LruCache, SparseSet, SmallDictionary, FenwickTree, …) has one, so nothing proved the trie's prefix operations survive a Native AOT publish.grep -c "Trie" src/Celerity.AotSmokeTest/Program.csreturned 0 before this change.Why the trie is worth pinning here
It is the one collection that walks a key character by character instead of hashing it, and the only one whose surface mixes an allocation-free struct enumerator with compiler-generated iterators (
GetByPrefix/GetKeysWithPrefix/Keys/Values). Both traversal shapes now get compiled and executed by the AOT job, not just the struct one.What the block exercises
IEnumerable<KeyValuePair<,>>ctor, the indexer,Add/TryAdd(duplicate leaves the value),TryGetValue,ContainsKey— including the empty string as a valid key held on the root, and an interior node ("ca") that is not a key.ContainsPrefix, andGetByPrefix/GetKeysWithPrefix: ascending ordinal ordering, theO(prefix + matches)contract (it descends to the prefix node and walks only that subtree — asserted behaviourally:"car"yields exactlycar,care,cartand a missing prefix yields nothing).TryGetLongestPrefixacross all three shapes: an interior match ("cartoon"→"cart"), an exact match, and the fallback to the stored empty-string key.Remove(key, out value)with the bottom-up prune, verifying siblings and the shorter prefix survive, plus the absent-key return.IEnumerable<KeyValuePair<,>>path, andIReadOnlyDictionary<string, TValue?>conformance (interface indexer — implemented explicitly, so only reachable through the interface —ContainsKey,TryGetValue,Keys,Values).TValueinstantiation (Trie<string>) built to 500 keys, so the AOT publish compiles another generic instantiation plus the child-array growth path, thenClear.Verification
(exit code 0; the CI AOT job runs the same program after a
PublishAot=truepublish.)Test-only hardening — no public API change, no production code touched. The CHANGELOG gets a single bullet, matching the precedent set by the differential-fuzz-targets entry; there is no open issue for this gap, so the entry carries no
Closeslink.🤖 Generated with Claude Code