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/24 14:47:02 UTC

[plc4x] branch develop updated: test(plc4go/spi): added test for DriverManager

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 693f84698c test(plc4go/spi): added test for DriverManager
693f84698c is described below

commit 693f84698cae94c431edda290bffb332ce2b93a4
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Wed May 24 16:46:48 2023 +0200

    test(plc4go/spi): added test for DriverManager
---
 plc4go/pkg/api/driverManager_test.go  | 832 ++++++++++++++++++++++++++++++++++
 plc4go/pkg/api/mock_Transport_test.go | 194 ++++++++
 plc4go/pkg/api/mock_requirements.go   |  31 ++
 plc4go/spi/options/DiscoveryOption.go |  26 ++
 4 files changed, 1083 insertions(+)

diff --git a/plc4go/pkg/api/driverManager_test.go b/plc4go/pkg/api/driverManager_test.go
new file mode 100644
index 0000000000..f5f4efed66
--- /dev/null
+++ b/plc4go/pkg/api/driverManager_test.go
@@ -0,0 +1,832 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package plc4go
+
+import (
+	"context"
+	"github.com/apache/plc4x/plc4go/spi/utils"
+	"testing"
+	"time"
+
+	"github.com/apache/plc4x/plc4go/pkg/api/model"
+	"github.com/apache/plc4x/plc4go/spi/options"
+	"github.com/apache/plc4x/plc4go/spi/transports"
+
+	"github.com/pkg/errors"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+func TestNewPlcDriverManager(t *testing.T) {
+	tests := []struct {
+		name string
+		want PlcDriverManager
+	}{
+		{
+			name: "create one",
+			want: &plcDriverManger{
+				drivers:    map[string]PlcDriver{},
+				transports: map[string]transports.Transport{},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := NewPlcDriverManager(); !assert.Equal(t, got, tt.want) {
+				t.Errorf("NewPlcDriverManager() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestWithDiscoveryOptionDeviceName(t *testing.T) {
+	type args struct {
+		deviceName string
+	}
+	tests := []struct {
+		name string
+		args args
+		want WithDiscoveryOption
+	}{
+		{
+			name: "with it",
+			args: args{
+				deviceName: "eth0",
+			},
+			want: withDiscoveryOption{options.WithDiscoveryOptionDeviceName("eth0")},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := WithDiscoveryOptionDeviceName(tt.args.deviceName); !assert.Equal(t, got, tt.want) {
+				t.Errorf("WithDiscoveryOptionDeviceName() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestWithDiscoveryOptionLocalAddress(t *testing.T) {
+	type args struct {
+		localAddress string
+	}
+	tests := []struct {
+		name string
+		args args
+		want WithDiscoveryOption
+	}{
+		{
+			name: "with it",
+			args: args{
+				localAddress: "1.1.1.1",
+			},
+			want: withDiscoveryOption{options.WithDiscoveryOptionLocalAddress("1.1.1.1")},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := WithDiscoveryOptionLocalAddress(tt.args.localAddress); !assert.Equal(t, got, tt.want) {
+				t.Errorf("WithDiscoveryOptionLocalAddress() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestWithDiscoveryOptionProtocol(t *testing.T) {
+	type args struct {
+		protocolName string
+	}
+	tests := []struct {
+		name string
+		args args
+		want WithDiscoveryOption
+	}{
+		{
+			name: "with it",
+			args: args{
+				protocolName: "test",
+			},
+			want: withDiscoveryOption{options.WithDiscoveryOptionProtocol("test")},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := WithDiscoveryOptionProtocol(tt.args.protocolName); !assert.Equal(t, got, tt.want) {
+				t.Errorf("WithDiscoveryOptionProtocol() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestWithDiscoveryOptionProtocolSpecific(t *testing.T) {
+	type args struct {
+		key   string
+		value any
+	}
+	tests := []struct {
+		name string
+		args args
+		want WithDiscoveryOption
+	}{
+		{
+			name: "with it",
+			args: args{
+				key:   "some option",
+				value: 13,
+			},
+			want: withDiscoveryOption{options.WithDiscoveryOptionProtocolSpecific("some option", 13)},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := WithDiscoveryOptionProtocolSpecific(tt.args.key, tt.args.value); !assert.Equal(t, got, tt.want) {
+				t.Errorf("WithDiscoveryOptionProtocolSpecific() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestWithDiscoveryOptionRemoteAddress(t *testing.T) {
+	type args struct {
+		remoteAddress string
+	}
+	tests := []struct {
+		name string
+		args args
+		want WithDiscoveryOption
+	}{
+		{
+			name: "with it",
+			args: args{
+				remoteAddress: "127.0.0.1",
+			},
+			want: withDiscoveryOption{options.WithDiscoveryOptionRemoteAddress("127.0.0.1")},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := WithDiscoveryOptionRemoteAddress(tt.args.remoteAddress); !assert.Equal(t, got, tt.want) {
+				t.Errorf("WithDiscoveryOptionRemoteAddress() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestWithDiscoveryOptionTransport(t *testing.T) {
+	type args struct {
+		transportName string
+	}
+	tests := []struct {
+		name string
+		args args
+		want WithDiscoveryOption
+	}{
+		{
+			name: "with it",
+			args: args{
+				transportName: "udp",
+			},
+			want: withDiscoveryOption{options.WithDiscoveryOptionTransport("udp")},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := WithDiscoveryOptionTransport(tt.args.transportName); !assert.Equal(t, got, tt.want) {
+				t.Errorf("WithDiscoveryOptionTransport() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_convertToInternalOptions(t *testing.T) {
+	type args struct {
+		withDiscoveryOptions []WithDiscoveryOption
+	}
+	tests := []struct {
+		name string
+		args args
+		want []options.WithDiscoveryOption
+	}{
+		{
+			name: "convert nothing",
+			want: []options.WithDiscoveryOption{},
+		},
+		{
+			name: "convert something",
+			args: args{
+				withDiscoveryOptions: []WithDiscoveryOption{
+					WithDiscoveryOptionRemoteAddress("remote"),
+					WithDiscoveryOptionTransport("udp"),
+				},
+			},
+			want: []options.WithDiscoveryOption{
+				options.WithDiscoveryOptionRemoteAddress("remote"),
+				options.WithDiscoveryOptionTransport("udp"),
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := convertToInternalOptions(tt.args.withDiscoveryOptions...); !assert.Equal(t, got, tt.want) {
+				t.Errorf("convertToInternalOptions() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_plcConnectionConnectResult_GetConnection(t *testing.T) {
+	type fields struct {
+		connection PlcConnection
+		err        error
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		want   PlcConnection
+	}{
+		{
+			name: "get it",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			d := &plcConnectionConnectResult{
+				connection: tt.fields.connection,
+				err:        tt.fields.err,
+			}
+			if got := d.GetConnection(); !assert.Equal(t, got, tt.want) {
+				t.Errorf("GetConnection() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_plcConnectionConnectResult_GetErr(t *testing.T) {
+	type fields struct {
+		connection PlcConnection
+		err        error
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		wantErr bool
+	}{
+		{
+			name: "get it",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			d := &plcConnectionConnectResult{
+				connection: tt.fields.connection,
+				err:        tt.fields.err,
+			}
+			if err := d.GetErr(); (err != nil) != tt.wantErr {
+				t.Errorf("GetErr() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func Test_plcDriverManger_Discover(t *testing.T) {
+	type fields struct {
+		drivers    map[string]PlcDriver
+		transports map[string]transports.Transport
+	}
+	type args struct {
+		callback         func(event model.PlcDiscoveryItem)
+		discoveryOptions []WithDiscoveryOption
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+	}{
+		{
+			name: "discover it",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			m := &plcDriverManger{
+				drivers:    tt.fields.drivers,
+				transports: tt.fields.transports,
+			}
+			if err := m.Discover(tt.args.callback, tt.args.discoveryOptions...); (err != nil) != tt.wantErr {
+				t.Errorf("Discover() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func Test_plcDriverManger_DiscoverWithContext(t *testing.T) {
+	type fields struct {
+		drivers    map[string]PlcDriver
+		transports map[string]transports.Transport
+	}
+	type args struct {
+		ctx              context.Context
+		callback         func(event model.PlcDiscoveryItem)
+		discoveryOptions []WithDiscoveryOption
+	}
+	tests := []struct {
+		name      string
+		fields    fields
+		args      args
+		mockSetup func(t *testing.T, fields *fields, args *args)
+		wantErr   bool
+	}{
+		{
+			name: "discover it",
+			fields: fields{
+				drivers: map[string]PlcDriver{},
+			},
+			args: args{
+				ctx: context.Background(),
+				callback: func(event model.PlcDiscoveryItem) {
+					// No-op
+				},
+				discoveryOptions: []WithDiscoveryOption{
+					WithDiscoveryOptionTransport("test"),
+					WithDiscoveryOptionProtocol("test"),
+				},
+			},
+			mockSetup: func(t *testing.T, fields *fields, args *args) {
+				driver := NewMockPlcDriver(t)
+				expect := driver.EXPECT()
+				expect.GetProtocolName().Return("test")
+				expect.SupportsDiscovery().Return(true)
+				expect.DiscoverWithContext(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
+				fields.drivers["test"] = driver
+			},
+		},
+		{
+			name: "discover it with an error",
+			fields: fields{
+				drivers: map[string]PlcDriver{},
+			},
+			args: args{
+				ctx: context.Background(),
+				callback: func(event model.PlcDiscoveryItem) {
+					// No-op
+				},
+				discoveryOptions: []WithDiscoveryOption{
+					WithDiscoveryOptionTransport("test"),
+					WithDiscoveryOptionProtocol("test"),
+				},
+			},
+			mockSetup: func(t *testing.T, fields *fields, args *args) {
+				driver := NewMockPlcDriver(t)
+				expect := driver.EXPECT()
+				expect.GetProtocolName().Return("test")
+				expect.SupportsDiscovery().Return(true)
+				expect.DiscoverWithContext(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("Uh no"))
+				fields.drivers["test"] = driver
+			},
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if tt.mockSetup != nil {
+				tt.mockSetup(t, &tt.fields, &tt.args)
+			}
+			m := &plcDriverManger{
+				drivers:    tt.fields.drivers,
+				transports: tt.fields.transports,
+			}
+			if err := m.DiscoverWithContext(tt.args.ctx, tt.args.callback, tt.args.discoveryOptions...); (err != nil) != tt.wantErr {
+				t.Errorf("DiscoverWithContext() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func Test_plcDriverManger_GetConnection(t *testing.T) {
+	type fields struct {
+		drivers    map[string]PlcDriver
+		transports map[string]transports.Transport
+	}
+	type args struct {
+		connectionString string
+	}
+	tests := []struct {
+		name         string
+		fields       fields
+		args         args
+		mockSetup    func(t *testing.T, fields *fields, args *args)
+		wantVerifier func(t *testing.T, results <-chan PlcConnectionConnectResult) bool
+	}{
+		{
+			name: "get one with wrong url",
+			args: args{
+				connectionString: "~:/?#[]@!$&'()*+,;=\n",
+			},
+			wantVerifier: func(t *testing.T, results <-chan PlcConnectionConnectResult) bool {
+				timeout := time.NewTimer(3 * time.Second)
+				t.Cleanup(func() {
+					utils.CleanupTimer(timeout)
+				})
+				select {
+				case <-timeout.C:
+					t.Error("timeout")
+				case result := <-results:
+					assert.NotNil(t, result)
+					assert.Nil(t, result.GetConnection())
+					assert.NotNil(t, result.GetErr())
+				}
+				return true
+			},
+		},
+		{
+			name: "get one without a driver",
+			wantVerifier: func(t *testing.T, results <-chan PlcConnectionConnectResult) bool {
+				timeout := time.NewTimer(3 * time.Second)
+				t.Cleanup(func() {
+					utils.CleanupTimer(timeout)
+				})
+				select {
+				case <-timeout.C:
+					t.Error("timeout")
+				case result := <-results:
+					assert.NotNil(t, result)
+					assert.Nil(t, result.GetConnection())
+					assert.NotNil(t, result.GetErr())
+				}
+				return true
+			},
+		},
+		{
+			name: "get one with a driver",
+			fields: fields{
+				drivers: map[string]PlcDriver{},
+			},
+			args: args{
+				connectionString: "test://something",
+			},
+			mockSetup: func(t *testing.T, fields *fields, args *args) {
+				driver := NewMockPlcDriver(t)
+				expect := driver.EXPECT()
+				expect.GetProtocolName().Return("test")
+				expect.GetDefaultTransport().Return("test")
+				results := make(chan PlcConnectionConnectResult, 1)
+				result := NewMockPlcConnectionConnectResult(t)
+				result.EXPECT().GetConnection().Return(nil)
+				result.EXPECT().GetErr().Return(nil)
+				results <- result
+				expect.GetConnection(mock.Anything, mock.Anything, mock.Anything).Return(results)
+				fields.drivers["test"] = driver
+			},
+			wantVerifier: func(t *testing.T, results <-chan PlcConnectionConnectResult) bool {
+				timeout := time.NewTimer(3 * time.Second)
+				t.Cleanup(func() {
+					utils.CleanupTimer(timeout)
+				})
+				select {
+				case <-timeout.C:
+					t.Error("timeout")
+				case result := <-results:
+					assert.NotNil(t, result)
+					assert.Nil(t, result.GetConnection())
+					assert.Nil(t, result.GetErr())
+				}
+				return true
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if tt.mockSetup != nil {
+				tt.mockSetup(t, &tt.fields, &tt.args)
+			}
+			m := &plcDriverManger{
+				drivers:    tt.fields.drivers,
+				transports: tt.fields.transports,
+			}
+			if got := m.GetConnection(tt.args.connectionString); !tt.wantVerifier(t, got) {
+				t.Errorf("GetConnection() = %v", got)
+			}
+		})
+	}
+}
+
+func Test_plcDriverManger_GetDriver(t *testing.T) {
+	type fields struct {
+		drivers    map[string]PlcDriver
+		transports map[string]transports.Transport
+	}
+	type args struct {
+		driverName string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		want    PlcDriver
+		wantErr bool
+	}{
+		{
+			name:    "get it (not there)",
+			wantErr: true,
+		},
+		{
+			name: "get it",
+			fields: fields{
+				drivers: map[string]PlcDriver{
+					"": nil,
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			m := &plcDriverManger{
+				drivers:    tt.fields.drivers,
+				transports: tt.fields.transports,
+			}
+			got, err := m.GetDriver(tt.args.driverName)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("GetDriver() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !assert.Equal(t, got, tt.want) {
+				t.Errorf("GetDriver() got = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_plcDriverManger_GetTransport(t *testing.T) {
+	type fields struct {
+		drivers    map[string]PlcDriver
+		transports map[string]transports.Transport
+	}
+	type args struct {
+		transportName string
+		in1           string
+		in2           map[string][]string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		want    transports.Transport
+		wantErr bool
+	}{
+		{
+			name:    "get it (no transport)",
+			wantErr: true,
+		},
+		{
+			name: "get it",
+			fields: fields{
+				transports: map[string]transports.Transport{
+					"": nil,
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			m := &plcDriverManger{
+				drivers:    tt.fields.drivers,
+				transports: tt.fields.transports,
+			}
+			got, err := m.GetTransport(tt.args.transportName, tt.args.in1, tt.args.in2)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("GetTransport() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !assert.Equal(t, got, tt.want) {
+				t.Errorf("GetTransport() got = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_plcDriverManger_ListDriverNames(t *testing.T) {
+	type fields struct {
+		drivers    map[string]PlcDriver
+		transports map[string]transports.Transport
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		want   []string
+	}{
+		{
+			name: "list em",
+		},
+		{
+			name: "list em all",
+			fields: fields{
+				drivers: map[string]PlcDriver{
+					"test": nil,
+				},
+			},
+			want: []string{"test"},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			m := &plcDriverManger{
+				drivers:    tt.fields.drivers,
+				transports: tt.fields.transports,
+			}
+			if got := m.ListDriverNames(); !assert.Equal(t, got, tt.want) {
+				t.Errorf("ListDriverNames() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_plcDriverManger_ListTransportNames(t *testing.T) {
+	type fields struct {
+		drivers    map[string]PlcDriver
+		transports map[string]transports.Transport
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		want   []string
+	}{
+		{
+			name: "list em",
+		},
+		{
+			name: "list em all",
+			fields: fields{
+				transports: map[string]transports.Transport{
+					"test": nil,
+				},
+			},
+			want: []string{"test"},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			m := &plcDriverManger{
+				drivers:    tt.fields.drivers,
+				transports: tt.fields.transports,
+			}
+			if got := m.ListTransportNames(); !assert.Equal(t, got, tt.want) {
+				t.Errorf("ListTransportNames() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_plcDriverManger_RegisterDriver(t *testing.T) {
+	type fields struct {
+		drivers    map[string]PlcDriver
+		transports map[string]transports.Transport
+	}
+	type args struct {
+		driver PlcDriver
+	}
+	tests := []struct {
+		name      string
+		fields    fields
+		args      args
+		mockSetup func(t *testing.T, fields *fields, args *args)
+	}{
+		{
+			name: "register it (already registered)",
+			fields: fields{
+				drivers: map[string]PlcDriver{
+					"test": nil,
+				},
+			},
+			mockSetup: func(t *testing.T, fields *fields, args *args) {
+				driver := NewMockPlcDriver(t)
+				expect := driver.EXPECT()
+				expect.GetProtocolName().Return("test")
+				expect.GetProtocolCode().Return("test")
+				args.driver = driver
+			},
+		},
+		{
+			name: "register it",
+			fields: fields{
+				drivers: map[string]PlcDriver{},
+			},
+			mockSetup: func(t *testing.T, fields *fields, args *args) {
+				driver := NewMockPlcDriver(t)
+				expect := driver.EXPECT()
+				expect.GetProtocolName().Return("test")
+				expect.GetProtocolCode().Return("test")
+				args.driver = driver
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if tt.mockSetup != nil {
+				tt.mockSetup(t, &tt.fields, &tt.args)
+			}
+			m := &plcDriverManger{
+				drivers:    tt.fields.drivers,
+				transports: tt.fields.transports,
+			}
+			m.RegisterDriver(tt.args.driver)
+		})
+	}
+}
+
+func Test_plcDriverManger_RegisterTransport(t *testing.T) {
+	type fields struct {
+		drivers    map[string]PlcDriver
+		transports map[string]transports.Transport
+	}
+	type args struct {
+		transport transports.Transport
+	}
+	tests := []struct {
+		name      string
+		fields    fields
+		args      args
+		mockSetup func(t *testing.T, fields *fields, args *args)
+	}{
+		{
+			name: "register it (already registered)",
+			fields: fields{
+				transports: map[string]transports.Transport{
+					"test": nil,
+				},
+			},
+			mockSetup: func(t *testing.T, fields *fields, args *args) {
+				transport := NewMockTransport(t)
+				transport.EXPECT().GetTransportName().Return("test")
+				transport.EXPECT().GetTransportCode().Return("test")
+				args.transport = transport
+			},
+		},
+		{
+			name: "register it",
+			fields: fields{
+				transports: map[string]transports.Transport{},
+			},
+			mockSetup: func(t *testing.T, fields *fields, args *args) {
+				transport := NewMockTransport(t)
+				transport.EXPECT().GetTransportName().Return("test")
+				transport.EXPECT().GetTransportCode().Return("test")
+				args.transport = transport
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if tt.mockSetup != nil {
+				tt.mockSetup(t, &tt.fields, &tt.args)
+			}
+			m := &plcDriverManger{
+				drivers:    tt.fields.drivers,
+				transports: tt.fields.transports,
+			}
+			m.RegisterTransport(tt.args.transport)
+		})
+	}
+}
+
+func Test_withDiscoveryOption_isDiscoveryOption(t *testing.T) {
+	type fields struct {
+		WithDiscoveryOption options.WithDiscoveryOption
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		want   bool
+	}{
+		{
+			name: "it is",
+			want: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			w := withDiscoveryOption{
+				WithDiscoveryOption: tt.fields.WithDiscoveryOption,
+			}
+			if got := w.isDiscoveryOption(); got != tt.want {
+				t.Errorf("isDiscoveryOption() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/plc4go/pkg/api/mock_Transport_test.go b/plc4go/pkg/api/mock_Transport_test.go
new file mode 100644
index 0000000000..f8af7c2a1f
--- /dev/null
+++ b/plc4go/pkg/api/mock_Transport_test.go
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Code generated by mockery v2.27.1. DO NOT EDIT.
+
+package plc4go
+
+import (
+	url "net/url"
+
+	transports "github.com/apache/plc4x/plc4go/spi/transports"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// MockTransport is an autogenerated mock type for the Transport type
+type MockTransport struct {
+	mock.Mock
+}
+
+type MockTransport_Expecter struct {
+	mock *mock.Mock
+}
+
+func (_m *MockTransport) EXPECT() *MockTransport_Expecter {
+	return &MockTransport_Expecter{mock: &_m.Mock}
+}
+
+// CreateTransportInstance provides a mock function with given fields: transportUrl, options
+func (_m *MockTransport) CreateTransportInstance(transportUrl url.URL, options map[string][]string) (transports.TransportInstance, error) {
+	ret := _m.Called(transportUrl, options)
+
+	var r0 transports.TransportInstance
+	var r1 error
+	if rf, ok := ret.Get(0).(func(url.URL, map[string][]string) (transports.TransportInstance, error)); ok {
+		return rf(transportUrl, options)
+	}
+	if rf, ok := ret.Get(0).(func(url.URL, map[string][]string) transports.TransportInstance); ok {
+		r0 = rf(transportUrl, options)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(transports.TransportInstance)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(url.URL, map[string][]string) error); ok {
+		r1 = rf(transportUrl, options)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// MockTransport_CreateTransportInstance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateTransportInstance'
+type MockTransport_CreateTransportInstance_Call struct {
+	*mock.Call
+}
+
+// CreateTransportInstance is a helper method to define mock.On call
+//   - transportUrl url.URL
+//   - options map[string][]string
+func (_e *MockTransport_Expecter) CreateTransportInstance(transportUrl interface{}, options interface{}) *MockTransport_CreateTransportInstance_Call {
+	return &MockTransport_CreateTransportInstance_Call{Call: _e.mock.On("CreateTransportInstance", transportUrl, options)}
+}
+
+func (_c *MockTransport_CreateTransportInstance_Call) Run(run func(transportUrl url.URL, options map[string][]string)) *MockTransport_CreateTransportInstance_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(url.URL), args[1].(map[string][]string))
+	})
+	return _c
+}
+
+func (_c *MockTransport_CreateTransportInstance_Call) Return(_a0 transports.TransportInstance, _a1 error) *MockTransport_CreateTransportInstance_Call {
+	_c.Call.Return(_a0, _a1)
+	return _c
+}
+
+func (_c *MockTransport_CreateTransportInstance_Call) RunAndReturn(run func(url.URL, map[string][]string) (transports.TransportInstance, error)) *MockTransport_CreateTransportInstance_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// GetTransportCode provides a mock function with given fields:
+func (_m *MockTransport) GetTransportCode() string {
+	ret := _m.Called()
+
+	var r0 string
+	if rf, ok := ret.Get(0).(func() string); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	return r0
+}
+
+// MockTransport_GetTransportCode_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTransportCode'
+type MockTransport_GetTransportCode_Call struct {
+	*mock.Call
+}
+
+// GetTransportCode is a helper method to define mock.On call
+func (_e *MockTransport_Expecter) GetTransportCode() *MockTransport_GetTransportCode_Call {
+	return &MockTransport_GetTransportCode_Call{Call: _e.mock.On("GetTransportCode")}
+}
+
+func (_c *MockTransport_GetTransportCode_Call) Run(run func()) *MockTransport_GetTransportCode_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run()
+	})
+	return _c
+}
+
+func (_c *MockTransport_GetTransportCode_Call) Return(_a0 string) *MockTransport_GetTransportCode_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *MockTransport_GetTransportCode_Call) RunAndReturn(run func() string) *MockTransport_GetTransportCode_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// GetTransportName provides a mock function with given fields:
+func (_m *MockTransport) GetTransportName() string {
+	ret := _m.Called()
+
+	var r0 string
+	if rf, ok := ret.Get(0).(func() string); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	return r0
+}
+
+// MockTransport_GetTransportName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTransportName'
+type MockTransport_GetTransportName_Call struct {
+	*mock.Call
+}
+
+// GetTransportName is a helper method to define mock.On call
+func (_e *MockTransport_Expecter) GetTransportName() *MockTransport_GetTransportName_Call {
+	return &MockTransport_GetTransportName_Call{Call: _e.mock.On("GetTransportName")}
+}
+
+func (_c *MockTransport_GetTransportName_Call) Run(run func()) *MockTransport_GetTransportName_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run()
+	})
+	return _c
+}
+
+func (_c *MockTransport_GetTransportName_Call) Return(_a0 string) *MockTransport_GetTransportName_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *MockTransport_GetTransportName_Call) RunAndReturn(run func() string) *MockTransport_GetTransportName_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+type mockConstructorTestingTNewMockTransport interface {
+	mock.TestingT
+	Cleanup(func())
+}
+
+// NewMockTransport creates a new instance of MockTransport. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+func NewMockTransport(t mockConstructorTestingTNewMockTransport) *MockTransport {
+	mock := &MockTransport{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/plc4go/pkg/api/mock_requirements.go b/plc4go/pkg/api/mock_requirements.go
new file mode 100644
index 0000000000..acd59053bc
--- /dev/null
+++ b/plc4go/pkg/api/mock_requirements.go
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package plc4go
+
+import (
+	"github.com/apache/plc4x/plc4go/spi/transports"
+)
+
+// Note this file is a Helper for mockery to generate use mocks from other package
+
+// Deprecated: don't use it in productive code
+type Transport interface {
+	transports.Transport
+}
diff --git a/plc4go/spi/options/DiscoveryOption.go b/plc4go/spi/options/DiscoveryOption.go
index c9873f90e9..929b401c96 100644
--- a/plc4go/spi/options/DiscoveryOption.go
+++ b/plc4go/spi/options/DiscoveryOption.go
@@ -19,6 +19,8 @@
 
 package options
 
+import "fmt"
+
 // WithDiscoveryOption is a marker interface for options regarding discovery
 type WithDiscoveryOption interface {
 	isDiscoveryOption() bool
@@ -153,6 +155,10 @@ func (d discoveryOptionProtocol) GetProtocolName() string {
 	return d.protocolName
 }
 
+func (d discoveryOptionProtocol) String() string {
+	return d.protocolName
+}
+
 type DiscoveryOptionTransport interface {
 	GetTransportName() string
 }
@@ -166,6 +172,10 @@ func (d discoveryOptionTransport) GetTransportName() string {
 	return d.transportName
 }
 
+func (d discoveryOptionTransport) String() string {
+	return d.transportName
+}
+
 type DiscoveryOptionDeviceName interface {
 	GetDeviceName() string
 }
@@ -179,6 +189,10 @@ func (d discoveryOptionDeviceName) GetDeviceName() string {
 	return d.deviceName
 }
 
+func (d discoveryOptionDeviceName) String() string {
+	return d.deviceName
+}
+
 type DiscoveryOptionLocalAddress interface {
 	GetLocalAddress() string
 }
@@ -192,6 +206,10 @@ func (d discoveryOptionLocalAddress) GetLocalAddress() string {
 	return d.localAddress
 }
 
+func (d discoveryOptionLocalAddress) String() string {
+	return d.localAddress
+}
+
 type DiscoveryOptionRemoteAddress interface {
 	GetRemoteAddress() string
 }
@@ -205,6 +223,10 @@ func (d discoveryOptionRemoteAddress) GetRemoteAddress() string {
 	return d.remoteAddress
 }
 
+func (d discoveryOptionRemoteAddress) String() string {
+	return d.remoteAddress
+}
+
 type DiscoveryOptionProtocolSpecific interface {
 	GetKey() string
 	GetValue() any
@@ -224,6 +246,10 @@ func (d discoveryOptionProtocolSpecific) GetValue() any {
 	return d.value
 }
 
+func (d discoveryOptionProtocolSpecific) String() any {
+	return fmt.Sprintf("%s:%v", d.key, d.value)
+}
+
 //
 // Internal section
 //