1// Copyright 2020 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package fieldmaskpb_test 6 7import ( 8 "testing" 9 10 "github.com/google/go-cmp/cmp" 11 "github.com/google/go-cmp/cmp/cmpopts" 12 "google.golang.org/protobuf/proto" 13 14 testpb "google.golang.org/protobuf/internal/testprotos/test" 15 fmpb "google.golang.org/protobuf/types/known/fieldmaskpb" 16) 17 18func TestAppend(t *testing.T) { 19 tests := []struct { 20 inMessage proto.Message 21 inPaths []string 22 wantPaths []string 23 wantError error 24 }{{ 25 inMessage: (*fmpb.FieldMask)(nil), 26 inPaths: []string{}, 27 wantPaths: []string{}, 28 }, { 29 inMessage: (*fmpb.FieldMask)(nil), 30 inPaths: []string{"paths", "paths"}, 31 wantPaths: []string{"paths", "paths"}, 32 }, { 33 inMessage: (*fmpb.FieldMask)(nil), 34 inPaths: []string{"paths", "<INVALID>", "paths"}, 35 wantPaths: []string{"paths"}, 36 wantError: cmpopts.AnyError, 37 }, { 38 inMessage: (*testpb.TestAllTypes)(nil), 39 inPaths: []string{"optional_int32", "OptionalGroup.optional_nested_message", "map_uint32_uint32", "map_string_nested_message.corecursive", "oneof_bool"}, 40 wantPaths: []string{"optional_int32", "OptionalGroup.optional_nested_message", "map_uint32_uint32"}, 41 wantError: cmpopts.AnyError, 42 }, { 43 inMessage: (*testpb.TestAllTypes)(nil), 44 inPaths: []string{"optional_nested_message", "optional_nested_message.corecursive", "optional_nested_message.corecursive.optional_nested_message", "optional_nested_message.corecursive.optional_nested_message.corecursive"}, 45 wantPaths: []string{"optional_nested_message", "optional_nested_message.corecursive", "optional_nested_message.corecursive.optional_nested_message", "optional_nested_message.corecursive.optional_nested_message.corecursive"}, 46 }, { 47 inMessage: (*testpb.TestAllTypes)(nil), 48 inPaths: []string{"optional_int32", "optional_nested_message.corecursive.optional_int64", "optional_nested_message.corecursive.<INVALID>", "optional_int64"}, 49 wantPaths: []string{"optional_int32", "optional_nested_message.corecursive.optional_int64"}, 50 wantError: cmpopts.AnyError, 51 }, { 52 inMessage: (*testpb.TestAllTypes)(nil), 53 inPaths: []string{"optional_int32", "optional_nested_message.corecursive.oneof_uint32", "optional_nested_message.oneof_field", "optional_int64"}, 54 wantPaths: []string{"optional_int32", "optional_nested_message.corecursive.oneof_uint32"}, 55 wantError: cmpopts.AnyError, 56 }} 57 58 for _, tt := range tests { 59 t.Run("", func(t *testing.T) { 60 var mask fmpb.FieldMask 61 gotError := mask.Append(tt.inMessage, tt.inPaths...) 62 gotPaths := mask.GetPaths() 63 if diff := cmp.Diff(tt.wantPaths, gotPaths, cmpopts.EquateEmpty()); diff != "" { 64 t.Errorf("Append() paths mismatch (-want +got):\n%s", diff) 65 } 66 if diff := cmp.Diff(tt.wantError, gotError, cmpopts.EquateErrors()); diff != "" { 67 t.Errorf("Append() error mismatch (-want +got):\n%s", diff) 68 } 69 }) 70 } 71} 72 73func TestCombine(t *testing.T) { 74 tests := []struct { 75 in [][]string 76 wantUnion []string 77 wantIntersect []string 78 }{{ 79 in: [][]string{ 80 {}, 81 {}, 82 }, 83 wantUnion: []string{}, 84 wantIntersect: []string{}, 85 }, { 86 in: [][]string{ 87 {"a"}, 88 {}, 89 }, 90 wantUnion: []string{"a"}, 91 wantIntersect: []string{}, 92 }, { 93 in: [][]string{ 94 {"a"}, 95 {"a"}, 96 }, 97 wantUnion: []string{"a"}, 98 wantIntersect: []string{"a"}, 99 }, { 100 in: [][]string{ 101 {"a"}, 102 {"b"}, 103 {"c"}, 104 }, 105 wantUnion: []string{"a", "b", "c"}, 106 wantIntersect: []string{}, 107 }, { 108 in: [][]string{ 109 {"a", "b"}, 110 {"b.b"}, 111 {"b"}, 112 {"b", "a.A"}, 113 {"b", "c", "c.a", "c.b"}, 114 }, 115 wantUnion: []string{"a", "b", "c"}, 116 wantIntersect: []string{"b.b"}, 117 }, { 118 in: [][]string{ 119 {"a.b", "a.c.d"}, 120 {"a"}, 121 }, 122 wantUnion: []string{"a"}, 123 wantIntersect: []string{"a.b", "a.c.d"}, 124 }, { 125 in: [][]string{ 126 {}, 127 {"a.b", "a.c", "d"}, 128 }, 129 wantUnion: []string{"a.b", "a.c", "d"}, 130 wantIntersect: []string{}, 131 }} 132 133 for _, tt := range tests { 134 t.Run("", func(t *testing.T) { 135 var masks []*fmpb.FieldMask 136 for _, paths := range tt.in { 137 masks = append(masks, &fmpb.FieldMask{Paths: paths}) 138 } 139 140 union := fmpb.Union(masks[0], masks[1], masks[2:]...) 141 gotUnion := union.GetPaths() 142 if diff := cmp.Diff(tt.wantUnion, gotUnion, cmpopts.EquateEmpty()); diff != "" { 143 t.Errorf("Union() mismatch (-want +got):\n%s", diff) 144 } 145 146 intersect := fmpb.Intersect(masks[0], masks[1], masks[2:]...) 147 gotIntersect := intersect.GetPaths() 148 if diff := cmp.Diff(tt.wantIntersect, gotIntersect, cmpopts.EquateEmpty()); diff != "" { 149 t.Errorf("Intersect() mismatch (-want +got):\n%s", diff) 150 } 151 }) 152 } 153} 154 155func TestNormalize(t *testing.T) { 156 tests := []struct { 157 in []string 158 want []string 159 }{{ 160 in: []string{}, 161 want: []string{}, 162 }, { 163 in: []string{"a"}, 164 want: []string{"a"}, 165 }, { 166 in: []string{"foo", "foo.bar", "foo.baz"}, 167 want: []string{"foo"}, 168 }, { 169 in: []string{"foo.bar", "foo.baz"}, 170 want: []string{"foo.bar", "foo.baz"}, 171 }, { 172 in: []string{"", "a.", ".b", "a.b", ".", "", "a.", ".b", "a.b", "."}, 173 want: []string{"", "a.", "a.b"}, 174 }, { 175 in: []string{"e.a", "e.b", "e.c", "e.d", "e.f", "e.g", "e.b.a", "e$c", "e.b.c"}, 176 want: []string{"e.a", "e.b", "e.c", "e.d", "e.f", "e.g", "e$c"}, 177 }, { 178 in: []string{"a", "aa", "aaa", "a$", "AAA", "aA.a", "a.a", "a", "aa", "aaa", "a$", "AAA", "aA.a"}, 179 want: []string{"AAA", "a", "aA.a", "aa", "aaa", "a$"}, 180 }, { 181 in: []string{"a.b", "aa.bb.cc", ".", "a$b", "aa", "a.", "a", "b.c.d", ".a", "", "a$", "a$", "a.b", "a", "a.bb", ""}, 182 want: []string{"", "a", "aa", "a$", "a$b", "b.c.d"}, 183 }} 184 185 for _, tt := range tests { 186 t.Run("", func(t *testing.T) { 187 mask := &fmpb.FieldMask{ 188 Paths: append([]string(nil), tt.in...), 189 } 190 mask.Normalize() 191 got := mask.GetPaths() 192 if diff := cmp.Diff(tt.want, got, cmpopts.EquateEmpty()); diff != "" { 193 t.Errorf("Normalize() mismatch (-want +got):\n%s", diff) 194 } 195 }) 196 } 197} 198 199func TestIsValid(t *testing.T) { 200 tests := []struct { 201 message proto.Message 202 paths []string 203 want bool 204 }{{ 205 message: (*testpb.TestAllTypes)(nil), 206 paths: []string{"no_such_field"}, 207 want: false, 208 }, { 209 message: (*testpb.TestAllTypes)(nil), 210 paths: []string{""}, 211 want: false, 212 }, { 213 message: (*testpb.TestAllTypes)(nil), 214 paths: []string{ 215 "optional_int32", 216 "optional_int32", 217 "optional_int64", 218 "optional_uint32", 219 "optional_uint64", 220 "optional_sint32", 221 "optional_sint64", 222 "optional_fixed32", 223 "optional_fixed64", 224 "optional_sfixed32", 225 "optional_sfixed64", 226 "optional_float", 227 "optional_double", 228 "optional_bool", 229 "optional_string", 230 "optional_bytes", 231 "OptionalGroup", 232 "optional_nested_message", 233 "optional_foreign_message", 234 "optional_import_message", 235 "optional_nested_enum", 236 "optional_foreign_enum", 237 "optional_import_enum", 238 "repeated_int32", 239 "repeated_int64", 240 "repeated_uint32", 241 "repeated_uint64", 242 "repeated_sint32", 243 "repeated_sint64", 244 "repeated_fixed32", 245 "repeated_fixed64", 246 "repeated_sfixed32", 247 "repeated_sfixed64", 248 "repeated_float", 249 "repeated_double", 250 "repeated_bool", 251 "repeated_string", 252 "repeated_bytes", 253 "RepeatedGroup", 254 "repeated_nested_message", 255 "repeated_foreign_message", 256 "repeated_importmessage", 257 "repeated_nested_enum", 258 "repeated_foreign_enum", 259 "repeated_importenum", 260 "map_int32_int32", 261 "map_int64_int64", 262 "map_uint32_uint32", 263 "map_uint64_uint64", 264 "map_sint32_sint32", 265 "map_sint64_sint64", 266 "map_fixed32_fixed32", 267 "map_fixed64_fixed64", 268 "map_sfixed32_sfixed32", 269 "map_sfixed64_sfixed64", 270 "map_int32_float", 271 "map_int32_double", 272 "map_bool_bool", 273 "map_string_string", 274 "map_string_bytes", 275 "map_string_nested_message", 276 "map_string_nested_enum", 277 "oneof_uint32", 278 "oneof_nested_message", 279 "oneof_string", 280 "oneof_bytes", 281 "oneof_bool", 282 "oneof_uint64", 283 "oneof_float", 284 "oneof_double", 285 "oneof_enum", 286 "OneofGroup", 287 }, 288 want: true, 289 }, { 290 message: (*testpb.TestAllTypes)(nil), 291 paths: []string{ 292 "optional_nested_message.a", 293 "optional_nested_message.corecursive", 294 "optional_nested_message.corecursive.optional_int32", 295 "optional_nested_message.corecursive.optional_nested_message.corecursive.optional_nested_message.a", 296 "OptionalGroup.a", 297 "OptionalGroup.optional_nested_message", 298 "OptionalGroup.optional_nested_message.corecursive", 299 "oneof_nested_message.a", 300 "oneof_nested_message.corecursive", 301 }, 302 want: true, 303 }, { 304 message: (*testpb.TestAllTypes)(nil), 305 paths: []string{"repeated_nested_message.a"}, 306 want: false, 307 }, { 308 message: (*testpb.TestAllTypes)(nil), 309 paths: []string{"repeated_nested_message[0]"}, 310 want: false, 311 }, { 312 message: (*testpb.TestAllTypes)(nil), 313 paths: []string{"repeated_nested_message[0].a"}, 314 want: false, 315 }, { 316 message: (*testpb.TestAllTypes)(nil), 317 paths: []string{"map_string_nested_message.a"}, 318 want: false, 319 }, { 320 message: (*testpb.TestAllTypes)(nil), 321 paths: []string{`map_string_nested_message["key"]`}, 322 want: false, 323 }, { 324 message: (*testpb.TestAllExtensions)(nil), 325 paths: []string{"nested_string_extension"}, 326 want: false, 327 }} 328 329 for _, tt := range tests { 330 t.Run("", func(t *testing.T) { 331 mask := &fmpb.FieldMask{Paths: tt.paths} 332 got := mask.IsValid(tt.message) 333 if got != tt.want { 334 t.Errorf("IsValid() returns %v want %v", got, tt.want) 335 } 336 }) 337 } 338} 339