Skip to content

Commit

Permalink
Add equality functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmalloc committed Mar 17, 2023
1 parent 9a7c96a commit 9bf15d6
Show file tree
Hide file tree
Showing 4 changed files with 265 additions and 0 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ The format is based on [Keep a Changelog], and this project adheres to

## [Unreleased]

### Added

- Added `dnssd.ServiceInstance.Equal()` method
- Added `dnssd.Attributes.Equal()` method
- Added `dnssd.AttributeCollectionsEqual()` function

### Changed

- **[BC]** Renamed `dnssd.ServiceInstance.Instance` to `Name`
Expand Down
43 changes: 43 additions & 0 deletions dnssd/attributes.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dnssd

import (
"bytes"
"errors"
"fmt"
"strings"
Expand Down Expand Up @@ -239,6 +240,20 @@ func (a Attributes) ToTXT() []string {
return result
}

// Equal returns true if the attributes are equal.
func (a Attributes) Equal(attr Attributes) bool {
return maps.EqualFunc(
a.m,
attr.m,
func(v1, v2 []byte) bool {
isFlag1 := v1 == nil
isFlag2 := v2 == nil

return isFlag1 == isFlag2 && bytes.Equal(v1, v2)
},
)
}

// mustNormalizeAttributeKey normalizes the DNS-SD TXT key, k, or panics if it
// can not be normalized.
//
Expand Down Expand Up @@ -289,3 +304,31 @@ func normalizeAttributeKey(k string) (string, error) {

return w.String(), nil
}

// AttributeCollectionsEqual returns true if lhs and rhs contain the same sets
// of attributes, in any order.
func AttributeCollectionsEqual(lhs, rhs []Attributes) bool {
if len(lhs) != len(rhs) {
return false
}

visited := make([]bool, len(rhs))

left:
for _, l := range lhs {
for i, r := range rhs {
if visited[i] {
continue
}

if l.Equal(r) {
visited[i] = true
continue left
}
}

return false
}

return true
}
13 changes: 13 additions & 0 deletions dnssd/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,19 @@ type ServiceInstance struct {
TTL time.Duration
}

// Equal returns true if i and inst are equal.
func (i ServiceInstance) Equal(inst ServiceInstance) bool {
return i.Name == inst.Name &&
i.ServiceType == inst.ServiceType &&
i.Domain == inst.Domain &&
i.TargetHost == inst.TargetHost &&
i.TargetPort == inst.TargetPort &&
i.Priority == inst.Priority &&
i.Weight == inst.Weight &&
AttributeCollectionsEqual(i.Attributes, inst.Attributes) &&
i.TTL == inst.TTL
}

// ServiceInstanceName returns the fully-qualfied DNS domain name that is
// queried to lookup records about a single service instance.
//
Expand Down
203 changes: 203 additions & 0 deletions dnssd/instance_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,214 @@
package dnssd_test

import (
"time"

. "github.com/dogmatiq/dissolve/dnssd"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("type ServiceInstance", func() {
Describe("func Equal()", func() {
DescribeTable(
"it returns true if the instances are equal",
func(a, b ServiceInstance) {
Expect(a.Equal(b)).To(BeTrue())
},
Entry(
"zero-value",
ServiceInstance{},
ServiceInstance{},
),
Entry(
"fully populated",
ServiceInstance{
Name: "Boardroom Printer",
ServiceType: "_http._tcp",
Domain: "example.org",
TargetHost: "boardroom-printer.example.org",
TargetPort: 80,
Priority: 10,
Weight: 20,
Attributes: []Attributes{
NewAttributes().
WithPair("txtvers", []byte{1}).
WithFlag("default"),
},
TTL: 30 * time.Second,
},
ServiceInstance{
Name: "Boardroom Printer",
ServiceType: "_http._tcp",
Domain: "example.org",
TargetHost: "boardroom-printer.example.org",
TargetPort: 80,
Priority: 10,
Weight: 20,
Attributes: []Attributes{
NewAttributes().
WithPair("txtvers", []byte{1}).
WithFlag("default"),
},
TTL: 30 * time.Second,
},
),
Entry(
"attributes in a different order",
ServiceInstance{
Attributes: []Attributes{
NewAttributes().
WithPair("txtvers", []byte{1}).
WithFlag("default"),
NewAttributes().
WithPair("txtvers", []byte{2}),
},
},
ServiceInstance{
Attributes: []Attributes{
NewAttributes().
WithPair("txtvers", []byte{2}),
NewAttributes().
WithFlag("default").
WithPair("txtvers", []byte{1}),
},
},
),
Entry(
"multiple copies of the same set of attributes",
ServiceInstance{
Attributes: []Attributes{
NewAttributes().
WithPair("txtvers", []byte{1}),
NewAttributes().
WithPair("txtvers", []byte{1}),
},
},
ServiceInstance{
Attributes: []Attributes{
NewAttributes().
WithPair("txtvers", []byte{1}),
NewAttributes().
WithPair("txtvers", []byte{1}),
},
},
),
)
DescribeTable(
"it returns false if the instances are not equal",
func(a, b ServiceInstance) {
Expect(a.Equal(b)).To(BeFalse())
},
Entry(
"different name",
ServiceInstance{Name: "Boardroom Printer"},
ServiceInstance{Name: "Boardroom Printer 2"},
),
Entry(
"different service type",
ServiceInstance{ServiceType: "_http._tcp"},
ServiceInstance{ServiceType: "_other._udp"},
),
Entry(
"different domain",
ServiceInstance{Domain: "example.org"},
ServiceInstance{Domain: "example.com"},
),
Entry(
"different target host",
ServiceInstance{TargetHost: "boardroom-printer.example.org"},
ServiceInstance{TargetHost: "boardroom-printer-2.example.org"},
),
Entry(
"different target port",
ServiceInstance{TargetPort: 80},
ServiceInstance{TargetPort: 8080},
),
Entry(
"different priority",
ServiceInstance{Priority: 10},
ServiceInstance{Priority: 20},
),
Entry(
"different weight",
ServiceInstance{Weight: 20},
ServiceInstance{Weight: 30},
),
Entry(
"different TTL",
ServiceInstance{TTL: 30 * time.Second},
ServiceInstance{TTL: 60 * time.Second},
),
Entry(
"different attributes",
ServiceInstance{
Attributes: []Attributes{
NewAttributes().
WithPair("txtvers", []byte{1}),
},
},
ServiceInstance{
Attributes: []Attributes{
NewAttributes().
WithPair("txtvers", []byte{2}),
},
},
),
Entry(
"different attributes - flag vs empty value",
ServiceInstance{
Attributes: []Attributes{
NewAttributes().
WithPair("flag", nil),
},
},
ServiceInstance{
Attributes: []Attributes{
NewAttributes().
WithFlag("flag"),
},
},
),
Entry(
"different attributes - subset",
ServiceInstance{
Attributes: []Attributes{
NewAttributes().
WithPair("txtvers", []byte{1}),
},
},
ServiceInstance{
Attributes: []Attributes{
NewAttributes().
WithPair("txtvers", []byte{1}), // <-- same
NewAttributes().
WithPair("txtvers", []byte{2}), // <-- different
},
},
),
Entry(
"different attributes - multiple copies of the same set of attributes",
ServiceInstance{
Attributes: []Attributes{
NewAttributes().
WithPair("txtvers", []byte{1}), // <-- same
NewAttributes().
WithPair("txtvers", []byte{1}), // <-- same
},
},
ServiceInstance{
Attributes: []Attributes{
NewAttributes().
WithPair("txtvers", []byte{1}), // <-- same
NewAttributes().
WithPair("txtvers", []byte{2}), // <-- different
},
},
),
)
})
})

var _ = Describe("func ServiceInstanceName()", func() {
It("returns the fully-qualified name, with appropriate escaping", func() {
d := ServiceInstanceName("Boardroom Printer.", "_http._tcp", "example.org")
Expand Down

0 comments on commit 9bf15d6

Please sign in to comment.