From d674bcca415695e99cc5a22bdee5028cf0771794 Mon Sep 17 00:00:00 2001 From: Arjan Bal Date: Wed, 21 Jan 2026 11:28:51 +0530 Subject: [PATCH 1/2] use public API for testing --- internal/hierarchy/hierarchy_ext_test.go | 174 +++++++++++++++++++++ internal/hierarchy/hierarchy_test.go | 185 +++-------------------- 2 files changed, 192 insertions(+), 167 deletions(-) create mode 100644 internal/hierarchy/hierarchy_ext_test.go diff --git a/internal/hierarchy/hierarchy_ext_test.go b/internal/hierarchy/hierarchy_ext_test.go new file mode 100644 index 000000000000..a75f4199b8f9 --- /dev/null +++ b/internal/hierarchy/hierarchy_ext_test.go @@ -0,0 +1,174 @@ +/* + * + * Copyright 2026 gRPC authors. + * + * 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 hierarchy_test + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "google.golang.org/grpc/attributes" + "google.golang.org/grpc/internal/hierarchy" + "google.golang.org/grpc/resolver" +) + +func TestFromEndpoint(t *testing.T) { + tests := []struct { + name string + ep resolver.Endpoint + want []string + }{ + { + name: "not set", + ep: resolver.Endpoint{}, + want: nil, + }, + { + name: "set", + ep: hierarchy.SetInEndpoint(resolver.Endpoint{}, []string{"a", "b"}), + want: []string{"a", "b"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := hierarchy.FromEndpoint(tt.ep); !cmp.Equal(got, tt.want) { + t.Errorf("FromEndpoint() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSetInEndpoint(t *testing.T) { + tests := []struct { + name string + ep resolver.Endpoint + path []string + }{ + { + name: "before is not set", + ep: resolver.Endpoint{}, + path: []string{"a", "b"}, + }, + { + name: "before is set", + ep: hierarchy.SetInEndpoint(resolver.Endpoint{}, []string{"before", "a", "b"}), + path: []string{"a", "b"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + newEP := hierarchy.SetInEndpoint(tt.ep, tt.path) + newPath := hierarchy.FromEndpoint(newEP) + if !cmp.Equal(newPath, tt.path) { + t.Errorf("path after SetInEndpoint() = %v, want %v", newPath, tt.path) + } + }) + } +} + +func TestGroup(t *testing.T) { + tests := []struct { + name string + eps []resolver.Endpoint + want map[string][]resolver.Endpoint + }{ + { + name: "all with hierarchy", + eps: []resolver.Endpoint{ + hierarchy.SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a0"}}}, []string{"a"}), + hierarchy.SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a1"}}}, []string{"a"}), + hierarchy.SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "b0"}}}, []string{"b"}), + hierarchy.SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "b1"}}}, []string{"b"}), + }, + want: map[string][]resolver.Endpoint{ + "a": { + hierarchy.SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a0"}}}, nil), + hierarchy.SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a1"}}}, nil), + }, + "b": { + hierarchy.SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "b0"}}}, nil), + hierarchy.SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "b1"}}}, nil), + }, + }, + }, + { + // Endpoints without hierarchy are ignored. + name: "without hierarchy", + eps: []resolver.Endpoint{ + hierarchy.SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a0"}}}, []string{"a"}), + hierarchy.SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a1"}}}, []string{"a"}), + {Addresses: []resolver.Address{{Addr: "b0"}}, Attributes: nil}, + {Addresses: []resolver.Address{{Addr: "b1"}}, Attributes: nil}, + }, + want: map[string][]resolver.Endpoint{ + "a": { + hierarchy.SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a0"}}}, nil), + hierarchy.SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a1"}}}, nil), + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := hierarchy.Group(tt.eps); !cmp.Equal(got, tt.want, cmp.AllowUnexported(attributes.Attributes{})) { + t.Errorf("Group() = %v, want %v", got, tt.want) + t.Errorf("diff: %v", cmp.Diff(got, tt.want, cmp.AllowUnexported(attributes.Attributes{}))) + } + }) + } +} + +func TestGroupE2E(t *testing.T) { + testHierarchy := map[string]map[string][]string{ + "p0": { + "wt0": {"addr0", "addr1"}, + "wt1": {"addr2", "addr3"}, + }, + "p1": { + "wt10": {"addr10", "addr11"}, + "wt11": {"addr12", "addr13"}, + }, + } + + var epsWithHierarchy []resolver.Endpoint + for p, wts := range testHierarchy { + path1 := []string{p} + for wt, addrs := range wts { + path2 := append([]string(nil), path1...) + path2 = append(path2, wt) + for _, addr := range addrs { + a := hierarchy.SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: addr}}}, path2) + epsWithHierarchy = append(epsWithHierarchy, a) + } + } + } + + gotHierarchy := make(map[string]map[string][]string) + for p1, wts := range hierarchy.Group(epsWithHierarchy) { + gotHierarchy[p1] = make(map[string][]string) + for p2, eps := range hierarchy.Group(wts) { + for _, ep := range eps { + gotHierarchy[p1][p2] = append(gotHierarchy[p1][p2], ep.Addresses[0].Addr) + } + } + } + + if !cmp.Equal(gotHierarchy, testHierarchy) { + t.Errorf("diff: %v", cmp.Diff(gotHierarchy, testHierarchy)) + } +} diff --git a/internal/hierarchy/hierarchy_test.go b/internal/hierarchy/hierarchy_test.go index fadfd0a19630..10f774ec4591 100644 --- a/internal/hierarchy/hierarchy_test.go +++ b/internal/hierarchy/hierarchy_test.go @@ -26,172 +26,23 @@ import ( "google.golang.org/grpc/resolver" ) -func TestFromEndpoint(t *testing.T) { - tests := []struct { - name string - ep resolver.Endpoint - want []string - }{ - { - name: "not set", - ep: resolver.Endpoint{}, - want: nil, - }, - { - name: "set", - ep: resolver.Endpoint{ - Attributes: attributes.New(pathKey, pathValue{"a", "b"}), - }, - want: []string{"a", "b"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := FromEndpoint(tt.ep); !cmp.Equal(got, tt.want) { - t.Errorf("FromEndpoint() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestSetInEndpoint(t *testing.T) { - tests := []struct { - name string - ep resolver.Endpoint - path []string - }{ - { - name: "before is not set", - ep: resolver.Endpoint{}, - path: []string{"a", "b"}, - }, - { - name: "before is set", - ep: resolver.Endpoint{ - Attributes: attributes.New(pathKey, pathValue{"before", "a", "b"}), - }, - path: []string{"a", "b"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - newEP := SetInEndpoint(tt.ep, tt.path) - newPath := FromEndpoint(newEP) - if !cmp.Equal(newPath, tt.path) { - t.Errorf("path after SetInEndpoint() = %v, want %v", newPath, tt.path) - } - }) - } -} - -func TestGroup(t *testing.T) { - tests := []struct { - name string - eps []resolver.Endpoint - want map[string][]resolver.Endpoint - }{ - { - name: "all with hierarchy", - eps: []resolver.Endpoint{ - {Addresses: []resolver.Address{{Addr: "a0"}}, Attributes: attributes.New(pathKey, pathValue{"a"})}, - {Addresses: []resolver.Address{{Addr: "a1"}}, Attributes: attributes.New(pathKey, pathValue{"a"})}, - {Addresses: []resolver.Address{{Addr: "b0"}}, Attributes: attributes.New(pathKey, pathValue{"b"})}, - {Addresses: []resolver.Address{{Addr: "b1"}}, Attributes: attributes.New(pathKey, pathValue{"b"})}, - }, - want: map[string][]resolver.Endpoint{ - "a": { - {Addresses: []resolver.Address{{Addr: "a0"}}, Attributes: attributes.New(pathKey, pathValue{})}, - {Addresses: []resolver.Address{{Addr: "a1"}}, Attributes: attributes.New(pathKey, pathValue{})}, - }, - "b": { - {Addresses: []resolver.Address{{Addr: "b0"}}, Attributes: attributes.New(pathKey, pathValue{})}, - {Addresses: []resolver.Address{{Addr: "b1"}}, Attributes: attributes.New(pathKey, pathValue{})}, - }, - }, - }, - { - // Endpoints without hierarchy are ignored. - name: "without hierarchy", - eps: []resolver.Endpoint{ - {Addresses: []resolver.Address{{Addr: "a0"}}, Attributes: attributes.New(pathKey, pathValue{"a"})}, - {Addresses: []resolver.Address{{Addr: "a1"}}, Attributes: attributes.New(pathKey, pathValue{"a"})}, - {Addresses: []resolver.Address{{Addr: "b0"}}, Attributes: nil}, - {Addresses: []resolver.Address{{Addr: "b1"}}, Attributes: nil}, - }, - want: map[string][]resolver.Endpoint{ - "a": { - {Addresses: []resolver.Address{{Addr: "a0"}}, Attributes: attributes.New(pathKey, pathValue{})}, - {Addresses: []resolver.Address{{Addr: "a1"}}, Attributes: attributes.New(pathKey, pathValue{})}, - }, - }, - }, - { - // If hierarchy is set to a wrong type (which should never happen), - // the endpoint is ignored. - name: "wrong type", - eps: []resolver.Endpoint{ - {Addresses: []resolver.Address{{Addr: "a0"}}, Attributes: attributes.New(pathKey, pathValue{"a"})}, - {Addresses: []resolver.Address{{Addr: "a1"}}, Attributes: attributes.New(pathKey, pathValue{"a"})}, - {Addresses: []resolver.Address{{Addr: "b0"}}, Attributes: attributes.New(pathKey, "b")}, - {Addresses: []resolver.Address{{Addr: "b1"}}, Attributes: attributes.New(pathKey, 314)}, - }, - want: map[string][]resolver.Endpoint{ - "a": { - {Addresses: []resolver.Address{{Addr: "a0"}}, Attributes: attributes.New(pathKey, pathValue{})}, - {Addresses: []resolver.Address{{Addr: "a1"}}, Attributes: attributes.New(pathKey, pathValue{})}, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := Group(tt.eps); !cmp.Equal(got, tt.want, cmp.AllowUnexported(attributes.Attributes{})) { - t.Errorf("Group() = %v, want %v", got, tt.want) - t.Errorf("diff: %v", cmp.Diff(got, tt.want, cmp.AllowUnexported(attributes.Attributes{}))) - } - }) - } -} - -func TestGroupE2E(t *testing.T) { - hierarchy := map[string]map[string][]string{ - "p0": { - "wt0": {"addr0", "addr1"}, - "wt1": {"addr2", "addr3"}, - }, - "p1": { - "wt10": {"addr10", "addr11"}, - "wt11": {"addr12", "addr13"}, - }, - } - - var epsWithHierarchy []resolver.Endpoint - for p, wts := range hierarchy { - path1 := pathValue{p} - for wt, addrs := range wts { - path2 := append(pathValue(nil), path1...) - path2 = append(path2, wt) - for _, addr := range addrs { - a := resolver.Endpoint{ - Addresses: []resolver.Address{{Addr: addr}}, - Attributes: attributes.New(pathKey, path2), - } - epsWithHierarchy = append(epsWithHierarchy, a) - } - } - } - - gotHierarchy := make(map[string]map[string][]string) - for p1, wts := range Group(epsWithHierarchy) { - gotHierarchy[p1] = make(map[string][]string) - for p2, eps := range Group(wts) { - for _, ep := range eps { - gotHierarchy[p1][p2] = append(gotHierarchy[p1][p2], ep.Addresses[0].Addr) - } - } - } - - if !cmp.Equal(gotHierarchy, hierarchy) { - t.Errorf("diff: %v", cmp.Diff(gotHierarchy, hierarchy)) +// Tests that if hierarchy is set to a wrong type (which should never happen), +// the endpoint is ignored. +func TestGroup_WrongType(t *testing.T) { + eps := []resolver.Endpoint{ + SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a0"}}}, []string{"a"}), + SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a1"}}}, []string{"a"}), + {Addresses: []resolver.Address{{Addr: "b0"}}, Attributes: attributes.New(pathKey, "b")}, + {Addresses: []resolver.Address{{Addr: "b1"}}, Attributes: attributes.New(pathKey, 314)}, + } + want := map[string][]resolver.Endpoint{ + "a": { + SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a0"}}}, nil), + SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a1"}}}, nil), + }, + } + if got := Group(eps); !cmp.Equal(got, want, cmp.AllowUnexported(attributes.Attributes{})) { + t.Errorf("Group() = %v, want %v", got, want) + t.Errorf("diff: %v", cmp.Diff(got, want, cmp.AllowUnexported(attributes.Attributes{}))) } } From daa45db9f608c73ffede7783de936c289dab5bab Mon Sep 17 00:00:00 2001 From: Arjan Bal Date: Wed, 21 Jan 2026 14:14:28 +0530 Subject: [PATCH 2/2] Remove paranoid test --- internal/hierarchy/hierarchy_test.go | 48 ---------------------------- 1 file changed, 48 deletions(-) delete mode 100644 internal/hierarchy/hierarchy_test.go diff --git a/internal/hierarchy/hierarchy_test.go b/internal/hierarchy/hierarchy_test.go deleted file mode 100644 index 10f774ec4591..000000000000 --- a/internal/hierarchy/hierarchy_test.go +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright 2020 gRPC authors. - * - * 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 hierarchy - -import ( - "testing" - - "github.com/google/go-cmp/cmp" - "google.golang.org/grpc/attributes" - "google.golang.org/grpc/resolver" -) - -// Tests that if hierarchy is set to a wrong type (which should never happen), -// the endpoint is ignored. -func TestGroup_WrongType(t *testing.T) { - eps := []resolver.Endpoint{ - SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a0"}}}, []string{"a"}), - SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a1"}}}, []string{"a"}), - {Addresses: []resolver.Address{{Addr: "b0"}}, Attributes: attributes.New(pathKey, "b")}, - {Addresses: []resolver.Address{{Addr: "b1"}}, Attributes: attributes.New(pathKey, 314)}, - } - want := map[string][]resolver.Endpoint{ - "a": { - SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a0"}}}, nil), - SetInEndpoint(resolver.Endpoint{Addresses: []resolver.Address{{Addr: "a1"}}}, nil), - }, - } - if got := Group(eps); !cmp.Equal(got, want, cmp.AllowUnexported(attributes.Attributes{})) { - t.Errorf("Group() = %v, want %v", got, want) - t.Errorf("diff: %v", cmp.Diff(got, want, cmp.AllowUnexported(attributes.Attributes{}))) - } -}