/*
 * 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 cbus

import (
	"context"
	"encoding/hex"
	"fmt"
	"net/url"
	"os"
	"sync"
	"sync/atomic"
	"testing"
	"time"

	"github.com/pkg/errors"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	plc4go "github.com/apache/plc4x/plc4go/pkg/api"
	apiModel "github.com/apache/plc4x/plc4go/pkg/api/model"
	apiValues "github.com/apache/plc4x/plc4go/pkg/api/values"
	readWriteModel "github.com/apache/plc4x/plc4go/protocols/cbus/readwrite/model"
	_default "github.com/apache/plc4x/plc4go/spi/default"
	spiModel "github.com/apache/plc4x/plc4go/spi/model"
	"github.com/apache/plc4x/plc4go/spi/testutils"
	"github.com/apache/plc4x/plc4go/spi/transports"
	"github.com/apache/plc4x/plc4go/spi/transports/test"
	spiValues "github.com/apache/plc4x/plc4go/spi/values"
)

func TestNewBrowser(t *testing.T) {
	assert.NotNil(t, NewBrowser(nil))
}

func TestBrowser_BrowseQuery(t *testing.T) {
	type fields struct {
		DefaultBrowser  _default.DefaultBrowser
		connection      plc4go.PlcConnection
		sequenceCounter uint8
	}
	type args struct {
		ctx         context.Context
		interceptor func(result apiModel.PlcBrowseItem) bool
		queryName   string
		query       apiModel.PlcQuery
	}
	tests := []struct {
		name             string
		fields           fields
		args             args
		setup            func(t *testing.T, fields *fields, args *args)
		wantResponseCode apiModel.PlcResponseCode
		wantQueryResults []apiModel.PlcBrowseItem
	}{
		{
			name:             "invalid address",
			wantResponseCode: apiModel.PlcResponseCode_INVALID_ADDRESS,
		},
		{
			name: "non responding browse",
			args: args{
				interceptor: func(result apiModel.PlcBrowseItem) bool {
					// No-OP
					return true
				},
				queryName: "testQuery",
				query:     NewUnitInfoQuery(readWriteModel.NewUnitAddress(2), nil, 1),
			},
			setup: func(t *testing.T, fields *fields, args *args) {
				if os.Getenv("ENABLE_RANDOMLY_FAILING_TESTS") == "" {
					t.Skip("Skipping randomly failing tests")
				}
				_options := testutils.EnrichOptionsWithOptionsForTesting(t)

				transport := test.NewTransport(_options...)
				transportUrl := url.URL{Scheme: "test"}
				transportInstance, err := transport.CreateTransportInstance(transportUrl, nil, _options...)
				require.NoError(t, err)
				t.Cleanup(func() {
					assert.NoError(t, transportInstance.Close())
				})
				type MockState uint8
				const (
					RESET MockState = iota
					APPLICATION_FILTER_1
					APPLICATION_FILTER_2
					INTERFACE_OPTIONS_3
					INTERFACE_OPTIONS_1_PUN
					INTERFACE_OPTIONS_1
					DONE
				)
				currentState := atomic.Value{}
				currentState.Store(RESET)
				stateChangeMutex := sync.Mutex{}
				transportInstance.(*test.TransportInstance).SetWriteInterceptor(func(transportInstance *test.TransportInstance, data []byte) {
					t.Logf("reacting to\n%s", hex.Dump(data))
					stateChangeMutex.Lock()
					defer stateChangeMutex.Unlock()
					switch currentState.Load().(MockState) {
					case RESET:
						t.Log("Dispatching reset echo")
						transportInstance.FillReadBuffer([]byte("~~~\r"))
						currentState.Store(APPLICATION_FILTER_1)
					case APPLICATION_FILTER_1:
						t.Log("Dispatching app1 echo and confirm")
						transportInstance.FillReadBuffer([]byte("@A32100FF\r"))
						transportInstance.FillReadBuffer([]byte("322100AD\r\n"))
						currentState.Store(APPLICATION_FILTER_2)
					case APPLICATION_FILTER_2:
						t.Log("Dispatching app2 echo and confirm")
						transportInstance.FillReadBuffer([]byte("@A32200FF\r"))
						transportInstance.FillReadBuffer([]byte("322200AC\r\n"))
						currentState.Store(INTERFACE_OPTIONS_3)
					case INTERFACE_OPTIONS_3:
						t.Log("Dispatching interface 3 echo and confirm")
						transportInstance.FillReadBuffer([]byte("@A342000A\r"))
						transportInstance.FillReadBuffer([]byte("3242008C\r\n"))
						currentState.Store(INTERFACE_OPTIONS_1_PUN)
					case INTERFACE_OPTIONS_1_PUN:
						t.Log("Dispatching interface 1 PUN echo and confirm???")
						transportInstance.FillReadBuffer([]byte("@A3410079\r"))
						transportInstance.FillReadBuffer([]byte("3241008D\r\n"))
						currentState.Store(INTERFACE_OPTIONS_1)
					case INTERFACE_OPTIONS_1:
						t.Log("Dispatching interface 1 echo and confirm???")
						transportInstance.FillReadBuffer([]byte("@A3300079\r"))
						transportInstance.FillReadBuffer([]byte("3230009E\r\n"))
						currentState.Store(DONE)
					case DONE:
						t.Log("Connection dance done")

						t.Log("Dispatching 3 MMI segments")
						transportInstance.FillReadBuffer([]byte("86020200F900FF0094120006000000000000000008000000000000000000CA\r\n"))
						transportInstance.FillReadBuffer([]byte("86020200F900FF580000000000000000000000000000000000000000000026\r\n"))
						transportInstance.FillReadBuffer([]byte("86020200F700FFB00000000000000000000000000000000000000000D0\r\n"))

						t.Log("Dispatching manufacturer")
						transportInstance.FillReadBuffer([]byte("g.890050435F434E49454422\r\n"))
					}
				})
				err = transport.AddPreregisteredInstances(transportUrl, transportInstance)
				require.NoError(t, err)
				driver := NewDriver(_options...)
				t.Cleanup(func() {
					assert.NoError(t, driver.Close())
				})
				connectionConnectResult := <-driver.GetConnection(transportUrl, map[string]transports.Transport{"test": transport}, map[string][]string{})
				require.NoError(t, connectionConnectResult.GetErr())
				fields.connection = connectionConnectResult.GetConnection()
				t.Cleanup(func() {
					timer := time.NewTimer(10 * time.Second)
					select {
					case <-fields.connection.Close():
					case <-timer.C:
						t.Error("timeout")
					}
				})

				args.ctx = testutils.TestContext(t)
			},
			wantResponseCode: apiModel.PlcResponseCode_OK,
			wantQueryResults: []apiModel.PlcBrowseItem{
				&spiModel.DefaultPlcBrowseItem{
					Tag:      NewCALIdentifyTag(readWriteModel.NewUnitAddress(2), nil, readWriteModel.Attribute_Manufacturer, 1),
					Name:     "testQuery",
					Readable: true,
					Options: map[string]apiValues.PlcValue{
						"CurrentValue": spiValues.NewPlcSTRING("PC_CNIED"),
					},
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.setup != nil {
				tt.setup(t, &tt.fields, &tt.args)
			}
			m := Browser{
				DefaultBrowser:  tt.fields.DefaultBrowser,
				connection:      tt.fields.connection,
				sequenceCounter: tt.fields.sequenceCounter,
				log:             testutils.ProduceTestingLogger(t),
			}
			got, got1 := m.BrowseQuery(tt.args.ctx, tt.args.interceptor, tt.args.queryName, tt.args.query)
			assert.Equalf(t, tt.wantResponseCode, got, "BrowseQuery(%v, func(), %v,\n%v\n)", tt.args.ctx, tt.args.queryName, tt.args.query)
			assert.Equalf(t, tt.wantQueryResults, got1, "BrowseQuery(%v, func(), %v, \n%v\n)", tt.args.ctx, tt.args.queryName, tt.args.query)
			if m.connection != nil && m.connection.IsConnected() {
				t.Log("Closing connection")
				<-m.connection.Close()
			}
		})
	}
}

func TestBrowser_browseUnitInfo(t *testing.T) {
	// TODO: Make this test less flaky.
	t.Skip("This test seems to be continuously randomly failing the build ... ")
	type fields struct {
		DefaultBrowser  _default.DefaultBrowser
		connection      plc4go.PlcConnection
		sequenceCounter uint8
	}
	type args struct {
		ctx         context.Context
		interceptor func(result apiModel.PlcBrowseItem) bool
		queryName   string
		query       *unitInfoQuery
	}
	tests := []struct {
		name             string
		fields           fields
		args             args
		setup            func(t *testing.T, fields *fields, args *args)
		wantResponseCode apiModel.PlcResponseCode
		wantQueryResults []apiModel.PlcBrowseItem
	}{
		{
			name: "non responding browse",
			args: args{
				interceptor: func(result apiModel.PlcBrowseItem) bool {
					// No-OP
					return true
				},
				queryName: "testQuery",
				query:     NewUnitInfoQuery(readWriteModel.NewUnitAddress(2), nil, 1).(*unitInfoQuery),
			},
			setup: func(t *testing.T, fields *fields, args *args) {
				_options := testutils.EnrichOptionsWithOptionsForTesting(t)

				transport := test.NewTransport(_options...)
				transportUrl := url.URL{Scheme: "test"}
				transportInstance, err := transport.CreateTransportInstance(transportUrl, nil, _options...)
				require.NoError(t, err)
				t.Cleanup(func() {
					assert.NoError(t, transportInstance.Close())
				})
				type MockState uint8
				const (
					RESET MockState = iota
					APPLICATION_FILTER_1
					APPLICATION_FILTER_2
					INTERFACE_OPTIONS_3
					INTERFACE_OPTIONS_1_PUN
					INTERFACE_OPTIONS_1
					DONE
				)
				currentState := atomic.Value{}
				currentState.Store(RESET)
				stateChangeMutex := sync.Mutex{}
				transportInstance.(*test.TransportInstance).SetWriteInterceptor(func(transportInstance *test.TransportInstance, data []byte) {
					t.Logf("reacting to\n%s", hex.Dump(data))
					stateChangeMutex.Lock()
					defer stateChangeMutex.Unlock()
					switch currentState.Load().(MockState) {
					case RESET:
						t.Log("Dispatching reset echo")
						transportInstance.FillReadBuffer([]byte("~~~\r"))
						currentState.Store(APPLICATION_FILTER_1)
					case APPLICATION_FILTER_1:
						t.Log("Dispatching app1 echo and confirm")
						transportInstance.FillReadBuffer([]byte("@A32100FF\r"))
						transportInstance.FillReadBuffer([]byte("322100AD\r\n"))
						currentState.Store(APPLICATION_FILTER_2)
					case APPLICATION_FILTER_2:
						t.Log("Dispatching app2 echo and confirm")
						transportInstance.FillReadBuffer([]byte("@A32200FF\r"))
						transportInstance.FillReadBuffer([]byte("322200AC\r\n"))
						currentState.Store(INTERFACE_OPTIONS_3)
					case INTERFACE_OPTIONS_3:
						t.Log("Dispatching interface 3 echo and confirm")
						transportInstance.FillReadBuffer([]byte("@A342000A\r"))
						transportInstance.FillReadBuffer([]byte("3242008C\r\n"))
						currentState.Store(INTERFACE_OPTIONS_1_PUN)
					case INTERFACE_OPTIONS_1_PUN:
						t.Log("Dispatching interface 1 PUN echo and confirm???")
						transportInstance.FillReadBuffer([]byte("@A3410079\r"))
						transportInstance.FillReadBuffer([]byte("3241008D\r\n"))
						currentState.Store(INTERFACE_OPTIONS_1)
					case INTERFACE_OPTIONS_1:
						t.Log("Dispatching interface 1 echo and confirm???")
						transportInstance.FillReadBuffer([]byte("@A3300079\r"))
						transportInstance.FillReadBuffer([]byte("3230009E\r\n"))
						currentState.Store(DONE)
					case DONE:
						t.Log("Connection dance done")

						t.Log("Dispatching 3 MMI segments")
						transportInstance.FillReadBuffer([]byte("86020200F900FF0094120006000000000000000008000000000000000000CA\r\n"))
						transportInstance.FillReadBuffer([]byte("86020200F900FF580000000000000000000000000000000000000000000026\r\n"))
						transportInstance.FillReadBuffer([]byte("86020200F700FFB00000000000000000000000000000000000000000D0\r\n"))

						t.Log("Dispatching manufacturer")
						transportInstance.FillReadBuffer([]byte("g.890050435F434E49454422\r\n"))
					}
				})
				err = transport.AddPreregisteredInstances(transportUrl, transportInstance)
				require.NoError(t, err)
				driver := NewDriver(_options...)
				t.Cleanup(func() {
					assert.NoError(t, driver.Close())
				})
				connectionConnectResult := <-driver.GetConnection(transportUrl, map[string]transports.Transport{"test": transport}, map[string][]string{})
				require.NoError(t, connectionConnectResult.GetErr())
				fields.connection = connectionConnectResult.GetConnection()
				t.Cleanup(func() {
					timer := time.NewTimer(10 * time.Second)
					select {
					case <-fields.connection.Close():
					case <-timer.C:
						t.Error("timeout")
					}
				})

				args.ctx = testutils.TestContext(t)
			},
			wantResponseCode: apiModel.PlcResponseCode_OK,
			wantQueryResults: []apiModel.PlcBrowseItem{
				&spiModel.DefaultPlcBrowseItem{
					Tag:      NewCALIdentifyTag(readWriteModel.NewUnitAddress(2), nil, readWriteModel.Attribute_Manufacturer, 1),
					Name:     "testQuery",
					Readable: true,
					Options: map[string]apiValues.PlcValue{
						"CurrentValue": spiValues.NewPlcSTRING("PC_CNIED"),
					},
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.setup != nil {
				tt.setup(t, &tt.fields, &tt.args)
				t.Log("Setup done")
			}
			m := &Browser{
				DefaultBrowser:  tt.fields.DefaultBrowser,
				connection:      tt.fields.connection,
				sequenceCounter: tt.fields.sequenceCounter,
				log:             testutils.ProduceTestingLogger(t),
			}
			gotResponseCode, gotQueryResults := m.browseUnitInfo(tt.args.ctx, tt.args.interceptor, tt.args.queryName, tt.args.query)
			assert.Equalf(t, tt.wantResponseCode, gotResponseCode, "browseUnitInfo(%v, %v, %v, %v)", tt.args.ctx, tt.args.interceptor != nil, tt.args.queryName, tt.args.query)
			assert.Equalf(t, tt.wantQueryResults, gotQueryResults, "browseUnitInfo(%v, %v, %v, %v)", tt.args.ctx, tt.args.interceptor != nil, tt.args.queryName, tt.args.query)
			if m.connection != nil && m.connection.IsConnected() {
				t.Log("Closing connection")
				<-m.connection.Close()
			}
		})
	}
}

func TestBrowser_extractUnits(t *testing.T) {
	type fields struct {
		DefaultBrowser  _default.DefaultBrowser
		connection      plc4go.PlcConnection
		sequenceCounter uint8
	}
	type args struct {
		ctx                          context.Context
		query                        *unitInfoQuery
		getInstalledUnitAddressBytes func(ctx context.Context) (map[byte]any, error)
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		setup   func(t *testing.T, fields *fields, args *args)
		want    []readWriteModel.UnitAddress
		want1   bool
		wantErr assert.ErrorAssertionFunc
	}{
		{
			name: "one unit",
			args: args{
				query: &unitInfoQuery{
					unitAddress: readWriteModel.NewUnitAddress(2),
				},
			},
			setup: func(t *testing.T, fields *fields, args *args) {
				args.ctx = testutils.TestContext(t)
			},
			want:    []readWriteModel.UnitAddress{readWriteModel.NewUnitAddress(2)},
			want1:   false,
			wantErr: assert.NoError,
		},
		{
			name: "all units error",
			args: args{
				query: &unitInfoQuery{},
				getInstalledUnitAddressBytes: func(ctx context.Context) (map[byte]any, error) {
					return nil, errors.New("not today")
				},
			},
			wantErr: assert.Error,
		},
		{
			name: "all units",
			args: args{
				query: &unitInfoQuery{},
				getInstalledUnitAddressBytes: func(ctx context.Context) (map[byte]any, error) {
					return map[byte]any{0xAF: true, 0xFE: true}, nil
				},
			},
			want:    []readWriteModel.UnitAddress{readWriteModel.NewUnitAddress(0xAF), readWriteModel.NewUnitAddress(0xFE)},
			want1:   true,
			wantErr: assert.NoError,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.setup != nil {
				tt.setup(t, &tt.fields, &tt.args)
			}
			m := Browser{
				DefaultBrowser:  tt.fields.DefaultBrowser,
				connection:      tt.fields.connection,
				sequenceCounter: tt.fields.sequenceCounter,
				log:             testutils.ProduceTestingLogger(t),
			}
			got, got1, err := m.extractUnits(tt.args.ctx, tt.args.query, tt.args.getInstalledUnitAddressBytes)
			if !tt.wantErr(t, err, fmt.Sprintf("extractUnits(%v, \n%v, func())", tt.args.ctx, tt.args.query)) {
				return
			}
			assert.Equalf(t, tt.want, got, "extractUnits(%v, \n%v, func())", tt.args.ctx, tt.args.query)
			assert.Equalf(t, tt.want1, got1, "extractUnits(%v, \n%v, func())", tt.args.ctx, tt.args.query)
		})
	}
}

func TestBrowser_extractAttributes(t *testing.T) {
	type fields struct {
		DefaultBrowser  _default.DefaultBrowser
		connection      plc4go.PlcConnection
		sequenceCounter uint8
	}
	type args struct {
		query *unitInfoQuery
	}
	tests := []struct {
		name   string
		fields fields
		args   args
		want   []readWriteModel.Attribute
		want1  bool
	}{
		{
			name: "one attribute",
			args: args{
				query: &unitInfoQuery{attribute: func() *readWriteModel.Attribute {
					attributeType := readWriteModel.Attribute_Type
					return &attributeType
				}()},
			},
			want:  []readWriteModel.Attribute{readWriteModel.Attribute_Type},
			want1: false,
		},
		{
			name: "all attributes",
			args: args{
				query: &unitInfoQuery{},
			},
			want:  readWriteModel.AttributeValues,
			want1: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			m := Browser{
				DefaultBrowser:  tt.fields.DefaultBrowser,
				connection:      tt.fields.connection,
				sequenceCounter: tt.fields.sequenceCounter,
				log:             testutils.ProduceTestingLogger(t),
			}
			got, got1 := m.extractAttributes(tt.args.query)
			assert.Equalf(t, tt.want, got, "extractAttributes(\n%v)", tt.args.query)
			assert.Equalf(t, tt.want1, got1, "extractAttributes(\n%v)", tt.args.query)
		})
	}
}

func TestBrowser_getInstalledUnitAddressBytes(t *testing.T) {
	type fields struct {
		DefaultBrowser  _default.DefaultBrowser
		connection      plc4go.PlcConnection
		sequenceCounter uint8
	}
	type args struct {
		ctx context.Context
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		setup   func(t *testing.T, fields *fields, args *args)
		want    map[byte]any
		wantErr assert.ErrorAssertionFunc
	}{
		{
			name: "get units",
			setup: func(t *testing.T, fields *fields, args *args) {
				_options := testutils.EnrichOptionsWithOptionsForTesting(t)

				transport := test.NewTransport(_options...)
				transportUrl := url.URL{Scheme: "test"}
				transportInstance, err := transport.CreateTransportInstance(transportUrl, nil, _options...)
				require.NoError(t, err)
				t.Cleanup(func() {
					assert.NoError(t, transportInstance.Close())
				})
				type MockState uint8
				const (
					RESET MockState = iota
					APPLICATION_FILTER_1
					APPLICATION_FILTER_2
					INTERFACE_OPTIONS_3
					INTERFACE_OPTIONS_1_PUN
					INTERFACE_OPTIONS_1
					DONE
				)
				currentState := atomic.Value{}
				currentState.Store(RESET)
				stateChangeMutex := sync.Mutex{}
				transportInstance.(*test.TransportInstance).SetWriteInterceptor(func(transportInstance *test.TransportInstance, data []byte) {
					t.Logf("reacting to\n%s", hex.Dump(data))
					t.Logf("current state %d", currentState.Load())
					stateChangeMutex.Lock()
					defer stateChangeMutex.Unlock()
					switch currentState.Load().(MockState) {
					case RESET:
						t.Log("Dispatching reset echo")
						transportInstance.FillReadBuffer([]byte("~~~\r"))
						currentState.Store(APPLICATION_FILTER_1)
					case APPLICATION_FILTER_1:
						t.Log("Dispatching app1 echo and confirm")
						transportInstance.FillReadBuffer([]byte("@A32100FF\r"))
						transportInstance.FillReadBuffer([]byte("322100AD\r\n"))
						currentState.Store(APPLICATION_FILTER_2)
					case APPLICATION_FILTER_2:
						t.Log("Dispatching app2 echo and confirm")
						transportInstance.FillReadBuffer([]byte("@A32200FF\r"))
						transportInstance.FillReadBuffer([]byte("322200AC\r\n"))
						currentState.Store(INTERFACE_OPTIONS_3)
					case INTERFACE_OPTIONS_3:
						t.Log("Dispatching interface 3 echo and confirm")
						transportInstance.FillReadBuffer([]byte("@A342000A\r"))
						transportInstance.FillReadBuffer([]byte("3242008C\r\n"))
						currentState.Store(INTERFACE_OPTIONS_1_PUN)
					case INTERFACE_OPTIONS_1_PUN:
						t.Log("Dispatching interface 1 PUN echo and confirm???")
						transportInstance.FillReadBuffer([]byte("@A3410079\r"))
						transportInstance.FillReadBuffer([]byte("3241008D\r\n"))
						currentState.Store(INTERFACE_OPTIONS_1)
					case INTERFACE_OPTIONS_1:
						t.Log("Dispatching interface 1 echo and confirm???")
						transportInstance.FillReadBuffer([]byte("@A3300079\r"))
						transportInstance.FillReadBuffer([]byte("3230009E\r\n"))
						currentState.Store(DONE)
					case DONE:
						t.Log("Connection dance done")

						t.Log("Dispatching 3 MMI segments")
						transportInstance.FillReadBuffer([]byte("86020200F900FF0094120006000000000000000008000000000000000000CA\r\n"))
						transportInstance.FillReadBuffer([]byte("86020200F900FF580000000000000000000000000000000000000000000026\r\n"))
						transportInstance.FillReadBuffer([]byte("86020200F700FFB00000000000000000000000000000000000000000D0\r\n"))
					}
				})
				err = transport.AddPreregisteredInstances(transportUrl, transportInstance)
				require.NoError(t, err)
				driver := NewDriver(_options...)
				t.Cleanup(func() {
					assert.NoError(t, driver.Close())
				})
				connectionConnectResult := <-driver.GetConnection(transportUrl, map[string]transports.Transport{"test": transport}, map[string][]string{})
				require.NoError(t, connectionConnectResult.GetErr())
				fields.connection = connectionConnectResult.GetConnection()
				t.Cleanup(func() {
					timer := time.NewTimer(6 * time.Second)
					select {
					case <-fields.connection.Close():
					case <-timer.C:
						t.Error("timeout waiting for connection close")
					}
				})

				args.ctx = testutils.TestContext(t)
			},
			want: map[byte]any{
				1:  true,
				2:  true,
				3:  true,
				4:  true,
				6:  true,
				12: true,
				13: true,
				49: true,
			},
			wantErr: assert.NoError,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.setup != nil {
				tt.setup(t, &tt.fields, &tt.args)
			}
			m := Browser{
				DefaultBrowser:  tt.fields.DefaultBrowser,
				connection:      tt.fields.connection,
				sequenceCounter: tt.fields.sequenceCounter,
				log:             testutils.ProduceTestingLogger(t),
			}
			got, err := m.getInstalledUnitAddressBytes(tt.args.ctx)
			if !tt.wantErr(t, err, fmt.Sprintf("getInstalledUnitAddressBytes(%v)", tt.args.ctx)) {
				return
			}
			assert.Equalf(t, tt.want, got, "getInstalledUnitAddressBytes(%v)", tt.args.ctx)
		})
	}
}
