blob: e85a7fbb6c3a7cb2076eee7f1980230499369282 [file]
// Copyright 2026 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//go:build linux
package serial
import (
"bytes"
"errors"
"fmt"
"os"
"testing"
"github.com/creack/pty"
"golang.org/x/sys/unix"
)
func openPTY(t *testing.T) (*os.File, *os.File) {
t.Helper()
primary, secondary, err := pty.Open()
if err != nil {
if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.ENOENT) || errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM) || errors.Is(err, unix.ENXIO) {
t.Skipf("skipping test: pty unavailable in environment: %v", err)
}
t.Fatalf("pty.Open() failed: %v", err)
}
return primary, secondary
}
func TestOpenBaudRate(t *testing.T) {
testCases := []struct {
baudRate int
wantRate uint32
}{
{baudRate: 9600, wantRate: unix.B9600},
{baudRate: 19200, wantRate: unix.B19200},
{baudRate: 38400, wantRate: unix.B38400},
{baudRate: 57600, wantRate: unix.B57600},
{baudRate: 115200, wantRate: unix.B115200},
{baudRate: 230400, wantRate: unix.B230400},
{baudRate: 460800, wantRate: unix.B460800},
{baudRate: 921600, wantRate: unix.B921600},
{baudRate: 1000000, wantRate: unix.B1000000},
{baudRate: 1500000, wantRate: unix.B1500000},
{baudRate: 2000000, wantRate: unix.B2000000},
}
for _, tc := range testCases {
t.Run(fmt.Sprintf("%d", tc.baudRate), func(t *testing.T) {
primary, secondary := openPTY(t)
defer primary.Close()
defer secondary.Close()
port, err := OpenWithOptions(secondary.Name(), tc.baudRate)
if err != nil {
t.Fatalf("OpenWithOptions(%q, %d) failed: %v", secondary.Name(), tc.baudRate, err)
}
defer port.Close()
termios, err := unix.IoctlGetTermios(int(secondary.Fd()), unix.TCGETS)
if err != nil {
t.Fatalf("unix.IoctlGetTermios failed: %v", err)
}
if got := termios.Cflag & unix.CBAUD; got != tc.wantRate {
t.Errorf("Cflag CBAUD mismatch: got %#x, want %#x", got, tc.wantRate)
}
if termios.Cflag&unix.CS8 == 0 {
t.Errorf("expected CS8 to be set in Cflag: %#x", termios.Cflag)
}
if termios.Cflag&unix.CREAD == 0 {
t.Errorf("expected CREAD to be set in Cflag: %#x", termios.Cflag)
}
if termios.Cflag&unix.CLOCAL == 0 {
t.Errorf("expected CLOCAL to be set in Cflag: %#x", termios.Cflag)
}
if termios.Cflag&unix.PARENB != 0 {
t.Errorf("expected PARENB not to be set in Cflag: %#x", termios.Cflag)
}
if termios.Cflag&unix.CSTOPB != 0 {
t.Errorf("expected CSTOPB not to be set in Cflag: %#x", termios.Cflag)
}
})
}
}
func TestOpenUnsupportedBaudRate(t *testing.T) {
if _, err := OpenWithOptions("/dev/null", 12345); err == nil {
t.Errorf("expected error for unsupported baud rate 12345, got nil")
}
}
func TestOpenReadWrite(t *testing.T) {
primary, secondary := openPTY(t)
defer primary.Close()
defer secondary.Close()
port, err := Open(secondary.Name())
if err != nil {
t.Fatalf("Open(%q) failed: %v", secondary.Name(), err)
}
defer port.Close()
// Write to primary, read from port
msg := []byte("hello serial\n")
if _, err := primary.Write(msg); err != nil {
t.Fatalf("primary.Write() failed: %v", err)
}
buf := make([]byte, len(msg))
if _, err := port.Read(buf); err != nil {
t.Fatalf("port.Read() failed: %v", err)
}
if !bytes.Equal(buf, msg) {
t.Errorf("read mismatch: got %q, want %q", string(buf), string(msg))
}
}