package loadsim import ( "context" "net" "sync/atomic" "testing" "time" ) func TestRunnerDialsTargetConnectionsAndWritesFrames(t *testing.T) { var writes atomic.Int64 var closed atomic.Int64 runner := Runner{ Dial: func(ctx context.Context, network, addr string) (net.Conn, error) { return &recordingConn{ write: func(p []byte) (int, error) { writes.Add(1) return len(p), nil }, close: func() error { closed.Add(1) return nil }, }, nil }, } stats, err := runner.Run(context.Background(), Config{ Protocol: ProtocolJT808, Addr: "127.0.0.1:808", Connections: 3, ConnectRatePerSecond: 1000, SendInterval: time.Millisecond, Duration: 5 * time.Millisecond, Template: "0200", SendFrames: true, }) if err != nil { t.Fatalf("Run() error = %v", err) } if stats.ConnectionsOpened != 3 { t.Fatalf("ConnectionsOpened = %d, want 3", stats.ConnectionsOpened) } if stats.FramesWritten == 0 { t.Fatalf("FramesWritten = 0, want at least one") } if writes.Load() != int64(stats.FramesWritten) { t.Fatalf("recorded writes = %d, stats = %d", writes.Load(), stats.FramesWritten) } if closed.Load() != 3 { t.Fatalf("closed = %d, want 3", closed.Load()) } } func TestRunnerWritesVariantFrames(t *testing.T) { writes := make(chan []byte, 8) runner := Runner{ Dial: func(ctx context.Context, network, addr string) (net.Conn, error) { return &recordingConn{ write: func(p []byte) (int, error) { cp := append([]byte(nil), p...) select { case writes <- cp: default: } return len(p), nil }, close: func() error { return nil }, }, nil }, } stats, err := runner.Run(context.Background(), Config{ Protocol: ProtocolJT808, Addr: "127.0.0.1:808", Connections: 1, ConnectRatePerSecond: 1000, SendInterval: time.Millisecond, Duration: 6 * time.Millisecond, Template: "0200", SendFrames: true, }) if err != nil { t.Fatalf("Run() error = %v", err) } if stats.FramesWritten < 2 { t.Fatalf("FramesWritten = %d, want at least 2", stats.FramesWritten) } first := <-writes second := <-writes if string(first) == string(second) { t.Fatal("runner wrote byte-identical frames; want per-frame variants") } } func TestRunnerCanHoldConnectionsWithoutWritingFrames(t *testing.T) { var writes atomic.Int64 runner := Runner{ Dial: func(ctx context.Context, network, addr string) (net.Conn, error) { return &recordingConn{ write: func(p []byte) (int, error) { writes.Add(1) return len(p), nil }, close: func() error { return nil }, }, nil }, } stats, err := runner.Run(context.Background(), Config{ Protocol: ProtocolJT808, Addr: "127.0.0.1:808", Connections: 2, ConnectRatePerSecond: 1000, SendInterval: time.Millisecond, Duration: 5 * time.Millisecond, Template: "0200", SendFrames: false, }) if err != nil { t.Fatalf("Run() error = %v", err) } if stats.ConnectionsOpened != 2 { t.Fatalf("ConnectionsOpened = %d, want 2", stats.ConnectionsOpened) } if stats.FramesWritten != 0 || writes.Load() != 0 { t.Fatalf("frames written stats=%d writes=%d, want 0", stats.FramesWritten, writes.Load()) } } func TestRunnerDrainsProtocolResponses(t *testing.T) { runner := Runner{ Dial: func(ctx context.Context, network, addr string) (net.Conn, error) { client, server := net.Pipe() go func() { defer server.Close() buffer := make([]byte, 4096) for { if _, err := server.Read(buffer); err != nil { return } if _, err := server.Write([]byte{0x7e, 0x80, 0x01, 0x7e}); err != nil { return } } }() return client, nil }, } stats, err := runner.Run(context.Background(), Config{ Protocol: ProtocolJT808, Addr: "127.0.0.1:808", Connections: 1, ConnectRatePerSecond: 1000, SendInterval: time.Millisecond, Duration: 10 * time.Millisecond, Template: "0200", SendFrames: true, DrainResponses: true, }) if err != nil { t.Fatalf("Run() error = %v", err) } if stats.ResponseBytes == 0 { t.Fatalf("ResponseBytes = 0, want drained protocol responses") } if stats.ReadErrors != 0 { t.Fatalf("ReadErrors = %d, want 0", stats.ReadErrors) } } type recordingConn struct { write func([]byte) (int, error) close func() error } func (c *recordingConn) Read(p []byte) (int, error) { return 0, context.Canceled } func (c *recordingConn) Write(p []byte) (int, error) { return c.write(p) } func (c *recordingConn) Close() error { return c.close() } func (c *recordingConn) LocalAddr() net.Addr { return testAddr("local") } func (c *recordingConn) RemoteAddr() net.Addr { return testAddr("remote") } func (c *recordingConn) SetDeadline(t time.Time) error { return nil } func (c *recordingConn) SetReadDeadline(t time.Time) error { return nil } func (c *recordingConn) SetWriteDeadline(t time.Time) error { return nil } type testAddr string func (a testAddr) Network() string { return "tcp" } func (a testAddr) String() string { return string(a) }