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  }