362 lines
7.4 KiB
Go
362 lines
7.4 KiB
Go
/*
|
|
* ============================================================================
|
|
* Projekt.....: rs2322tcp
|
|
* Datei.......: data_handler_test.go
|
|
* Copyright (C) 2026 Dieter Lang
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* Beschreibung:
|
|
* Tests für die Verbindung von DataListener, TCP-Datenverbindung und
|
|
* serieller Geräteverbindung.
|
|
* ============================================================================
|
|
*/
|
|
package server_test
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"net"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.lang-dieter.de/rs2322tcp/internal/server"
|
|
)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Test constants
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
const testHardwareErrorResponse = "TEST ERROR RESPONSE"
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Test serial connection
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
type handlerTestSerial struct {
|
|
reader *bytes.Reader
|
|
|
|
mu sync.Mutex
|
|
writer bytes.Buffer
|
|
closed bool
|
|
|
|
closeCh chan struct{}
|
|
closeOnce sync.Once
|
|
}
|
|
|
|
func newHandlerTestSerial(data []byte) *handlerTestSerial {
|
|
return &handlerTestSerial{
|
|
reader: bytes.NewReader(data),
|
|
closeCh: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
func (s *handlerTestSerial) Read(p []byte) (int, error) {
|
|
s.mu.Lock()
|
|
|
|
if s.closed {
|
|
s.mu.Unlock()
|
|
return 0, io.ErrClosedPipe
|
|
}
|
|
|
|
n, err := s.reader.Read(p)
|
|
|
|
s.mu.Unlock()
|
|
|
|
if err != io.EOF {
|
|
return n, err
|
|
}
|
|
|
|
<-s.closeCh
|
|
|
|
return 0, io.ErrClosedPipe
|
|
}
|
|
|
|
func (s *handlerTestSerial) Write(p []byte) (int, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
if s.closed {
|
|
return 0, io.ErrClosedPipe
|
|
}
|
|
|
|
return s.writer.Write(p)
|
|
}
|
|
|
|
func (s *handlerTestSerial) Close() error {
|
|
s.closeOnce.Do(func() {
|
|
s.mu.Lock()
|
|
s.closed = true
|
|
s.mu.Unlock()
|
|
|
|
close(s.closeCh)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *handlerTestSerial) WrittenData() []byte {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
data := make([]byte, s.writer.Len())
|
|
|
|
copy(data, s.writer.Bytes())
|
|
|
|
return data
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Constructor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
func TestNewDataHandler(t *testing.T) {
|
|
listener, err := server.NewDataListener()
|
|
if err != nil {
|
|
t.Fatalf("NewDataListener() failed: %v", err)
|
|
}
|
|
defer listener.Close()
|
|
|
|
serial := newHandlerTestSerial(nil)
|
|
|
|
handler, err := server.NewDataHandler(
|
|
listener,
|
|
func() (io.ReadWriteCloser, error) {
|
|
return serial, nil
|
|
},
|
|
testHardwareErrorResponse,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("NewDataHandler() failed: %v", err)
|
|
}
|
|
|
|
if handler == nil {
|
|
t.Fatal("NewDataHandler() returned nil")
|
|
}
|
|
}
|
|
|
|
func TestNewDataHandlerRejectsNilListener(t *testing.T) {
|
|
handler, err := server.NewDataHandler(
|
|
nil,
|
|
func() (io.ReadWriteCloser, error) {
|
|
return newHandlerTestSerial(nil), nil
|
|
},
|
|
testHardwareErrorResponse,
|
|
)
|
|
|
|
if err == nil {
|
|
t.Fatal("NewDataHandler() succeeded, want error")
|
|
}
|
|
|
|
if handler != nil {
|
|
t.Fatal("NewDataHandler() returned handler despite error")
|
|
}
|
|
}
|
|
|
|
func TestNewDataHandlerRejectsNilFactory(t *testing.T) {
|
|
listener, err := server.NewDataListener()
|
|
if err != nil {
|
|
t.Fatalf("NewDataListener() failed: %v", err)
|
|
}
|
|
defer listener.Close()
|
|
|
|
handler, err := server.NewDataHandler(
|
|
listener,
|
|
nil,
|
|
testHardwareErrorResponse,
|
|
)
|
|
|
|
if err == nil {
|
|
t.Fatal("NewDataHandler() succeeded, want error")
|
|
}
|
|
|
|
if handler != nil {
|
|
t.Fatal("NewDataHandler() returned handler despite error")
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// TCP -> Serial
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
func TestDataHandlerTCPToSerial(t *testing.T) {
|
|
listener, err := server.NewDataListener()
|
|
if err != nil {
|
|
t.Fatalf("NewDataListener() failed: %v", err)
|
|
}
|
|
defer listener.Close()
|
|
|
|
serial := newHandlerTestSerial(nil)
|
|
|
|
handler, err := server.NewDataHandler(
|
|
listener,
|
|
func() (io.ReadWriteCloser, error) {
|
|
return serial, nil
|
|
},
|
|
testHardwareErrorResponse,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("NewDataHandler() failed: %v", err)
|
|
}
|
|
|
|
serveDone := make(chan error, 1)
|
|
|
|
go func() {
|
|
serveDone <- handler.Serve()
|
|
}()
|
|
|
|
tcpConn, err := net.Dial("tcp", listener.Addr().String())
|
|
if err != nil {
|
|
t.Fatalf("net.Dial() failed: %v", err)
|
|
}
|
|
|
|
testData := []byte("handler tcp to serial")
|
|
|
|
if _, err := tcpConn.Write(testData); err != nil {
|
|
t.Fatalf("TCP Write() failed: %v", err)
|
|
}
|
|
|
|
deadline := time.Now().Add(time.Second)
|
|
|
|
for {
|
|
if bytes.Equal(serial.WrittenData(), testData) {
|
|
break
|
|
}
|
|
|
|
if time.Now().After(deadline) {
|
|
t.Fatalf(
|
|
"serial data = %q, want %q",
|
|
serial.WrittenData(),
|
|
testData,
|
|
)
|
|
}
|
|
|
|
time.Sleep(time.Millisecond)
|
|
}
|
|
|
|
_ = tcpConn.Close()
|
|
_ = listener.Close()
|
|
|
|
select {
|
|
case <-serveDone:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("DataHandler.Serve() did not terminate")
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Serial -> TCP
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
func TestDataHandlerSerialToTCP(t *testing.T) {
|
|
listener, err := server.NewDataListener()
|
|
if err != nil {
|
|
t.Fatalf("NewDataListener() failed: %v", err)
|
|
}
|
|
defer listener.Close()
|
|
|
|
testData := []byte("handler serial to tcp")
|
|
|
|
serial := newHandlerTestSerial(testData)
|
|
|
|
handler, err := server.NewDataHandler(
|
|
listener,
|
|
func() (io.ReadWriteCloser, error) {
|
|
return serial, nil
|
|
},
|
|
testHardwareErrorResponse,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("NewDataHandler() failed: %v", err)
|
|
}
|
|
|
|
serveDone := make(chan error, 1)
|
|
|
|
go func() {
|
|
serveDone <- handler.Serve()
|
|
}()
|
|
|
|
tcpConn, err := net.Dial("tcp", listener.Addr().String())
|
|
if err != nil {
|
|
t.Fatalf("net.Dial() failed: %v", err)
|
|
}
|
|
|
|
buffer := make([]byte, len(testData))
|
|
|
|
if _, err := io.ReadFull(tcpConn, buffer); err != nil {
|
|
t.Fatalf("TCP Read() failed: %v", err)
|
|
}
|
|
|
|
if !bytes.Equal(buffer, testData) {
|
|
t.Fatalf(
|
|
"TCP data = %q, want %q",
|
|
buffer,
|
|
testData,
|
|
)
|
|
}
|
|
|
|
_ = tcpConn.Close()
|
|
_ = listener.Close()
|
|
|
|
select {
|
|
case <-serveDone:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("DataHandler.Serve() did not terminate")
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Serial factory error
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
func TestDataHandlerSerialFactoryError(t *testing.T) {
|
|
listener, err := server.NewDataListener()
|
|
if err != nil {
|
|
t.Fatalf("NewDataListener() failed: %v", err)
|
|
}
|
|
defer listener.Close()
|
|
|
|
handler, err := server.NewDataHandler(
|
|
listener,
|
|
func() (io.ReadWriteCloser, error) {
|
|
return nil, io.ErrClosedPipe
|
|
},
|
|
testHardwareErrorResponse,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("NewDataHandler() failed: %v", err)
|
|
}
|
|
|
|
serveDone := make(chan error, 1)
|
|
|
|
go func() {
|
|
serveDone <- handler.Serve()
|
|
}()
|
|
|
|
tcpConn, err := net.Dial("tcp", listener.Addr().String())
|
|
if err != nil {
|
|
t.Fatalf("net.Dial() failed: %v", err)
|
|
}
|
|
|
|
// The handler must close the TCP connection when the serial factory
|
|
// fails.
|
|
buffer := make([]byte, 1)
|
|
|
|
_ = tcpConn.SetReadDeadline(time.Now().Add(time.Second))
|
|
|
|
_, readErr := tcpConn.Read(buffer)
|
|
|
|
if readErr == nil {
|
|
t.Fatal("TCP connection remained open after serial factory error")
|
|
}
|
|
|
|
_ = tcpConn.Close()
|
|
_ = listener.Close()
|
|
|
|
select {
|
|
case <-serveDone:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("DataHandler.Serve() did not terminate")
|
|
}
|
|
}
|