// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package fieldmaskpb_test import ( "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "google.golang.org/protobuf/proto" testpb "google.golang.org/protobuf/internal/testprotos/test" fmpb "google.golang.org/protobuf/types/known/fieldmaskpb" ) func TestAppend(t *testing.T) { tests := []struct { inMessage proto.Message inPaths []string wantPaths []string wantError error }{{ inMessage: (*fmpb.FieldMask)(nil), inPaths: []string{}, wantPaths: []string{}, }, { inMessage: (*fmpb.FieldMask)(nil), inPaths: []string{"paths", "paths"}, wantPaths: []string{"paths", "paths"}, }, { inMessage: (*fmpb.FieldMask)(nil), inPaths: []string{"paths", "", "paths"}, wantPaths: []string{"paths"}, wantError: cmpopts.AnyError, }, { inMessage: (*testpb.TestAllTypes)(nil), inPaths: []string{"optional_int32", "OptionalGroup.optional_nested_message", "map_uint32_uint32", "map_string_nested_message.corecursive", "oneof_bool"}, wantPaths: []string{"optional_int32", "OptionalGroup.optional_nested_message", "map_uint32_uint32"}, wantError: cmpopts.AnyError, }, { inMessage: (*testpb.TestAllTypes)(nil), inPaths: []string{"optional_nested_message", "optional_nested_message.corecursive", "optional_nested_message.corecursive.optional_nested_message", "optional_nested_message.corecursive.optional_nested_message.corecursive"}, wantPaths: []string{"optional_nested_message", "optional_nested_message.corecursive", "optional_nested_message.corecursive.optional_nested_message", "optional_nested_message.corecursive.optional_nested_message.corecursive"}, }, { inMessage: (*testpb.TestAllTypes)(nil), inPaths: []string{"optional_int32", "optional_nested_message.corecursive.optional_int64", "optional_nested_message.corecursive.", "optional_int64"}, wantPaths: []string{"optional_int32", "optional_nested_message.corecursive.optional_int64"}, wantError: cmpopts.AnyError, }, { inMessage: (*testpb.TestAllTypes)(nil), inPaths: []string{"optional_int32", "optional_nested_message.corecursive.oneof_uint32", "optional_nested_message.oneof_field", "optional_int64"}, wantPaths: []string{"optional_int32", "optional_nested_message.corecursive.oneof_uint32"}, wantError: cmpopts.AnyError, }} for _, tt := range tests { t.Run("", func(t *testing.T) { var mask fmpb.FieldMask gotError := mask.Append(tt.inMessage, tt.inPaths...) gotPaths := mask.GetPaths() if diff := cmp.Diff(tt.wantPaths, gotPaths, cmpopts.EquateEmpty()); diff != "" { t.Errorf("Append() paths mismatch (-want +got):\n%s", diff) } if diff := cmp.Diff(tt.wantError, gotError, cmpopts.EquateErrors()); diff != "" { t.Errorf("Append() error mismatch (-want +got):\n%s", diff) } }) } } func TestCombine(t *testing.T) { tests := []struct { in [][]string wantUnion []string wantIntersect []string }{{ in: [][]string{ {}, {}, }, wantUnion: []string{}, wantIntersect: []string{}, }, { in: [][]string{ {"a"}, {}, }, wantUnion: []string{"a"}, wantIntersect: []string{}, }, { in: [][]string{ {"a"}, {"a"}, }, wantUnion: []string{"a"}, wantIntersect: []string{"a"}, }, { in: [][]string{ {"a"}, {"b"}, {"c"}, }, wantUnion: []string{"a", "b", "c"}, wantIntersect: []string{}, }, { in: [][]string{ {"a", "b"}, {"b.b"}, {"b"}, {"b", "a.A"}, {"b", "c", "c.a", "c.b"}, }, wantUnion: []string{"a", "b", "c"}, wantIntersect: []string{"b.b"}, }, { in: [][]string{ {"a.b", "a.c.d"}, {"a"}, }, wantUnion: []string{"a"}, wantIntersect: []string{"a.b", "a.c.d"}, }, { in: [][]string{ {}, {"a.b", "a.c", "d"}, }, wantUnion: []string{"a.b", "a.c", "d"}, wantIntersect: []string{}, }} for _, tt := range tests { t.Run("", func(t *testing.T) { var masks []*fmpb.FieldMask for _, paths := range tt.in { masks = append(masks, &fmpb.FieldMask{Paths: paths}) } union := fmpb.Union(masks[0], masks[1], masks[2:]...) gotUnion := union.GetPaths() if diff := cmp.Diff(tt.wantUnion, gotUnion, cmpopts.EquateEmpty()); diff != "" { t.Errorf("Union() mismatch (-want +got):\n%s", diff) } intersect := fmpb.Intersect(masks[0], masks[1], masks[2:]...) gotIntersect := intersect.GetPaths() if diff := cmp.Diff(tt.wantIntersect, gotIntersect, cmpopts.EquateEmpty()); diff != "" { t.Errorf("Intersect() mismatch (-want +got):\n%s", diff) } }) } } func TestNormalize(t *testing.T) { tests := []struct { in []string want []string }{{ in: []string{}, want: []string{}, }, { in: []string{"a"}, want: []string{"a"}, }, { in: []string{"foo", "foo.bar", "foo.baz"}, want: []string{"foo"}, }, { in: []string{"foo.bar", "foo.baz"}, want: []string{"foo.bar", "foo.baz"}, }, { in: []string{"", "a.", ".b", "a.b", ".", "", "a.", ".b", "a.b", "."}, want: []string{"", "a.", "a.b"}, }, { in: []string{"e.a", "e.b", "e.c", "e.d", "e.f", "e.g", "e.b.a", "e$c", "e.b.c"}, want: []string{"e.a", "e.b", "e.c", "e.d", "e.f", "e.g", "e$c"}, }, { in: []string{"a", "aa", "aaa", "a$", "AAA", "aA.a", "a.a", "a", "aa", "aaa", "a$", "AAA", "aA.a"}, want: []string{"AAA", "a", "aA.a", "aa", "aaa", "a$"}, }, { in: []string{"a.b", "aa.bb.cc", ".", "a$b", "aa", "a.", "a", "b.c.d", ".a", "", "a$", "a$", "a.b", "a", "a.bb", ""}, want: []string{"", "a", "aa", "a$", "a$b", "b.c.d"}, }} for _, tt := range tests { t.Run("", func(t *testing.T) { mask := &fmpb.FieldMask{ Paths: append([]string(nil), tt.in...), } mask.Normalize() got := mask.GetPaths() if diff := cmp.Diff(tt.want, got, cmpopts.EquateEmpty()); diff != "" { t.Errorf("Normalize() mismatch (-want +got):\n%s", diff) } }) } } func TestIsValid(t *testing.T) { tests := []struct { message proto.Message paths []string want bool }{{ message: (*testpb.TestAllTypes)(nil), paths: []string{"no_such_field"}, want: false, }, { message: (*testpb.TestAllTypes)(nil), paths: []string{""}, want: false, }, { message: (*testpb.TestAllTypes)(nil), paths: []string{ "optional_int32", "optional_int32", "optional_int64", "optional_uint32", "optional_uint64", "optional_sint32", "optional_sint64", "optional_fixed32", "optional_fixed64", "optional_sfixed32", "optional_sfixed64", "optional_float", "optional_double", "optional_bool", "optional_string", "optional_bytes", "OptionalGroup", "optional_nested_message", "optional_foreign_message", "optional_import_message", "optional_nested_enum", "optional_foreign_enum", "optional_import_enum", "repeated_int32", "repeated_int64", "repeated_uint32", "repeated_uint64", "repeated_sint32", "repeated_sint64", "repeated_fixed32", "repeated_fixed64", "repeated_sfixed32", "repeated_sfixed64", "repeated_float", "repeated_double", "repeated_bool", "repeated_string", "repeated_bytes", "RepeatedGroup", "repeated_nested_message", "repeated_foreign_message", "repeated_importmessage", "repeated_nested_enum", "repeated_foreign_enum", "repeated_importenum", "map_int32_int32", "map_int64_int64", "map_uint32_uint32", "map_uint64_uint64", "map_sint32_sint32", "map_sint64_sint64", "map_fixed32_fixed32", "map_fixed64_fixed64", "map_sfixed32_sfixed32", "map_sfixed64_sfixed64", "map_int32_float", "map_int32_double", "map_bool_bool", "map_string_string", "map_string_bytes", "map_string_nested_message", "map_string_nested_enum", "oneof_uint32", "oneof_nested_message", "oneof_string", "oneof_bytes", "oneof_bool", "oneof_uint64", "oneof_float", "oneof_double", "oneof_enum", "OneofGroup", }, want: true, }, { message: (*testpb.TestAllTypes)(nil), paths: []string{ "optional_nested_message.a", "optional_nested_message.corecursive", "optional_nested_message.corecursive.optional_int32", "optional_nested_message.corecursive.optional_nested_message.corecursive.optional_nested_message.a", "OptionalGroup.a", "OptionalGroup.optional_nested_message", "OptionalGroup.optional_nested_message.corecursive", "oneof_nested_message.a", "oneof_nested_message.corecursive", }, want: true, }, { message: (*testpb.TestAllTypes)(nil), paths: []string{"repeated_nested_message.a"}, want: false, }, { message: (*testpb.TestAllTypes)(nil), paths: []string{"repeated_nested_message[0]"}, want: false, }, { message: (*testpb.TestAllTypes)(nil), paths: []string{"repeated_nested_message[0].a"}, want: false, }, { message: (*testpb.TestAllTypes)(nil), paths: []string{"map_string_nested_message.a"}, want: false, }, { message: (*testpb.TestAllTypes)(nil), paths: []string{`map_string_nested_message["key"]`}, want: false, }, { message: (*testpb.TestAllExtensions)(nil), paths: []string{"nested_string_extension"}, want: false, }} for _, tt := range tests { t.Run("", func(t *testing.T) { mask := &fmpb.FieldMask{Paths: tt.paths} got := mask.IsValid(tt.message) if got != tt.want { t.Errorf("IsValid() returns %v want %v", got, tt.want) } }) } }