unicode/utf8 — validation proof

Go’s own unicode/utf8 test suite, converted to C# by go2cs, built against the converted standard library, run under the Go-semantics test host, and compared verdict for verdict against a clean go test -json baseline of the same sources. This page is generated by the converter from that comparison — it is the evidence behind the unicode/utf8 row in Validated Test Packages.

Validated 2026-09-23 · converter f95f88866

14 matched · 1 disclosed — Go 1.24.13, windows/amd64, converted package src/core/unicode/utf8.

Measured at Release (tiered JIT off), oracle go version go1.24.13 windows/amd64.

Verdicts

Test go test go2cs
TestAppendRune pass pass
TestConstants pass pass
TestDecodeInvalidSequence pass pass
TestDecodeRune pass pass
TestDecodeSurrogateRune pass pass
TestEncodeRune pass pass
TestFullRune pass pass
TestNegativeRune pass pass
TestRuneCount pass pass
TestRuneCountNonASCIIAllocation pass fail (disclosed)
TestRuneLen pass pass
TestRuntimeConversion pass pass
TestSequencing pass pass
TestValid pass pass
TestValidRune pass pass

Disclosed divergences

A disclosed divergence is a specific Go assertion this conversion does not satisfy — not a skipped test and not a tolerance. Each one is pinned by exact failure signature in the package’s hand-owned go2cs_test_disclosures.json; a disclosed test that fails any other way is still a hard mismatch.

The Class column says which kind each one is: a deferred entry is an assertion the managed CLR can meet, pinned against the named plan that will retire it; every other class is one it provably cannot satisfy.

Test Class Pinned reason
TestRuneCountNonASCIIAllocation deferred NEW at 1.24: a want-zero AllocsPerRun over s := []byte("日本語日本語日本語日"); _ = RuneCount(s). Go’s escape analysis keeps the []byte(const) conversion and RuneCount’s slow-path string on the stack, so it asserts zero; the converted path charges 3 per run. DEFERRED, not alloc-count-semantics: the run’s own unit note says counted 30 go2cs-runtime object allocations ... over 10 run(s) ... an allocation COUNT per run – Go’s own unit, so the counter saw them. The three sites, each read in golib at the 1.24.13 hop: (1) the test’s []byte(const) -> slice from a span -> AllocationCounter.CopyOf; (2) RuneCount's non-ASCII slow path `RuneCountInString((@string)(p[n..]))` -> @string(slice) -> ToArray -> CopyOf; (3) ranging over that @string -> RuneSpanEnumerator, a class charged by AllocationCounter.Count(). (2) and (3) are LOCAL to RuneCount and removable; (1) is the escape-analysis class no local change removes, hence the floor.

Excluded declarations

Declarations filtered from both sides of the comparison, and therefore not claimed above: Benchmark, Fuzz and Example declarations the converted host does not execute, plus any test requiring a capability the managed runtime does not provide — a testing member the host has not implemented, or a platform behavior it provably cannot reproduce. Each is named with the capability it needs.