test: retire copied oracles and implementation inventories (#32206)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-23 00:01:39 -07:00
committed by GitHub
co-authored by OrcaWin
parent 456e5830bb
commit a5b2543d85
6 changed files with 56 additions and 1083 deletions
@@ -1,153 +1,10 @@
import { describe, expect, it } from 'vitest'
import {
MIN_NATIVE_BYTE_LENGTH_CODE_UNITS,
measureTerminalStreamByteLength,
terminalStreamByteLength,
terminalStreamByteLengthExceeds
} from './terminal-stream-byte-length'
// Copy of the pre-change implementation (shared/clipboard-text.ts
// measureClipboardTextByteLength), kept here so equivalence is checked against the
// ACTUAL old code path rather than a paraphrase of it. The one deliberate deviation is
// `legacyCodePointAt`: raw `String.prototype.codePointAt` reads one code unit past the end
// of a sliced string once V8 optimizes its caller, so the naive copy is not a stable
// reference. See src/shared/utf8-byte-limits.ts (readUtf8CodePointAt).
function legacyCodePointAt(text: string, index: number): number {
const leadUnit = text.charCodeAt(index)
if (leadUnit < 0xd800 || leadUnit > 0xdbff || index + 1 >= text.length) {
return leadUnit
}
const trailUnit = text.charCodeAt(index + 1)
if (trailUnit < 0xdc00 || trailUnit > 0xdfff) {
return leadUnit
}
return (leadUnit - 0xd800) * 0x400 + (trailUnit - 0xdc00) + 0x10000
}
function legacyUtf8ByteLengthForCodePoint(codePoint: number): number {
if (codePoint <= 0x7f) {
return 1
}
if (codePoint <= 0x7ff) {
return 2
}
if (codePoint <= 0xffff) {
return 3
}
return 4
}
function legacyMeasure(
text: string,
options: { stopAfterBytes?: number } = {}
): { byteLength: number; exceededLimit: boolean } {
const stopAfterBytes = options.stopAfterBytes
let byteLength = 0
for (let index = 0; index < text.length; index += 1) {
const codePoint = legacyCodePointAt(text, index)
byteLength += legacyUtf8ByteLengthForCodePoint(codePoint)
if (Number.isFinite(stopAfterBytes) && byteLength > (stopAfterBytes ?? 0)) {
return { byteLength, exceededLimit: true }
}
if (codePoint > 0xffff) {
index += 1
}
}
return { byteLength, exceededLimit: false }
}
function legacyByteLength(data: string): number {
return legacyMeasure(data).byteLength
}
function legacyExceeds(data: string, maxBytes: number): boolean {
return legacyMeasure(data, { stopAfterBytes: maxBytes }).exceededLimit
}
function mulberry32(seed: number): () => number {
let state = seed >>> 0
return () => {
state = (state + 0x6d2b79f5) >>> 0
let t = state
t = Math.imul(t ^ (t >>> 15), t | 1)
t ^= t + Math.imul(t ^ (t >>> 7), t | 61)
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
}
}
// Mixed generator: ASCII, 2-byte, 3-byte, astral pairs, and LONE surrogates, so the
// fixtures exercise every branch of the legacy code-point scan.
function randomUnit(random: () => number): string {
const roll = random()
if (roll < 0.4) {
return String.fromCharCode(Math.floor(random() * 0x80))
}
if (roll < 0.55) {
return String.fromCharCode(0x80 + Math.floor(random() * 0x780))
}
if (roll < 0.72) {
return String.fromCharCode(0x800 + Math.floor(random() * 0xd000))
}
if (roll < 0.88) {
return String.fromCodePoint(0x10000 + Math.floor(random() * 0x100000))
}
return String.fromCharCode(0xd800 + Math.floor(random() * 0x800))
}
function randomString(random: () => number, maxUnits: number): string {
const count = Math.floor(random() * maxUnits)
let text = ''
for (let index = 0; index < count; index += 1) {
text += randomUnit(random)
}
return text
}
// Raw UTF-16 with no code-point discipline at all: catches anything that assumes
// well-formedness (unpaired high after high, low before high, etc).
function rawUtf16(random: () => number, maxUnits: number): string {
const count = Math.floor(random() * maxUnits)
let text = ''
for (let index = 0; index < count; index += 1) {
text += String.fromCharCode(Math.floor(random() * 0x11000))
}
return text
}
const EDGE_STRINGS = [
'',
'a',
'\u0000',
'\u007f',
'€',
'߿',
'ࠀ',
'￿',
'�',
'\u{10000}',
'\u{10ffff}',
'\ud800',
'\udfff',
'\ud800\ud800',
'\udc00\ud800',
'😀',
'😀\ud800',
'\ud800😀',
'a\ud800b',
'\r\n\u001b[0m',
'é́',
'\u{1f469}‍\u{1f4bb}',
'x'.repeat(1000),
'\u{1f600}'.repeat(300),
`${'é'.repeat(500)}\ud800`
]
// Regression for the intermittent "measurement diverged at 13 units" failure: the fuzzers
// build a rope and cut it at a fixed code-unit count, which can split a surrogate pair and
// leave the low half in the parent just past the slice. Optimized `codePointAt` pairs across
// that boundary, so the scan measured one byte too many, but only after the enclosing function
// tiered up, which made the failure look load-dependent. 13 code units is V8's minimum length
// for a sliced string, which is why the divergence started exactly there.
describe('measuring a prefix slice that cuts a surrogate pair in half', () => {
// Kept first in the file so the scan is still specializing on this shape when it tiers up.
it('measures the slice like the encoder does in every JIT tier', () => {
@@ -175,165 +32,70 @@ describe('measuring a prefix slice that cuts a surrogate pair in half', () => {
})
})
describe('terminal stream byte length equivalence with the legacy code-point scan', () => {
it('matches the legacy total byte length on edge strings', () => {
for (const text of EDGE_STRINGS) {
expect(terminalStreamByteLength(text)).toBe(legacyByteLength(text))
}
})
it('matches the legacy total byte length over every Unicode code point', () => {
for (let codePoint = 0; codePoint <= 0x10ffff; codePoint += 1) {
const text = String.fromCodePoint(codePoint)
if (terminalStreamByteLength(text) !== legacyByteLength(text)) {
throw new Error(`byte length diverged at code point U+${codePoint.toString(16)}`)
describe('terminal stream UTF-8 byte accounting', () => {
it('matches the encoder across Unicode widths and the native-call boundary', () => {
const samples = [
'',
'\u0000\u007f',
'\u0080\u07ff',
'\u0800\uffff',
'\u{10000}\u{10ffff}',
'\ud800',
'\udfff',
'\ud800\ud800',
'\udc00\ud800',
'😀\ud800',
'\ud800😀',
'e\u0301',
'👩‍💻',
'\r\n\u001b[0m'
]
for (const units of [15, 16, 17, 32]) {
for (const unit of ['a', 'é', '走', '😀', '\ud800']) {
samples.push(unit.repeat(units).slice(0, units))
}
}
expect(terminalStreamByteLength('\u{10ffff}')).toBe(4)
})
it('matches the legacy total byte length over every lone surrogate', () => {
for (let unit = 0xd800; unit <= 0xdfff; unit += 1) {
const text = `a${String.fromCharCode(unit)}b`
expect(terminalStreamByteLength(text)).toBe(legacyByteLength(text))
}
})
it('matches the legacy total byte length over fuzzed mixed and raw UTF-16 input', () => {
const random = mulberry32(0x5eed01)
for (let iteration = 0; iteration < 20000; iteration += 1) {
const text = iteration % 2 === 0 ? randomString(random, 24) : rawUtf16(random, 24)
if (terminalStreamByteLength(text) !== legacyByteLength(text)) {
throw new Error(`byte length diverged for ${JSON.stringify(text)}`)
}
}
expect(true).toBe(true)
})
it('matches the legacy exceededLimit decision across a dense (string, limit) sweep', () => {
const random = mulberry32(0xc0ffee)
let compared = 0
for (let iteration = 0; iteration < 10000; iteration += 1) {
const text = iteration % 2 === 0 ? randomString(random, 16) : rawUtf16(random, 16)
const total = legacyByteLength(text)
// Sweep every limit from below zero to past the true total so the boundary is hit exactly.
for (let limit = -2; limit <= total + 2; limit += 1) {
if (terminalStreamByteLengthExceeds(text, limit) !== legacyExceeds(text, limit)) {
throw new Error(`exceededLimit diverged for ${JSON.stringify(text)} at limit ${limit}`)
}
compared += 1
}
}
expect(compared).toBeGreaterThan(100000)
})
it('matches the legacy exceededLimit decision for non-finite and fractional limits', () => {
const random = mulberry32(0xfeed42)
for (const limit of [
Number.NaN,
Number.POSITIVE_INFINITY,
Number.NEGATIVE_INFINITY,
0.5,
2.5,
-0.5,
Number.MAX_SAFE_INTEGER
]) {
for (let iteration = 0; iteration < 400; iteration += 1) {
const text = rawUtf16(random, 12)
expect(terminalStreamByteLengthExceeds(text, limit)).toBe(legacyExceeds(text, limit))
}
for (const text of EDGE_STRINGS) {
expect(terminalStreamByteLengthExceeds(text, limit)).toBe(legacyExceeds(text, limit))
for (const text of samples) {
const byteLength = Buffer.byteLength(text, 'utf8')
expect(terminalStreamByteLength(text), JSON.stringify(text)).toBe(byteLength)
expect(measureTerminalStreamByteLength(text)).toEqual({
byteLength,
exceededLimit: false
})
for (const limit of [-1, 0, 0.5, byteLength - 1, byteLength, byteLength + 1]) {
expect(
terminalStreamByteLengthExceeds(text, limit),
`${JSON.stringify(text)} at ${limit}`
).toBe(text.length > 0 && byteLength > limit)
}
}
})
it('matches the legacy measurement pair, byteLength included, across a stopAfterBytes sweep', () => {
const random = mulberry32(0xa11ce)
for (let iteration = 0; iteration < 3000; iteration += 1) {
const text = iteration % 2 === 0 ? randomString(random, 20) : rawUtf16(random, 20)
const total = legacyByteLength(text)
for (let stopAfterBytes = -1; stopAfterBytes <= total + 2; stopAfterBytes += 1) {
const actual = measureTerminalStreamByteLength(text, { stopAfterBytes })
const expected = legacyMeasure(text, { stopAfterBytes })
if (
actual.byteLength !== expected.byteLength ||
actual.exceededLimit !== expected.exceededLimit
) {
throw new Error(
`measurement diverged for ${JSON.stringify(text)} at stopAfterBytes ${stopAfterBytes}`
)
}
}
it.each([
{ text: '', limit: -1, byteLength: 0, exceededLimit: false },
{ text: 'abc', limit: 0, byteLength: 1, exceededLimit: true },
{ text: 'éé', limit: 2, byteLength: 4, exceededLimit: true },
{ text: '😀😀😀', limit: 5, byteLength: 8, exceededLimit: true },
{ text: '\ud800z', limit: 2, byteLength: 3, exceededLimit: true },
{ text: '😀'.repeat(9), limit: 5, byteLength: 8, exceededLimit: true },
{ text: 'é'.repeat(16), limit: 32, byteLength: 32, exceededLimit: false },
{ text: '走'.repeat(16), limit: 48, byteLength: 48, exceededLimit: false }
])(
'measures only through the first code point over $limit bytes',
({ text, limit, ...expected }) => {
expect(measureTerminalStreamByteLength(text, { stopAfterBytes: limit })).toEqual(expected)
}
expect(
measureTerminalStreamByteLength('\u{1f600}\u{1f600}\u{1f600}', { stopAfterBytes: 5 })
).toEqual(legacyMeasure('\u{1f600}\u{1f600}\u{1f600}', { stopAfterBytes: 5 }))
})
)
it('keeps the partial byteLength the legacy scan returned when the limit is exceeded', () => {
// A drop-in Buffer.byteLength would report 12 here; the legacy scan stops at 8.
const measurement = measureTerminalStreamByteLength('\u{1f600}\u{1f600}\u{1f600}', {
stopAfterBytes: 5
})
expect(measurement).toEqual({ byteLength: 8, exceededLimit: true })
})
it('matches the legacy measurement with no stopAfterBytes and with an undefined option bag', () => {
const random = mulberry32(0xb0b)
for (let iteration = 0; iteration < 2000; iteration += 1) {
const text = rawUtf16(random, 32)
expect(measureTerminalStreamByteLength(text)).toEqual(legacyMeasure(text))
expect(measureTerminalStreamByteLength(text, {})).toEqual(legacyMeasure(text, {}))
expect(measureTerminalStreamByteLength(text, { stopAfterBytes: undefined })).toEqual(
legacyMeasure(text, { stopAfterBytes: undefined })
)
}
})
// The code-unit floor routes short inputs back through the scan. Both sides of that
// boundary must stay legacy-identical, so sweep it exhaustively rather than by sampling.
it('matches the legacy result on both sides of the native-call floor', () => {
const random = mulberry32(0xf100a)
for (let units = 0; units <= MIN_NATIVE_BYTE_LENGTH_CODE_UNITS * 2; units += 1) {
for (let iteration = 0; iteration < 60; iteration += 1) {
let text = ''
while (text.length < units) {
text +=
iteration % 2 === 0
? randomUnit(random)
: String.fromCharCode(Math.floor(random() * 0x11000))
}
text = text.slice(0, units)
const total = legacyByteLength(text)
expect(terminalStreamByteLength(text)).toBe(total)
for (let limit = -1; limit <= total + 2; limit += 1) {
if (terminalStreamByteLengthExceeds(text, limit) !== legacyExceeds(text, limit)) {
throw new Error(`exceeds diverged at ${units} units, limit ${limit}`)
}
const actual = measureTerminalStreamByteLength(text, { stopAfterBytes: limit })
const expected = legacyMeasure(text, { stopAfterBytes: limit })
if (
actual.byteLength !== expected.byteLength ||
actual.exceededLimit !== expected.exceededLimit
) {
throw new Error(`measurement diverged at ${units} units, limit ${limit}`)
}
}
it('treats non-finite limits as unbounded for both scan and native inputs', () => {
for (const text of ['', '😀é走a', '😀é走a'.repeat(8)]) {
for (const limit of [Number.NaN, Infinity, -Infinity]) {
expect(terminalStreamByteLengthExceeds(text, limit)).toBe(false)
expect(measureTerminalStreamByteLength(text, { stopAfterBytes: limit })).toEqual({
byteLength: Buffer.byteLength(text, 'utf8'),
exceededLimit: false
})
}
}
})
})
// trimPendingOutputCoveredBySnapshot re-measures a sliced chunk with terminalStreamByteLength.
// The RPC suites never reach that slice branch (a `data.length` mutant there survives on
// unmodified HEAD too), so pin the byte accounting the branch depends on here.
describe('resync trim byte accounting for a snapshot-sliced chunk', () => {
it('re-measures a sliced chunk in UTF-8 bytes, not UTF-16 code units', () => {
const data = '\u{1f600}é走a'
const sliced = data.slice(2)
// Guards the mutant: code-unit length would be 4 here, UTF-8 is 6.
expect(terminalStreamByteLength(sliced)).toBe(6)
expect(terminalStreamByteLength(sliced)).not.toBe(sliced.length)
})
})
@@ -1,60 +0,0 @@
import { describe, expect, it } from 'vitest'
import type { GlobalSettings } from '../../shared/global-settings-types'
import { GIT_METHODS } from './rpc/methods/git'
import { RuntimeGitCommands } from './orca-runtime-git'
const RPC_TO_RUNTIME_COMMAND = {
'git.status': 'getRuntimeGitStatus',
'git.checkIgnored': 'checkRuntimeGitIgnoredPaths',
'git.submoduleStatus': 'getRuntimeGitSubmoduleStatus',
'git.history': 'getRuntimeGitHistory',
'git.conflictOperation': 'getRuntimeGitConflictOperation',
'git.mutationInFlight': 'getRuntimeGitMutationInFlight',
'git.abortMerge': 'abortRuntimeGitMerge',
'git.abortRebase': 'abortRuntimeGitRebase',
'git.checkout': 'checkoutRuntimeGitBranch',
'git.localBranches': 'listRuntimeGitLocalBranches',
'git.diff': 'getRuntimeGitDiff',
'git.branchDiff': 'getRuntimeGitBranchDiff',
'git.commitDiff': 'getRuntimeGitCommitDiff',
'git.branchCompare': 'getRuntimeGitBranchCompare',
'git.commitCompare': 'getRuntimeGitCommitCompare',
'git.upstreamStatus': 'getRuntimeGitUpstreamStatus',
'git.fetch': 'fetchRuntimeGit',
'git.forkSync': 'syncRuntimeGitForkDefaultBranch',
'git.pull': 'pullRuntimeGit',
'git.fastForward': 'fastForwardRuntimeGit',
'git.rebaseFromBase': 'rebaseRuntimeGitFromBase',
'git.push': 'pushRuntimeGit',
'git.commit': 'commitRuntimeGit',
'git.generateCommitMessage': 'generateRuntimeCommitMessage',
'git.discoverCommitMessageModels': 'discoverRuntimeCommitMessageModels',
'git.cancelGenerateCommitMessage': 'cancelRuntimeGenerateCommitMessage',
'git.generatePullRequestFields': 'generateRuntimePullRequestFields',
'git.cancelGeneratePullRequestFields': 'cancelRuntimeGeneratePullRequestFields',
'git.stage': 'stageRuntimeGitPath',
'git.bulkStage': 'bulkStageRuntimeGitPaths',
'git.unstage': 'unstageRuntimeGitPath',
'git.bulkUnstage': 'bulkUnstageRuntimeGitPaths',
'git.discard': 'discardRuntimeGitPath',
'git.bulkDiscard': 'bulkDiscardRuntimeGitPaths',
'git.remoteFileUrl': 'getRuntimeGitRemoteFileUrl',
'git.remoteCommitUrl': 'getRuntimeGitRemoteCommitUrl'
} as const satisfies Record<string, keyof RuntimeGitCommands>
describe('runtime Git API contract', () => {
it('keeps every registered Git RPC paired with one public runtime command', () => {
const commands = new RuntimeGitCommands({
resolveRuntimeGitTarget: async () => {
throw new Error('not called')
},
getRuntimeSettings: () => ({}) as GlobalSettings
})
const registeredMethods = GIT_METHODS.map((method) => method.name).sort()
expect(registeredMethods).toEqual(Object.keys(RPC_TO_RUNTIME_COMMAND).sort())
for (const commandName of Object.values(RPC_TO_RUNTIME_COMMAND)) {
expect(commands[commandName]).toBeTypeOf('function')
}
})
})
@@ -1,72 +0,0 @@
import { readdirSync, readFileSync, statSync } from 'node:fs'
import { join, relative } from 'node:path'
import { describe, expect, it } from 'vitest'
const EDITOR_DIR = __dirname
const RAW_WRITE = /\b(?:writeRuntimeFile|fs\.writeFile)\s*\(/
const EDITOR_WRITE = /\bwriteEditorSave\s*\(/
function sourceFiles(dir: string): string[] {
return readdirSync(dir).flatMap((name) => {
const path = join(dir, name)
if (statSync(path).isDirectory()) {
return sourceFiles(path)
}
return /\.tsx?$/.test(name) && !/\.test\.tsx?$|fixture/.test(name) ? [path] : []
})
}
function filesMatching(dir: string, pattern: RegExp): string[] {
return sourceFiles(dir)
.filter((path) => pattern.test(readFileSync(path, 'utf8')))
.map((path) => relative(EDITOR_DIR, path).split('\\').join('/'))
.sort()
}
function functionBody(source: string, name: string): string {
const body = source.slice(source.indexOf(`export function ${name}`))
return body.slice(0, body.indexOf('\n}\n'))
}
describe('editor save paths', () => {
it('make the raw file write only inside writeEditorSave', () => {
expect(filesMatching(EDITOR_DIR, RAW_WRITE)).toEqual(['editor-save-disk-precondition.ts'])
const source = readFileSync(join(EDITOR_DIR, 'editor-save-disk-precondition.ts'), 'utf8')
expect(source.split('writeRuntimeFile(')).toHaveLength(2)
const writer = functionBody(source, 'writeEditorSave')
expect(writer).toContain('writeRuntimeFile(')
// Only the explicit Overwrite reaches the writer without a precondition.
expect(writer).toContain("write.kind === 'guarded'")
})
it('route every editor surface that saves a file through writeEditorSave', () => {
expect(filesMatching(EDITOR_DIR, EDITOR_WRITE)).toEqual([
'combined-diff/review-controls/use-combined-diff-section-actions.ts',
'editor-save-disk-precondition.ts',
'editor-save-queue.ts'
])
})
it('never turn a missing or disputed baseline into a write', () => {
const source = readFileSync(join(EDITOR_DIR, 'editor-save-disk-precondition.ts'), 'utf8')
const builder = functionBody(source, 'editorSavePrecondition')
expect(builder).not.toMatch(/return null|return undefined/)
expect(builder.match(/kind: 'explicit-overwrite'/g)).toHaveLength(1)
expect(builder).toMatch(
/if \(trigger === 'overwrite'\) \{\n\s*return \{ kind: 'explicit-overwrite' \}/
)
expect(builder).not.toContain('mode')
})
it('grant overwrite consent only from the conflict UI', () => {
const grants = filesMatching(
join(EDITOR_DIR, '..', '..'),
/overwrite: true|\? 'overwrite'|, 'overwrite'\)/
)
expect(grants).toEqual([
'DiffSectionItem.tsx',
'ExternalFileChangeBanner.tsx',
'editor-autosave-controller.ts'
])
})
})
@@ -9,11 +9,8 @@ import { readFileSync } from 'node:fs'
// event sequences match.
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { createAgentStatusOscProcessor } from '../../../../shared/agent-status-osc'
import { createCommandCodeOutputStatusDetector } from '../../../../shared/command-code-output-status'
import { createTerminalGitHubPRLinkDetector } from '../../../../shared/terminal-github-pr-link-detector'
import { createTerminalTitleTracker } from '../../../../shared/terminal-output-side-effects'
import { createPtyOutputProcessor } from './pty-output-processor'
import { createTerminalCommandLifecycle } from './terminal-command-lifecycle'
const ESC = '\x1b'
const BEL = '\x07'
@@ -336,187 +333,3 @@ describe('main title tracker parity with the renderer transport processor', () =
expect(paths.main.events).toEqual([])
})
})
// Why: slice 3 moves the renderer's OSC 133;D and PR-link byte parsing into
// main's tracker for local/SSH PTYs. Both must derive identical fact
// sequences from the same chunk boundaries, or flipping the kill switch
// changes which commands/links are observed.
type LifecycleFactEvent = ['command-finished', number | null] | ['pr-link', string, number]
type LifecycleFactPath = {
events: LifecycleFactEvent[]
feed: (chunk: string) => void
}
function createRendererLifecyclePath(): LifecycleFactPath {
const events: LifecycleFactEvent[] = []
// Why: mirrors pty-connection's dataCallback wiring — the transport
// processor strips OSC 9999 before the lifecycle/PR-link byte scans run.
const processAgentStatusChunk = createAgentStatusOscProcessor()
const lifecycle = createTerminalCommandLifecycle({
onCommandFinished: (exitCode) => events.push(['command-finished', exitCode])
})
const detectPRLinks = createTerminalGitHubPRLinkDetector()
return {
events,
feed(chunk: string): void {
const clean = processAgentStatusChunk(chunk).cleanData
lifecycle.handlePtyData(clean)
for (const link of detectPRLinks(clean)) {
events.push(['pr-link', link.url, link.number])
}
}
}
}
function createMainLifecyclePath(): LifecycleFactPath {
const events: LifecycleFactEvent[] = []
const processAgentStatusChunk = createAgentStatusOscProcessor()
const tracker = createTerminalTitleTracker({
onCommandFinished: (exitCode) => events.push(['command-finished', exitCode]),
onPrLink: (link) => events.push(['pr-link', link.url, link.number])
})
return {
events,
feed(chunk: string): void {
tracker.handleChunk(processAgentStatusChunk(chunk).cleanData)
}
}
}
describe('main tracker parity with renderer 133;D and PR-link byte parsers', () => {
let paths: { renderer: LifecycleFactPath; main: LifecycleFactPath }
beforeEach(() => {
paths = { renderer: createRendererLifecyclePath(), main: createMainLifecyclePath() }
})
it('derives identical command-finished facts from split OSC 133;D chunks', () => {
feedBoth(paths, `output${ESC}]133`)
feedBoth(paths, ';D;13')
feedBoth(paths, `0${BEL}prompt $ `)
feedBoth(paths, `${ESC}]133;D;0${BEL}`)
feedBoth(paths, `${ESC}]133;D${ST}`)
expect(paths.main.events).toEqual(paths.renderer.events)
expect(paths.main.events).toEqual([
['command-finished', 130],
['command-finished', 0],
['command-finished', null]
])
})
it('derives identical pr-link facts from split and repeated URLs', () => {
feedBoth(paths, 'Created https://github.com/acme/orca/pull/4')
feedBoth(paths, '2\r\nAlso https://github.com/acme/orca/pull/43 merged\r\n')
feedBoth(paths, 'again https://github.com/acme/orca/pull/42\r\n')
expect(paths.main.events).toEqual(paths.renderer.events)
expect(paths.main.events).toEqual([
['pr-link', 'https://github.com/acme/orca/pull/42', 42],
['pr-link', 'https://github.com/acme/orca/pull/43', 43]
])
})
it('ignores 133;D and PR URLs inside stripped OSC 9999 payloads in both paths', () => {
feedBoth(
paths,
`${ESC}]9999;{"state":"done","prompt":"https://github.com/acme/orca/pull/9"}${BEL}\r\n`
)
expect(paths.main.events).toEqual(paths.renderer.events)
expect(paths.main.events).toEqual([])
})
})
// Why: slice 4 moves the Command Code output scrape into main for local/SSH
// PTYs. The renderer byte path observes raw transport data; main observes the
// OSC 9999-stripped cleanData. Both must derive identical working/done
// sequences from the same chunk boundaries, or flipping the kill switch
// changes Command Code status rows.
type CommandCodeFactEvent = ['working' | 'done', string]
type CommandCodeFactPath = {
events: CommandCodeFactEvent[]
feed: (chunk: string) => void
}
function createCommandCodePath(options: { stripStatusPayloads: boolean }): CommandCodeFactPath {
const events: CommandCodeFactEvent[] = []
const processAgentStatusChunk = createAgentStatusOscProcessor()
const detector = createCommandCodeOutputStatusDetector({
startupCommand: null,
onWorking: (prompt) => events.push(['working', prompt]),
onDone: (prompt) => events.push(['done', prompt])
})
return {
events,
feed(chunk: string): void {
detector.observe(
options.stripStatusPayloads ? processAgentStatusChunk(chunk).cleanData : chunk
)
}
}
}
describe('main Command Code scrape parity with the renderer byte detector', () => {
let paths: { renderer: CommandCodeFactPath; main: CommandCodeFactPath }
beforeEach(() => {
paths = {
renderer: createCommandCodePath({ stripStatusPayloads: false }),
main: createCommandCodePath({ stripStatusPayloads: true })
}
})
it('derives identical working facts after the banner arms across chunks', () => {
feedBoth(paths, '# Command')
feedBoth(paths, ' Code v0.27.3\r\n')
feedBoth(paths, '❯ Fix the spinner\r\n\x1b[35m✻ Thinking...\x1b[0m')
expect(paths.main.events).toEqual(paths.renderer.events)
expect(paths.main.events).toEqual([['working', 'Fix the spinner']])
})
it('derives identical done facts for a no-tool turn in both paths', () => {
feedBoth(paths, '# Command Code v0.27.3\r\n')
feedBoth(paths, '❯ say hi\r\n✻ Thinking...')
feedBoth(paths, '\r\n:: Hi!\r\n❯ Ask your question...')
expect(paths.main.events).toEqual(paths.renderer.events)
expect(paths.main.events).toEqual([
['working', 'say hi'],
['done', 'say hi']
])
})
it('recovers prompt capture from interleaved OSC 9999 payloads (main improvement)', () => {
// Deliberate divergence, not drift: the renderer's raw byte path lets an
// OSC 9999 payload leak partial text into the scrape window (its ANSI
// strip consumes only the ESC] introducer), which breaks the prompt-echo
// line match. Main feeds the OSC 9999-stripped cleanData, so the prompt
// (and therefore the done settle hint) survives an adjacent payload.
const payloadThenPrompt = [
'# Command Code v0.27.3\r\n',
`${ESC}]9999;{"state":"working","agentType":"command-code"}${BEL}`,
'❯ say hi\r\n✻ Thinking...',
'\r\n:: Hi!\r\n❯ Ask your question...'
]
for (const chunk of payloadThenPrompt) {
feedBoth(paths, chunk)
}
expect(paths.renderer.events).toEqual([['working', '']])
expect(paths.main.events).toEqual([
['working', 'say hi'],
['done', 'say hi']
])
})
it('stays silent without the Command Code banner in both paths', () => {
feedBoth(paths, '❯ Fix the spinner\r\nThinking about unrelated shell output...')
expect(paths.main.events).toEqual(paths.renderer.events)
expect(paths.main.events).toEqual([])
})
})
+1 -204
View File
@@ -1,210 +1,7 @@
/**
* Proves the substring/char-code guards added to `cross-platform-path.ts` and `parseWslUncPath`
* are pure fast paths: a seeded differential fuzz against the pre-guard copy in
* `cross-platform-path-unguarded.test-fixture.ts`, plus counters that fail if the guards regress.
*/
import { describe, expect, it, afterEach } from 'vitest'
import { afterEach, describe, expect, it } from 'vitest'
import * as guarded from './cross-platform-path'
import * as unguarded from './cross-platform-path-unguarded.test-fixture'
import { parseWslUncPath } from './wsl-paths'
// ─── Deterministic path generator ────────────────────────────────────
function createRandom(seed: number): () => number {
let state = seed >>> 0
return () => {
state = (state + 0x6d2b79f5) >>> 0
let t = Math.imul(state ^ (state >>> 15), 1 | state)
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
}
}
const PREFIXES = [
'',
'/',
'//',
'///',
'.',
'./',
'../',
'C:/',
'c:\\',
'Z:',
'\\\\',
'\\\\wsl.localhost\\Ubuntu',
'//wsl.localhost/Ubuntu-22.04',
'//WSL$/Debian',
'\\\\wsl$\\ubuntu',
'//server/share',
'/mnt/c',
'\\\\wsl.localhost\\Ubuntu\\mnt\\c'
]
// NFD + KELVIN SIGN are the folds `normalizeRuntimePathForComparison` is built around.
const SEGMENTS = [
'home',
'user',
'orca',
'workspaces',
'..',
'.',
'',
'a',
'B',
'wsl$',
'wsl.localhost',
'mnt',
'c',
'C',
'répertoire',
're\u0301pertoire',
'\u212Aelvin',
'Kelvin',
'back\\slash',
'sp ace',
'Ubuntu'
]
const JOINERS = ['/', '/', '/', '//', '///', '\\', '\\\\']
const SUFFIXES = ['', '', '', '/', '//', '\\', '/.', '/..']
function generatePath(random: () => number): string {
const pick = <T>(items: readonly T[]): T => items[Math.floor(random() * items.length)]
let path = pick(PREFIXES)
const segmentCount = Math.floor(random() * 5)
for (let index = 0; index < segmentCount; index++) {
path += (path === '' ? '' : pick(JOINERS)) + pick(SEGMENTS)
}
return path + pick(SUFFIXES)
}
/** Roots that actually contain the candidate, so the matching branches get exercised too. */
function generateRoot(random: () => number, candidate: string): string {
const roll = random()
if (roll < 0.35) {
const cut = Math.floor(random() * (candidate.length + 1))
return candidate.slice(0, cut)
}
if (roll < 0.45) {
return candidate
}
return generatePath(random)
}
// ─── Differential fuzz ───────────────────────────────────────────────
const FUZZ_ITERATIONS = 20_000
describe('guarded path normalization matches the pre-guard implementation', () => {
it(`agrees on every export across ${FUZZ_ITERATIONS} seeded paths`, () => {
const random = createRandom(0x5eed)
const mismatches: string[] = []
const record = (label: string, path: string, root: string): void => {
if (mismatches.length < 5) {
mismatches.push(`${label}: candidate=${JSON.stringify(path)} root=${JSON.stringify(root)}`)
}
}
for (let iteration = 0; iteration < FUZZ_ITERATIONS; iteration++) {
const path = generatePath(random)
const root = generateRoot(random, path)
const distro = Math.floor(random() * 2) === 0 ? 'Ubuntu' : 'debian'
const singles: [string, (value: string) => unknown, (value: string) => unknown][] = [
[
'isWindowsAbsolutePathLike',
guarded.isWindowsAbsolutePathLike,
unguarded.isWindowsAbsolutePathLike
],
[
'isCaseInsensitiveRuntimeRoot',
guarded.isCaseInsensitiveRuntimeRoot,
unguarded.isCaseInsensitiveRuntimeRoot
],
[
'normalizeRuntimePathSeparators',
guarded.normalizeRuntimePathSeparators,
unguarded.normalizeRuntimePathSeparators
],
[
'normalizeRuntimePathForComparison',
guarded.normalizeRuntimePathForComparison,
unguarded.normalizeRuntimePathForComparison
],
['isRuntimePathAbsolute', guarded.isRuntimePathAbsolute, unguarded.isRuntimePathAbsolute],
['getRuntimePathBasename', guarded.getRuntimePathBasename, unguarded.getRuntimePathBasename]
]
for (const [label, left, right] of singles) {
if (left(path) !== right(path)) {
record(label, path, root)
}
}
const identity = guarded.getLocalWindowsWslPathIdentity(path)
const expectedIdentity = unguarded.getLocalWindowsWslPathIdentity(path)
if (
identity.normalizedPath !== expectedIdentity.normalizedPath ||
identity.aliasComparisonPath !== expectedIdentity.aliasComparisonPath ||
identity.isWslUnc !== expectedIdentity.isWslUnc
) {
record('getLocalWindowsWslPathIdentity', path, root)
}
const wslUnc = parseWslUncPath(path)
const expectedWslUnc = unguarded.parseWslUncPath(path)
if (
wslUnc?.distro !== expectedWslUnc?.distro ||
wslUnc?.linuxPath !== expectedWslUnc?.linuxPath
) {
record('parseWslUncPath', path, root)
}
if (
guarded.areLocalWindowsWslPathAliases(root, path) !==
unguarded.areLocalWindowsWslPathAliases(root, path)
) {
record('areLocalWindowsWslPathAliases', path, root)
}
if (
guarded.isWslUncPathForCallerLinuxPath(root, path, distro) !==
unguarded.isWslUncPathForCallerLinuxPath(root, path, distro)
) {
record('isWslUncPathForCallerLinuxPath', path, root)
}
if (
guarded.isWslUncPathForLinuxMountedPath(root, path) !==
unguarded.isWslUncPathForLinuxMountedPath(root, path)
) {
record('isWslUncPathForLinuxMountedPath', path, root)
}
if (guarded.resolveRuntimePath(root, path) !== unguarded.resolveRuntimePath(root, path)) {
record('resolveRuntimePath', path, root)
}
if (guarded.isPathInsideOrEqual(root, path) !== unguarded.isPathInsideOrEqual(root, path)) {
record('isPathInsideOrEqual', path, root)
}
if (
guarded.createNormalizedPathInsideOrEqualMatcher(root)(
guarded.normalizeRuntimePathForComparison(path)
) !==
unguarded.createNormalizedPathInsideOrEqualMatcher(root)(
unguarded.normalizeRuntimePathForComparison(path)
)
) {
record('createNormalizedPathInsideOrEqualMatcher', path, root)
}
if (
guarded.relativePathInsideRoot(root, path) !== unguarded.relativePathInsideRoot(root, path)
) {
record('relativePathInsideRoot', path, root)
}
}
expect(mismatches).toEqual([])
}, 120_000)
})
// ─── The guards must not skip work that was actually needed ──────────
describe('guards still do the work when the fast path does not apply', () => {
it('collapses doubled slashes', () => {
expect(guarded.normalizeRuntimePathForComparison('/a//b///c')).toBe('/a/b/c')
@@ -1,267 +0,0 @@
/**
* Verbatim pre-guard copy of `cross-platform-path.ts` and `parseWslUncPath`, kept only so
* `cross-platform-path-guards.test.ts` can differentially fuzz the guarded versions
* against what shipped. Comments were stripped; the code is otherwise unchanged. Update this file
* only when the guarded originals are meant to change behaviour.
*/
import { toWindowsWslPath } from './wsl-paths'
type WslUncPathInfo = { distro: string; linuxPath: string }
export function parseWslUncPath(path: string): WslUncPathInfo | null {
const normalized = path.replace(/\\/g, '/')
const match = normalized.match(/^\/\/(wsl\.localhost|wsl\$)\/([^/]+)(\/.*)?$/i)
if (!match) {
return null
}
return { distro: match[2], linuxPath: match[3] || '/' }
}
function isWslUncPath(path: string): boolean {
return parseWslUncPath(path) !== null
}
const SLASH_CHAR_CODE = '/'.charCodeAt(0)
export function isWindowsAbsolutePathLike(value: string): boolean {
return /^[A-Za-z]:[\\/]/.test(value) || value.startsWith('\\\\') || value.startsWith('//')
}
export function isCaseInsensitiveRuntimeRoot(rootPath: string): boolean {
return isWindowsAbsolutePathLike(rootPath) && !isWslUncPath(rootPath)
}
export function normalizeRuntimePathSeparators(value: string): string {
const normalized = value.replace(/\\/g, '/').replace(/\/+/g, '/')
if (value.startsWith('\\\\') || value.startsWith('//')) {
return `//${normalized.replace(/^\/+/, '')}`
}
return normalized
}
export function normalizeRuntimePathForComparison(rawValue: string): string {
const value = rawValue.normalize('NFC')
const isWindowsPath = isWindowsAbsolutePathLike(value)
const normalized = trimRuntimePathTrailingSlash(
isWindowsPath ? normalizeRuntimePathSeparators(value) : value.replace(/\/+/g, '/')
)
const wslUnc = normalized.match(/^\/\/(?:wsl\.localhost|wsl\$)\/([^/]+)(\/[\s\S]*)?$/i)
if (wslUnc) {
return `//wsl/${wslUnc[1].toLowerCase()}${wslUnc[2] ?? ''}`
}
return isWindowsPath ? normalized.toLowerCase() : normalized
}
export function isWslUncPathForCallerLinuxPath(
uncPath: string,
linuxPath: string,
callerDistro: string
): boolean {
const parsed = parseWslUncPath(uncPath)
if (!parsed) {
return false
}
return (
parsed.distro.toLowerCase() === callerDistro.toLowerCase() &&
normalizeRuntimePathForComparison(parsed.linuxPath) ===
normalizeRuntimePathForComparison(linuxPath)
)
}
export function isWslUncPathForLinuxMountedPath(uncPath: string, linuxPath: string): boolean {
const parsed = parseWslUncPath(uncPath)
if (!parsed || !/^\/mnt\/[A-Za-z](?:\/|$)/.test(parsed.linuxPath)) {
return false
}
if (!/^\/mnt\/[A-Za-z](?:\/|$)/.test(linuxPath)) {
return false
}
return (
normalizeRuntimePathForComparison(toWindowsWslPath(parsed.linuxPath, parsed.distro)) ===
normalizeRuntimePathForComparison(toWindowsWslPath(linuxPath, parsed.distro))
)
}
export function areLocalWindowsWslPathAliases(left: string, right: string): boolean {
const leftIdentity = getLocalWindowsWslPathIdentity(left)
const rightIdentity = getLocalWindowsWslPathIdentity(right)
return (
(leftIdentity.isWslUnc || rightIdentity.isWslUnc) &&
leftIdentity.aliasComparisonPath === rightIdentity.aliasComparisonPath
)
}
export type LocalWindowsWslPathIdentity = {
normalizedPath: string
aliasComparisonPath: string
isWslUnc: boolean
}
export function getLocalWindowsWslPathIdentity(value: string): LocalWindowsWslPathIdentity {
const wslPath = parseWslUncPath(value)
const normalizedPath = normalizeRuntimePathForComparison(value)
return {
normalizedPath,
aliasComparisonPath: wslPath
? normalizeRuntimePathForComparison(toWindowsWslPath(wslPath.linuxPath, wslPath.distro))
: normalizedPath,
isWslUnc: wslPath !== null
}
}
export function isRuntimePathAbsolute(
value: string,
pathFlavor: 'posix' | 'windows' = isWindowsPathFlavor(value) ? 'windows' : 'posix'
): boolean {
if (pathFlavor === 'windows') {
return /^[A-Za-z]:[\\/]/.test(value) || value.startsWith('\\') || value.startsWith('/')
}
return value.startsWith('/')
}
export function resolveRuntimePath(basePath: string, targetPath: string): string {
const pathFlavor =
isWindowsPathFlavor(basePath) || isWindowsPathFlavor(targetPath) ? 'windows' : 'posix'
if (isRuntimePathAbsolute(targetPath, pathFlavor)) {
return normalizeRuntimePathDots(targetPath, pathFlavor)
}
return normalizeRuntimePathDots(
`${trimRuntimePathTrailingSlash(normalizeRuntimePathSeparators(basePath))}/${targetPath}`,
pathFlavor
)
}
export function getRuntimePathBasename(value: string): string {
const trimmed = value.replace(/[\\/]+$/g, '')
if (!trimmed) {
return ''
}
return trimmed.split(/[\\/]/).findLast(Boolean) ?? ''
}
export function createNormalizedPathInsideOrEqualMatcher(
rootPath: string
): (normalizedCandidate: string) => boolean {
const root = normalizeRuntimePathForComparison(rootPath)
const rootWithBoundary =
root === '/' || /^[a-z]:\/$/i.test(root) ? root : `${root.replace(/\/+$/, '')}/`
return (normalizedCandidate) =>
normalizedCandidate === root || normalizedCandidate.startsWith(rootWithBoundary)
}
export function isPathInsideOrEqual(rootPath: string, candidatePath: string): boolean {
return createNormalizedPathInsideOrEqualMatcher(rootPath)(
normalizeRuntimePathForComparison(candidatePath)
)
}
export function relativePathInsideRoot(rootPath: string, candidatePath: string): string | null {
const normalizedCandidate = trimRuntimePathTrailingSlash(
isWindowsAbsolutePathLike(candidatePath.normalize('NFC'))
? normalizeRuntimePathSeparators(candidatePath)
: candidatePath.replace(/\/+/g, '/')
)
const comparisonRoot = normalizeRuntimePathForComparison(rootPath)
const comparisonCandidate = normalizeRuntimePathForComparison(candidatePath)
if (comparisonCandidate === comparisonRoot) {
return ''
}
const isRoot = comparisonRoot === '/' || /^[a-z]:\/$/i.test(comparisonRoot)
const comparisonPrefix = isRoot ? comparisonRoot : `${comparisonRoot}/`
if (!comparisonCandidate.startsWith(comparisonPrefix)) {
return null
}
return sliceCandidatePastRootSegments(comparisonRoot, normalizedCandidate)
}
function sliceCandidatePastRootSegments(root: string, candidate: string): string {
let remainingRootSegments = 0
let inRootSegment = false
for (let index = 0; index < root.length; index++) {
if (root.charCodeAt(index) === SLASH_CHAR_CODE) {
inRootSegment = false
} else if (!inRootSegment) {
inRootSegment = true
remainingRootSegments++
}
}
let inSegment = false
for (let index = 0; index < candidate.length; index++) {
if (candidate.charCodeAt(index) === SLASH_CHAR_CODE) {
inSegment = false
continue
}
if (!inSegment) {
inSegment = true
if (remainingRootSegments-- === 0) {
return candidate.slice(index)
}
}
}
return ''
}
function trimRuntimePathTrailingSlash(value: string): string {
if (value === '/' || /^[A-Za-z]:\/$/.test(value)) {
return value
}
return value.replace(/\/+$/, '')
}
function isWindowsPathFlavor(value: string): boolean {
return /^[A-Za-z]:[\\/]/.test(value) || value.includes('\\') || value.startsWith('//')
}
function normalizeRuntimePathDots(value: string, pathFlavor: 'posix' | 'windows'): string {
const normalized = normalizeRuntimePathSeparators(value)
const { root, rest } = splitRuntimePathRoot(normalized, pathFlavor)
const segments: string[] = []
for (const segment of rest.split('/')) {
if (!segment || segment === '.') {
continue
}
if (segment === '..') {
if (segments.length > 0 && segments.at(-1) !== '..') {
segments.pop()
} else if (!root) {
segments.push(segment)
}
continue
}
segments.push(segment)
}
const suffix = segments.join('/')
if (!root) {
return suffix || '.'
}
return suffix ? `${root}${suffix}` : trimRuntimePathTrailingSlash(root)
}
function splitRuntimePathRoot(
value: string,
pathFlavor: 'posix' | 'windows'
): { root: string; rest: string } {
if (pathFlavor === 'windows') {
const drive = value.match(/^([A-Za-z]:)(?:\/|$)/)
if (drive) {
return { root: `${drive[1]}/`, rest: value.slice(drive[0].length) }
}
if (value.startsWith('//')) {
const parts = value.slice(2).split('/')
if (parts.length >= 2 && parts[0] && parts[1]) {
const root = `//${parts[0]}/${parts[1]}/`
return { root, rest: parts.slice(2).join('/') }
}
return { root: '//', rest: value.slice(2) }
}
if (value.startsWith('/')) {
return { root: '/', rest: value.slice(1) }
}
}
if (value.startsWith('/')) {
return { root: '/', rest: value.slice(1) }
}
return { root: '', rest: value }
}