blob: 82c44a9fcf844a314f339d5ec766958b4a5546fa [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can
// found in the LICENSE file.
package serial
import (
"bufio"
"context"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
func createFakeServer(socketPath string, serialDevice *os.File, serialPath string) (*Server, error) {
if err := os.Remove(socketPath); err != nil && !os.IsNotExist(err) {
return nil, err
}
addr := &net.UnixAddr{
Name: socketPath,
Net: "unix",
}
l, err := net.ListenUnix("unix", addr)
if err != nil {
return nil, err
}
server := &Server{
serialPath: serialPath,
serialDevice: serialDevice,
listener: l,
clientFds: make(map[int]bool),
}
return server, nil
}
func createSerialAndSocket() (string, string, error) {
testDir, err := ioutil.TempDir("", "serialTests")
if err != nil {
return "", "", err
}
fakeSerialPath := filepath.Join(testDir, "bogus")
socketPath := filepath.Join(testDir, "tmp.sock")
f, err := os.Create(fakeSerialPath)
if err != nil {
return "", "", err
}
f.Close()
f2, err := os.Create(socketPath)
if err != nil {
return "", "", err
}
f2.Close()
return fakeSerialPath, socketPath, nil
}
func TestReadMultiplexing(t *testing.T) {
// Emulate a read only serial device using a pipe.
serial, device, err := os.Pipe()
if err != nil {
t.Fatalf("failed to create pipe: %v", err)
}
// Create a fake serial server.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
fakeSerialPath, socketPath, err := createSerialAndSocket()
if err != nil {
t.Fatalf("failed to set up fake serial line and socket: %v", err)
}
defer os.RemoveAll(filepath.Dir(fakeSerialPath))
server, err := createFakeServer(socketPath, serial, fakeSerialPath)
if err != nil {
t.Fatalf("failed to create fake serial server: %v", err)
}
go server.Run(ctx)
// Spin up multiple connections to the server.
expectedString := "Hello World!\n"
errs := make(chan error)
numConnections := 10
for i := 0; i < numConnections; i++ {
conn, err := net.Dial("unix", socketPath)
if err != nil {
t.Fatalf("failed to connect to serial mux: %v", err)
}
// Read on loop.
b := bufio.NewReader(conn)
go func() {
line, err := b.ReadString('\n')
if err != nil {
t.Fatalf("could not read from connection: %v", err)
}
if line != expectedString {
errs <- fmt.Errorf("expected %s, got %s", expectedString, line)
return
}
errs <- nil
}()
}
// Sleep to let the serial server start up.
time.Sleep(1 * time.Second)
// Write to the serial device.
if _, err := device.WriteString(expectedString); err != nil {
t.Fatalf("failed to write to device: %v", err)
}
for i := 0; i < numConnections; i++ {
if err := <-errs; err != nil {
t.Error(err)
}
}
}
func TestWriteMultiplexing(t *testing.T) {
// Emulate a write only serial device using a pipe.
device, serial, err := os.Pipe()
if err != nil {
t.Fatalf("failed to create pipe: %v", err)
}
// Create a fake serial server.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
fakeSerialPath, socketPath, err := createSerialAndSocket()
if err != nil {
t.Fatalf("failed to set up fake serial line and socket: %v", err)
}
defer os.RemoveAll(filepath.Dir(fakeSerialPath))
server, err := createFakeServer(socketPath, serial, fakeSerialPath)
if err != nil {
t.Fatalf("failed to create fake serial server: %v", err)
}
go server.Run(ctx)
// Spin up multiple connections to the server.
numConnections := 10
for i := 0; i < numConnections; i++ {
conn, err := net.Dial("unix", socketPath)
if err != nil {
t.Fatalf("failed to connect to serial mux: %v", err)
}
go conn.Write([]byte("Hello"))
}
// Ensure that we got 10 hellos on the device.
expected := strings.Repeat("Hello", numConnections)
buf := make([]byte, len(expected))
if _, err := io.ReadFull(device, buf); err != nil {
t.Fatalf("failed to read from device: %v", err)
}
if string(buf) != expected {
t.Errorf("expected %s in serial device, got %s", expected, string(buf))
}
}