report: add StringSet (port of IDList, effectively)

This commit is contained in:
Peter Bourgon
2015-09-28 11:41:57 +02:00
committed by Tom Wilkie
parent 6a4c997048
commit c68516ec22
2 changed files with 164 additions and 2 deletions

View File

@@ -2,6 +2,7 @@ package report
import (
"fmt"
"sort"
"strings"
)
@@ -78,8 +79,9 @@ func (n Nodes) Merge(other Nodes) Nodes {
// given node in a given topology, along with the edges emanating from the
// node and metadata about those edges.
type Node struct {
Metadata `json:"metadata,omitempty"`
Counters `json:"counters,omitempty"`
Metadata Metadata `json:"metadata,omitempty"`
Counters Counters `json:"counters,omitempty"`
Sets Sets `json:"sets,omitempty"`
Adjacency IDList `json:"adjacency"`
Edges EdgeMetadatas `json:"edges,omitempty"`
}
@@ -195,6 +197,100 @@ func (c Counters) Copy() Counters {
return result
}
// Sets is a string->set-of-strings map.
type Sets map[string]StringSet
// Merge merges two sets maps into a fresh set, performing set-union merges as
// appropriate.
func (s Sets) Merge(other Sets) Sets {
result := s.Copy()
for k, v := range other {
result[k].Merge(v)
}
return result
}
// Copy returns a value copy of the sets map.
func (s Sets) Copy() Sets {
result := Sets{}
for k, v := range s {
result[k] = v.Copy()
}
return result
}
// StringSet is a sorted set of unique strings. Clients must use the Add
// method to add strings.
type StringSet []string
// MakeStringSet makes a new StringSet with the given strings.
func MakeStringSet(strs ...string) StringSet {
if len(strs) <= 0 {
return StringSet{}
}
sort.Strings(strs)
for i := 1; i < len(strs); { // shuffle down any duplicates
if strs[i-1] == strs[i] {
strs = append(strs[:i-1], strs[i:]...)
continue
}
i++
}
return StringSet(strs)
}
// Add adds the strings to the StringSet. Add is the only valid way to grow a
// StringSet. Add returns the StringSet to enable chaining.
func (s StringSet) Add(strs ...string) StringSet {
for _, str := range strs {
i := sort.Search(len(s), func(i int) bool { return s[i] >= str })
if i < len(s) && s[i] == str {
// The list already has the element.
continue
}
// It a new element, insert it in order.
s = append(s, "")
copy(s[i+1:], s[i:])
s[i] = str
}
return s
}
// Merge combines the two StringSets and returns a new result.
func (s StringSet) Merge(other StringSet) StringSet {
if len(other) == 0 { // Optimise special case, to avoid allocating
return s // (note unit test DeepEquals breaks if we don't do this)
}
result := make(StringSet, len(s)+len(other))
for i, j, k := 0, 0, 0; ; k++ {
switch {
case i >= len(s):
copy(result[k:], other[j:])
return result[:k+len(other)-j]
case j >= len(other):
copy(result[k:], s[i:])
return result[:k+len(s)-i]
case s[i] < other[j]:
result[k] = s[i]
i++
case s[i] > other[j]:
result[k] = other[j]
j++
default: // equal
result[k] = s[i]
i++
j++
}
}
}
// Copy returns a value copy of the StringSet.
func (s StringSet) Copy() StringSet {
result := make(StringSet, len(s))
copy(result, s)
return result
}
// EdgeMetadatas collect metadata about each edge in a topology. Keys are the
// remote node IDs, as in Adjacency.
type EdgeMetadatas map[string]EdgeMetadata

66
report/topology_test.go Normal file
View File

@@ -0,0 +1,66 @@
package report_test
import (
"reflect"
"testing"
"github.com/weaveworks/scope/report"
)
func TestMakeStringSet(t *testing.T) {
for _, testcase := range []struct {
input []string
want report.StringSet
}{
{input: []string{}, want: report.MakeStringSet()},
{input: []string{"a"}, want: report.MakeStringSet("a")},
{input: []string{"a", "a"}, want: report.MakeStringSet("a")},
{input: []string{"b", "c", "a"}, want: report.MakeStringSet("a", "b", "c")},
} {
if want, have := testcase.want, report.MakeStringSet(testcase.input...); !reflect.DeepEqual(want, have) {
t.Errorf("%v: want %v, have %v", testcase.input, want, have)
}
}
}
func TestStringSetAdd(t *testing.T) {
for _, testcase := range []struct {
input report.StringSet
strs []string
want report.StringSet
}{
{input: report.MakeStringSet(), strs: []string{}, want: report.MakeStringSet()},
{input: report.MakeStringSet("a"), strs: []string{}, want: report.MakeStringSet("a")},
{input: report.MakeStringSet(), strs: []string{"a"}, want: report.MakeStringSet("a")},
{input: report.MakeStringSet("a"), strs: []string{"a"}, want: report.MakeStringSet("a")},
{input: report.MakeStringSet("b"), strs: []string{"a", "b"}, want: report.MakeStringSet("a", "b")},
{input: report.MakeStringSet("a"), strs: []string{"c", "b"}, want: report.MakeStringSet("a", "b", "c")},
{input: report.MakeStringSet("a", "c"), strs: []string{"b", "b", "b"}, want: report.MakeStringSet("a", "b", "c")},
} {
if want, have := testcase.want, testcase.input.Add(testcase.strs...); !reflect.DeepEqual(want, have) {
t.Errorf("%v + %v: want %v, have %v", testcase.input, testcase.strs, want, have)
}
}
}
func TestStringSetMerge(t *testing.T) {
for _, testcase := range []struct {
input report.StringSet
other report.StringSet
want report.StringSet
}{
{input: report.MakeStringSet(), other: report.MakeStringSet(), want: report.MakeStringSet()},
{input: report.MakeStringSet("a"), other: report.MakeStringSet(), want: report.MakeStringSet("a")},
{input: report.MakeStringSet(), other: report.MakeStringSet("a"), want: report.MakeStringSet("a")},
{input: report.MakeStringSet("a"), other: report.MakeStringSet("b"), want: report.MakeStringSet("a", "b")},
{input: report.MakeStringSet("b"), other: report.MakeStringSet("a"), want: report.MakeStringSet("a", "b")},
{input: report.MakeStringSet("a"), other: report.MakeStringSet("a"), want: report.MakeStringSet("a")},
{input: report.MakeStringSet("a", "c"), other: report.MakeStringSet("a", "b"), want: report.MakeStringSet("a", "b", "c")},
{input: report.MakeStringSet("b"), other: report.MakeStringSet("a"), want: report.MakeStringSet("a", "b")},
} {
if want, have := testcase.want, testcase.input.Merge(testcase.other); !reflect.DeepEqual(want, have) {
t.Errorf("%v + %v: want %v, have %v", testcase.input, testcase.other, want, have)
}
}
}