1//go:build darwin
2
3package rawterm
4
5import (
6 "os"
7 "syscall"
8 "testing"
9 "unsafe"
10)
11
12// openPTYPair opens a real Darwin PTY master/slave pair through the package
13// under test, exercising the grant, unlock, name, and open protocol.
14func openPTYPair(t *testing.T) (master, slave *os.File) {
15 t.Helper()
16
17 masterFD, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
18 if err != nil {
19 t.Skipf("open /dev/ptmx: %v", err)
20 }
21 master = os.NewFile(uintptr(masterFD), "pty-master")
22
23 slave, err = PreparePty(masterFD)
24 if err != nil {
25 _ = master.Close()
26 t.Fatalf("PreparePty: %v", err)
27 }
28
29 return master, slave
30}
31
32func TestPreparePtyOpensSlave(t *testing.T) {
33 master, slave := openPTYPair(t)
34 defer func() { _ = master.Close() }()
35 defer func() { _ = slave.Close() }()
36
37 if !IsTerminal(int(slave.Fd())) {
38 t.Error("PreparePty slave is not a terminal")
39 }
40}
41
42func TestMakeRawRestore(t *testing.T) {
43 master, slave := openPTYPair(t)
44 defer func() { _ = master.Close() }()
45 defer func() { _ = slave.Close() }()
46
47 fd := int(slave.Fd())
48 old, err := MakeRaw(fd)
49 if err != nil {
50 t.Fatalf("MakeRaw: %v", err)
51 }
52 if old == nil {
53 t.Fatal("MakeRaw returned nil state")
54 }
55
56 var raw syscall.Termios
57 if err := ioctl(fd, reqGetTermios, unsafe.Pointer(&raw)); err != nil {
58 t.Fatalf("get termios after MakeRaw: %v", err)
59 }
60 if raw.Lflag&(syscall.ECHO|syscall.ICANON) != 0 {
61 t.Errorf("Lflag after MakeRaw = %#x, ECHO and ICANON must be clear", raw.Lflag)
62 }
63 if raw.Cc[syscall.VMIN] != 1 || raw.Cc[syscall.VTIME] != 0 {
64 t.Errorf("Cc[VMIN, VTIME] = [%d, %d], want [1, 0]", raw.Cc[syscall.VMIN], raw.Cc[syscall.VTIME])
65 }
66
67 if err := Restore(fd, old); err != nil {
68 t.Fatalf("Restore: %v", err)
69 }
70 var restored syscall.Termios
71 if err := ioctl(fd, reqGetTermios, unsafe.Pointer(&restored)); err != nil {
72 t.Fatalf("get termios after Restore: %v", err)
73 }
74 if restored != old.termios {
75 t.Error("Restore did not restore the original termios settings")
76 }
77}
78
79func TestSetWinsizeGetSizeRoundTrip(t *testing.T) {
80 master, slave := openPTYPair(t)
81 defer func() { _ = master.Close() }()
82 defer func() { _ = slave.Close() }()
83
84 tests := []struct {
85 name string
86 cols uint16
87 rows uint16
88 }{
89 {name: "typical", cols: 80, rows: 24},
90 {name: "wide", cols: 220, rows: 60},
91 {name: "minimal", cols: 1, rows: 1},
92 }
93 for _, tt := range tests {
94 t.Run(tt.name, func(t *testing.T) {
95 if err := SetWinsize(int(slave.Fd()), tt.cols, tt.rows); err != nil {
96 t.Fatalf("SetWinsize: %v", err)
97 }
98 cols, rows, err := GetSize(int(slave.Fd()))
99 if err != nil {
100 t.Fatalf("GetSize: %v", err)
101 }
102 if cols != int(tt.cols) || rows != int(tt.rows) {
103 t.Errorf("GetSize = (%d, %d), want (%d, %d)", cols, rows, tt.cols, tt.rows)
104 }
105 })
106 }
107}