diff --git a/pkg/proxy/roundrobin.go b/pkg/proxy/roundrobin.go index 0e50b0879b9..8d4adbcbbd7 100644 --- a/pkg/proxy/roundrobin.go +++ b/pkg/proxy/roundrobin.go @@ -25,6 +25,7 @@ import ( "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util/slice" "github.com/golang/glog" ) @@ -210,14 +211,14 @@ func (lb *LoadBalancerRR) OnUpdate(endpoints []api.Endpoints) { for _, endpoint := range endpoints { existingEndpoints, exists := lb.endpointsMap[endpoint.Name] validEndpoints := filterValidEndpoints(endpoint.Endpoints) - if !exists || !reflect.DeepEqual(existingEndpoints, validEndpoints) { + if !exists || !reflect.DeepEqual(slice.SortStrings(slice.CopyStrings(existingEndpoints)), slice.SortStrings(validEndpoints)) { glog.V(3).Infof("LoadBalancerRR: Setting endpoints for %s to %+v", endpoint.Name, endpoint.Endpoints) updateServiceDetailMap(lb, endpoint.Name, validEndpoints) // On update can be called without NewService being called externally. // to be safe we will call it here. A new service will only be created // if one does not already exist. lb.NewService(endpoint.Name, api.AffinityTypeNone, 0) - lb.endpointsMap[endpoint.Name] = validEndpoints + lb.endpointsMap[endpoint.Name] = slice.ShuffleStrings(validEndpoints) // Reset the round-robin index. lb.rrIndex[endpoint.Name] = 0 diff --git a/pkg/proxy/roundrobin_test.go b/pkg/proxy/roundrobin_test.go index bb9f54b030f..29bac114fbc 100644 --- a/pkg/proxy/roundrobin_test.go +++ b/pkg/proxy/roundrobin_test.go @@ -75,7 +75,7 @@ func expectEndpoint(t *testing.T, loadBalancer *LoadBalancerRR, service string, t.Errorf("Didn't find a service for %s, expected %s, failed with: %v", service, expected, err) } if endpoint != expected { - t.Errorf("Didn't get expected endpoint for service %s, expected %s, got: %s", service, expected, endpoint) + t.Errorf("Didn't get expected endpoint for service %s client %v, expected %s, got: %s", service, netaddr, expected, endpoint) } } @@ -109,10 +109,11 @@ func TestLoadBalanceWorksWithMultipleEndpoints(t *testing.T) { Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"}, } loadBalancer.OnUpdate(endpoints) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", nil) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", nil) - expectEndpoint(t, loadBalancer, "foo", "endpoint:3", nil) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", nil) + shuffledEndpoints := loadBalancer.endpointsMap["foo"] + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], nil) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], nil) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], nil) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], nil) } func TestLoadBalanceWorksWithMultipleEndpointsAndUpdates(t *testing.T) { @@ -127,21 +128,23 @@ func TestLoadBalanceWorksWithMultipleEndpointsAndUpdates(t *testing.T) { Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"}, } loadBalancer.OnUpdate(endpoints) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", nil) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", nil) - expectEndpoint(t, loadBalancer, "foo", "endpoint:3", nil) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", nil) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", nil) + shuffledEndpoints := loadBalancer.endpointsMap["foo"] + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], nil) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], nil) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], nil) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], nil) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], nil) // Then update the configuration with one fewer endpoints, make sure // we start in the beginning again endpoints[0] = api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "foo"}, Endpoints: []string{"endpoint:8", "endpoint:9"}, } loadBalancer.OnUpdate(endpoints) - expectEndpoint(t, loadBalancer, "foo", "endpoint:8", nil) - expectEndpoint(t, loadBalancer, "foo", "endpoint:9", nil) - expectEndpoint(t, loadBalancer, "foo", "endpoint:8", nil) - expectEndpoint(t, loadBalancer, "foo", "endpoint:9", nil) + shuffledEndpoints = loadBalancer.endpointsMap["foo"] + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], nil) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], nil) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], nil) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], nil) // Clear endpoints endpoints[0] = api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "foo"}, Endpoints: []string{}} loadBalancer.OnUpdate(endpoints) @@ -168,17 +171,19 @@ func TestLoadBalanceWorksWithServiceRemoval(t *testing.T) { Endpoints: []string{"endpoint:4", "endpoint:5"}, } loadBalancer.OnUpdate(endpoints) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", nil) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", nil) - expectEndpoint(t, loadBalancer, "foo", "endpoint:3", nil) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", nil) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", nil) + shuffledFooEndpoints := loadBalancer.endpointsMap["foo"] + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], nil) + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[1], nil) + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[2], nil) + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], nil) + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[1], nil) - expectEndpoint(t, loadBalancer, "bar", "endpoint:4", nil) - expectEndpoint(t, loadBalancer, "bar", "endpoint:5", nil) - expectEndpoint(t, loadBalancer, "bar", "endpoint:4", nil) - expectEndpoint(t, loadBalancer, "bar", "endpoint:5", nil) - expectEndpoint(t, loadBalancer, "bar", "endpoint:4", nil) + shuffledBarEndpoints := loadBalancer.endpointsMap["bar"] + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], nil) + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[1], nil) + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], nil) + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[1], nil) + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], nil) // Then update the configuration by removing foo loadBalancer.OnUpdate(endpoints[1:]) @@ -188,10 +193,10 @@ func TestLoadBalanceWorksWithServiceRemoval(t *testing.T) { } // but bar is still there, and we continue RR from where we left off. - expectEndpoint(t, loadBalancer, "bar", "endpoint:5", nil) - expectEndpoint(t, loadBalancer, "bar", "endpoint:4", nil) - expectEndpoint(t, loadBalancer, "bar", "endpoint:5", nil) - expectEndpoint(t, loadBalancer, "bar", "endpoint:4", nil) + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[1], nil) + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], nil) + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[1], nil) + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], nil) } func TestStickyLoadBalanceWorksWithSingleEndpoint(t *testing.T) { @@ -232,14 +237,15 @@ func TestStickyLoadBalanaceWorksWithMultipleEndpoints(t *testing.T) { Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"}, } loadBalancer.OnUpdate(endpoints) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2) - expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client3) - expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client3) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) + shuffledEndpoints := loadBalancer.endpointsMap["foo"] + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], client3) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], client3) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1) } func TestStickyLoadBalanaceWorksWithMultipleEndpointsStickyNone(t *testing.T) { @@ -259,14 +265,15 @@ func TestStickyLoadBalanaceWorksWithMultipleEndpointsStickyNone(t *testing.T) { Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"}, } loadBalancer.OnUpdate(endpoints) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client2) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client2) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client3) - expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client3) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client1) + shuffledEndpoints := loadBalancer.endpointsMap["foo"] + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], client2) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client2) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client3) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], client3) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client1) } func TestStickyLoadBalanaceWorksWithMultipleEndpointsRemoveOne(t *testing.T) { @@ -289,32 +296,45 @@ func TestStickyLoadBalanaceWorksWithMultipleEndpointsRemoveOne(t *testing.T) { Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"}, } loadBalancer.OnUpdate(endpoints) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2) - expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client3) + shuffledEndpoints := loadBalancer.endpointsMap["foo"] + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1) + client1Endpoint := shuffledEndpoints[0] + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2) + client2Endpoint := shuffledEndpoints[1] + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], client3) + client3Endpoint := shuffledEndpoints[2] endpoints[0] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: "foo"}, Endpoints: []string{"endpoint:1", "endpoint:2"}, } loadBalancer.OnUpdate(endpoints) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client3) + shuffledEndpoints = loadBalancer.endpointsMap["foo"] + if client1Endpoint == "endpoint:3" { + client1Endpoint = shuffledEndpoints[0] + } else if client2Endpoint == "endpoint:3" { + client2Endpoint = shuffledEndpoints[0] + } else if client3Endpoint == "endpoint:3" { + client3Endpoint = shuffledEndpoints[0] + } + expectEndpoint(t, loadBalancer, "foo", client1Endpoint, client1) + expectEndpoint(t, loadBalancer, "foo", client2Endpoint, client2) + expectEndpoint(t, loadBalancer, "foo", client3Endpoint, client3) endpoints[0] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: "foo"}, Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:4"}, } loadBalancer.OnUpdate(endpoints) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client3) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client4) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client5) - expectEndpoint(t, loadBalancer, "foo", "endpoint:4", client6) + shuffledEndpoints = loadBalancer.endpointsMap["foo"] + expectEndpoint(t, loadBalancer, "foo", client1Endpoint, client1) + expectEndpoint(t, loadBalancer, "foo", client2Endpoint, client2) + expectEndpoint(t, loadBalancer, "foo", client3Endpoint, client3) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client4) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client5) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], client6) } func TestStickyLoadBalanceWorksWithMultipleEndpointsAndUpdates(t *testing.T) { @@ -334,24 +354,26 @@ func TestStickyLoadBalanceWorksWithMultipleEndpointsAndUpdates(t *testing.T) { Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"}, } loadBalancer.OnUpdate(endpoints) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2) - expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client3) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2) + shuffledEndpoints := loadBalancer.endpointsMap["foo"] + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], client3) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2) // Then update the configuration with one fewer endpoints, make sure // we start in the beginning again endpoints[0] = api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "foo"}, Endpoints: []string{"endpoint:4", "endpoint:5"}, } loadBalancer.OnUpdate(endpoints) - expectEndpoint(t, loadBalancer, "foo", "endpoint:4", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:5", client2) - expectEndpoint(t, loadBalancer, "foo", "endpoint:4", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:5", client2) - expectEndpoint(t, loadBalancer, "foo", "endpoint:5", client2) + shuffledEndpoints = loadBalancer.endpointsMap["foo"] + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2) + expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2) // Clear endpoints endpoints[0] = api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "foo"}, Endpoints: []string{}} @@ -384,19 +406,21 @@ func TestStickyLoadBalanceWorksWithServiceRemoval(t *testing.T) { Endpoints: []string{"endpoint:4", "endpoint:5"}, } loadBalancer.OnUpdate(endpoints) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2) - expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client3) - expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client3) - expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1) - expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2) + shuffledFooEndpoints := loadBalancer.endpointsMap["foo"] + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[1], client2) + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[2], client3) + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[2], client3) + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[1], client2) - expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1) - expectEndpoint(t, loadBalancer, "bar", "endpoint:5", client2) - expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1) - expectEndpoint(t, loadBalancer, "bar", "endpoint:5", client2) - expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1) - expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1) + shuffledBarEndpoints := loadBalancer.endpointsMap["bar"] + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[1], client2) + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[1], client2) + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], client1) // Then update the configuration by removing foo loadBalancer.OnUpdate(endpoints[1:]) @@ -406,10 +430,11 @@ func TestStickyLoadBalanceWorksWithServiceRemoval(t *testing.T) { } // but bar is still there, and we continue RR from where we left off. - expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1) - expectEndpoint(t, loadBalancer, "bar", "endpoint:5", client2) - expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1) - expectEndpoint(t, loadBalancer, "bar", "endpoint:5", client2) - expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1) - expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1) + shuffledBarEndpoints = loadBalancer.endpointsMap["bar"] + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[1], client2) + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[1], client2) + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], client1) + expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], client1) } diff --git a/pkg/util/slice/slice.go b/pkg/util/slice/slice.go new file mode 100644 index 00000000000..d6f5eb74e42 --- /dev/null +++ b/pkg/util/slice/slice.go @@ -0,0 +1,49 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 slice provides utility methods for common operations on slices. +package slice + +import ( + "math/rand" + "sort" +) + +// CopyStrings copies the contents of the specified string slice +// into a new slice. +func CopyStrings(s []string) []string { + c := make([]string, len(s)) + copy(c, s) + return c +} + +// SortStrings sorts the specified string slice in place. It returns the same +// slice that was provided in order to facilitate method chaining. +func SortStrings(s []string) []string { + sort.Strings(s) + return s +} + +// ShuffleStrings copies strings from the specified slice into a copy in random +// order. It returns a new slice. +func ShuffleStrings(s []string) []string { + shuffled := make([]string, len(s)) + perm := rand.Perm(len(s)) + for i, j := range perm { + shuffled[j] = s[i] + } + return shuffled +} diff --git a/pkg/util/slice/slice_test.go b/pkg/util/slice/slice_test.go new file mode 100644 index 00000000000..a0a61a5b1d4 --- /dev/null +++ b/pkg/util/slice/slice_test.go @@ -0,0 +1,70 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 slice + +import ( + "reflect" + "testing" +) + +func TestCopyStrings(t *testing.T) { + src := []string{"a", "c", "b"} + dest := CopyStrings(src) + + if !reflect.DeepEqual(src, dest) { + t.Errorf("%v and %v are not equal", src, dest) + } + + src[0] = "A" + if reflect.DeepEqual(src, dest) { + t.Errorf("CopyStrings didn't make a copy") + } +} + +func TestSortStrings(t *testing.T) { + src := []string{"a", "c", "b"} + dest := SortStrings(src) + expected := []string{"a", "b", "c"} + + if !reflect.DeepEqual(dest, expected) { + t.Errorf("SortString didn't sort the strings") + } + + if !reflect.DeepEqual(src, expected) { + t.Errorf("SortString didn't sort in place") + } +} + +func TestShuffleStrings(t *testing.T) { + src := []string{"a", "b", "c", "d", "e", "f"} + dest := ShuffleStrings(src) + + if len(src) != len(dest) { + t.Errorf("Shuffled slice is wrong length, expected %v got %v", len(src), len(dest)) + } + + m := make(map[string]bool, len(dest)) + for _, s := range dest { + m[s] = true + } + + for _, k := range src { + if _, exists := m[k]; !exists { + t.Errorf("Element %v missing from shuffled slice", k) + } + } +}