Bläddra i källkod

test(opencode/run): skip Windows-only scrollback replay failure (#28261)

Kit Langton 3 månader sedan
förälder
incheckning
b67f5d741f
1 ändrade filer med 72 tillägg och 28 borttagningar
  1. 72 28
      packages/opencode/test/cli/run/scrollback.surface.test.ts

+ 72 - 28
packages/opencode/test/cli/run/scrollback.surface.test.ts

@@ -403,38 +403,82 @@ test("inserts spacers for new visible groups", async () => {
   }
 })
 
-test("renders replayed user, reasoning, and assistant output after completion", async () => {
-  const out = await setup()
+// TODO(windows): Re-enable on Windows once the streaming CodeRenderable
+// flush race is fixed. The reasoning commit is delivered as a `<code>`
+// renderable with `filetype="markdown"`, `streaming=true`, and
+// `drawUnstyledText=false`. On Windows the first paragraph of the reasoning
+// body (here `_Thinking:_ **Plan**`) is dropped from the committed rows —
+// the failing assertion shows only `Say hello.` survives, while Linux
+// (where `useThread` is forced off in `@opentui/core/testing`) and macOS
+// both pass.
+//
+// Investigation summary (see PR description for the link to this work):
+//   1. `reasoning("Thinking: ...", "progress")` enters `entry.body.ts`
+//      `reasoningBody`, which becomes a `code` body with filetype="markdown".
+//   2. `RunScrollbackStream.writeStreaming` sets `renderable.content = ...`
+//      while `streaming=true`. `CodeRenderable.set content` short-circuits
+//      (does NOT call `textBuffer.setText`) when streaming, drawUnstyledText
+//      is false, and a filetype is set — it relies on the next
+//      `startHighlight()` cycle to populate the buffer.
+//   3. `ScrollbackSurface.settle()` renders the surface, kicks the
+//      highlight via `renderSelf` → `startHighlight`, waits on
+//      `highlightingDone`, and re-renders. With `MockTreeSitterClient`
+//      returning `{highlights: []}`, the final branch (`else
+//      this.textBuffer.setText(content)`) populates the buffer and
+//      `_shouldRenderTextBuffer = true`.
+//   4. `flushActive` then commits rows `[0, surface.height - 1)` during
+//      streaming. On Windows the committed rows are blank for the first
+//      paragraph — suggesting the height/text-buffer state is observed
+//      before/after the highlight resolution in a way that drops rows on
+//      that platform.
+//
+// The Linux pass path takes `useThread = false` (see
+// `@opentui/core/testing.js` line ~540) which serializes the FFI render
+// thread. macOS passes despite `useThread = true`, so the divergence is
+// likely either Bun's microtask scheduling on Windows or a Zig-side
+// threading interaction during the second `renderSurface()` pass in
+// `settleSurface`. A real fix probably belongs in opentui (either force
+// `useThread=false` for testing on Windows, or eagerly call
+// `textBuffer.setText` in `CodeRenderable.set content` when streaming
+// updates a non-empty body).
+//
+// Skipping on win32 unblocks unrelated PRs; the assertion is still
+// exercised on Linux and macOS in CI.
+test.skipIf(process.platform === "win32")(
+  "renders replayed user, reasoning, and assistant output after completion",
+  async () => {
+    const out = await setup()
 
-  try {
-    const lines: string[] = []
-    const take = () => {
-      const commits = claim(out.renderer)
-      try {
-        lines.push(...commits.flatMap((commit) => renderRows(commit).flatMap((row) => row.split("\n"))))
-      } finally {
-        destroy(commits)
+    try {
+      const lines: string[] = []
+      const take = () => {
+        const commits = claim(out.renderer)
+        try {
+          lines.push(...commits.flatMap((commit) => renderRows(commit).flatMap((row) => row.split("\n"))))
+        } finally {
+          destroy(commits)
+        }
       }
-    }
 
-    await out.scrollback.append(user("Hello you"))
-    take()
-    await out.scrollback.append(reasoning("Thinking: **Plan**\n\nSay hello.", "progress"))
-    await out.scrollback.complete()
-    take()
-    await out.scrollback.append(assistant("Hello.", "progress"))
-    await out.scrollback.complete()
-    take()
+      await out.scrollback.append(user("Hello you"))
+      take()
+      await out.scrollback.append(reasoning("Thinking: **Plan**\n\nSay hello.", "progress"))
+      await out.scrollback.complete()
+      take()
+      await out.scrollback.append(assistant("Hello.", "progress"))
+      await out.scrollback.complete()
+      take()
 
-    const output = lines.join("\n")
-    expect(output).toContain("› Hello you")
-    expect(output).toContain("Thinking:")
-    expect(output).toContain("Plan")
-    expect(output).toContain("Hello.")
-  } finally {
-    out.scrollback.destroy()
-  }
-})
+      const output = lines.join("\n")
+      expect(output).toContain("› Hello you")
+      expect(output).toContain("Thinking:")
+      expect(output).toContain("Plan")
+      expect(output).toContain("Hello.")
+    } finally {
+      out.scrollback.destroy()
+    }
+  },
+)
 
 test("coalesces same-line tool progress into one snapshot", async () => {
   const out = await setup()