|
|
@@ -20,9 +20,6 @@ jest.mock("vscode", () => ({
|
|
|
ThemeIcon: jest.fn(),
|
|
|
}))
|
|
|
|
|
|
-const TERMINAL_OUTPUT_LIMIT = 100 * 1024
|
|
|
-const STALL_TIMEOUT = 100
|
|
|
-
|
|
|
describe("TerminalProcess", () => {
|
|
|
let terminalProcess: TerminalProcess
|
|
|
let mockTerminal: jest.Mocked<
|
|
|
@@ -37,7 +34,7 @@ describe("TerminalProcess", () => {
|
|
|
let mockStream: AsyncIterableIterator<string>
|
|
|
|
|
|
beforeEach(() => {
|
|
|
- terminalProcess = new TerminalProcess(TERMINAL_OUTPUT_LIMIT, STALL_TIMEOUT)
|
|
|
+ terminalProcess = new TerminalProcess(100 * 1024)
|
|
|
|
|
|
// Create properly typed mock terminal
|
|
|
mockTerminal = {
|
|
|
@@ -176,156 +173,4 @@ describe("TerminalProcess", () => {
|
|
|
expect(terminalProcess["isListening"]).toBe(false)
|
|
|
})
|
|
|
})
|
|
|
-
|
|
|
- describe("stalled stream handling", () => {
|
|
|
- it("emits stream_stalled event when no output is received within timeout", async () => {
|
|
|
- // Create a promise that resolves when stream_stalled is emitted
|
|
|
- const streamStalledPromise = new Promise<number>((resolve) => {
|
|
|
- terminalProcess.once("stream_stalled", (id: number) => {
|
|
|
- resolve(id)
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- // Create a stream that doesn't emit any data
|
|
|
- mockStream = (async function* () {
|
|
|
- yield "\x1b]633;C\x07" // Command start sequence
|
|
|
- // No data is yielded after this, causing the stall
|
|
|
- await new Promise((resolve) => setTimeout(resolve, STALL_TIMEOUT * 2))
|
|
|
- // This would normally be yielded, but the stall timer will fire first
|
|
|
- yield "Output after stall"
|
|
|
- yield "\x1b]633;D\x07" // Command end sequence
|
|
|
- terminalProcess.emit("shell_execution_complete", mockTerminalInfo.id, { exitCode: 0 })
|
|
|
- })()
|
|
|
-
|
|
|
- mockExecution = {
|
|
|
- read: jest.fn().mockReturnValue(mockStream),
|
|
|
- }
|
|
|
-
|
|
|
- mockTerminal.shellIntegration.executeCommand.mockReturnValue(mockExecution)
|
|
|
-
|
|
|
- // Start the terminal process
|
|
|
- const runPromise = terminalProcess.run(mockTerminal, "test command")
|
|
|
- terminalProcess.emit("stream_available", mockTerminalInfo.id, mockStream)
|
|
|
-
|
|
|
- // Wait for the stream_stalled event
|
|
|
- const stalledId = await streamStalledPromise
|
|
|
-
|
|
|
- // Verify the event was emitted with the correct terminal ID
|
|
|
- expect(stalledId).toBe(mockTerminalInfo.id)
|
|
|
-
|
|
|
- // Complete the run
|
|
|
- await runPromise
|
|
|
- })
|
|
|
-
|
|
|
- it("clears stall timer when output is received", async () => {
|
|
|
- // Spy on the emit method to check if stream_stalled is emitted
|
|
|
- const emitSpy = jest.spyOn(terminalProcess, "emit")
|
|
|
-
|
|
|
- // Create a stream that emits data before the stall timeout
|
|
|
- mockStream = (async function* () {
|
|
|
- yield "\x1b]633;C\x07" // Command start sequence
|
|
|
- yield "Initial output\n" // This should clear the stall timer
|
|
|
-
|
|
|
- // Wait longer than the stall timeout
|
|
|
- await new Promise((resolve) => setTimeout(resolve, STALL_TIMEOUT * 2))
|
|
|
-
|
|
|
- yield "More output\n"
|
|
|
- yield "\x1b]633;D\x07" // Command end sequence
|
|
|
- terminalProcess.emit("shell_execution_complete", mockTerminalInfo.id, { exitCode: 0 })
|
|
|
- })()
|
|
|
-
|
|
|
- mockExecution = {
|
|
|
- read: jest.fn().mockReturnValue(mockStream),
|
|
|
- }
|
|
|
-
|
|
|
- mockTerminal.shellIntegration.executeCommand.mockReturnValue(mockExecution)
|
|
|
-
|
|
|
- // Start the terminal process
|
|
|
- const runPromise = terminalProcess.run(mockTerminal, "test command")
|
|
|
- terminalProcess.emit("stream_available", mockTerminalInfo.id, mockStream)
|
|
|
-
|
|
|
- // Wait for the run to complete
|
|
|
- await runPromise
|
|
|
-
|
|
|
- // Wait a bit longer to ensure the stall timer would have fired if not cleared
|
|
|
- await new Promise((resolve) => setTimeout(resolve, STALL_TIMEOUT * 2))
|
|
|
-
|
|
|
- // Verify stream_stalled was not emitted
|
|
|
- expect(emitSpy).not.toHaveBeenCalledWith("stream_stalled", expect.anything())
|
|
|
- })
|
|
|
-
|
|
|
- it("returns true from flushLine when a line is emitted", async () => {
|
|
|
- // Create a stream with output
|
|
|
- mockStream = (async function* () {
|
|
|
- yield "\x1b]633;C\x07" // Command start sequence
|
|
|
- yield "Test output\n" // This should be flushed as a line
|
|
|
- yield "\x1b]633;D\x07" // Command end sequence
|
|
|
- terminalProcess.emit("shell_execution_complete", mockTerminalInfo.id, { exitCode: 0 })
|
|
|
- })()
|
|
|
-
|
|
|
- mockExecution = {
|
|
|
- read: jest.fn().mockReturnValue(mockStream),
|
|
|
- }
|
|
|
-
|
|
|
- mockTerminal.shellIntegration.executeCommand.mockReturnValue(mockExecution)
|
|
|
-
|
|
|
- // Spy on the flushLine method
|
|
|
- const flushLineSpy = jest.spyOn(terminalProcess as any, "flushLine")
|
|
|
-
|
|
|
- // Spy on the emit method to check if line is emitted
|
|
|
- const emitSpy = jest.spyOn(terminalProcess, "emit")
|
|
|
-
|
|
|
- // Start the terminal process
|
|
|
- const runPromise = terminalProcess.run(mockTerminal, "test command")
|
|
|
- terminalProcess.emit("stream_available", mockTerminalInfo.id, mockStream)
|
|
|
-
|
|
|
- // Wait for the run to complete
|
|
|
- await runPromise
|
|
|
-
|
|
|
- // Verify flushLine was called and returned true
|
|
|
- expect(flushLineSpy).toHaveBeenCalled()
|
|
|
- expect(flushLineSpy.mock.results.some((result) => result.value === true)).toBe(true)
|
|
|
-
|
|
|
- // Verify line event was emitted
|
|
|
- expect(emitSpy).toHaveBeenCalledWith("line", expect.any(String))
|
|
|
- })
|
|
|
-
|
|
|
- it("returns false from flushLine when no line is emitted", async () => {
|
|
|
- // Create a stream with no complete lines
|
|
|
- mockStream = (async function* () {
|
|
|
- yield "\x1b]633;C\x07" // Command start sequence
|
|
|
- yield "Test output" // No newline, so this won't be flushed as a line yet
|
|
|
- yield "\x1b]633;D\x07" // Command end sequence
|
|
|
- terminalProcess.emit("shell_execution_complete", mockTerminalInfo.id, { exitCode: 0 })
|
|
|
- })()
|
|
|
-
|
|
|
- mockExecution = {
|
|
|
- read: jest.fn().mockReturnValue(mockStream),
|
|
|
- }
|
|
|
-
|
|
|
- mockTerminal.shellIntegration.executeCommand.mockReturnValue(mockExecution)
|
|
|
-
|
|
|
- // Create a custom implementation to test flushLine directly
|
|
|
- const testFlushLine = async () => {
|
|
|
- // Create a new instance with the same configuration
|
|
|
- const testProcess = new TerminalProcess(TERMINAL_OUTPUT_LIMIT, STALL_TIMEOUT)
|
|
|
-
|
|
|
- // Set up the output builder with content that doesn't have a newline
|
|
|
- testProcess["outputBuilder"] = {
|
|
|
- readLine: jest.fn().mockReturnValue(""),
|
|
|
- append: jest.fn(),
|
|
|
- reset: jest.fn(),
|
|
|
- content: "Test output",
|
|
|
- } as any
|
|
|
-
|
|
|
- // Call flushLine directly
|
|
|
- const result = testProcess["flushLine"]()
|
|
|
- return result
|
|
|
- }
|
|
|
-
|
|
|
- // Test flushLine directly
|
|
|
- const flushLineResult = await testFlushLine()
|
|
|
- expect(flushLineResult).toBe(false)
|
|
|
- })
|
|
|
- })
|
|
|
})
|