Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d7f08bedd | ||
|
|
a22457ce5f |
@@ -747,6 +747,7 @@ func (device *Device) GetMany(ctx context.Context, names []string) ([]DeviceProp
|
||||
return nil, err
|
||||
}
|
||||
byIdentity := make(map[propertyIdentity]PropertyResult, len(chunkResults))
|
||||
duplicateIdentities := make(map[propertyIdentity]struct{})
|
||||
requested := make(map[propertyIdentity]struct{}, end-start)
|
||||
for _, request := range requests[start:end] {
|
||||
requested[propertyIdentity{did: request.DID, siid: request.SIID, piid: request.PIID}] = struct{}{}
|
||||
@@ -757,17 +758,24 @@ func (device *Device) GetMany(ctx context.Context, names []string) ([]DeviceProp
|
||||
return nil, fmt.Errorf("get properties protocol error: unexpected identity (%s,%d,%d)", result.DID, result.SIID, result.PIID)
|
||||
}
|
||||
if _, duplicate := byIdentity[identity]; duplicate {
|
||||
return nil, fmt.Errorf("get properties protocol error: duplicate identity (%s,%d,%d)", result.DID, result.SIID, result.PIID)
|
||||
duplicateIdentities[identity] = struct{}{}
|
||||
continue
|
||||
}
|
||||
byIdentity[identity] = result
|
||||
}
|
||||
for index, request := range requests[start:end] {
|
||||
identity := propertyIdentity{did: request.DID, siid: request.SIID, piid: request.PIID}
|
||||
name := names[start+index]
|
||||
if _, duplicate := duplicateIdentities[identity]; duplicate {
|
||||
results[start+index] = DevicePropertyResult{Name: name, Code: PropertyResultCodeDuplicate}
|
||||
continue
|
||||
}
|
||||
result, ok := byIdentity[identity]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("get properties protocol error: missing identity (%s,%d,%d)", request.DID, request.SIID, request.PIID)
|
||||
results[start+index] = DevicePropertyResult{Name: name, Code: PropertyResultCodeMissing}
|
||||
continue
|
||||
}
|
||||
results[start+index] = DevicePropertyResult{Name: names[start+index], Value: result.Value, Code: result.Code}
|
||||
results[start+index] = DevicePropertyResult{Name: name, Value: result.Value, Code: result.Code}
|
||||
}
|
||||
}
|
||||
if err := device.wait(ctx); err != nil {
|
||||
|
||||
+51
-6
@@ -18,6 +18,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ = DevicePropertyResult{"x", nil, 0}
|
||||
|
||||
type deviceTestServer struct {
|
||||
t *testing.T
|
||||
fixture []byte
|
||||
@@ -625,26 +627,55 @@ func TestDeviceGetManyMatchesIdentityAndPreservesBusinessErrors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceGetManyRejectsInvalidResponseIdentities(t *testing.T) {
|
||||
func TestDeviceGetManyClassifiesMissingAndDuplicateResults(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
response string
|
||||
want []DevicePropertyResult
|
||||
}{
|
||||
{name: "missing", response: `[{"did":"a","siid":2,"piid":1,"value":true,"code":0}]`},
|
||||
{name: "duplicate", response: `[{"did":"a","siid":2,"piid":1,"value":true,"code":0},{"did":"a","siid":2,"piid":1,"value":false,"code":0}]`},
|
||||
{name: "extra", response: `[{"did":"a","siid":2,"piid":1,"value":true,"code":0},{"did":"a","siid":2,"piid":2,"value":5,"code":0},{"did":"other","siid":9,"piid":9,"value":1,"code":0}]`},
|
||||
{
|
||||
name: "missing",
|
||||
response: `[{"did":"a","siid":2,"piid":1,"value":true,"code":0}]`,
|
||||
want: []DevicePropertyResult{{"power", true, 0}, {"brightness", nil, PropertyResultCodeMissing}},
|
||||
},
|
||||
{
|
||||
name: "duplicate",
|
||||
response: `[{"did":"a","siid":2,"piid":1,"value":true,"code":0},{"did":"a","siid":2,"piid":1,"value":false,"code":0},{"did":"a","siid":2,"piid":2,"value":5,"code":0}]`,
|
||||
want: []DevicePropertyResult{{"power", nil, PropertyResultCodeDuplicate}, {"brightness", json.Number("5"), 0}},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
device := fixtureDeviceWithResults(t, []string{test.response}, 0)
|
||||
results, err := device.GetMany(context.Background(), []string{"power", "brightness"})
|
||||
if err == nil || results != nil || !strings.Contains(err.Error(), "protocol") {
|
||||
t.Fatalf("GetMany() = %#v, %v, want nil protocol error", results, err)
|
||||
if err != nil || !reflect.DeepEqual(results, test.want) {
|
||||
t.Fatalf("GetMany() = %#v, %v, want %#v, nil", results, err, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceGetManyRejectsExtraResult(t *testing.T) {
|
||||
device := fixtureDeviceWithResults(t, []string{`[
|
||||
{"did":"a","siid":2,"piid":1,"value":true,"code":0},
|
||||
{"did":"a","siid":2,"piid":2,"value":5,"code":0},
|
||||
{"did":"other","siid":9,"piid":9,"value":1,"code":0}
|
||||
]`}, 0)
|
||||
results, err := device.GetMany(context.Background(), []string{"power", "brightness"})
|
||||
if err == nil || results != nil || !strings.Contains(err.Error(), "protocol") {
|
||||
t.Fatalf("GetMany() = %#v, %v, want nil protocol error", results, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceGetManyReturnsTransportError(t *testing.T) {
|
||||
device, testServer := fixtureDeviceWithServer(t, nil)
|
||||
testServer.server.Close()
|
||||
|
||||
results, err := device.GetMany(context.Background(), []string{"power", "brightness"})
|
||||
if err == nil || results != nil {
|
||||
t.Fatalf("GetMany() = %#v, %v, want nil transport error", results, err)
|
||||
}
|
||||
}
|
||||
func TestDeviceGetManyValidatesBeforeNetwork(t *testing.T) {
|
||||
device, testServer := fixtureDeviceWithServer(t, nil)
|
||||
tests := [][]string{{"power", "power"}, {"power", "missing"}, {"power", "write-only"}}
|
||||
@@ -692,6 +723,20 @@ func TestDeviceGetManyWaitsOnceAfterAllChunks(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceGetManyWaitsOnceWithPartialProtocolErrors(t *testing.T) {
|
||||
device := fixtureDeviceWithResults(t, []string{`[{"did":"a","siid":2,"piid":1,"value":true,"code":0}]`}, 40*time.Millisecond)
|
||||
|
||||
started := time.Now()
|
||||
results, err := device.GetMany(context.Background(), []string{"power", "brightness"})
|
||||
elapsed := time.Since(started)
|
||||
if err != nil || len(results) != 2 || results[1].Code != PropertyResultCodeMissing {
|
||||
t.Fatalf("GetMany() = %#v, %v", results, err)
|
||||
}
|
||||
if elapsed < 30*time.Millisecond || elapsed >= 75*time.Millisecond {
|
||||
t.Fatalf("GetMany() delay = %v, want one approximately 40ms wait", elapsed)
|
||||
}
|
||||
}
|
||||
|
||||
func batchPropertyFixture(count int) (map[string]PropertySpec, []string) {
|
||||
properties := make(map[string]PropertySpec, count)
|
||||
names := make([]string, count)
|
||||
|
||||
@@ -5,9 +5,17 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// PropertyResultCodeMissing classifies a missing GetMany result locally and is never returned by upstream.
|
||||
PropertyResultCodeMissing int = math.MinInt32
|
||||
// PropertyResultCodeDuplicate classifies duplicate GetMany results locally and is never returned by upstream.
|
||||
PropertyResultCodeDuplicate int = math.MinInt32 + 1
|
||||
)
|
||||
|
||||
type Home struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
||||
Reference in New Issue
Block a user