class Termisu::Testing::Pty

Overview

Allocates a pseudo-terminal and spawns a program attached to it, exposing the master side as an ordinary IO::FileDescriptor (read it for the child's output; write to it to deliver input).

SAFETY: spawning goes through Crystal's Process (a fork+exec the runtime is built for, child reaped via Process#wait). We never call a raw fork/forkpty — unsafe inside a GC'd, fibered runtime. The child adopts the PTY slave as its controlling terminal via the ctty-exec shim (see ctty_exec.cr), so a Termisu app's open("/dev/tty") resolves to our PTY.

Defined in:

termisu/testing/pty.cr

Constant Summary

CTTY_EXEC_SRC = {{ read_file("/home/runner/work/termisu/termisu/src/termisu/testing/ctty_exec.cr") }}

Embedded shim source, so we don't depend on the .cr file existing at runtime.

Constructors

Class Method Summary

Instance Method Summary

Constructor Detail

def self.new(command : String, args : Array(String) = [] of String, *, cols : Int32 = 80, rows : Int32 = 24, env : Process::Env = nil) #

Spawns command (with args) on a fresh PTY sized cols x rows.


[View source]

Class Method Detail

def self.ctty_exec_path : String #

[View source]

Instance Method Detail

def close : Nil #

Hangs up the child and releases the master fd. Idempotent.


[View source]
def closed? : Bool #

[View source]
def master : IO::FileDescriptor #

The master side of the PTY. A PTY master is a character device, which Crystal treats as non-blocking, so fiber reads yield through the event loop.


[View source]
def process : Process #

The spawned child. Signalling/reaping always target exactly this process.


[View source]
def reap : Int32 | Nil #

Waits for the child to exit and returns its exit code (nil if signalled). Cached; intended to be called after the master reports EOF.


[View source]
def resize(cols : Int32, rows : Int32) : Nil #

Tells the kernel (and child) about new terminal geometry via TIOCSWINSZ.


[View source]
def write(data : String | Bytes) : Nil #

Writes input bytes to the child, flushing immediately (keystrokes must not sit buffered).


[View source]