| // 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)) |
| } |
| } |