go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/client/flagpb/unmarshal_test.go (about) 1 // Copyright 2016 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package flagpb 16 17 import ( 18 "os" 19 "testing" 20 21 "google.golang.org/protobuf/proto" 22 "google.golang.org/protobuf/types/descriptorpb" 23 24 . "github.com/smartystreets/goconvey/convey" 25 26 "go.chromium.org/luci/common/proto/google/descutil" 27 . "go.chromium.org/luci/common/testing/assertions" 28 ) 29 30 func TestUnmarshal(t *testing.T) { 31 t.Parallel() 32 33 Convey("Unmarshal", t, func() { 34 descFileBytes, err := os.ReadFile("unmarshal_test.desc") 35 So(err, ShouldBeNil) 36 37 var desc descriptorpb.FileDescriptorSet 38 err = proto.Unmarshal(descFileBytes, &desc) 39 So(err, ShouldBeNil) 40 41 resolver := NewResolver(&desc) 42 43 resolveMsg := func(name string) *descriptorpb.DescriptorProto { 44 _, obj, _ := descutil.Resolve(&desc, "flagpb."+name) 45 So(obj, ShouldNotBeNil) 46 return obj.(*descriptorpb.DescriptorProto) 47 } 48 49 unmarshalOK := func(typeName string, args ...string) map[string]any { 50 msg, err := UnmarshalUntyped(args, resolveMsg(typeName), resolver) 51 So(err, ShouldBeNil) 52 return msg 53 } 54 55 unmarshalErr := func(typeName string, args ...string) error { 56 msg, err := UnmarshalUntyped(args, resolveMsg(typeName), resolver) 57 So(msg, ShouldBeNil) 58 return err 59 } 60 61 Convey("empty", func() { 62 So(unmarshalOK("M1"), ShouldResemble, msg()) 63 }) 64 Convey("non-flag", func() { 65 So(unmarshalErr("M1", "abc"), ShouldErrLike, `abc: a flag was expected`) 66 }) 67 Convey("string", func() { 68 Convey("next arg", func() { 69 So(unmarshalOK("M1", "-s", "x"), ShouldResemble, msg("s", "x")) 70 }) 71 Convey("equals sign", func() { 72 So(unmarshalOK("M1", "-s=x"), ShouldResemble, msg("s", "x")) 73 }) 74 }) 75 Convey("int32", func() { 76 Convey("next arg", func() { 77 So(unmarshalOK("M1", "-i", "1"), ShouldResemble, msg("i", int32(1))) 78 }) 79 Convey("equals sign", func() { 80 So(unmarshalOK("M1", "-i=1"), ShouldResemble, msg( 81 "i", int32(1), 82 )) 83 }) 84 Convey("error", func() { 85 So(unmarshalErr("M1", "-i", "abc"), ShouldErrLike, "invalid syntax") 86 So(unmarshalErr("M1", "-i=abc"), ShouldErrLike, "invalid syntax") 87 }) 88 }) 89 Convey("enum", func() { 90 Convey("by name", func() { 91 So(unmarshalOK("M2", "-e", "V0"), ShouldResemble, msg("e", int32(0))) 92 }) 93 Convey("error", func() { 94 So(unmarshalErr("M2", "-e", "abc"), ShouldErrLike, `invalid value "abc" for enum E`) 95 }) 96 Convey("by value", func() { 97 So(unmarshalOK("M2", "-e", "0"), ShouldResemble, msg("e", int32(0))) 98 }) 99 }) 100 Convey("bool", func() { 101 Convey("without value", func() { 102 So(unmarshalOK("M1", "-b"), ShouldResemble, msg("b", true)) 103 104 So(unmarshalOK("M1", "-b", "-s", "x"), ShouldResemble, msg( 105 "b", true, 106 "s", "x", 107 )) 108 }) 109 Convey("without value, repeated", func() { 110 So(unmarshalOK("M1", "-rb=false", "-rb"), ShouldResemble, msg("rb", repeated(false, true))) 111 }) 112 Convey("with value", func() { 113 So(unmarshalOK("M1", "-b=true"), ShouldResemble, msg("b", true)) 114 So(unmarshalOK("M1", "-b=false"), ShouldResemble, msg("b", false)) 115 }) 116 }) 117 Convey("bytes", func() { 118 Convey("next arg", func() { 119 So(unmarshalOK("M1", "-bb", "6869"), ShouldResemble, msg("bb", []byte("hi"))) 120 }) 121 Convey("equals sign", func() { 122 So(unmarshalOK("M1", "-bb=6869"), ShouldResemble, msg("bb", []byte("hi"))) 123 }) 124 Convey("error", func() { 125 So(unmarshalErr("M1", "-bb", "xx"), ShouldErrLike, "invalid byte: U+0078 'x'") 126 }) 127 }) 128 129 Convey("many dashes", func() { 130 Convey("2", func() { 131 So(unmarshalOK("M1", "--s", "x"), ShouldResemble, msg("s", "x")) 132 }) 133 Convey("3", func() { 134 So(unmarshalErr("M1", "---s", "x"), ShouldErrLike, "---s: bad flag syntax") 135 }) 136 }) 137 138 Convey("field not found", func() { 139 So(unmarshalErr("M2", "-abc", "abc"), ShouldErrLike, `-abc: field abc not found in message M2`) 140 }) 141 Convey("value not specified", func() { 142 So(unmarshalErr("M1", "-s"), ShouldErrLike, `value was expected`) 143 }) 144 145 Convey("message", func() { 146 Convey("level 1", func() { 147 So(unmarshalOK("M2", "-m1.s", "x"), ShouldResemble, msg( 148 "m1", msg("s", "x"), 149 )) 150 So(unmarshalOK("M2", "-m1.s", "x", "-m1.b"), ShouldResemble, msg( 151 "m1", msg( 152 "s", "x", 153 "b", true, 154 ), 155 )) 156 }) 157 Convey("level 2", func() { 158 So(unmarshalOK("M3", "-m2.m1.s", "x"), ShouldResemble, msg( 159 "m2", msg( 160 "m1", msg("s", "x"), 161 ), 162 )) 163 }) 164 Convey("not found", func() { 165 So(unmarshalErr("M2", "-abc.s", "x"), ShouldErrLike, `field "abc" not found in message M2`) 166 }) 167 Convey("non-msg subfield", func() { 168 So(unmarshalErr("M1", "-s.dummy", "x"), ShouldErrLike, "field s is not a message") 169 }) 170 Convey("message value", func() { 171 const err = "M2.m1 is a message field. Specify its field values, not the message itself" 172 So(unmarshalErr("M2", "-m1", "x"), ShouldErrLike, err) 173 So(unmarshalErr("M2", "-m1=x"), ShouldErrLike, err) 174 }) 175 }) 176 177 Convey("string and int32", func() { 178 So(unmarshalOK("M1", "-s", "x", "-i", "1"), ShouldResemble, msg( 179 "s", "x", 180 "i", int32(1), 181 )) 182 }) 183 184 Convey("repeated", func() { 185 Convey("int32", func() { 186 So(unmarshalOK("M1", "-ri", "1", "-ri", "2"), ShouldResemble, msg( 187 "ri", repeated(int32(1), int32(2)), 188 )) 189 }) 190 Convey("submessage string", func() { 191 Convey("works", func() { 192 So(unmarshalOK("M3", "-m1.s", "x", "-m1", "-m1.s", "y"), ShouldResemble, msg( 193 "m1", repeated( 194 msg("s", "x"), 195 msg("s", "y"), 196 ), 197 )) 198 }) 199 Convey("reports meaningful error", func() { 200 err := unmarshalErr("M3", "-m1.s", "x", "-m1.s", "y") 201 So(err, ShouldErrLike, `-m1.s: value is already set`) 202 So(err, ShouldErrLike, `insert -m1`) 203 }) 204 }) 205 }) 206 207 Convey("map", func() { 208 Convey("map<string, string>", func() { 209 So(unmarshalOK("MapContainer", "-ss.x", "a", "-ss.y", "b"), ShouldResemble, msg( 210 "ss", msg("x", "a", "y", "b"), 211 )) 212 }) 213 Convey("map<int32, int32>", func() { 214 So(unmarshalOK("MapContainer", "-ii.1", "10", "-ii.2", "20"), ShouldResemble, msg( 215 "ii", msg("1", int32(10), "2", int32(20)), 216 )) 217 }) 218 Convey("map<string, M1>", func() { 219 So(unmarshalOK("MapContainer", "-sm1.x.s", "a", "-sm1.y.s", "b"), ShouldResemble, msg( 220 "sm1", msg( 221 "x", msg("s", "a"), 222 "y", msg("s", "b"), 223 ), 224 )) 225 }) 226 }) 227 }) 228 }