You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2023/05/08 10:40:16 UTC

[plc4x] branch develop updated: test(plc4go/spi): add test for udp transport

This is an automated email from the ASF dual-hosted git repository.

sruehl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/develop by this push:
     new 7ec93b5095 test(plc4go/spi): add test for udp transport
7ec93b5095 is described below

commit 7ec93b50953f683b88b8ae9f5a98dc2526623626
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Mon May 8 12:40:07 2023 +0200

    test(plc4go/spi): add test for udp transport
---
 plc4go/spi/transports/tcp/Transport_test.go |  11 +-
 plc4go/spi/transports/udp/Transport.go      |  20 +-
 plc4go/spi/transports/udp/Transport_test.go | 441 +++++++++++++++++++++++++++-
 3 files changed, 440 insertions(+), 32 deletions(-)

diff --git a/plc4go/spi/transports/tcp/Transport_test.go b/plc4go/spi/transports/tcp/Transport_test.go
index 3e819581fa..8eb9ae8093 100644
--- a/plc4go/spi/transports/tcp/Transport_test.go
+++ b/plc4go/spi/transports/tcp/Transport_test.go
@@ -25,6 +25,7 @@ import (
 	"fmt"
 	"github.com/apache/plc4x/plc4go/spi/transports"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"golang.org/x/net/nettest"
 	"net"
 	"net/url"
@@ -109,7 +110,7 @@ func TestTransportInstance_Close(t *testing.T) {
 			fields: fields{
 				tcpConn: func() *net.TCPConn {
 					listener, err := nettest.NewLocalListener("tcp")
-					assert.NoError(t, err)
+					require.NoError(t, err)
 					t.Cleanup(func() {
 						assert.NoError(t, listener.Close())
 					})
@@ -117,7 +118,7 @@ func TestTransportInstance_Close(t *testing.T) {
 						_, _ = listener.Accept()
 					}()
 					tcp, err := net.DialTCP("tcp", nil, listener.Addr().(*net.TCPAddr))
-					assert.NoError(t, err)
+					require.NoError(t, err)
 					t.Cleanup(func() {
 						// As we already closed the connection with the whole method this should error
 						assert.Error(t, tcp.Close())
@@ -207,7 +208,7 @@ func TestTransportInstance_ConnectWithContext(t *testing.T) {
 			fields: fields{
 				RemoteAddress: func() *net.TCPAddr {
 					listener, err := nettest.NewLocalListener("tcp")
-					assert.NoError(t, err)
+					require.NoError(t, err)
 					t.Cleanup(func() {
 						assert.NoError(t, listener.Close())
 					})
@@ -401,7 +402,7 @@ func TestTransportInstance_Write(t *testing.T) {
 			fields: fields{
 				tcpConn: func() *net.TCPConn {
 					listener, err := nettest.NewLocalListener("tcp")
-					assert.NoError(t, err)
+					require.NoError(t, err)
 					t.Cleanup(func() {
 						assert.NoError(t, listener.Close())
 					})
@@ -409,7 +410,7 @@ func TestTransportInstance_Write(t *testing.T) {
 						_, _ = listener.Accept()
 					}()
 					tcp, err := net.DialTCP("tcp", nil, listener.Addr().(*net.TCPAddr))
-					assert.NoError(t, err)
+					require.NoError(t, err)
 					t.Cleanup(func() {
 						assert.NoError(t, tcp.Close())
 					})
diff --git a/plc4go/spi/transports/udp/Transport.go b/plc4go/spi/transports/udp/Transport.go
index d869ad621c..caefed21ba 100644
--- a/plc4go/spi/transports/udp/Transport.go
+++ b/plc4go/spi/transports/udp/Transport.go
@@ -139,11 +139,11 @@ func (m *TransportInstance) Connect() error {
 func (m *TransportInstance) ConnectWithContext(ctx context.Context) error {
 	// If we haven't provided a local address, have the system figure it out by dialing
 	// the remote address and then using that connections local address as local address.
-	if m.LocalAddress == nil {
+	if m.LocalAddress == nil && m.RemoteAddress != nil {
 		var d net.Dialer
 		udpTest, err := d.DialContext(ctx, "udp", m.RemoteAddress.String())
 		if err != nil {
-			return errors.Wrap(err, "error connecting to remote address")
+			return errors.Wrapf(err, "error connecting to remote address '%s'", m.RemoteAddress)
 		}
 		m.LocalAddress = udpTest.LocalAddr().(*net.UDPAddr)
 		err = udpTest.Close()
@@ -156,17 +156,17 @@ func (m *TransportInstance) ConnectWithContext(ctx context.Context) error {
 	var err error
 	if m.RemoteAddress != nil {
 		if m.udpConn, err = net.DialUDP("udp", m.LocalAddress, m.RemoteAddress); err != nil {
-			return errors.Wrap(err, "error connecting to remote address")
+			return errors.Wrapf(err, "error connecting to remote address '%s'", m.RemoteAddress)
 		}
-	} else if m.SoReUse {
+	} else if m.SoReUse && m.LocalAddress != nil {
 		if packetConn, err := reuseport.ListenPacket("udp", m.LocalAddress.String()); err != nil {
-			return errors.Wrap(err, "error connecting to local address")
+			return errors.Wrapf(err, "error connecting to local address '%s'", m.LocalAddress)
 		} else {
 			m.udpConn = packetConn.(*net.UDPConn)
 		}
 	} else {
 		if m.udpConn, err = net.ListenUDP("udp", m.LocalAddress); err != nil {
-			return errors.Wrap(err, "error connecting to local address")
+			return errors.Wrapf(err, "error connecting to local address '%s'", m.LocalAddress)
 		}
 	}
 
@@ -251,19 +251,17 @@ func (m *TransportInstance) Write(data []byte) error {
 	}
 	var num int
 	var err error
-	if m.RemoteAddress != nil {
-
+	if m.RemoteAddress == nil {
 		// TODO: usually this happens on the dial port... is there a better way to catch that?
 		num, err = m.udpConn.Write(data)
 	} else {
 		num, err = m.udpConn.WriteToUDP(data, m.RemoteAddress)
 	}
-
 	if err != nil {
-		return errors.Wrap(err, "error writing")
+		return errors.Wrapf(err, "error writing (remote address: %s)", m.RemoteAddress)
 	}
 	if num != len(data) {
-		return errors.New("error writing: not all bytes written")
+		return errors.Errorf("error writing: not all bytes written (Expected %d, Actual %d)", len(data), num)
 	}
 	return nil
 }
diff --git a/plc4go/spi/transports/udp/Transport_test.go b/plc4go/spi/transports/udp/Transport_test.go
index 10fba75736..24ecb55a1a 100644
--- a/plc4go/spi/transports/udp/Transport_test.go
+++ b/plc4go/spi/transports/udp/Transport_test.go
@@ -21,9 +21,13 @@ package udp
 
 import (
 	"bufio"
+	"bytes"
 	"context"
+	"fmt"
 	"github.com/apache/plc4x/plc4go/spi/transports"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"golang.org/x/net/nettest"
 	"net"
 	"net/url"
 	"testing"
@@ -61,7 +65,10 @@ func TestNewTransportInstance(t *testing.T) {
 		args args
 		want *TransportInstance
 	}{
-		// TODO: Add test cases.
+		{
+			name: "create it",
+			want: &TransportInstance{},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -87,7 +94,29 @@ func TestTransportInstance_Close(t *testing.T) {
 		fields  fields
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name: "close it",
+		},
+		{
+			name: "close it failing",
+			fields: fields{
+				udpConn: &net.UDPConn{},
+			},
+			wantErr: true,
+		},
+		{
+			name: "close success",
+			fields: fields{
+				udpConn: func() *net.UDPConn {
+					listener, err := nettest.NewLocalPacketListener("udp")
+					require.NoError(t, err)
+					t.Cleanup(func() {
+						assert.Error(t, listener.Close()) // Note: connection should have been closed
+					})
+					return listener.(*net.UDPConn)
+				}(),
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -122,7 +151,9 @@ func TestTransportInstance_Connect(t *testing.T) {
 		fields  fields
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name: "connect it (error)",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -161,7 +192,104 @@ func TestTransportInstance_ConnectWithContext(t *testing.T) {
 		args    args
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name: "connect it",
+			args: args{ctx: context.Background()},
+		},
+		{
+			name: "connect",
+			fields: fields{
+				RemoteAddress: func() *net.UDPAddr {
+					listener, err := nettest.NewLocalPacketListener("udp")
+					require.NoError(t, err)
+					t.Logf("remote listener %#q", listener.LocalAddr())
+					t.Cleanup(func() {
+						assert.NoError(t, listener.Close())
+					})
+					return listener.LocalAddr().(*net.UDPAddr)
+				}(),
+			},
+			args: args{ctx: context.Background()},
+		},
+		{
+			name: "connect with wrong address", // TODO: not sure how to tests undialable ips here
+			fields: fields{
+				RemoteAddress: &net.UDPAddr{IP: net.IPv4(255, 255, 255, 255), Port: 12},
+			},
+			args: args{ctx: context.Background()},
+		},
+		{
+			name: "connect with localAddress",
+			fields: fields{
+				LocalAddress: func() *net.UDPAddr {
+					listener, err := nettest.NewLocalPacketListener("udp")
+					require.NoError(t, err)
+					t.Logf("local listener %#q", listener.LocalAddr())
+					t.Cleanup(func() {
+						assert.NoError(t, listener.Close())
+					})
+					return listener.LocalAddr().(*net.UDPAddr)
+				}(),
+				RemoteAddress: func() *net.UDPAddr {
+					listener, err := nettest.NewLocalPacketListener("udp")
+					require.NoError(t, err)
+					t.Logf("remote listener %#q", listener.LocalAddr())
+					t.Cleanup(func() {
+						assert.NoError(t, listener.Close())
+					})
+					return listener.LocalAddr().(*net.UDPAddr)
+				}(),
+			},
+			args:    args{ctx: context.Background()},
+			wantErr: true,
+		},
+		{
+			name: "connect reuse",
+			fields: fields{
+				LocalAddress: func() *net.UDPAddr {
+					listener, err := nettest.NewLocalPacketListener("udp")
+					require.NoError(t, err)
+					t.Logf("local listener %#q", listener.LocalAddr())
+					assert.NoError(t, listener.Close()) // We close directly again
+					return listener.LocalAddr().(*net.UDPAddr)
+				}(),
+				SoReUse: true,
+			},
+			args: args{ctx: context.Background()},
+		},
+		{
+			name: "connect reuse (used)",
+			fields: fields{
+				LocalAddress: func() *net.UDPAddr {
+					listener, err := nettest.NewLocalPacketListener("udp")
+					require.NoError(t, err)
+					t.Logf("local listener %#q", listener.LocalAddr())
+					t.Cleanup(func() {
+						assert.NoError(t, listener.Close())
+					})
+					return listener.LocalAddr().(*net.UDPAddr)
+				}(),
+				SoReUse: true,
+			},
+			args:    args{ctx: context.Background()},
+			wantErr: true,
+		},
+		{
+			name: "connect reuse (used)",
+			fields: fields{
+				LocalAddress: func() *net.UDPAddr {
+					listener, err := nettest.NewLocalPacketListener("udp")
+					require.NoError(t, err)
+					t.Logf("local listener %#q", listener.LocalAddr())
+					t.Cleanup(func() {
+						assert.NoError(t, listener.Close())
+					})
+					return listener.LocalAddr().(*net.UDPAddr)
+				}(),
+			},
+			args:    args{ctx: context.Background()},
+			wantErr: true,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -200,7 +328,21 @@ func TestTransportInstance_FillBuffer(t *testing.T) {
 		args    args
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name:    "do it",
+			wantErr: true,
+		},
+		{
+			name: "do it with reader",
+			fields: fields{
+				reader: bufio.NewReader(bytes.NewReader([]byte{1, 2, 3, 4})),
+			},
+			args: args{
+				until: func(pos uint, currentByte byte, reader *bufio.Reader) bool {
+					return pos < 2
+				},
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -236,7 +378,16 @@ func TestTransportInstance_GetNumBytesAvailableInBuffer(t *testing.T) {
 		want    uint32
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name: "get em",
+			fields: fields{
+				reader: bufio.NewReader(bytes.NewReader([]byte{1, 2, 3, 4})),
+			},
+			want: 4,
+		},
+		{
+			name: "get em (no reader)",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -276,7 +427,9 @@ func TestTransportInstance_IsConnected(t *testing.T) {
 		fields fields
 		want   bool
 	}{
-		// TODO: Add test cases.
+		{
+			name: "check it",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -316,7 +469,27 @@ func TestTransportInstance_PeekReadableBytes(t *testing.T) {
 		want    []byte
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name: "peek it",
+			fields: fields{
+				reader: bufio.NewReader(bytes.NewReader([]byte{1, 2, 3, 4})),
+			},
+			want: []byte{},
+		},
+		{
+			name: "peek it 3",
+			fields: fields{
+				reader: bufio.NewReader(bytes.NewReader([]byte{1, 2, 3, 4})),
+			},
+			args: args{
+				numBytes: 3,
+			},
+			want: []byte{1, 2, 3},
+		},
+		{
+			name:    "peek it (no reader)",
+			wantErr: true,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -361,7 +534,37 @@ func TestTransportInstance_Read(t *testing.T) {
 		want    []byte
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name: "read it",
+			fields: fields{
+				reader: bufio.NewReader(bytes.NewReader([]byte{1, 2, 3, 4})),
+			},
+			want: []byte{},
+		},
+		{
+			name: "read it 3",
+			fields: fields{
+				reader: bufio.NewReader(bytes.NewReader([]byte{1, 2, 3, 4})),
+			},
+			args: args{
+				numBytes: 3,
+			},
+			want: []byte{1, 2, 3},
+		},
+		{
+			name: "read it 5",
+			fields: fields{
+				reader: bufio.NewReader(bytes.NewReader([]byte{1, 2, 3, 4})),
+			},
+			args: args{
+				numBytes: 5,
+			},
+			wantErr: true,
+		},
+		{
+			name:    "read it (no reader available)",
+			wantErr: true,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -401,7 +604,18 @@ func TestTransportInstance_String(t *testing.T) {
 		fields fields
 		want   string
 	}{
-		// TODO: Add test cases.
+		{
+			name: "string it",
+			want: "udp:<nil>-><nil>",
+		},
+		{
+			name: "string it with content",
+			fields: fields{
+				LocalAddress:  &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 5},
+				RemoteAddress: &net.UDPAddr{IP: net.IPv4(6, 7, 8, 9), Port: 10},
+			},
+			want: "udp:1.2.3.4:5->6.7.8.9:10",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -440,7 +654,45 @@ func TestTransportInstance_Write(t *testing.T) {
 		args    args
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name:    "write it (no con)",
+			wantErr: true,
+		},
+		{
+			name: "write it",
+			fields: fields{
+				udpConn: func() *net.UDPConn {
+					listener, err := nettest.NewLocalPacketListener("udp")
+					require.NoError(t, err)
+					t.Cleanup(func() {
+						assert.NoError(t, listener.Close())
+					})
+					udp, err := net.DialUDP("udp", nil, listener.LocalAddr().(*net.UDPAddr))
+					require.NoError(t, err)
+					return udp
+				}(),
+			},
+		},
+		{
+			name: "write it with remote",
+			fields: func() fields {
+				listener, err := nettest.NewLocalPacketListener("udp")
+				require.NoError(t, err)
+				t.Cleanup(func() {
+					assert.NoError(t, listener.Close())
+				})
+				remoteAddress := listener.LocalAddr().(*net.UDPAddr)
+				return fields{
+					RemoteAddress: remoteAddress,
+					udpConn: func() *net.UDPConn {
+						udp, err := net.ListenUDP("udp", nil)
+						require.NoError(t, err)
+						return udp
+					}(),
+				}
+			}(),
+			args: args{data: []byte{1, 2, 3, 4}},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -471,7 +723,18 @@ func TestTransport_CreateTransportInstance(t *testing.T) {
 		want    transports.TransportInstance
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name: "create it",
+			want: func() transports.TransportInstance {
+				remoteAddress, err := net.ResolveUDPAddr("udp", ":0")
+				require.NoError(t, err)
+				return &TransportInstance{
+					ConnectTimeout: 1000,
+					RemoteAddress:  remoteAddress,
+					transport:      NewTransport(),
+				}
+			}(),
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -500,7 +763,144 @@ func TestTransport_CreateTransportInstanceForLocalAddress(t *testing.T) {
 		want    transports.TransportInstance
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name: "Create it",
+			want: &TransportInstance{
+				transport:      NewTransport(),
+				RemoteAddress:  &net.UDPAddr{},
+				ConnectTimeout: 1000,
+			},
+		},
+		{
+			name: "Create it with transport url",
+			args: args{
+				transportUrl: url.URL{Host: "127.0.0.1:123"},
+			},
+			want: func() transports.TransportInstance {
+				udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", "127.0.0.1", 123))
+				assert.NoError(t, err)
+				ti := &TransportInstance{
+					transport:      NewTransport(),
+					RemoteAddress:  udpAddr,
+					ConnectTimeout: 1000,
+				}
+				return ti
+			}(),
+		},
+		{
+			name: "Create it with transport url (named host)",
+			args: args{
+				transportUrl: url.URL{Host: "localhost:123"},
+			},
+			want: func() transports.TransportInstance {
+				udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", "localhost", 123))
+				assert.NoError(t, err)
+				ti := &TransportInstance{
+					transport:      NewTransport(),
+					RemoteAddress:  udpAddr,
+					ConnectTimeout: 1000,
+				}
+				return ti
+			}(),
+		},
+		{
+			name: "Create it with transport url (without port)",
+			args: args{
+				transportUrl: url.URL{Host: "127.0.0.1"},
+			},
+			wantErr: true,
+		},
+		{
+			name: "Create it with transport url (with nonsense port)",
+			args: args{
+				transportUrl: url.URL{Host: "127.0.0.1:banana"},
+			},
+			wantErr: true,
+		},
+		{
+			name: "Create it with transport url (with default port)",
+			args: args{
+				transportUrl: url.URL{Host: "127.0.0.1"},
+				options: map[string][]string{
+					"defaultUdpPort": {"123"},
+				},
+			},
+			want: func() transports.TransportInstance {
+				udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", "127.0.0.1", 123))
+				assert.NoError(t, err)
+				ti := &TransportInstance{
+					transport:      NewTransport(),
+					RemoteAddress:  udpAddr,
+					ConnectTimeout: 1000,
+				}
+				return ti
+			}(),
+		},
+		{
+			name: "Create it with transport url (with broken default port)",
+			args: args{
+				transportUrl: url.URL{Host: "127.0.0.1"},
+				options: map[string][]string{
+					"defaultTcpPort": {"default"},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "Create it with transport url (with default port and connection timeout and reuse)",
+			args: args{
+				transportUrl: url.URL{Host: "127.0.0.1"},
+				options: map[string][]string{
+					"defaultUdpPort":  {"123"},
+					"connect-timeout": {"123"},
+					"so-reuse":        {"true"},
+				},
+			},
+			want: func() transports.TransportInstance {
+				udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", "127.0.0.1", 123))
+				assert.NoError(t, err)
+				ti := &TransportInstance{
+					transport:      NewTransport(),
+					RemoteAddress:  udpAddr,
+					ConnectTimeout: 123,
+					SoReUse:        true,
+				}
+				return ti
+			}(),
+		},
+		{
+			name: "Create it with transport url (with default port and connection timeout and reuse broken)",
+			args: args{
+				transportUrl: url.URL{Host: "127.0.0.1"},
+				options: map[string][]string{
+					"defaultUdpPort":  {"123"},
+					"connect-timeout": {"123"},
+					"so-reuse":        {"banana"},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "Create it with transport url (with default port and connection timeout broken)",
+			args: args{
+				transportUrl: url.URL{Host: "127.0.0.1"},
+				options: map[string][]string{
+					"defaultUdpPort":  {"123"},
+					"connect-timeout": {"banana"},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "Create it with unresolvable host",
+			args: args{
+				transportUrl: url.URL{Host: "plc4xhostnothere"},
+				options: map[string][]string{
+					"defaultUdpPort": {"123"},
+				},
+			},
+			wantErr: true,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -522,7 +922,10 @@ func TestTransport_GetTransportCode(t *testing.T) {
 		name string
 		want string
 	}{
-		// TODO: Add test cases.
+		{
+			name: "get it",
+			want: "udp",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -539,7 +942,10 @@ func TestTransport_GetTransportName(t *testing.T) {
 		name string
 		want string
 	}{
-		// TODO: Add test cases.
+		{
+			name: "get it",
+			want: "UDP Datagram Transport",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -556,7 +962,10 @@ func TestTransport_String(t *testing.T) {
 		name string
 		want string
 	}{
-		// TODO: Add test cases.
+		{
+			name: "string it",
+			want: "udp(UDP Datagram Transport)",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {