go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/flag/protomsgsliceflag_test.go (about) 1 // Copyright 2020 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 flag 16 17 import ( 18 "flag" 19 "fmt" 20 "testing" 21 22 "github.com/golang/protobuf/jsonpb" 23 "github.com/golang/protobuf/proto" 24 25 pb "go.chromium.org/luci/buildbucket/proto" 26 27 . "github.com/smartystreets/goconvey/convey" 28 . "go.chromium.org/luci/common/testing/assertions" 29 ) 30 31 // special type that implements proto.Message interface for test purpose 32 type messageImpl string 33 34 func (messageImpl) Reset() {} 35 func (m messageImpl) String() string { 36 return string(m) 37 } 38 func (messageImpl) ProtoMessage() {} 39 40 // Assert that messageImpl implements to the proto.Message interface. 41 var _ = proto.Message(new(messageImpl)) 42 43 func TestProtoMessageSliceFlag(t *testing.T) { 44 Convey("Basic", t, func() { 45 var builds []*pb.Build 46 fs := flag.NewFlagSet("test", flag.ContinueOnError) 47 flag := MessageSliceFlag(&builds) 48 fs.Var(flag, "build", "Test Proto Message") 49 50 var err error 51 err = flag.Set("{\"id\": 111111, \"status\": \"SUCCESS\"}") 52 So(err, ShouldBeNil) 53 err = flag.Set("{\"id\": 222222, \"status\": \"FAILURE\"}") 54 So(err, ShouldBeNil) 55 err = flag.Set("{\"id\": 333333, \"status\": \"CANCELED\"}") 56 So(err, ShouldBeNil) 57 58 So(flag.Get(), ShouldResembleProto, builds) 59 So(builds, ShouldHaveLength, 3) 60 So(builds[0].Id, ShouldEqual, 111111) 61 So(builds[0].Status, ShouldEqual, pb.Status_SUCCESS) 62 So(builds[1].Id, ShouldEqual, 222222) 63 So(builds[1].Status, ShouldEqual, pb.Status_FAILURE) 64 So(builds[2].Id, ShouldEqual, 333333) 65 So(builds[2].Status, ShouldEqual, pb.Status_CANCELED) 66 // Hard to set expectation for JSON string due to indentation 67 // and field order. Therefore, simply check if it's empty or not. 68 So(flag.String(), ShouldNotBeBlank) 69 }) 70 Convey("Panic when input is not pointer", t, func() { 71 So(func() { MessageSliceFlag("a string") }, ShouldPanic) 72 }) 73 Convey("Panic when input pointer dose not point to a slice", t, func() { 74 So(func() { MessageSliceFlag(&map[string]string{}) }, ShouldPanic) 75 }) 76 Convey("Panic when element does not implement proto.Message", t, func() { 77 So(func() { MessageSliceFlag(&[]string{}) }, ShouldPanic) 78 }) 79 Convey("Panic when element is not pointer", t, func() { 80 So(func() { MessageSliceFlag(&[]messageImpl{}) }, ShouldPanic) 81 }) 82 Convey("Panic when element pointer does not point to a struct", t, func() { 83 So(func() { MessageSliceFlag(&[]*messageImpl{}) }, ShouldPanic) 84 }) 85 } 86 87 func ExampleMessageSliceFlag() { 88 var builds []*pb.Build 89 fs := flag.NewFlagSet("test", flag.ContinueOnError) 90 flag := MessageSliceFlag(&builds) 91 fs.Var(flag, "build", "Test Proto Message") 92 fs.Parse([]string{"-build", 93 "{\"id\": 111111, \"status\": \"SUCCESS\"}"}) 94 marshaler := &jsonpb.Marshaler{Indent: " "} 95 jsonMsg, _ := marshaler.MarshalToString(builds[0]) 96 fmt.Println(jsonMsg) 97 // Output: 98 // { 99 // "id": "111111", 100 // "status": "SUCCESS" 101 // } 102 }