From 8238298fbbf27eca9741cbbe6f41c4c76e191232 Mon Sep 17 00:00:00 2001 From: Dawn Chen Date: Tue, 24 Mar 2015 16:20:18 -0700 Subject: [PATCH] Include coreos/etcd/pkg/types pakcage --- .../github.com/coreos/etcd/pkg/types/id.go | 41 ++++ .../coreos/etcd/pkg/types/id_test.go | 95 +++++++++ .../github.com/coreos/etcd/pkg/types/set.go | 178 +++++++++++++++++ .../coreos/etcd/pkg/types/set_test.go | 186 ++++++++++++++++++ .../github.com/coreos/etcd/pkg/types/slice.go | 22 +++ .../coreos/etcd/pkg/types/slice_test.go | 30 +++ .../github.com/coreos/etcd/pkg/types/urls.go | 74 +++++++ .../coreos/etcd/pkg/types/urls_test.go | 169 ++++++++++++++++ 8 files changed, 795 insertions(+) create mode 100644 Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/id.go create mode 100644 Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/id_test.go create mode 100644 Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/set.go create mode 100644 Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/set_test.go create mode 100644 Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/slice.go create mode 100644 Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/slice_test.go create mode 100644 Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/urls.go create mode 100644 Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/urls_test.go diff --git a/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/id.go b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/id.go new file mode 100644 index 00000000000..88cb9e63494 --- /dev/null +++ b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/id.go @@ -0,0 +1,41 @@ +// Copyright 2015 CoreOS, 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 types + +import ( + "strconv" +) + +// ID represents a generic identifier which is canonically +// stored as a uint64 but is typically represented as a +// base-16 string for input/output +type ID uint64 + +func (i ID) String() string { + return strconv.FormatUint(uint64(i), 16) +} + +// IDFromString attempts to create an ID from a base-16 string. +func IDFromString(s string) (ID, error) { + i, err := strconv.ParseUint(s, 16, 64) + return ID(i), err +} + +// IDSlice implements the sort interface +type IDSlice []ID + +func (p IDSlice) Len() int { return len(p) } +func (p IDSlice) Less(i, j int) bool { return uint64(p[i]) < uint64(p[j]) } +func (p IDSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/id_test.go b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/id_test.go new file mode 100644 index 00000000000..97d168f58e2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/id_test.go @@ -0,0 +1,95 @@ +// Copyright 2015 CoreOS, 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 types + +import ( + "reflect" + "sort" + "testing" +) + +func TestIDString(t *testing.T) { + tests := []struct { + input ID + want string + }{ + { + input: 12, + want: "c", + }, + { + input: 4918257920282737594, + want: "444129853c343bba", + }, + } + + for i, tt := range tests { + got := tt.input.String() + if tt.want != got { + t.Errorf("#%d: ID.String failure: want=%v, got=%v", i, tt.want, got) + } + } +} + +func TestIDFromString(t *testing.T) { + tests := []struct { + input string + want ID + }{ + { + input: "17", + want: 23, + }, + { + input: "612840dae127353", + want: 437557308098245459, + }, + } + + for i, tt := range tests { + got, err := IDFromString(tt.input) + if err != nil { + t.Errorf("#%d: IDFromString failure: err=%v", i, err) + continue + } + if tt.want != got { + t.Errorf("#%d: IDFromString failure: want=%v, got=%v", i, tt.want, got) + } + } +} + +func TestIDFromStringFail(t *testing.T) { + tests := []string{ + "", + "XXX", + "612840dae127353612840dae127353", + } + + for i, tt := range tests { + _, err := IDFromString(tt) + if err == nil { + t.Fatalf("#%d: IDFromString expected error, but err=nil", i) + } + } +} + +func TestIDSlice(t *testing.T) { + g := []ID{10, 500, 5, 1, 100, 25} + w := []ID{1, 5, 10, 25, 100, 500} + sort.Sort(IDSlice(g)) + if !reflect.DeepEqual(g, w) { + t.Errorf("slice after sort = %#v, want %#v", g, w) + } +} diff --git a/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/set.go b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/set.go new file mode 100644 index 00000000000..32287522b11 --- /dev/null +++ b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/set.go @@ -0,0 +1,178 @@ +// Copyright 2015 CoreOS, 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 types + +import ( + "reflect" + "sort" + "sync" +) + +type Set interface { + Add(string) + Remove(string) + Contains(string) bool + Equals(Set) bool + Length() int + Values() []string + Copy() Set + Sub(Set) Set +} + +func NewUnsafeSet(values ...string) *unsafeSet { + set := &unsafeSet{make(map[string]struct{})} + for _, v := range values { + set.Add(v) + } + return set +} + +func NewThreadsafeSet(values ...string) *tsafeSet { + us := NewUnsafeSet(values...) + return &tsafeSet{us, sync.RWMutex{}} +} + +type unsafeSet struct { + d map[string]struct{} +} + +// Add adds a new value to the set (no-op if the value is already present) +func (us *unsafeSet) Add(value string) { + us.d[value] = struct{}{} +} + +// Remove removes the given value from the set +func (us *unsafeSet) Remove(value string) { + delete(us.d, value) +} + +// Contains returns whether the set contains the given value +func (us *unsafeSet) Contains(value string) (exists bool) { + _, exists = us.d[value] + return +} + +// ContainsAll returns whether the set contains all given values +func (us *unsafeSet) ContainsAll(values []string) bool { + for _, s := range values { + if !us.Contains(s) { + return false + } + } + return true +} + +// Equals returns whether the contents of two sets are identical +func (us *unsafeSet) Equals(other Set) bool { + v1 := sort.StringSlice(us.Values()) + v2 := sort.StringSlice(other.Values()) + v1.Sort() + v2.Sort() + return reflect.DeepEqual(v1, v2) +} + +// Length returns the number of elements in the set +func (us *unsafeSet) Length() int { + return len(us.d) +} + +// Values returns the values of the Set in an unspecified order. +func (us *unsafeSet) Values() (values []string) { + values = make([]string, 0) + for val, _ := range us.d { + values = append(values, val) + } + return +} + +// Copy creates a new Set containing the values of the first +func (us *unsafeSet) Copy() Set { + cp := NewUnsafeSet() + for val, _ := range us.d { + cp.Add(val) + } + + return cp +} + +// Sub removes all elements in other from the set +func (us *unsafeSet) Sub(other Set) Set { + oValues := other.Values() + result := us.Copy().(*unsafeSet) + + for _, val := range oValues { + if _, ok := result.d[val]; !ok { + continue + } + delete(result.d, val) + } + + return result +} + +type tsafeSet struct { + us *unsafeSet + m sync.RWMutex +} + +func (ts *tsafeSet) Add(value string) { + ts.m.Lock() + defer ts.m.Unlock() + ts.us.Add(value) +} + +func (ts *tsafeSet) Remove(value string) { + ts.m.Lock() + defer ts.m.Unlock() + ts.us.Remove(value) +} + +func (ts *tsafeSet) Contains(value string) (exists bool) { + ts.m.RLock() + defer ts.m.RUnlock() + return ts.us.Contains(value) +} + +func (ts *tsafeSet) Equals(other Set) bool { + ts.m.RLock() + defer ts.m.RUnlock() + return ts.us.Equals(other) +} + +func (ts *tsafeSet) Length() int { + ts.m.RLock() + defer ts.m.RUnlock() + return ts.us.Length() +} + +func (ts *tsafeSet) Values() (values []string) { + ts.m.RLock() + defer ts.m.RUnlock() + return ts.us.Values() +} + +func (ts *tsafeSet) Copy() Set { + ts.m.RLock() + defer ts.m.RUnlock() + usResult := ts.us.Copy().(*unsafeSet) + return &tsafeSet{usResult, sync.RWMutex{}} +} + +func (ts *tsafeSet) Sub(other Set) Set { + ts.m.RLock() + defer ts.m.RUnlock() + usResult := ts.us.Sub(other).(*unsafeSet) + return &tsafeSet{usResult, sync.RWMutex{}} +} diff --git a/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/set_test.go b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/set_test.go new file mode 100644 index 00000000000..ff1ecc68d3c --- /dev/null +++ b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/set_test.go @@ -0,0 +1,186 @@ +// Copyright 2015 CoreOS, 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 types + +import ( + "reflect" + "sort" + "testing" +) + +func TestUnsafeSet(t *testing.T) { + driveSetTests(t, NewUnsafeSet()) +} + +func TestThreadsafeSet(t *testing.T) { + driveSetTests(t, NewThreadsafeSet()) +} + +// Check that two slices contents are equal; order is irrelevant +func equal(a, b []string) bool { + as := sort.StringSlice(a) + bs := sort.StringSlice(b) + as.Sort() + bs.Sort() + return reflect.DeepEqual(as, bs) +} + +func driveSetTests(t *testing.T, s Set) { + // Verify operations on an empty set + eValues := []string{} + values := s.Values() + if !reflect.DeepEqual(values, eValues) { + t.Fatalf("Expect values=%v got %v", eValues, values) + } + if l := s.Length(); l != 0 { + t.Fatalf("Expected length=0, got %d", l) + } + for _, v := range []string{"foo", "bar", "baz"} { + if s.Contains(v) { + t.Fatalf("Expect s.Contains(%q) to be fale, got true", v) + } + } + + // Add three items, ensure they show up + s.Add("foo") + s.Add("bar") + s.Add("baz") + + eValues = []string{"foo", "bar", "baz"} + values = s.Values() + if !equal(values, eValues) { + t.Fatalf("Expect values=%v got %v", eValues, values) + } + + for _, v := range eValues { + if !s.Contains(v) { + t.Fatalf("Expect s.Contains(%q) to be true, got false", v) + } + } + + if l := s.Length(); l != 3 { + t.Fatalf("Expected length=3, got %d", l) + } + + // Add the same item a second time, ensuring it is not duplicated + s.Add("foo") + + values = s.Values() + if !equal(values, eValues) { + t.Fatalf("Expect values=%v got %v", eValues, values) + } + if l := s.Length(); l != 3 { + t.Fatalf("Expected length=3, got %d", l) + } + + // Remove all items, ensure they are gone + s.Remove("foo") + s.Remove("bar") + s.Remove("baz") + + eValues = []string{} + values = s.Values() + if !equal(values, eValues) { + t.Fatalf("Expect values=%v got %v", eValues, values) + } + + if l := s.Length(); l != 0 { + t.Fatalf("Expected length=0, got %d", l) + } + + // Create new copies of the set, and ensure they are unlinked to the + // original Set by making modifications + s.Add("foo") + s.Add("bar") + cp1 := s.Copy() + cp2 := s.Copy() + s.Remove("foo") + cp3 := s.Copy() + cp1.Add("baz") + + for i, tt := range []struct { + want []string + got []string + }{ + {[]string{"bar"}, s.Values()}, + {[]string{"foo", "bar", "baz"}, cp1.Values()}, + {[]string{"foo", "bar"}, cp2.Values()}, + {[]string{"bar"}, cp3.Values()}, + } { + if !equal(tt.want, tt.got) { + t.Fatalf("case %d: expect values=%v got %v", i, tt.want, tt.got) + } + } + + for i, tt := range []struct { + want bool + got bool + }{ + {true, s.Equals(cp3)}, + {true, cp3.Equals(s)}, + {false, s.Equals(cp2)}, + {false, s.Equals(cp1)}, + {false, cp1.Equals(s)}, + {false, cp2.Equals(s)}, + {false, cp2.Equals(cp1)}, + } { + if tt.got != tt.want { + t.Fatalf("case %d: want %t, got %t", i, tt.want, tt.got) + + } + } + + // Subtract values from a Set, ensuring a new Set is created and + // the original Sets are unmodified + sub1 := cp1.Sub(s) + sub2 := cp2.Sub(cp1) + + for i, tt := range []struct { + want []string + got []string + }{ + {[]string{"foo", "bar", "baz"}, cp1.Values()}, + {[]string{"foo", "bar"}, cp2.Values()}, + {[]string{"bar"}, s.Values()}, + {[]string{"foo", "baz"}, sub1.Values()}, + {[]string{}, sub2.Values()}, + } { + if !equal(tt.want, tt.got) { + t.Fatalf("case %d: expect values=%v got %v", i, tt.want, tt.got) + } + } +} + +func TestUnsafeSetContainsAll(t *testing.T) { + vals := []string{"foo", "bar", "baz"} + s := NewUnsafeSet(vals...) + + tests := []struct { + strs []string + wcontain bool + }{ + {[]string{}, true}, + {vals[:1], true}, + {vals[:2], true}, + {vals, true}, + {[]string{"cuz"}, false}, + {[]string{vals[0], "cuz"}, false}, + } + for i, tt := range tests { + if g := s.ContainsAll(tt.strs); g != tt.wcontain { + t.Errorf("#%d: ok = %v, want %v", i, g, tt.wcontain) + } + } +} diff --git a/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/slice.go b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/slice.go new file mode 100644 index 00000000000..0327950f706 --- /dev/null +++ b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/slice.go @@ -0,0 +1,22 @@ +// Copyright 2015 CoreOS, 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 types + +// Uint64Slice implements sort interface +type Uint64Slice []uint64 + +func (p Uint64Slice) Len() int { return len(p) } +func (p Uint64Slice) Less(i, j int) bool { return p[i] < p[j] } +func (p Uint64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/slice_test.go b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/slice_test.go new file mode 100644 index 00000000000..95e37e04d20 --- /dev/null +++ b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/slice_test.go @@ -0,0 +1,30 @@ +// Copyright 2015 CoreOS, 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 types + +import ( + "reflect" + "sort" + "testing" +) + +func TestUint64Slice(t *testing.T) { + g := Uint64Slice{10, 500, 5, 1, 100, 25} + w := Uint64Slice{1, 5, 10, 25, 100, 500} + sort.Sort(g) + if !reflect.DeepEqual(g, w) { + t.Errorf("slice after sort = %#v, want %#v", g, w) + } +} diff --git a/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/urls.go b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/urls.go new file mode 100644 index 00000000000..ce2483ffaaa --- /dev/null +++ b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/urls.go @@ -0,0 +1,74 @@ +// Copyright 2015 CoreOS, 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 types + +import ( + "errors" + "fmt" + "net" + "net/url" + "sort" + "strings" +) + +type URLs []url.URL + +func NewURLs(strs []string) (URLs, error) { + all := make([]url.URL, len(strs)) + if len(all) == 0 { + return nil, errors.New("no valid URLs given") + } + for i, in := range strs { + in = strings.TrimSpace(in) + u, err := url.Parse(in) + if err != nil { + return nil, err + } + if u.Scheme != "http" && u.Scheme != "https" { + return nil, fmt.Errorf("URL scheme must be http or https: %s", in) + } + if _, _, err := net.SplitHostPort(u.Host); err != nil { + return nil, fmt.Errorf(`URL address does not have the form "host:port": %s`, in) + } + if u.Path != "" { + return nil, fmt.Errorf("URL must not contain a path: %s", in) + } + all[i] = *u + } + us := URLs(all) + us.Sort() + + return us, nil +} + +func (us URLs) String() string { + return strings.Join(us.StringSlice(), ",") +} + +func (us *URLs) Sort() { + sort.Sort(us) +} +func (us URLs) Len() int { return len(us) } +func (us URLs) Less(i, j int) bool { return us[i].String() < us[j].String() } +func (us URLs) Swap(i, j int) { us[i], us[j] = us[j], us[i] } + +func (us URLs) StringSlice() []string { + out := make([]string, len(us)) + for i := range us { + out[i] = us[i].String() + } + + return out +} diff --git a/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/urls_test.go b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/urls_test.go new file mode 100644 index 00000000000..41caa5d68a5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types/urls_test.go @@ -0,0 +1,169 @@ +// Copyright 2015 CoreOS, 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 types + +import ( + "reflect" + "testing" + + "github.com/coreos/etcd/pkg/testutil" +) + +func TestNewURLs(t *testing.T) { + tests := []struct { + strs []string + wurls URLs + }{ + { + []string{"http://127.0.0.1:4001"}, + testutil.MustNewURLs(t, []string{"http://127.0.0.1:4001"}), + }, + // it can trim space + { + []string{" http://127.0.0.1:4001 "}, + testutil.MustNewURLs(t, []string{"http://127.0.0.1:4001"}), + }, + // it does sort + { + []string{ + "http://127.0.0.2:4001", + "http://127.0.0.1:4001", + }, + testutil.MustNewURLs(t, []string{ + "http://127.0.0.1:4001", + "http://127.0.0.2:4001", + }), + }, + } + for i, tt := range tests { + urls, _ := NewURLs(tt.strs) + if !reflect.DeepEqual(urls, tt.wurls) { + t.Errorf("#%d: urls = %+v, want %+v", i, urls, tt.wurls) + } + } +} + +func TestURLsString(t *testing.T) { + tests := []struct { + us URLs + wstr string + }{ + { + URLs{}, + "", + }, + { + testutil.MustNewURLs(t, []string{"http://127.0.0.1:4001"}), + "http://127.0.0.1:4001", + }, + { + testutil.MustNewURLs(t, []string{ + "http://127.0.0.1:4001", + "http://127.0.0.2:4001", + }), + "http://127.0.0.1:4001,http://127.0.0.2:4001", + }, + { + testutil.MustNewURLs(t, []string{ + "http://127.0.0.2:4001", + "http://127.0.0.1:4001", + }), + "http://127.0.0.2:4001,http://127.0.0.1:4001", + }, + } + for i, tt := range tests { + g := tt.us.String() + if g != tt.wstr { + t.Errorf("#%d: string = %s, want %s", i, g, tt.wstr) + } + } +} + +func TestURLsSort(t *testing.T) { + g := testutil.MustNewURLs(t, []string{ + "http://127.0.0.4:4001", + "http://127.0.0.2:4001", + "http://127.0.0.1:4001", + "http://127.0.0.3:4001", + }) + w := testutil.MustNewURLs(t, []string{ + "http://127.0.0.1:4001", + "http://127.0.0.2:4001", + "http://127.0.0.3:4001", + "http://127.0.0.4:4001", + }) + gurls := URLs(g) + gurls.Sort() + if !reflect.DeepEqual(g, w) { + t.Errorf("URLs after sort = %#v, want %#v", g, w) + } +} + +func TestURLsStringSlice(t *testing.T) { + tests := []struct { + us URLs + wstr []string + }{ + { + URLs{}, + []string{}, + }, + { + testutil.MustNewURLs(t, []string{"http://127.0.0.1:4001"}), + []string{"http://127.0.0.1:4001"}, + }, + { + testutil.MustNewURLs(t, []string{ + "http://127.0.0.1:4001", + "http://127.0.0.2:4001", + }), + []string{"http://127.0.0.1:4001", "http://127.0.0.2:4001"}, + }, + { + testutil.MustNewURLs(t, []string{ + "http://127.0.0.2:4001", + "http://127.0.0.1:4001", + }), + []string{"http://127.0.0.2:4001", "http://127.0.0.1:4001"}, + }, + } + for i, tt := range tests { + g := tt.us.StringSlice() + if !reflect.DeepEqual(g, tt.wstr) { + t.Errorf("#%d: string slice = %+v, want %+v", i, g, tt.wstr) + } + } +} + +func TestNewURLsFail(t *testing.T) { + tests := [][]string{ + // no urls given + {}, + // missing protocol scheme + {"://127.0.0.1:4001"}, + // unsupported scheme + {"mailto://127.0.0.1:4001"}, + // not conform to host:port + {"http://127.0.0.1"}, + // contain a path + {"http://127.0.0.1:4001/path"}, + } + for i, tt := range tests { + _, err := NewURLs(tt) + if err == nil { + t.Errorf("#%d: err = nil, but error", i) + } + } +}