From 3b44e2b2851dc1c606cfd163c443ff7f1b1d749e Mon Sep 17 00:00:00 2001 From: David Eliahu Date: Mon, 2 Nov 2020 13:34:59 -0800 Subject: [PATCH 1/3] Add threadsafe string set --- pkg/lib/sets/strset/strset.go | 5 +- pkg/lib/sets/strset/threadsafe/strset.go | 353 +++++++++++++++ pkg/lib/sets/strset/threadsafe/strset_test.go | 407 ++++++++++++++++++ 3 files changed, 761 insertions(+), 4 deletions(-) create mode 100644 pkg/lib/sets/strset/threadsafe/strset.go create mode 100644 pkg/lib/sets/strset/threadsafe/strset_test.go diff --git a/pkg/lib/sets/strset/strset.go b/pkg/lib/sets/strset/strset.go index e78465c866..696bd6834f 100644 --- a/pkg/lib/sets/strset/strset.go +++ b/pkg/lib/sets/strset/strset.go @@ -23,9 +23,6 @@ import ( "strings" ) -// string - -// Set functionality adapted from github.com/scylladb/go-set type Set map[string]struct{} var _keyExists = struct{}{} @@ -266,7 +263,7 @@ func Union(sets ...Set) Set { return u } -// Difference returns a new set which contains items which are in in the first +// Difference returns a new set which contains items which are in the first // set but not in the others. func Difference(set1 Set, sets ...Set) Set { s := set1.Copy() diff --git a/pkg/lib/sets/strset/threadsafe/strset.go b/pkg/lib/sets/strset/threadsafe/strset.go new file mode 100644 index 0000000000..bb97b43fe1 --- /dev/null +++ b/pkg/lib/sets/strset/threadsafe/strset.go @@ -0,0 +1,353 @@ +/* +Copyright 2020 Cortex Labs, Inc. + +Licensed 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 + + http://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 threadsafe + +import ( + "sync" + + "github.com/cortexlabs/cortex/pkg/lib/sets/strset" +) + +// New creates and initializes a new Set. +type Set struct { + sync.RWMutex + s strset.Set +} + +func New(ts ...string) *Set { + set := Set{} + set.s = strset.New(ts...) + return &set +} + +func FromSlice(items []string) *Set { + return New(items...) +} + +// NewWithSize creates a new Set and gives make map a size hint. +func NewWithSize(size int) *Set { + set := Set{} + set.s = strset.NewWithSize(size) + return &set +} + +func (s *Set) Len() int { + s.RLock() + defer s.RUnlock() + return len(s.s) +} + +func (s *Set) ToStrset() strset.Set { + s.RLock() + defer s.RUnlock() + return s.s.Copy() +} + +// Add includes the specified items (one or more) to the Set. The underlying +// Set s is modified. If passed nothing it silently returns. +func (s *Set) Add(items ...string) { + s.Lock() + defer s.Unlock() + s.s.Add(items...) +} + +// Remove deletes the specified items from the Set. The underlying Set s is +// modified. If passed nothing it silently returns. +func (s *Set) Remove(items ...string) { + s.Lock() + defer s.Unlock() + s.s.Remove(items...) +} + +// GetOne returns an item from the set or "" if the set is empty. +func (s *Set) GetOne() string { + s.RLock() + defer s.RUnlock() + return s.s.GetOne() +} + +// GetOne2 returns an item from the set. The second value is a bool that is +// true if an item exists in the set, or false if the set is empty. +func (s *Set) GetOne2() (string, bool) { + s.RLock() + defer s.RUnlock() + return s.s.GetOne2() +} + +// Pop deletes and returns an item from the Set. The underlying Set s is +// modified. If Set is empty, the zero value is returned. +func (s *Set) Pop() string { + s.Lock() + defer s.Unlock() + return s.s.Pop() +} + +// Pop2 tries to delete and return an item from the Set. The underlying Set s +// is modified. The second value is a bool that is true if the item existed in +// the set, and false if not. If Set is empty, the zero value and false are +// returned. +func (s *Set) Pop2() (string, bool) { + s.Lock() + defer s.Unlock() + return s.s.Pop2() +} + +// Has looks for the existence of items passed. It returns false if nothing is +// passed. For multiple items it returns true only if all of the items exist. +func (s *Set) Has(items ...string) bool { + s.RLock() + defer s.RUnlock() + return s.s.Has(items...) +} + +// HasAny looks for the existence of any of the items passed. +// It returns false if nothing is passed. +// For multiple items it returns true if any of the items exist. +func (s *Set) HasAny(items ...string) bool { + s.RLock() + defer s.RUnlock() + return s.s.HasAny(items...) +} + +// Clear removes all items from the Set. +func (s *Set) Clear() { + s.Lock() + defer s.Unlock() + s.s.Clear() +} + +// IsEqual test whether s and t are the same in size and have the same items. +func (s *Set) IsEqual(t strset.Set) bool { + s.RLock() + defer s.RUnlock() + return s.s.IsEqual(t) +} + +// IsEqualThreadsafe test whether s and t are the same in size and have the same items. +func (s *Set) IsEqualThreadsafe(t *Set) bool { + s.RLock() + defer s.RUnlock() + t.RLock() + defer t.RUnlock() + return s.s.IsEqual(t.s) +} + +// IsSubset tests whether t is a subset of s. +func (s *Set) IsSubset(t strset.Set) bool { + s.RLock() + defer s.RUnlock() + return s.s.IsSubset(t) +} + +// IsSubsetThreadsafe tests whether t is a subset of s. +func (s *Set) IsSubsetThreadsafe(t *Set) bool { + s.RLock() + defer s.RUnlock() + t.RLock() + defer t.RUnlock() + return s.s.IsSubset(t.s) +} + +// IsSuperset tests whether t is a superset of s. +func (s *Set) IsSuperset(t strset.Set) bool { + s.RLock() + defer s.RUnlock() + return s.s.IsSuperset(t) +} + +// IsSupersetThreadsafe tests whether t is a superset of s. +func (s *Set) IsSupersetThreadsafe(t *Set) bool { + s.RLock() + defer s.RUnlock() + t.RLock() + defer t.RUnlock() + return s.s.IsSuperset(t.s) +} + +// Copy returns a new Set with a copy of s. +func (s *Set) Copy() strset.Set { + s.RLock() + defer s.RUnlock() + return s.s.Copy() +} + +// CopyToThreadsafe returns a new Set with a copy of s. +func (s *Set) CopyToThreadsafe() *Set { + s.RLock() + defer s.RUnlock() + + newSet := Set{} + newSet.s = s.s.Copy() + return &newSet +} + +// String returns a string representation of s +func (s *Set) String() string { + s.RLock() + defer s.RUnlock() + return s.s.String() +} + +// List returns a slice of all items. +func (s *Set) Slice() []string { + s.RLock() + defer s.RUnlock() + return s.s.Slice() +} + +// List returns a sorted slice of all items. +func (s *Set) SliceSorted() []string { + s.RLock() + defer s.RUnlock() + return s.s.SliceSorted() +} + +// Merge is like Union, however it modifies the current Set it's applied on +// with the given t Set. +func (s *Set) Merge(sets ...strset.Set) { + s.Lock() + defer s.Unlock() + s.s.Merge(sets...) +} + +// MergeThreadsafe is like UnionThreadsafe, however it modifies the current Set it's applied on +// with the given t Set. +func (s *Set) MergeThreadsafe(sets ...*Set) { + s.Lock() + defer s.Unlock() + + for _, set := range sets { + set.RLock() + s.s.Merge(set.s) + set.RUnlock() + } +} + +// Subtract removes the Set items contained in sets from Set s +func (s *Set) Subtract(sets ...strset.Set) { + s.Lock() + defer s.Unlock() + s.s.Subtract(sets...) +} + +// SubtractThreadsafe removes the Set items contained in sets from Set s +func (s *Set) SubtractThreadsafe(sets ...*Set) { + s.Lock() + defer s.Unlock() + + for _, set := range sets { + set.RLock() + s.s.Subtract(set.s) + set.RUnlock() + } +} + +// Remove items until len(s) <= targetLen +func (s *Set) Shrink(targetLen int) { + s.Lock() + defer s.Unlock() + s.s.Shrink(targetLen) +} + +// remove items alphabetically until len(s) <= targetLen +func (s *Set) ShrinkSorted(targetLen int) { + s.Lock() + defer s.Unlock() + s.s.ShrinkSorted(targetLen) +} + +// Union is the merger of multiple sets. It returns a new set with all the +// elements present in all the sets that are passed. +func Union(set1 *Set, sets ...strset.Set) *Set { + finalSet := set1.CopyToThreadsafe() + for _, set := range sets { + finalSet.s.Merge(set) + } + return finalSet +} + +// UnionThreadsafe is the merger of multiple sets. It returns a new set with all the +// elements present in all the sets that are passed. +func UnionThreadsafe(sets ...*Set) *Set { + finalSet := New() + for _, set := range sets { + set.RLock() + finalSet.s.Merge(set.s) + set.RUnlock() + } + return finalSet +} + +// Difference returns a new set which contains items which are in the first +// set but not in the others. +func Difference(set1 *Set, sets ...strset.Set) *Set { + s := set1.CopyToThreadsafe() + for _, set := range sets { + s.s.Subtract(set) + } + return s +} + +// DifferenceThreadsafe returns a new set which contains items which are in in the first +// set but not in the others. +func DifferenceThreadsafe(set1 *Set, sets ...*Set) *Set { + s := set1.CopyToThreadsafe() + for _, set := range sets { + set.RLock() + s.s.Subtract(set.s) + set.RUnlock() + } + return s +} + +// Intersection returns a new set which contains items that only exist in all +// given sets. +func Intersection(set1 *Set, sets ...strset.Set) *Set { + t := set1.CopyToThreadsafe() + for _, set := range sets { + for item := range t.s { + if _, has := set[item]; !has { + delete(t.s, item) + } + } + } + return t +} + +// IntersectionThreadsafe returns a new set which contains items that only exist in all +// given sets. +func IntersectionThreadsafe(set1 *Set, sets ...*Set) *Set { + t := set1.CopyToThreadsafe() + for _, set := range sets { + set.RLock() + for item := range t.s { + if _, has := set.s[item]; !has { + delete(t.s, item) + } + } + set.RUnlock() + } + return t +} + +// SymmetricDifferenceThreadsafe returns a new set which s is the difference of items +// which are in one of either, but not in both. +func SymmetricDifferenceThreadsafe(s *Set, t *Set) *Set { + u := DifferenceThreadsafe(s, t) + v := DifferenceThreadsafe(t, s) + return UnionThreadsafe(u, v) +} diff --git a/pkg/lib/sets/strset/threadsafe/strset_test.go b/pkg/lib/sets/strset/threadsafe/strset_test.go new file mode 100644 index 0000000000..fd1bc5fb24 --- /dev/null +++ b/pkg/lib/sets/strset/threadsafe/strset_test.go @@ -0,0 +1,407 @@ +/* +Copyright 2020 Cortex Labs, Inc. + +Licensed 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 + + http://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 threadsafe_test + +import ( + "testing" + + "github.com/cortexlabs/cortex/pkg/lib/sets/strset" + "github.com/cortexlabs/cortex/pkg/lib/sets/strset/threadsafe" + "github.com/stretchr/testify/require" +) + +// Also tests Add +func TestNew(t *testing.T) { + set := threadsafe.New() + require.Equal(t, 0, set.Len()) + + set = threadsafe.New("a", "b", "a") + require.Equal(t, 2, set.Len()) + if !set.Has("a") { + require.FailNow(t, "a not found in set") + } + if !set.Has("b") { + require.FailNow(t, "b not found in set") + } +} + +func TestAdd(t *testing.T) { + set := threadsafe.New() + set.Add("a") + set.Add("b", "c") + require.Equal(t, set, threadsafe.New("a", "b", "c")) +} + +func TestRemove(t *testing.T) { + set := threadsafe.New("a", "b") + set.Remove("c") + require.Equal(t, set, threadsafe.New("a", "b")) + + set.Remove() + require.Equal(t, set, threadsafe.New("a", "b")) + + set.Remove("a") + require.Equal(t, set, threadsafe.New("b")) + + set.Add("a") + set.Remove("a", "b") + require.Equal(t, set, threadsafe.New()) +} + +func TestPop(t *testing.T) { + set := threadsafe.New("a", "b") + p := set.Pop() + require.Contains(t, []string{"a", "b"}, p) + require.Equal(t, 1, set.Len()) + p = set.Pop() + require.Contains(t, []string{"a", "b"}, p) + require.Equal(t, 0, set.Len()) + p = set.Pop() + require.Equal(t, "", p) + require.Equal(t, 0, set.Len()) +} + +func TestPop2(t *testing.T) { + set := threadsafe.New("a", "b") + p, ok := set.Pop2() + require.Contains(t, []string{"a", "b"}, p) + require.Equal(t, 1, set.Len()) + require.True(t, ok) + p, ok = set.Pop2() + require.Contains(t, []string{"a", "b"}, p) + require.Equal(t, 0, set.Len()) + require.True(t, ok) + p, ok = set.Pop2() + require.Equal(t, "", p) + require.Equal(t, 0, set.Len()) + require.False(t, ok) +} + +func TestHas(t *testing.T) { + set := threadsafe.New("a", "b", "c") + require.True(t, set.Has("a")) + require.True(t, set.Has("a", "b")) + require.False(t, set.Has("z")) + require.False(t, set.Has("a", "z")) +} + +func TestHasAny(t *testing.T) { + set := threadsafe.New("a", "b", "c") + require.True(t, set.HasAny("a")) + require.True(t, set.HasAny("a", "b")) + require.False(t, set.HasAny("z")) + require.True(t, set.HasAny("a", "z")) +} + +func TestClear(t *testing.T) { + set := threadsafe.New("a", "b", "c") + require.Equal(t, 3, set.Len()) + set.Clear() + require.Equal(t, 0, set.Len()) +} + +func TestIsEqual(t *testing.T) { + set1 := threadsafe.New("a", "b", "c") + set2 := strset.New("a", "b", "c") + set3 := strset.New("a", "b") + set4 := strset.New("a", "b", "z") + require.True(t, set1.IsEqual(set2)) + require.False(t, set1.IsEqual(set3)) + require.False(t, set1.IsEqual(set4)) +} + +func IsEqualThreadsafe(t *testing.T) { + set1 := threadsafe.New("a", "b", "c") + set2 := threadsafe.New("a", "b", "c") + set3 := threadsafe.New("a", "b") + set4 := threadsafe.New("a", "b", "z") + require.True(t, set1.IsEqualThreadsafe(set2)) + require.False(t, set1.IsEqualThreadsafe(set3)) + require.False(t, set1.IsEqualThreadsafe(set4)) +} + +func TestIsSubset(t *testing.T) { + set1 := threadsafe.New("a", "b", "c") + set2 := strset.New("a", "b", "c") + set3 := strset.New("a", "b") + set4 := strset.New("a", "b", "z") + set5 := strset.New("a", "b", "c", "d") + require.True(t, set1.IsSubset(set2)) + require.True(t, set1.IsSubset(set3)) + require.False(t, set1.IsSubset(set4)) + require.False(t, set1.IsSubset(set5)) +} + +func IsSubsetThreadsafe(t *testing.T) { + set1 := threadsafe.New("a", "b", "c") + set2 := threadsafe.New("a", "b", "c") + set3 := threadsafe.New("a", "b") + set4 := threadsafe.New("a", "b", "z") + set5 := threadsafe.New("a", "b", "c", "d") + require.True(t, set1.IsSubsetThreadsafe(set2)) + require.True(t, set1.IsSubsetThreadsafe(set3)) + require.False(t, set1.IsSubsetThreadsafe(set4)) + require.False(t, set1.IsSubsetThreadsafe(set5)) +} + +func TestIsSuperset(t *testing.T) { + set1 := threadsafe.New("a", "b", "c") + set2 := strset.New("a", "b", "c") + set3 := strset.New("a", "b") + set4 := strset.New("a", "b", "z") + set5 := strset.New("a", "b", "c", "d") + require.True(t, set1.IsSuperset(set2)) + require.False(t, set1.IsSuperset(set3)) + require.False(t, set1.IsSuperset(set4)) + require.True(t, set1.IsSuperset(set5)) +} + +func IsSupersetThreadsafe(t *testing.T) { + set1 := threadsafe.New("a", "b", "c") + set2 := threadsafe.New("a", "b", "c") + set3 := threadsafe.New("a", "b") + set4 := threadsafe.New("a", "b", "z") + set5 := threadsafe.New("a", "b", "c", "d") + require.True(t, set1.IsSupersetThreadsafe(set2)) + require.False(t, set1.IsSupersetThreadsafe(set3)) + require.False(t, set1.IsSupersetThreadsafe(set4)) + require.True(t, set1.IsSupersetThreadsafe(set5)) +} + +func TestCopy(t *testing.T) { + set := threadsafe.New() + cset := set.Copy() + require.Equal(t, 0, len(cset)) + + set = threadsafe.New("a", "b") + cset = set.Copy() + require.Equal(t, 2, len(cset)) + if !set.Has("a") { + require.FailNow(t, "a not found in set") + } + if !set.Has("b") { + require.FailNow(t, "b not found in set") + } +} + +func TestCopyToThreadsafe(t *testing.T) { + set := threadsafe.New() + cset := set.CopyToThreadsafe() + require.Equal(t, 0, cset.Len()) + + set = threadsafe.New("a", "b") + cset = set.CopyToThreadsafe() + require.Equal(t, 2, cset.Len()) + if !set.Has("a") { + require.FailNow(t, "a not found in set") + } + if !set.Has("b") { + require.FailNow(t, "b not found in set") + } +} + +func TestSlice(t *testing.T) { + set := threadsafe.New() + require.Equal(t, set.Slice(), []string{}) + + set.Add("a") + require.Equal(t, set.Slice(), []string{"a"}) + + set.Add("a", "b") + require.ElementsMatch(t, set.Slice(), []string{"a", "b"}) +} + +func TestMerge(t *testing.T) { + set := threadsafe.New() + emptySet := strset.New() + set.Merge(emptySet) + require.Equal(t, 0, set.Len()) + + set.Merge(strset.New("a")) + require.Equal(t, 1, set.Len()) + + set.Merge(emptySet) + require.Equal(t, 1, set.Len()) + + set.Add("a", "b", "c") + set.Merge(strset.New("e", "e", "d")) + require.Equal(t, set, threadsafe.New("a", "b", "c", "e", "d")) + + set.Merge(strset.New("a", "e")) + require.Equal(t, set, threadsafe.New("a", "b", "c", "e", "d")) + set.Merge(strset.New("a", "e", "i"), strset.New("o", "u"), strset.New("sometimes y")) + require.Equal(t, set, threadsafe.New("a", "b", "c", "e", "d", "i", "o", "u", "sometimes y")) +} + +func TestMergeThreadsafe(t *testing.T) { + set := threadsafe.New() + emptySet := threadsafe.New() + set.MergeThreadsafe(emptySet) + require.Equal(t, 0, set.Len()) + + set.MergeThreadsafe(threadsafe.New("a")) + require.Equal(t, 1, set.Len()) + + set.MergeThreadsafe(emptySet) + require.Equal(t, 1, set.Len()) + + set.Add("a", "b", "c") + set.MergeThreadsafe(threadsafe.New("e", "e", "d")) + require.Equal(t, set, threadsafe.New("a", "b", "c", "e", "d")) + + set.MergeThreadsafe(threadsafe.New("a", "e")) + require.Equal(t, set, threadsafe.New("a", "b", "c", "e", "d")) + set.MergeThreadsafe(threadsafe.New("a", "e", "i"), threadsafe.New("o", "u"), threadsafe.New("sometimes y")) + require.Equal(t, set, threadsafe.New("a", "b", "c", "e", "d", "i", "o", "u", "sometimes y")) +} + +func TestSubtract(t *testing.T) { + set := threadsafe.New("a", "b", "c") + set.Subtract(strset.New("z", "a", "b")) + require.Equal(t, set, threadsafe.New("c")) + set.Subtract(strset.New("x")) + require.Equal(t, set, threadsafe.New("c")) +} + +func SubtractThreadsafe(t *testing.T) { + set := threadsafe.New("a", "b", "c") + set.SubtractThreadsafe(threadsafe.New("z", "a", "b")) + require.Equal(t, set, threadsafe.New("c")) + set.SubtractThreadsafe(threadsafe.New("x")) + require.Equal(t, set, threadsafe.New("c")) +} + +func TestShrink(t *testing.T) { + set := threadsafe.New("a", "b", "c", "d") + set.Shrink(2) + require.Equal(t, set.Len(), 2) + + set = threadsafe.New("g", "f", "e", "d", "c", "b", "a") + set.ShrinkSorted(3) + require.Equal(t, set.Len(), 3) + + set = threadsafe.New("a", "b") + set.Shrink(2) + require.Equal(t, set, threadsafe.New("a", "b")) + + set = threadsafe.New("a") + set.Shrink(2) + require.Equal(t, set, threadsafe.New("a")) + + set = threadsafe.New() + set.Shrink(2) + require.Equal(t, set.Len(), 0) +} + +func TestShrinkSorted(t *testing.T) { + for i := 0; i < 10; i++ { + set := threadsafe.New("g", "f", "e", "d", "c", "b", "a") + set.ShrinkSorted(2) + require.Equal(t, set, threadsafe.New("a", "b")) + + set = threadsafe.New("g", "f", "e", "d", "c", "b", "a") + set.ShrinkSorted(3) + require.Equal(t, set, threadsafe.New("a", "b", "c")) + } + + set := threadsafe.New("a", "b") + set.ShrinkSorted(2) + require.Equal(t, set, threadsafe.New("a", "b")) + + set = threadsafe.New("a") + set.ShrinkSorted(2) + require.Equal(t, set, threadsafe.New("a")) + + set = threadsafe.New() + set.ShrinkSorted(2) + require.Equal(t, set.Len(), 0) +} + +func TestUnion(t *testing.T) { + require.Equal(t, threadsafe.Union(threadsafe.New(), strset.New()).Len(), 0) + require.Equal(t, threadsafe.Union(threadsafe.New("a", "b"), strset.New()), threadsafe.New("a", "b")) + require.Equal(t, threadsafe.Union(threadsafe.New(), strset.New("a", "b")), threadsafe.New("a", "b")) + require.Equal(t, threadsafe.Union(threadsafe.New("a", "a"), strset.New("a", "b")), threadsafe.New("a", "b")) + require.Equal(t, threadsafe.Union(threadsafe.New("a"), strset.New("b")), threadsafe.New("a", "b")) +} + +func TestUnionThreadsafe(t *testing.T) { + require.Equal(t, threadsafe.UnionThreadsafe(threadsafe.New(), threadsafe.New()).Len(), 0) + require.Equal(t, threadsafe.UnionThreadsafe(threadsafe.New("a", "b"), threadsafe.New()), threadsafe.New("a", "b")) + require.Equal(t, threadsafe.UnionThreadsafe(threadsafe.New(), threadsafe.New("a", "b")), threadsafe.New("a", "b")) + require.Equal(t, threadsafe.UnionThreadsafe(threadsafe.New("a", "a"), threadsafe.New("a", "b")), threadsafe.New("a", "b")) + require.Equal(t, threadsafe.UnionThreadsafe(threadsafe.New("a"), threadsafe.New("b")), threadsafe.New("a", "b")) +} + +func TestDifference(t *testing.T) { + set1 := threadsafe.New("a", "b", "c") + set1Strset := strset.New("a", "b", "c") + set2 := threadsafe.New("z", "a", "b") + set2Strset := strset.New("z", "a", "b") + + d := threadsafe.Difference(set1, set2Strset) + require.Equal(t, d, threadsafe.New("c")) + d = threadsafe.Difference(set2, set1Strset) + require.Equal(t, d, threadsafe.New("z")) + d = threadsafe.Difference(set1, set1Strset) + require.Equal(t, d, threadsafe.New()) +} + +func TestDifferenceThreadsafe(t *testing.T) { + set1 := threadsafe.New("a", "b", "c") + set2 := threadsafe.New("z", "a", "b") + + d := threadsafe.DifferenceThreadsafe(set1, set2) + require.Equal(t, d, threadsafe.New("c")) + d = threadsafe.DifferenceThreadsafe(set2, set1) + require.Equal(t, d, threadsafe.New("z")) + d = threadsafe.DifferenceThreadsafe(set1, set1) + require.Equal(t, d, threadsafe.New()) +} + +func TestIntersection(t *testing.T) { + set1 := threadsafe.New("a", "b", "c") + set2 := threadsafe.New("a", "x", "y") + set2Strset := strset.New("a", "x", "y") + set3Strset := strset.New("z", "b", "c") + set4Strset := strset.New("z", "b", "w") + + d := threadsafe.Intersection(set1, set2Strset) + require.Equal(t, d, threadsafe.New("a")) + d = threadsafe.Intersection(set1, set3Strset) + require.Equal(t, d, threadsafe.New("b", "c")) + d = threadsafe.Intersection(set2, set3Strset) + require.Equal(t, d, threadsafe.New()) + d = threadsafe.Intersection(set1, set3Strset, set4Strset) + require.Equal(t, d, threadsafe.New("b")) +} + +func TestIntersectionThreadsafe(t *testing.T) { + set1 := threadsafe.New("a", "b", "c") + set2 := threadsafe.New("a", "x", "y") + set3 := threadsafe.New("z", "b", "c") + set4 := threadsafe.New("z", "b", "w") + + d := threadsafe.IntersectionThreadsafe(set1, set2) + require.Equal(t, d, threadsafe.New("a")) + d = threadsafe.IntersectionThreadsafe(set1, set3) + require.Equal(t, d, threadsafe.New("b", "c")) + d = threadsafe.IntersectionThreadsafe(set2, set3) + require.Equal(t, d, threadsafe.New()) + d = threadsafe.IntersectionThreadsafe(set1, set3, set4) + require.Equal(t, d, threadsafe.New("b")) +} From 3470b31484dc3878219b9eefc18358080a891d7e Mon Sep 17 00:00:00 2001 From: David Eliahu Date: Mon, 2 Nov 2020 13:58:17 -0800 Subject: [PATCH 2/3] Use error exit code when cluster fails to deploy --- cli/cmd/deploy.go | 21 +++++++++++++--- pkg/lib/errors/message.go | 3 +-- pkg/lib/exit/exit.go | 6 ++--- pkg/lib/print/print.go | 25 ++++++++++++++++--- .../cortex/client/cortex/binary/__init__.py | 4 +++ 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/cli/cmd/deploy.go b/cli/cmd/deploy.go index ce4c23c877..53882fa216 100644 --- a/cli/cmd/deploy.go +++ b/cli/cmd/deploy.go @@ -120,16 +120,22 @@ var _deployCmd = &cobra.Command{ exit.Error(err) } fmt.Println(string(bytes)) - return case flags.MixedOutputType: err := mixedPrint(deployResults) if err != nil { exit.Error(err) } - return case flags.PrettyOutputType: message := deployMessage(deployResults, env.Name) - print.BoldFirstBlock(message) + if didAnyResultsError(deployResults) { + print.StderrBoldFirstBlock(message) + } else { + print.BoldFirstBlock(message) + } + } + + if didAnyResultsError(deployResults) { + exit.Error(nil) } }, } @@ -297,6 +303,15 @@ func didAllResultsError(results []schema.DeployResult) bool { return true } +func didAnyResultsError(results []schema.DeployResult) bool { + for _, result := range results { + if result.Error != "" { + return true + } + } + return false +} + func getAPICommandsMessage(results []schema.DeployResult, envName string) string { apiName := "" if len(results) == 1 { diff --git a/pkg/lib/errors/message.go b/pkg/lib/errors/message.go index 6606f183cb..c174adf5b2 100644 --- a/pkg/lib/errors/message.go +++ b/pkg/lib/errors/message.go @@ -17,7 +17,6 @@ limitations under the License. package errors import ( - "os" "strings" "github.com/aws/aws-sdk-go/aws/awserr" @@ -25,7 +24,7 @@ import ( ) func PrintError(err error, strs ...string) { - os.Stderr.WriteString(ErrorStr(err, strs...) + "\n") + print.StderrPrintln(ErrorStr(err, strs...)) // PrintStacktrace(err) } diff --git a/pkg/lib/exit/exit.go b/pkg/lib/exit/exit.go index 78564a04bb..86af746d16 100644 --- a/pkg/lib/exit/exit.go +++ b/pkg/lib/exit/exit.go @@ -33,11 +33,11 @@ func Error(err error, wrapStrs ...string) { err = errors.Wrap(err, str) } - if !errors.IsNoTelemetry(err) { + if err != nil && !errors.IsNoTelemetry(err) { telemetry.Error(err) } - if !errors.IsNoPrint(err) { + if err != nil && !errors.IsNoPrint(err) { errors.PrintErrorForUser(err) } @@ -51,7 +51,7 @@ func Panic(err error, wrapStrs ...string) { err = errors.Wrap(err, str) } - if !errors.IsNoTelemetry(err) { + if err != nil && !errors.IsNoTelemetry(err) { telemetry.Error(err) } diff --git a/pkg/lib/print/print.go b/pkg/lib/print/print.go index b5942064da..7445043949 100644 --- a/pkg/lib/print/print.go +++ b/pkg/lib/print/print.go @@ -45,14 +45,14 @@ func StderrBoldFirstLine(msg string) { msgParts := strings.Split(msg, "\n") if len(msgParts[0]) > _maxBoldLength { - os.Stderr.WriteString(msg + "\n") + StderrPrintln(msg) return } - os.Stderr.WriteString(console.Bold(msgParts[0]) + "\n") + StderrPrintln(console.Bold(msgParts[0])) if len(msgParts) > 1 { - os.Stderr.WriteString(strings.Join(msgParts[1:], "\n") + "\n") + StderrPrintln(strings.Join(msgParts[1:], "\n")) } } @@ -71,7 +71,26 @@ func BoldFirstBlock(msg string) { } } +func StderrBoldFirstBlock(msg string) { + msgParts := strings.Split(msg, "\n\n") + + if len(msgParts[0]) > _maxBoldLength { + StderrPrintln(msg) + return + } + + StderrPrintln(console.Bold(msgParts[0])) + + if len(msgParts) > 1 { + StderrPrintln("\n" + strings.Join(msgParts[1:], "\n\n")) + } +} + func Dot() error { fmt.Print(".") return nil } + +func StderrPrintln(str string) { + os.Stderr.WriteString(str + "\n") +} diff --git a/pkg/workloads/cortex/client/cortex/binary/__init__.py b/pkg/workloads/cortex/client/cortex/binary/__init__.py index f7cf580015..2cd4fc2c5a 100644 --- a/pkg/workloads/cortex/client/cortex/binary/__init__.py +++ b/pkg/workloads/cortex/client/cortex/binary/__init__.py @@ -77,6 +77,7 @@ def run_cli( result = "~" output = output[:-1] if result_found: + output = output[:-1] if c == "\n": result_found = False result = result[len(MIXED_CORTEX_MARKER) : -len(MIXED_CORTEX_MARKER)] @@ -96,6 +97,9 @@ def run_cli( return result return output + if result != "": + raise CortexBinaryException(result + "\n" + process.stderr.read()) + raise CortexBinaryException(process.stderr.read()) From cc80cc44a675471644287ba9e3525687bbab4cff Mon Sep 17 00:00:00 2001 From: David Eliahu Date: Mon, 2 Nov 2020 14:01:11 -0800 Subject: [PATCH 3/3] Revert threadsafe set --- pkg/lib/sets/strset/strset.go | 5 +- pkg/lib/sets/strset/threadsafe/strset.go | 353 --------------- pkg/lib/sets/strset/threadsafe/strset_test.go | 407 ------------------ 3 files changed, 4 insertions(+), 761 deletions(-) delete mode 100644 pkg/lib/sets/strset/threadsafe/strset.go delete mode 100644 pkg/lib/sets/strset/threadsafe/strset_test.go diff --git a/pkg/lib/sets/strset/strset.go b/pkg/lib/sets/strset/strset.go index 696bd6834f..e78465c866 100644 --- a/pkg/lib/sets/strset/strset.go +++ b/pkg/lib/sets/strset/strset.go @@ -23,6 +23,9 @@ import ( "strings" ) +// string + +// Set functionality adapted from github.com/scylladb/go-set type Set map[string]struct{} var _keyExists = struct{}{} @@ -263,7 +266,7 @@ func Union(sets ...Set) Set { return u } -// Difference returns a new set which contains items which are in the first +// Difference returns a new set which contains items which are in in the first // set but not in the others. func Difference(set1 Set, sets ...Set) Set { s := set1.Copy() diff --git a/pkg/lib/sets/strset/threadsafe/strset.go b/pkg/lib/sets/strset/threadsafe/strset.go deleted file mode 100644 index bb97b43fe1..0000000000 --- a/pkg/lib/sets/strset/threadsafe/strset.go +++ /dev/null @@ -1,353 +0,0 @@ -/* -Copyright 2020 Cortex Labs, Inc. - -Licensed 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 - - http://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 threadsafe - -import ( - "sync" - - "github.com/cortexlabs/cortex/pkg/lib/sets/strset" -) - -// New creates and initializes a new Set. -type Set struct { - sync.RWMutex - s strset.Set -} - -func New(ts ...string) *Set { - set := Set{} - set.s = strset.New(ts...) - return &set -} - -func FromSlice(items []string) *Set { - return New(items...) -} - -// NewWithSize creates a new Set and gives make map a size hint. -func NewWithSize(size int) *Set { - set := Set{} - set.s = strset.NewWithSize(size) - return &set -} - -func (s *Set) Len() int { - s.RLock() - defer s.RUnlock() - return len(s.s) -} - -func (s *Set) ToStrset() strset.Set { - s.RLock() - defer s.RUnlock() - return s.s.Copy() -} - -// Add includes the specified items (one or more) to the Set. The underlying -// Set s is modified. If passed nothing it silently returns. -func (s *Set) Add(items ...string) { - s.Lock() - defer s.Unlock() - s.s.Add(items...) -} - -// Remove deletes the specified items from the Set. The underlying Set s is -// modified. If passed nothing it silently returns. -func (s *Set) Remove(items ...string) { - s.Lock() - defer s.Unlock() - s.s.Remove(items...) -} - -// GetOne returns an item from the set or "" if the set is empty. -func (s *Set) GetOne() string { - s.RLock() - defer s.RUnlock() - return s.s.GetOne() -} - -// GetOne2 returns an item from the set. The second value is a bool that is -// true if an item exists in the set, or false if the set is empty. -func (s *Set) GetOne2() (string, bool) { - s.RLock() - defer s.RUnlock() - return s.s.GetOne2() -} - -// Pop deletes and returns an item from the Set. The underlying Set s is -// modified. If Set is empty, the zero value is returned. -func (s *Set) Pop() string { - s.Lock() - defer s.Unlock() - return s.s.Pop() -} - -// Pop2 tries to delete and return an item from the Set. The underlying Set s -// is modified. The second value is a bool that is true if the item existed in -// the set, and false if not. If Set is empty, the zero value and false are -// returned. -func (s *Set) Pop2() (string, bool) { - s.Lock() - defer s.Unlock() - return s.s.Pop2() -} - -// Has looks for the existence of items passed. It returns false if nothing is -// passed. For multiple items it returns true only if all of the items exist. -func (s *Set) Has(items ...string) bool { - s.RLock() - defer s.RUnlock() - return s.s.Has(items...) -} - -// HasAny looks for the existence of any of the items passed. -// It returns false if nothing is passed. -// For multiple items it returns true if any of the items exist. -func (s *Set) HasAny(items ...string) bool { - s.RLock() - defer s.RUnlock() - return s.s.HasAny(items...) -} - -// Clear removes all items from the Set. -func (s *Set) Clear() { - s.Lock() - defer s.Unlock() - s.s.Clear() -} - -// IsEqual test whether s and t are the same in size and have the same items. -func (s *Set) IsEqual(t strset.Set) bool { - s.RLock() - defer s.RUnlock() - return s.s.IsEqual(t) -} - -// IsEqualThreadsafe test whether s and t are the same in size and have the same items. -func (s *Set) IsEqualThreadsafe(t *Set) bool { - s.RLock() - defer s.RUnlock() - t.RLock() - defer t.RUnlock() - return s.s.IsEqual(t.s) -} - -// IsSubset tests whether t is a subset of s. -func (s *Set) IsSubset(t strset.Set) bool { - s.RLock() - defer s.RUnlock() - return s.s.IsSubset(t) -} - -// IsSubsetThreadsafe tests whether t is a subset of s. -func (s *Set) IsSubsetThreadsafe(t *Set) bool { - s.RLock() - defer s.RUnlock() - t.RLock() - defer t.RUnlock() - return s.s.IsSubset(t.s) -} - -// IsSuperset tests whether t is a superset of s. -func (s *Set) IsSuperset(t strset.Set) bool { - s.RLock() - defer s.RUnlock() - return s.s.IsSuperset(t) -} - -// IsSupersetThreadsafe tests whether t is a superset of s. -func (s *Set) IsSupersetThreadsafe(t *Set) bool { - s.RLock() - defer s.RUnlock() - t.RLock() - defer t.RUnlock() - return s.s.IsSuperset(t.s) -} - -// Copy returns a new Set with a copy of s. -func (s *Set) Copy() strset.Set { - s.RLock() - defer s.RUnlock() - return s.s.Copy() -} - -// CopyToThreadsafe returns a new Set with a copy of s. -func (s *Set) CopyToThreadsafe() *Set { - s.RLock() - defer s.RUnlock() - - newSet := Set{} - newSet.s = s.s.Copy() - return &newSet -} - -// String returns a string representation of s -func (s *Set) String() string { - s.RLock() - defer s.RUnlock() - return s.s.String() -} - -// List returns a slice of all items. -func (s *Set) Slice() []string { - s.RLock() - defer s.RUnlock() - return s.s.Slice() -} - -// List returns a sorted slice of all items. -func (s *Set) SliceSorted() []string { - s.RLock() - defer s.RUnlock() - return s.s.SliceSorted() -} - -// Merge is like Union, however it modifies the current Set it's applied on -// with the given t Set. -func (s *Set) Merge(sets ...strset.Set) { - s.Lock() - defer s.Unlock() - s.s.Merge(sets...) -} - -// MergeThreadsafe is like UnionThreadsafe, however it modifies the current Set it's applied on -// with the given t Set. -func (s *Set) MergeThreadsafe(sets ...*Set) { - s.Lock() - defer s.Unlock() - - for _, set := range sets { - set.RLock() - s.s.Merge(set.s) - set.RUnlock() - } -} - -// Subtract removes the Set items contained in sets from Set s -func (s *Set) Subtract(sets ...strset.Set) { - s.Lock() - defer s.Unlock() - s.s.Subtract(sets...) -} - -// SubtractThreadsafe removes the Set items contained in sets from Set s -func (s *Set) SubtractThreadsafe(sets ...*Set) { - s.Lock() - defer s.Unlock() - - for _, set := range sets { - set.RLock() - s.s.Subtract(set.s) - set.RUnlock() - } -} - -// Remove items until len(s) <= targetLen -func (s *Set) Shrink(targetLen int) { - s.Lock() - defer s.Unlock() - s.s.Shrink(targetLen) -} - -// remove items alphabetically until len(s) <= targetLen -func (s *Set) ShrinkSorted(targetLen int) { - s.Lock() - defer s.Unlock() - s.s.ShrinkSorted(targetLen) -} - -// Union is the merger of multiple sets. It returns a new set with all the -// elements present in all the sets that are passed. -func Union(set1 *Set, sets ...strset.Set) *Set { - finalSet := set1.CopyToThreadsafe() - for _, set := range sets { - finalSet.s.Merge(set) - } - return finalSet -} - -// UnionThreadsafe is the merger of multiple sets. It returns a new set with all the -// elements present in all the sets that are passed. -func UnionThreadsafe(sets ...*Set) *Set { - finalSet := New() - for _, set := range sets { - set.RLock() - finalSet.s.Merge(set.s) - set.RUnlock() - } - return finalSet -} - -// Difference returns a new set which contains items which are in the first -// set but not in the others. -func Difference(set1 *Set, sets ...strset.Set) *Set { - s := set1.CopyToThreadsafe() - for _, set := range sets { - s.s.Subtract(set) - } - return s -} - -// DifferenceThreadsafe returns a new set which contains items which are in in the first -// set but not in the others. -func DifferenceThreadsafe(set1 *Set, sets ...*Set) *Set { - s := set1.CopyToThreadsafe() - for _, set := range sets { - set.RLock() - s.s.Subtract(set.s) - set.RUnlock() - } - return s -} - -// Intersection returns a new set which contains items that only exist in all -// given sets. -func Intersection(set1 *Set, sets ...strset.Set) *Set { - t := set1.CopyToThreadsafe() - for _, set := range sets { - for item := range t.s { - if _, has := set[item]; !has { - delete(t.s, item) - } - } - } - return t -} - -// IntersectionThreadsafe returns a new set which contains items that only exist in all -// given sets. -func IntersectionThreadsafe(set1 *Set, sets ...*Set) *Set { - t := set1.CopyToThreadsafe() - for _, set := range sets { - set.RLock() - for item := range t.s { - if _, has := set.s[item]; !has { - delete(t.s, item) - } - } - set.RUnlock() - } - return t -} - -// SymmetricDifferenceThreadsafe returns a new set which s is the difference of items -// which are in one of either, but not in both. -func SymmetricDifferenceThreadsafe(s *Set, t *Set) *Set { - u := DifferenceThreadsafe(s, t) - v := DifferenceThreadsafe(t, s) - return UnionThreadsafe(u, v) -} diff --git a/pkg/lib/sets/strset/threadsafe/strset_test.go b/pkg/lib/sets/strset/threadsafe/strset_test.go deleted file mode 100644 index fd1bc5fb24..0000000000 --- a/pkg/lib/sets/strset/threadsafe/strset_test.go +++ /dev/null @@ -1,407 +0,0 @@ -/* -Copyright 2020 Cortex Labs, Inc. - -Licensed 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 - - http://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 threadsafe_test - -import ( - "testing" - - "github.com/cortexlabs/cortex/pkg/lib/sets/strset" - "github.com/cortexlabs/cortex/pkg/lib/sets/strset/threadsafe" - "github.com/stretchr/testify/require" -) - -// Also tests Add -func TestNew(t *testing.T) { - set := threadsafe.New() - require.Equal(t, 0, set.Len()) - - set = threadsafe.New("a", "b", "a") - require.Equal(t, 2, set.Len()) - if !set.Has("a") { - require.FailNow(t, "a not found in set") - } - if !set.Has("b") { - require.FailNow(t, "b not found in set") - } -} - -func TestAdd(t *testing.T) { - set := threadsafe.New() - set.Add("a") - set.Add("b", "c") - require.Equal(t, set, threadsafe.New("a", "b", "c")) -} - -func TestRemove(t *testing.T) { - set := threadsafe.New("a", "b") - set.Remove("c") - require.Equal(t, set, threadsafe.New("a", "b")) - - set.Remove() - require.Equal(t, set, threadsafe.New("a", "b")) - - set.Remove("a") - require.Equal(t, set, threadsafe.New("b")) - - set.Add("a") - set.Remove("a", "b") - require.Equal(t, set, threadsafe.New()) -} - -func TestPop(t *testing.T) { - set := threadsafe.New("a", "b") - p := set.Pop() - require.Contains(t, []string{"a", "b"}, p) - require.Equal(t, 1, set.Len()) - p = set.Pop() - require.Contains(t, []string{"a", "b"}, p) - require.Equal(t, 0, set.Len()) - p = set.Pop() - require.Equal(t, "", p) - require.Equal(t, 0, set.Len()) -} - -func TestPop2(t *testing.T) { - set := threadsafe.New("a", "b") - p, ok := set.Pop2() - require.Contains(t, []string{"a", "b"}, p) - require.Equal(t, 1, set.Len()) - require.True(t, ok) - p, ok = set.Pop2() - require.Contains(t, []string{"a", "b"}, p) - require.Equal(t, 0, set.Len()) - require.True(t, ok) - p, ok = set.Pop2() - require.Equal(t, "", p) - require.Equal(t, 0, set.Len()) - require.False(t, ok) -} - -func TestHas(t *testing.T) { - set := threadsafe.New("a", "b", "c") - require.True(t, set.Has("a")) - require.True(t, set.Has("a", "b")) - require.False(t, set.Has("z")) - require.False(t, set.Has("a", "z")) -} - -func TestHasAny(t *testing.T) { - set := threadsafe.New("a", "b", "c") - require.True(t, set.HasAny("a")) - require.True(t, set.HasAny("a", "b")) - require.False(t, set.HasAny("z")) - require.True(t, set.HasAny("a", "z")) -} - -func TestClear(t *testing.T) { - set := threadsafe.New("a", "b", "c") - require.Equal(t, 3, set.Len()) - set.Clear() - require.Equal(t, 0, set.Len()) -} - -func TestIsEqual(t *testing.T) { - set1 := threadsafe.New("a", "b", "c") - set2 := strset.New("a", "b", "c") - set3 := strset.New("a", "b") - set4 := strset.New("a", "b", "z") - require.True(t, set1.IsEqual(set2)) - require.False(t, set1.IsEqual(set3)) - require.False(t, set1.IsEqual(set4)) -} - -func IsEqualThreadsafe(t *testing.T) { - set1 := threadsafe.New("a", "b", "c") - set2 := threadsafe.New("a", "b", "c") - set3 := threadsafe.New("a", "b") - set4 := threadsafe.New("a", "b", "z") - require.True(t, set1.IsEqualThreadsafe(set2)) - require.False(t, set1.IsEqualThreadsafe(set3)) - require.False(t, set1.IsEqualThreadsafe(set4)) -} - -func TestIsSubset(t *testing.T) { - set1 := threadsafe.New("a", "b", "c") - set2 := strset.New("a", "b", "c") - set3 := strset.New("a", "b") - set4 := strset.New("a", "b", "z") - set5 := strset.New("a", "b", "c", "d") - require.True(t, set1.IsSubset(set2)) - require.True(t, set1.IsSubset(set3)) - require.False(t, set1.IsSubset(set4)) - require.False(t, set1.IsSubset(set5)) -} - -func IsSubsetThreadsafe(t *testing.T) { - set1 := threadsafe.New("a", "b", "c") - set2 := threadsafe.New("a", "b", "c") - set3 := threadsafe.New("a", "b") - set4 := threadsafe.New("a", "b", "z") - set5 := threadsafe.New("a", "b", "c", "d") - require.True(t, set1.IsSubsetThreadsafe(set2)) - require.True(t, set1.IsSubsetThreadsafe(set3)) - require.False(t, set1.IsSubsetThreadsafe(set4)) - require.False(t, set1.IsSubsetThreadsafe(set5)) -} - -func TestIsSuperset(t *testing.T) { - set1 := threadsafe.New("a", "b", "c") - set2 := strset.New("a", "b", "c") - set3 := strset.New("a", "b") - set4 := strset.New("a", "b", "z") - set5 := strset.New("a", "b", "c", "d") - require.True(t, set1.IsSuperset(set2)) - require.False(t, set1.IsSuperset(set3)) - require.False(t, set1.IsSuperset(set4)) - require.True(t, set1.IsSuperset(set5)) -} - -func IsSupersetThreadsafe(t *testing.T) { - set1 := threadsafe.New("a", "b", "c") - set2 := threadsafe.New("a", "b", "c") - set3 := threadsafe.New("a", "b") - set4 := threadsafe.New("a", "b", "z") - set5 := threadsafe.New("a", "b", "c", "d") - require.True(t, set1.IsSupersetThreadsafe(set2)) - require.False(t, set1.IsSupersetThreadsafe(set3)) - require.False(t, set1.IsSupersetThreadsafe(set4)) - require.True(t, set1.IsSupersetThreadsafe(set5)) -} - -func TestCopy(t *testing.T) { - set := threadsafe.New() - cset := set.Copy() - require.Equal(t, 0, len(cset)) - - set = threadsafe.New("a", "b") - cset = set.Copy() - require.Equal(t, 2, len(cset)) - if !set.Has("a") { - require.FailNow(t, "a not found in set") - } - if !set.Has("b") { - require.FailNow(t, "b not found in set") - } -} - -func TestCopyToThreadsafe(t *testing.T) { - set := threadsafe.New() - cset := set.CopyToThreadsafe() - require.Equal(t, 0, cset.Len()) - - set = threadsafe.New("a", "b") - cset = set.CopyToThreadsafe() - require.Equal(t, 2, cset.Len()) - if !set.Has("a") { - require.FailNow(t, "a not found in set") - } - if !set.Has("b") { - require.FailNow(t, "b not found in set") - } -} - -func TestSlice(t *testing.T) { - set := threadsafe.New() - require.Equal(t, set.Slice(), []string{}) - - set.Add("a") - require.Equal(t, set.Slice(), []string{"a"}) - - set.Add("a", "b") - require.ElementsMatch(t, set.Slice(), []string{"a", "b"}) -} - -func TestMerge(t *testing.T) { - set := threadsafe.New() - emptySet := strset.New() - set.Merge(emptySet) - require.Equal(t, 0, set.Len()) - - set.Merge(strset.New("a")) - require.Equal(t, 1, set.Len()) - - set.Merge(emptySet) - require.Equal(t, 1, set.Len()) - - set.Add("a", "b", "c") - set.Merge(strset.New("e", "e", "d")) - require.Equal(t, set, threadsafe.New("a", "b", "c", "e", "d")) - - set.Merge(strset.New("a", "e")) - require.Equal(t, set, threadsafe.New("a", "b", "c", "e", "d")) - set.Merge(strset.New("a", "e", "i"), strset.New("o", "u"), strset.New("sometimes y")) - require.Equal(t, set, threadsafe.New("a", "b", "c", "e", "d", "i", "o", "u", "sometimes y")) -} - -func TestMergeThreadsafe(t *testing.T) { - set := threadsafe.New() - emptySet := threadsafe.New() - set.MergeThreadsafe(emptySet) - require.Equal(t, 0, set.Len()) - - set.MergeThreadsafe(threadsafe.New("a")) - require.Equal(t, 1, set.Len()) - - set.MergeThreadsafe(emptySet) - require.Equal(t, 1, set.Len()) - - set.Add("a", "b", "c") - set.MergeThreadsafe(threadsafe.New("e", "e", "d")) - require.Equal(t, set, threadsafe.New("a", "b", "c", "e", "d")) - - set.MergeThreadsafe(threadsafe.New("a", "e")) - require.Equal(t, set, threadsafe.New("a", "b", "c", "e", "d")) - set.MergeThreadsafe(threadsafe.New("a", "e", "i"), threadsafe.New("o", "u"), threadsafe.New("sometimes y")) - require.Equal(t, set, threadsafe.New("a", "b", "c", "e", "d", "i", "o", "u", "sometimes y")) -} - -func TestSubtract(t *testing.T) { - set := threadsafe.New("a", "b", "c") - set.Subtract(strset.New("z", "a", "b")) - require.Equal(t, set, threadsafe.New("c")) - set.Subtract(strset.New("x")) - require.Equal(t, set, threadsafe.New("c")) -} - -func SubtractThreadsafe(t *testing.T) { - set := threadsafe.New("a", "b", "c") - set.SubtractThreadsafe(threadsafe.New("z", "a", "b")) - require.Equal(t, set, threadsafe.New("c")) - set.SubtractThreadsafe(threadsafe.New("x")) - require.Equal(t, set, threadsafe.New("c")) -} - -func TestShrink(t *testing.T) { - set := threadsafe.New("a", "b", "c", "d") - set.Shrink(2) - require.Equal(t, set.Len(), 2) - - set = threadsafe.New("g", "f", "e", "d", "c", "b", "a") - set.ShrinkSorted(3) - require.Equal(t, set.Len(), 3) - - set = threadsafe.New("a", "b") - set.Shrink(2) - require.Equal(t, set, threadsafe.New("a", "b")) - - set = threadsafe.New("a") - set.Shrink(2) - require.Equal(t, set, threadsafe.New("a")) - - set = threadsafe.New() - set.Shrink(2) - require.Equal(t, set.Len(), 0) -} - -func TestShrinkSorted(t *testing.T) { - for i := 0; i < 10; i++ { - set := threadsafe.New("g", "f", "e", "d", "c", "b", "a") - set.ShrinkSorted(2) - require.Equal(t, set, threadsafe.New("a", "b")) - - set = threadsafe.New("g", "f", "e", "d", "c", "b", "a") - set.ShrinkSorted(3) - require.Equal(t, set, threadsafe.New("a", "b", "c")) - } - - set := threadsafe.New("a", "b") - set.ShrinkSorted(2) - require.Equal(t, set, threadsafe.New("a", "b")) - - set = threadsafe.New("a") - set.ShrinkSorted(2) - require.Equal(t, set, threadsafe.New("a")) - - set = threadsafe.New() - set.ShrinkSorted(2) - require.Equal(t, set.Len(), 0) -} - -func TestUnion(t *testing.T) { - require.Equal(t, threadsafe.Union(threadsafe.New(), strset.New()).Len(), 0) - require.Equal(t, threadsafe.Union(threadsafe.New("a", "b"), strset.New()), threadsafe.New("a", "b")) - require.Equal(t, threadsafe.Union(threadsafe.New(), strset.New("a", "b")), threadsafe.New("a", "b")) - require.Equal(t, threadsafe.Union(threadsafe.New("a", "a"), strset.New("a", "b")), threadsafe.New("a", "b")) - require.Equal(t, threadsafe.Union(threadsafe.New("a"), strset.New("b")), threadsafe.New("a", "b")) -} - -func TestUnionThreadsafe(t *testing.T) { - require.Equal(t, threadsafe.UnionThreadsafe(threadsafe.New(), threadsafe.New()).Len(), 0) - require.Equal(t, threadsafe.UnionThreadsafe(threadsafe.New("a", "b"), threadsafe.New()), threadsafe.New("a", "b")) - require.Equal(t, threadsafe.UnionThreadsafe(threadsafe.New(), threadsafe.New("a", "b")), threadsafe.New("a", "b")) - require.Equal(t, threadsafe.UnionThreadsafe(threadsafe.New("a", "a"), threadsafe.New("a", "b")), threadsafe.New("a", "b")) - require.Equal(t, threadsafe.UnionThreadsafe(threadsafe.New("a"), threadsafe.New("b")), threadsafe.New("a", "b")) -} - -func TestDifference(t *testing.T) { - set1 := threadsafe.New("a", "b", "c") - set1Strset := strset.New("a", "b", "c") - set2 := threadsafe.New("z", "a", "b") - set2Strset := strset.New("z", "a", "b") - - d := threadsafe.Difference(set1, set2Strset) - require.Equal(t, d, threadsafe.New("c")) - d = threadsafe.Difference(set2, set1Strset) - require.Equal(t, d, threadsafe.New("z")) - d = threadsafe.Difference(set1, set1Strset) - require.Equal(t, d, threadsafe.New()) -} - -func TestDifferenceThreadsafe(t *testing.T) { - set1 := threadsafe.New("a", "b", "c") - set2 := threadsafe.New("z", "a", "b") - - d := threadsafe.DifferenceThreadsafe(set1, set2) - require.Equal(t, d, threadsafe.New("c")) - d = threadsafe.DifferenceThreadsafe(set2, set1) - require.Equal(t, d, threadsafe.New("z")) - d = threadsafe.DifferenceThreadsafe(set1, set1) - require.Equal(t, d, threadsafe.New()) -} - -func TestIntersection(t *testing.T) { - set1 := threadsafe.New("a", "b", "c") - set2 := threadsafe.New("a", "x", "y") - set2Strset := strset.New("a", "x", "y") - set3Strset := strset.New("z", "b", "c") - set4Strset := strset.New("z", "b", "w") - - d := threadsafe.Intersection(set1, set2Strset) - require.Equal(t, d, threadsafe.New("a")) - d = threadsafe.Intersection(set1, set3Strset) - require.Equal(t, d, threadsafe.New("b", "c")) - d = threadsafe.Intersection(set2, set3Strset) - require.Equal(t, d, threadsafe.New()) - d = threadsafe.Intersection(set1, set3Strset, set4Strset) - require.Equal(t, d, threadsafe.New("b")) -} - -func TestIntersectionThreadsafe(t *testing.T) { - set1 := threadsafe.New("a", "b", "c") - set2 := threadsafe.New("a", "x", "y") - set3 := threadsafe.New("z", "b", "c") - set4 := threadsafe.New("z", "b", "w") - - d := threadsafe.IntersectionThreadsafe(set1, set2) - require.Equal(t, d, threadsafe.New("a")) - d = threadsafe.IntersectionThreadsafe(set1, set3) - require.Equal(t, d, threadsafe.New("b", "c")) - d = threadsafe.IntersectionThreadsafe(set2, set3) - require.Equal(t, d, threadsafe.New()) - d = threadsafe.IntersectionThreadsafe(set1, set3, set4) - require.Equal(t, d, threadsafe.New("b")) -}