blob: b33225c8337a828be17d39b52d7d5e2b4e02c909 [file] [log] [blame]
/*
* 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 simulated
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/apache/plc4x/plc4go/internal/s7"
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/s7/readwrite/model"
simulatedReadWriteModel "github.com/apache/plc4x/plc4go/protocols/simulated/readwrite/model"
spiModel "github.com/apache/plc4x/plc4go/spi/model"
"github.com/apache/plc4x/plc4go/spi/options"
"github.com/apache/plc4x/plc4go/spi/testutils"
spiValues "github.com/apache/plc4x/plc4go/spi/values"
)
func TestReader_Read(t *testing.T) {
type fields struct {
device *Device
options map[string][]string
}
type args struct {
fields map[string]apiModel.PlcTag
fieldNames []string
}
tests := []struct {
name string
fields fields
args args
want apiModel.PlcReadResponse
delayAtLeast time.Duration
}{
{
name: "simple state",
fields: fields{
device: &Device{
Name: "hurz",
State: map[simulatedTag]*apiValues.PlcValue{
NewSimulatedTag(TagState, "test", simulatedReadWriteModel.SimulatedDataTypeSizes_BOOL, 1).(simulatedTag): ToReference(spiValues.NewPlcBOOL(true)),
},
},
options: map[string][]string{},
},
args: args{
fields: map[string]apiModel.PlcTag{
"test": NewSimulatedTag(TagState, "test", simulatedReadWriteModel.SimulatedDataTypeSizes_BOOL, 1),
},
fieldNames: []string{"test"},
},
want: spiModel.NewDefaultPlcReadResponse(nil,
map[string]apiModel.PlcResponseCode{
"test": apiModel.PlcResponseCode_OK,
},
map[string]apiValues.PlcValue{
"test": spiValues.NewPlcBOOL(true),
}),
delayAtLeast: 0,
},
{
name: "simple state delayed",
fields: fields{
device: &Device{
Name: "hurz",
State: map[simulatedTag]*apiValues.PlcValue{
NewSimulatedTag(TagState, "test", simulatedReadWriteModel.SimulatedDataTypeSizes_BOOL, 1).(simulatedTag): ToReference(spiValues.NewPlcBOOL(true)),
},
},
options: map[string][]string{
"readDelay": {"1000"},
},
},
args: args{
fields: map[string]apiModel.PlcTag{
"test": NewSimulatedTag(TagState, "test", simulatedReadWriteModel.SimulatedDataTypeSizes_BOOL, 1),
},
fieldNames: []string{"test"},
},
want: spiModel.NewDefaultPlcReadResponse(nil,
map[string]apiModel.PlcResponseCode{
"test": apiModel.PlcResponseCode_OK,
},
map[string]apiValues.PlcValue{
"test": spiValues.NewPlcBOOL(true),
}),
delayAtLeast: 1000,
},
{
name: "state not found",
fields: fields{
device: &Device{
Name: "hurz",
State: map[simulatedTag]*apiValues.PlcValue{
NewSimulatedTag(TagState, "test", simulatedReadWriteModel.SimulatedDataTypeSizes_BOOL, 1).(simulatedTag): ToReference(spiValues.NewPlcBOOL(true)),
},
},
options: map[string][]string{},
},
args: args{
fields: map[string]apiModel.PlcTag{
"test": NewSimulatedTag(TagState, "lalala", simulatedReadWriteModel.SimulatedDataTypeSizes_BOOL, 1),
},
fieldNames: []string{"test"},
},
want: spiModel.NewDefaultPlcReadResponse(nil,
map[string]apiModel.PlcResponseCode{
"test": apiModel.PlcResponseCode_NOT_FOUND,
},
map[string]apiValues.PlcValue{
"test": nil,
}),
delayAtLeast: 0,
},
// Passing in a completely wrong type of tag.
{
name: "invalid tag type",
fields: fields{
device: &Device{
Name: "hurz",
State: map[simulatedTag]*apiValues.PlcValue{
NewSimulatedTag(TagState, "test", simulatedReadWriteModel.SimulatedDataTypeSizes_BOOL, 1).(simulatedTag): ToReference(spiValues.NewPlcBOOL(true)),
},
},
options: map[string][]string{},
},
args: args{
fields: map[string]apiModel.PlcTag{
"test": s7.NewTag(readWriteModel.MemoryArea_DATA_BLOCKS, 1, 1, 0, 1, readWriteModel.TransportSize_BOOL),
},
fieldNames: []string{"test"},
},
want: spiModel.NewDefaultPlcReadResponse(nil,
map[string]apiModel.PlcResponseCode{
"test": apiModel.PlcResponseCode_INVALID_ADDRESS,
},
map[string]apiValues.PlcValue{
"test": nil,
}),
delayAtLeast: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := NewReader(tt.fields.device, tt.fields.options, nil, options.WithCustomLogger(testutils.ProduceTestingLogger(t)))
readRequest := spiModel.NewDefaultPlcReadRequest(tt.args.fields, tt.args.fieldNames, r, nil)
timeBeforeReadRequest := time.Now()
readResponseChannel := r.Read(testutils.TestContext(t), readRequest)
select {
case readResponse := <-readResponseChannel:
timeAfterReadRequest := time.Now()
// If an expected delay was defined, check if closing
// took at least this long.
if tt.delayAtLeast > 0 {
pingTime := timeAfterReadRequest.Sub(timeBeforeReadRequest)
if pingTime < tt.delayAtLeast {
t.Errorf("Reader.Read() completed too fast. Expected at least %v but returned after %v", tt.delayAtLeast, pingTime)
}
}
if !assert.Equal(t, readRequest, readResponse.GetRequest()) {
t.Errorf("Reader.Read() ReadRequest = %v, want %v", readResponse.GetRequest(), readRequest)
}
for _, fieldName := range readRequest.GetTagNames() {
wantCode := tt.want.GetResponseCode(fieldName)
gotCode := readResponse.GetResponse().GetResponseCode(fieldName)
if !assert.Equal(t, wantCode, gotCode) {
t.Errorf("Reader.Read() PlcResponse.ResponseCode = %v, want %v",
readResponse.GetResponse().GetResponseCode(fieldName), tt.want.GetResponseCode(fieldName))
}
wantValue := tt.want.GetValue(fieldName)
gotValue := readResponse.GetResponse().GetValue(fieldName)
if !assert.Equal(t, wantValue, gotValue) {
t.Errorf("Reader.Read() PlcResponse.Value = %v, want %v",
readResponse.GetResponse().GetValue(fieldName), tt.want.GetValue(fieldName))
}
}
case <-t.Context().Done():
t.Errorf("Reader.Read() got timeout")
}
})
}
}