...

Source file src/github.com/golang/protobuf/proto/text_test.go

Documentation: github.com/golang/protobuf/proto

     1  // Copyright 2016 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  
     5  package proto_test
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"math"
    11  	"strings"
    12  	"sync"
    13  	"testing"
    14  
    15  	"github.com/golang/protobuf/proto"
    16  	"github.com/google/go-cmp/cmp"
    17  
    18  	pb2 "github.com/golang/protobuf/internal/testprotos/proto2_proto"
    19  	pb3 "github.com/golang/protobuf/internal/testprotos/proto3_proto"
    20  	anypb "github.com/golang/protobuf/ptypes/any"
    21  )
    22  
    23  var (
    24  	expandedMarshaler        = proto.TextMarshaler{ExpandAny: true}
    25  	expandedCompactMarshaler = proto.TextMarshaler{Compact: true, ExpandAny: true}
    26  )
    27  
    28  // anyEqual reports whether two messages which may be google.protobuf.Any or may
    29  // contain google.protobuf.Any fields are equal. We can't use proto.Equal for
    30  // comparison, because semantically equivalent messages may be marshaled to
    31  // binary in different tag order. Instead, trust that TextMarshaler with
    32  // ExpandAny option works and compare the text marshaling results.
    33  func anyEqual(got, want proto.Message) bool {
    34  	// if messages are proto.Equal, no need to marshal.
    35  	if proto.Equal(got, want) {
    36  		return true
    37  	}
    38  	g := expandedMarshaler.Text(got)
    39  	w := expandedMarshaler.Text(want)
    40  	return g == w
    41  }
    42  
    43  type golden struct {
    44  	m    proto.Message
    45  	t, c string
    46  }
    47  
    48  var goldenMessages = makeGolden()
    49  
    50  func makeGolden() []golden {
    51  	nested := &pb3.Nested{Bunny: "Monty"}
    52  	nb, err := proto.Marshal(nested)
    53  	if err != nil {
    54  		panic(err)
    55  	}
    56  	m1 := &pb3.Message{
    57  		Name:        "David",
    58  		ResultCount: 47,
    59  		Anything:    &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(nested), Value: nb},
    60  	}
    61  	m2 := &pb3.Message{
    62  		Name:        "David",
    63  		ResultCount: 47,
    64  		Anything:    &anypb.Any{TypeUrl: "http://[::1]/type.googleapis.com/" + proto.MessageName(nested), Value: nb},
    65  	}
    66  	m3 := &pb3.Message{
    67  		Name:        "David",
    68  		ResultCount: 47,
    69  		Anything:    &anypb.Any{TypeUrl: `type.googleapis.com/"/` + proto.MessageName(nested), Value: nb},
    70  	}
    71  	m4 := &pb3.Message{
    72  		Name:        "David",
    73  		ResultCount: 47,
    74  		Anything:    &anypb.Any{TypeUrl: "type.googleapis.com/a/path/" + proto.MessageName(nested), Value: nb},
    75  	}
    76  	m5 := &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(nested), Value: nb}
    77  
    78  	any1 := &pb2.MyMessage{Count: proto.Int32(47), Name: proto.String("David")}
    79  	proto.SetExtension(any1, pb2.E_Ext_More, &pb2.Ext{Data: proto.String("foo")})
    80  	proto.SetExtension(any1, pb2.E_Ext_Text, proto.String("bar"))
    81  	any1b, err := proto.Marshal(any1)
    82  	if err != nil {
    83  		panic(err)
    84  	}
    85  	any2 := &pb2.MyMessage{Count: proto.Int32(42), Bikeshed: pb2.MyMessage_GREEN.Enum(), RepBytes: [][]byte{[]byte("roboto")}}
    86  	proto.SetExtension(any2, pb2.E_Ext_More, &pb2.Ext{Data: proto.String("baz")})
    87  	any2b, err := proto.Marshal(any2)
    88  	if err != nil {
    89  		panic(err)
    90  	}
    91  	m6 := &pb3.Message{
    92  		Name:        "David",
    93  		ResultCount: 47,
    94  		Anything:    &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any1), Value: any1b},
    95  		ManyThings: []*anypb.Any{
    96  			&anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any2), Value: any2b},
    97  			&anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any1), Value: any1b},
    98  		},
    99  	}
   100  
   101  	const (
   102  		m1Golden = `
   103  name: "David"
   104  result_count: 47
   105  anything: <
   106    [type.googleapis.com/proto3_test.Nested]: <
   107      bunny: "Monty"
   108    >
   109  >
   110  `
   111  		m2Golden = `
   112  name: "David"
   113  result_count: 47
   114  anything: <
   115    ["http://[::1]/type.googleapis.com/proto3_test.Nested"]: <
   116      bunny: "Monty"
   117    >
   118  >
   119  `
   120  		m3Golden = `
   121  name: "David"
   122  result_count: 47
   123  anything: <
   124    ["type.googleapis.com/\"/proto3_test.Nested"]: <
   125      bunny: "Monty"
   126    >
   127  >
   128  `
   129  		m4Golden = `
   130  name: "David"
   131  result_count: 47
   132  anything: <
   133    [type.googleapis.com/a/path/proto3_test.Nested]: <
   134      bunny: "Monty"
   135    >
   136  >
   137  `
   138  		m5Golden = `
   139  [type.googleapis.com/proto3_test.Nested]: <
   140    bunny: "Monty"
   141  >
   142  `
   143  		m6Golden = `
   144  name: "David"
   145  result_count: 47
   146  anything: <
   147    [type.googleapis.com/proto2_test.MyMessage]: <
   148      count: 47
   149      name: "David"
   150      [proto2_test.Ext.more]: <
   151        data: "foo"
   152      >
   153      [proto2_test.Ext.text]: "bar"
   154    >
   155  >
   156  many_things: <
   157    [type.googleapis.com/proto2_test.MyMessage]: <
   158      count: 42
   159      bikeshed: GREEN
   160      rep_bytes: "roboto"
   161      [proto2_test.Ext.more]: <
   162        data: "baz"
   163      >
   164    >
   165  >
   166  many_things: <
   167    [type.googleapis.com/proto2_test.MyMessage]: <
   168      count: 47
   169      name: "David"
   170      [proto2_test.Ext.more]: <
   171        data: "foo"
   172      >
   173      [proto2_test.Ext.text]: "bar"
   174    >
   175  >
   176  `
   177  	)
   178  	return []golden{
   179  		{m1, strings.TrimSpace(m1Golden) + "\n", strings.TrimSpace(compact(m1Golden)) + " "},
   180  		{m2, strings.TrimSpace(m2Golden) + "\n", strings.TrimSpace(compact(m2Golden)) + " "},
   181  		{m3, strings.TrimSpace(m3Golden) + "\n", strings.TrimSpace(compact(m3Golden)) + " "},
   182  		{m4, strings.TrimSpace(m4Golden) + "\n", strings.TrimSpace(compact(m4Golden)) + " "},
   183  		{m5, strings.TrimSpace(m5Golden) + "\n", strings.TrimSpace(compact(m5Golden)) + " "},
   184  		{m6, strings.TrimSpace(m6Golden) + "\n", strings.TrimSpace(compact(m6Golden)) + " "},
   185  	}
   186  }
   187  
   188  func TestMarshalGolden(t *testing.T) {
   189  	for _, tt := range goldenMessages {
   190  		t.Run("", func(t *testing.T) {
   191  			if got, want := expandedMarshaler.Text(tt.m), tt.t; got != want {
   192  				t.Errorf("message %v: got:\n%s\nwant:\n%s", tt.m, got, want)
   193  			}
   194  			if got, want := expandedCompactMarshaler.Text(tt.m), tt.c; got != want {
   195  				t.Errorf("message %v: got:\n`%s`\nwant:\n`%s`", tt.m, got, want)
   196  			}
   197  		})
   198  	}
   199  }
   200  
   201  func TestUnmarshalGolden(t *testing.T) {
   202  	for _, tt := range goldenMessages {
   203  		t.Run("", func(t *testing.T) {
   204  			want := tt.m
   205  			got := proto.Clone(tt.m)
   206  			got.Reset()
   207  			if err := proto.UnmarshalText(tt.t, got); err != nil {
   208  				t.Errorf("failed to unmarshal\n%s\nerror: %v", tt.t, err)
   209  			}
   210  			if !anyEqual(got, want) {
   211  				t.Errorf("message:\n%s\ngot:\n%s\nwant:\n%s", tt.t, got, want)
   212  			}
   213  			got.Reset()
   214  			if err := proto.UnmarshalText(tt.c, got); err != nil {
   215  				t.Errorf("failed to unmarshal\n%s\nerror: %v", tt.c, err)
   216  			}
   217  			if !anyEqual(got, want) {
   218  				t.Errorf("message:\n%s\ngot:\n%s\nwant:\n%s", tt.c, got, want)
   219  			}
   220  		})
   221  	}
   222  }
   223  
   224  func TestMarshalUnknownAny(t *testing.T) {
   225  	m := &pb3.Message{
   226  		Anything: &anypb.Any{
   227  			TypeUrl: "foo",
   228  			Value:   []byte("bar"),
   229  		},
   230  	}
   231  	want := `anything: <
   232    type_url: "foo"
   233    value: "bar"
   234  >
   235  `
   236  	got := expandedMarshaler.Text(m)
   237  	if got != want {
   238  		t.Errorf("got:\n%s\nwant:\n%s", got, want)
   239  	}
   240  }
   241  
   242  func TestAmbiguousAny(t *testing.T) {
   243  	pb := &anypb.Any{}
   244  	err := proto.UnmarshalText(`
   245  	type_url: "ttt/proto3_test.Nested"
   246  	value: "\n\x05Monty"
   247  	`, pb)
   248  	if err != nil {
   249  		t.Errorf("unexpected proto.UnmarshalText error: %v", err)
   250  	}
   251  }
   252  
   253  func TestUnmarshalOverwriteAny(t *testing.T) {
   254  	pb := &anypb.Any{}
   255  	err := proto.UnmarshalText(`
   256    [type.googleapis.com/a/path/proto3_test.Nested]: <
   257      bunny: "Monty"
   258    >
   259    [type.googleapis.com/a/path/proto3_test.Nested]: <
   260      bunny: "Rabbit of Caerbannog"
   261    >
   262  	`, pb)
   263  	want := `line 7: Any message unpacked multiple times, or "type_url" already set`
   264  	if err.Error() != want {
   265  		t.Errorf("incorrect error:\ngot:  %v\nwant: %v", err.Error(), want)
   266  	}
   267  }
   268  
   269  func TestUnmarshalAnyMixAndMatch(t *testing.T) {
   270  	pb := &anypb.Any{}
   271  	err := proto.UnmarshalText(`
   272  	value: "\n\x05Monty"
   273    [type.googleapis.com/a/path/proto3_test.Nested]: <
   274      bunny: "Rabbit of Caerbannog"
   275    >
   276  	`, pb)
   277  	want := `line 5: Any message unpacked multiple times, or "value" already set`
   278  	if err.Error() != want {
   279  		t.Errorf("incorrect error:\ngot:  %v\nwant: %v", err.Error(), want)
   280  	}
   281  }
   282  
   283  // textMessage implements the methods that allow it to marshal and unmarshal
   284  // itself as text.
   285  type textMessage struct {
   286  }
   287  
   288  func (*textMessage) MarshalText() ([]byte, error) {
   289  	return []byte("custom"), nil
   290  }
   291  
   292  func (*textMessage) UnmarshalText(bytes []byte) error {
   293  	if string(bytes) != "custom" {
   294  		return errors.New("expected 'custom'")
   295  	}
   296  	return nil
   297  }
   298  
   299  func (*textMessage) Reset()         {}
   300  func (*textMessage) String() string { return "" }
   301  func (*textMessage) ProtoMessage()  {}
   302  
   303  func newTestMessage() *pb2.MyMessage {
   304  	msg := &pb2.MyMessage{
   305  		Count: proto.Int32(42),
   306  		Name:  proto.String("Dave"),
   307  		Quote: proto.String(`"I didn't want to go."`),
   308  		Pet:   []string{"bunny", "kitty", "horsey"},
   309  		Inner: &pb2.InnerMessage{
   310  			Host:      proto.String("footrest.syd"),
   311  			Port:      proto.Int32(7001),
   312  			Connected: proto.Bool(true),
   313  		},
   314  		Others: []*pb2.OtherMessage{
   315  			{
   316  				Key:   proto.Int64(0xdeadbeef),
   317  				Value: []byte{1, 65, 7, 12},
   318  			},
   319  			{
   320  				Weight: proto.Float32(6.022),
   321  				Inner: &pb2.InnerMessage{
   322  					Host: proto.String("lesha.mtv"),
   323  					Port: proto.Int32(8002),
   324  				},
   325  			},
   326  		},
   327  		Bikeshed: pb2.MyMessage_BLUE.Enum(),
   328  		Somegroup: &pb2.MyMessage_SomeGroup{
   329  			GroupField: proto.Int32(8),
   330  		},
   331  		// One normally wouldn't do this.
   332  		// This is an undeclared tag 13, as a varint (wire type 0) with value 4.
   333  		XXX_unrecognized: []byte{13<<3 | 0, 4},
   334  	}
   335  	ext := &pb2.Ext{
   336  		Data: proto.String("Big gobs for big rats"),
   337  	}
   338  	if err := proto.SetExtension(msg, pb2.E_Ext_More, ext); err != nil {
   339  		panic(err)
   340  	}
   341  	greetings := []string{"adg", "easy", "cow"}
   342  	if err := proto.SetExtension(msg, pb2.E_Greeting, greetings); err != nil {
   343  		panic(err)
   344  	}
   345  
   346  	// Add an unknown extension. We marshal a pb2.Ext, and fake the ID.
   347  	b, err := proto.Marshal(&pb2.Ext{Data: proto.String("3G skiing")})
   348  	if err != nil {
   349  		panic(err)
   350  	}
   351  	b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...)
   352  	proto.SetRawExtension(msg, 201, b)
   353  
   354  	// Extensions can be plain fields, too, so let's test that.
   355  	b = append(proto.EncodeVarint(202<<3|proto.WireVarint), 19)
   356  	proto.SetRawExtension(msg, 202, b)
   357  
   358  	return msg
   359  }
   360  
   361  const text = `count: 42
   362  name: "Dave"
   363  quote: "\"I didn't want to go.\""
   364  pet: "bunny"
   365  pet: "kitty"
   366  pet: "horsey"
   367  inner: <
   368    host: "footrest.syd"
   369    port: 7001
   370    connected: true
   371  >
   372  others: <
   373    key: 3735928559
   374    value: "\001A\007\014"
   375  >
   376  others: <
   377    weight: 6.022
   378    inner: <
   379      host: "lesha.mtv"
   380      port: 8002
   381    >
   382  >
   383  bikeshed: BLUE
   384  SomeGroup {
   385    group_field: 8
   386  }
   387  /* 18 unknown bytes */
   388  13: 4
   389  201: "\t3G skiing"
   390  202: 19
   391  [proto2_test.Ext.more]: <
   392    data: "Big gobs for big rats"
   393  >
   394  [proto2_test.greeting]: "adg"
   395  [proto2_test.greeting]: "easy"
   396  [proto2_test.greeting]: "cow"
   397  `
   398  
   399  func TestMarshalText(t *testing.T) {
   400  	buf := new(bytes.Buffer)
   401  	if err := proto.MarshalText(buf, newTestMessage()); err != nil {
   402  		t.Fatalf("proto.MarshalText: %v", err)
   403  	}
   404  	got := buf.String()
   405  	if diff := cmp.Diff(text, got); got != text {
   406  		t.Errorf("diff (-want +got):\n%v\n\ngot:\n%v\n\nwant:\n%v", diff, got, text)
   407  	}
   408  }
   409  
   410  func TestMarshalTextCustomMessage(t *testing.T) {
   411  	buf := new(bytes.Buffer)
   412  	if err := proto.MarshalText(buf, &textMessage{}); err != nil {
   413  		t.Fatalf("proto.MarshalText: %v", err)
   414  	}
   415  	got := buf.String()
   416  	if got != "custom" {
   417  		t.Errorf("got:\n%v\n\nwant:\n%v", got, "custom")
   418  	}
   419  }
   420  func TestMarshalTextNil(t *testing.T) {
   421  	want := "<nil>"
   422  	tests := []proto.Message{nil, (*pb2.MyMessage)(nil)}
   423  	for i, test := range tests {
   424  		buf := new(bytes.Buffer)
   425  		if err := proto.MarshalText(buf, test); err != nil {
   426  			t.Fatal(err)
   427  		}
   428  		if got := buf.String(); got != want {
   429  			t.Errorf("%d: got %q want %q", i, got, want)
   430  		}
   431  	}
   432  }
   433  
   434  func TestMarshalTextUnknownEnum(t *testing.T) {
   435  	// The Color enum only specifies values 0-2.
   436  	m := &pb2.MyMessage{Bikeshed: pb2.MyMessage_Color(3).Enum()}
   437  	got := m.String()
   438  	const want = `bikeshed:3 `
   439  	if got != want {
   440  		t.Errorf("\n got %q\nwant %q", got, want)
   441  	}
   442  }
   443  
   444  func TestTextOneof(t *testing.T) {
   445  	tests := []struct {
   446  		m    proto.Message
   447  		want string
   448  	}{
   449  		// zero message
   450  		{&pb2.Communique{}, ``},
   451  		// scalar field
   452  		{&pb2.Communique{Union: &pb2.Communique_Number{4}}, `number:4`},
   453  		// message field
   454  		{&pb2.Communique{Union: &pb2.Communique_Msg{
   455  			&pb2.Strings{StringField: proto.String("why hello!")},
   456  		}}, `msg:<string_field:"why hello!" >`},
   457  		// bad oneof (should not panic)
   458  		{&pb2.Communique{Union: &pb2.Communique_Msg{nil}}, `msg:<>`},
   459  	}
   460  	for _, test := range tests {
   461  		got := strings.TrimSpace(test.m.String())
   462  		if got != test.want {
   463  			t.Errorf("got:\n%s\n\nwant:\n%s", got, test.want)
   464  		}
   465  	}
   466  }
   467  
   468  func compact(src string) string {
   469  	// s/[ \n]+/ /g; s/ $//;
   470  	dst := make([]byte, len(src))
   471  	space, comment := false, false
   472  	j := 0
   473  	for i := 0; i < len(src); i++ {
   474  		if strings.HasPrefix(src[i:], "/*") {
   475  			comment = true
   476  			i++
   477  			continue
   478  		}
   479  		if comment && strings.HasPrefix(src[i:], "*/") {
   480  			comment = false
   481  			i++
   482  			continue
   483  		}
   484  		if comment {
   485  			continue
   486  		}
   487  		c := src[i]
   488  		if c == ' ' || c == '\n' {
   489  			space = true
   490  			continue
   491  		}
   492  		if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') {
   493  			space = false
   494  		}
   495  		if c == '{' {
   496  			space = false
   497  		}
   498  		if space {
   499  			dst[j] = ' '
   500  			j++
   501  			space = false
   502  		}
   503  		dst[j] = c
   504  		j++
   505  	}
   506  	if space {
   507  		dst[j] = ' '
   508  		j++
   509  	}
   510  	return string(dst[0:j])
   511  }
   512  
   513  func TestCompactText(t *testing.T) {
   514  	got := proto.CompactTextString(newTestMessage())
   515  	if got != compact(text) {
   516  		t.Errorf("got:\n%v\n\nwant:\n%v", got, compact(text))
   517  	}
   518  }
   519  
   520  func TestStringEscaping(t *testing.T) {
   521  	testCases := []struct {
   522  		in  *pb2.Strings
   523  		out string
   524  	}{
   525  		{
   526  			// Test data from C++ test (TextFormatTest.StringEscape).
   527  			// Single divergence: we don't escape apostrophes.
   528  			&pb2.Strings{StringField: proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and  multiple   spaces")},
   529  			"string_field: \"\\\"A string with ' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and  multiple   spaces\"\n",
   530  		},
   531  		{
   532  			// Test data from the same C++ test.
   533  			&pb2.Strings{StringField: proto.String("\350\260\267\346\255\214")},
   534  			"string_field: \"\\350\\260\\267\\346\\255\\214\"\n",
   535  		},
   536  		{
   537  			// Some UTF-8.
   538  			&pb2.Strings{StringField: proto.String("\x00\x01\xff\x81")},
   539  			`string_field: "\000\001\377\201"` + "\n",
   540  		},
   541  	}
   542  
   543  	for _, tc := range testCases {
   544  		t.Run("", func(t *testing.T) {
   545  			var buf bytes.Buffer
   546  			if err := proto.MarshalText(&buf, tc.in); err != nil {
   547  				t.Fatalf("proto.MarsalText error: %v", err)
   548  			}
   549  			got := buf.String()
   550  			if got != tc.out {
   551  				t.Fatalf("want:\n%s\n\nwant:\n%s", got, tc.out)
   552  			}
   553  
   554  			// Check round-trip.
   555  			pb := new(pb2.Strings)
   556  			if err := proto.UnmarshalText(got, pb); err != nil {
   557  				t.Fatalf("proto.UnmarshalText error: %v", err)
   558  			}
   559  			if !proto.Equal(pb, tc.in) {
   560  				t.Fatalf("proto.Equal mismatch:\ngot:\n%v\n\nwant:\n%v", pb, tc.in)
   561  			}
   562  		})
   563  	}
   564  }
   565  
   566  // A limitedWriter accepts some output before it fails.
   567  // This is a proxy for something like a nearly-full or imminently-failing disk,
   568  // or a network connection that is about to die.
   569  type limitedWriter struct {
   570  	b     bytes.Buffer
   571  	limit int
   572  }
   573  
   574  var outOfSpace = errors.New("proto: insufficient space")
   575  
   576  func (w *limitedWriter) Write(p []byte) (n int, err error) {
   577  	var avail = w.limit - w.b.Len()
   578  	if avail <= 0 {
   579  		return 0, outOfSpace
   580  	}
   581  	if len(p) <= avail {
   582  		return w.b.Write(p)
   583  	}
   584  	n, _ = w.b.Write(p[:avail])
   585  	return n, outOfSpace
   586  }
   587  
   588  func TestMarshalTextFailing(t *testing.T) {
   589  	// Try lots of different sizes to exercise more error code-paths.
   590  	for lim := 0; lim < len(text); lim++ {
   591  		buf := new(limitedWriter)
   592  		buf.limit = lim
   593  		err := proto.MarshalText(buf, newTestMessage())
   594  		// We expect a certain error, but also some partial results in the buffer.
   595  		if err != outOfSpace {
   596  			t.Errorf("error mismatch: got %v, want %v", err, outOfSpace)
   597  		}
   598  		got := buf.b.String()
   599  		want := text[:buf.limit]
   600  		if got != want {
   601  			t.Errorf("text mismatch:\n\ngot:\n%v\n\nwant:\n%v", got, want)
   602  		}
   603  	}
   604  }
   605  
   606  func TestFloats(t *testing.T) {
   607  	tests := []struct {
   608  		f    float64
   609  		want string
   610  	}{
   611  		{0, "0"},
   612  		{4.7, "4.7"},
   613  		{math.Inf(1), "inf"},
   614  		{math.Inf(-1), "-inf"},
   615  		{math.NaN(), "nan"},
   616  	}
   617  	for _, test := range tests {
   618  		msg := &pb2.FloatingPoint{F: &test.f}
   619  		got := strings.TrimSpace(msg.String())
   620  		want := `f:` + test.want
   621  		if got != want {
   622  			t.Errorf("f=%f: got %q, want %q", test.f, got, want)
   623  		}
   624  	}
   625  }
   626  
   627  func TestRepeatedNilText(t *testing.T) {
   628  	m := &pb2.MessageList{
   629  		Message: []*pb2.MessageList_Message{
   630  			nil,
   631  			&pb2.MessageList_Message{
   632  				Name: proto.String("Horse"),
   633  			},
   634  			nil,
   635  		},
   636  	}
   637  	want := `Message {
   638  }
   639  Message {
   640    name: "Horse"
   641  }
   642  Message {
   643  }
   644  `
   645  	if got := proto.MarshalTextString(m); got != want {
   646  		t.Errorf("got:\n%s\n\nwant:\n%s", got, want)
   647  	}
   648  }
   649  
   650  func TestProto3Text(t *testing.T) {
   651  	tests := []struct {
   652  		m    proto.Message
   653  		want string
   654  	}{
   655  		// zero message
   656  		{&pb3.Message{}, ``},
   657  		// zero message except for an empty byte slice
   658  		{&pb3.Message{Data: []byte{}}, ``},
   659  		// trivial case
   660  		{&pb3.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`},
   661  		// empty map
   662  		{&pb2.MessageWithMap{}, ``},
   663  		// non-empty map; map format is the same as a repeated struct,
   664  		// and they are sorted by key (numerically for numeric keys).
   665  		{
   666  			&pb2.MessageWithMap{NameMapping: map[int32]string{
   667  				-1:      "Negatory",
   668  				7:       "Lucky",
   669  				1234:    "Feist",
   670  				6345789: "Otis",
   671  			}},
   672  			`name_mapping:<key:-1 value:"Negatory" > ` +
   673  				`name_mapping:<key:7 value:"Lucky" > ` +
   674  				`name_mapping:<key:1234 value:"Feist" > ` +
   675  				`name_mapping:<key:6345789 value:"Otis" >`,
   676  		},
   677  		// map with nil value; not well-defined, but we shouldn't crash
   678  		{
   679  			&pb2.MessageWithMap{MsgMapping: map[int64]*pb2.FloatingPoint{7: nil}},
   680  			`msg_mapping:<key:7 value:<> >`,
   681  		},
   682  	}
   683  	for _, test := range tests {
   684  		got := strings.TrimSpace(test.m.String())
   685  		if got != test.want {
   686  			t.Errorf("got:\n%s\n\nwant:\n%s", got, test.want)
   687  		}
   688  	}
   689  }
   690  
   691  func TestRacyMarshal(t *testing.T) {
   692  	// This test should be run with the race detector.
   693  
   694  	any := &pb2.MyMessage{Count: proto.Int32(47), Name: proto.String("David")}
   695  	proto.SetExtension(any, pb2.E_Ext_Text, proto.String("bar"))
   696  	b, err := proto.Marshal(any)
   697  	if err != nil {
   698  		panic(err)
   699  	}
   700  	m := &pb3.Message{
   701  		Name:        "David",
   702  		ResultCount: 47,
   703  		Anything:    &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any), Value: b},
   704  	}
   705  
   706  	wantText := proto.MarshalTextString(m)
   707  	wantBytes, err := proto.Marshal(m)
   708  	if err != nil {
   709  		t.Fatalf("proto.Marshal error: %v", err)
   710  	}
   711  
   712  	var wg sync.WaitGroup
   713  	defer wg.Wait()
   714  	wg.Add(20)
   715  	for i := 0; i < 10; i++ {
   716  		go func() {
   717  			defer wg.Done()
   718  			got := proto.MarshalTextString(m)
   719  			if got != wantText {
   720  				t.Errorf("proto.MarshalTextString = %q, want %q", got, wantText)
   721  			}
   722  		}()
   723  		go func() {
   724  			defer wg.Done()
   725  			got, err := proto.Marshal(m)
   726  			if !bytes.Equal(got, wantBytes) || err != nil {
   727  				t.Errorf("proto.Marshal = (%x, %v), want (%x, nil)", got, err, wantBytes)
   728  			}
   729  		}()
   730  	}
   731  }
   732  
   733  type UnmarshalTextTest struct {
   734  	in  string
   735  	err string // if "", no error expected
   736  	out *pb2.MyMessage
   737  }
   738  
   739  func buildExtStructTest(text string) UnmarshalTextTest {
   740  	msg := &pb2.MyMessage{
   741  		Count: proto.Int32(42),
   742  	}
   743  	proto.SetExtension(msg, pb2.E_Ext_More, &pb2.Ext{
   744  		Data: proto.String("Hello, world!"),
   745  	})
   746  	return UnmarshalTextTest{in: text, out: msg}
   747  }
   748  
   749  func buildExtDataTest(text string) UnmarshalTextTest {
   750  	msg := &pb2.MyMessage{
   751  		Count: proto.Int32(42),
   752  	}
   753  	proto.SetExtension(msg, pb2.E_Ext_Text, proto.String("Hello, world!"))
   754  	proto.SetExtension(msg, pb2.E_Ext_Number, proto.Int32(1729))
   755  	return UnmarshalTextTest{in: text, out: msg}
   756  }
   757  
   758  func buildExtRepStringTest(text string) UnmarshalTextTest {
   759  	msg := &pb2.MyMessage{
   760  		Count: proto.Int32(42),
   761  	}
   762  	if err := proto.SetExtension(msg, pb2.E_Greeting, []string{"bula", "hola"}); err != nil {
   763  		panic(err)
   764  	}
   765  	return UnmarshalTextTest{in: text, out: msg}
   766  }
   767  
   768  var unmarshalTextTests = []UnmarshalTextTest{
   769  	// Basic
   770  	{
   771  		in: " count:42\n  name:\"Dave\" ",
   772  		out: &pb2.MyMessage{
   773  			Count: proto.Int32(42),
   774  			Name:  proto.String("Dave"),
   775  		},
   776  	},
   777  
   778  	// Empty quoted string
   779  	{
   780  		in: `count:42 name:""`,
   781  		out: &pb2.MyMessage{
   782  			Count: proto.Int32(42),
   783  			Name:  proto.String(""),
   784  		},
   785  	},
   786  
   787  	// Quoted string concatenation with double quotes
   788  	{
   789  		in: `count:42 name: "My name is "` + "\n" + `"elsewhere"`,
   790  		out: &pb2.MyMessage{
   791  			Count: proto.Int32(42),
   792  			Name:  proto.String("My name is elsewhere"),
   793  		},
   794  	},
   795  
   796  	// Quoted string concatenation with single quotes
   797  	{
   798  		in: "count:42 name: 'My name is '\n'elsewhere'",
   799  		out: &pb2.MyMessage{
   800  			Count: proto.Int32(42),
   801  			Name:  proto.String("My name is elsewhere"),
   802  		},
   803  	},
   804  
   805  	// Quoted string concatenations with mixed quotes
   806  	{
   807  		in: "count:42 name: 'My name is '\n\"elsewhere\"",
   808  		out: &pb2.MyMessage{
   809  			Count: proto.Int32(42),
   810  			Name:  proto.String("My name is elsewhere"),
   811  		},
   812  	},
   813  	{
   814  		in: "count:42 name: \"My name is \"\n'elsewhere'",
   815  		out: &pb2.MyMessage{
   816  			Count: proto.Int32(42),
   817  			Name:  proto.String("My name is elsewhere"),
   818  		},
   819  	},
   820  
   821  	// Quoted string with escaped apostrophe
   822  	{
   823  		in: `count:42 name: "HOLIDAY - New Year\'s Day"`,
   824  		out: &pb2.MyMessage{
   825  			Count: proto.Int32(42),
   826  			Name:  proto.String("HOLIDAY - New Year's Day"),
   827  		},
   828  	},
   829  
   830  	// Quoted string with single quote
   831  	{
   832  		in: `count:42 name: 'Roger "The Ramster" Ramjet'`,
   833  		out: &pb2.MyMessage{
   834  			Count: proto.Int32(42),
   835  			Name:  proto.String(`Roger "The Ramster" Ramjet`),
   836  		},
   837  	},
   838  
   839  	// Quoted string with all the accepted special characters from the C++ test
   840  	{
   841  		in: `count:42 name: ` + "\"\\\"A string with \\' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and  multiple   spaces\"",
   842  		out: &pb2.MyMessage{
   843  			Count: proto.Int32(42),
   844  			Name:  proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and  multiple   spaces"),
   845  		},
   846  	},
   847  
   848  	// Quoted string with quoted backslash
   849  	{
   850  		in: `count:42 name: "\\'xyz"`,
   851  		out: &pb2.MyMessage{
   852  			Count: proto.Int32(42),
   853  			Name:  proto.String(`\'xyz`),
   854  		},
   855  	},
   856  
   857  	// Quoted string with UTF-8 bytes.
   858  	{
   859  		in: "count:42 name: '\303\277\302\201\x00\xAB\xCD\xEF'",
   860  		out: &pb2.MyMessage{
   861  			Count: proto.Int32(42),
   862  			Name:  proto.String("\303\277\302\201\x00\xAB\xCD\xEF"),
   863  		},
   864  	},
   865  
   866  	// Quoted string with unicode escapes.
   867  	{
   868  		in: `count: 42 name: "\u0047\U00000047\uffff\U0010ffff"`,
   869  		out: &pb2.MyMessage{
   870  			Count: proto.Int32(42),
   871  			Name:  proto.String("GG\uffff\U0010ffff"),
   872  		},
   873  	},
   874  
   875  	// Bad quoted string
   876  	{
   877  		in:  `inner: < host: "\0" >` + "\n",
   878  		err: `line 1.15: invalid quoted string "\0": \0 requires 2 following digits`,
   879  	},
   880  
   881  	// Bad \u escape
   882  	{
   883  		in:  `count: 42 name: "\u000"`,
   884  		err: `line 1.16: invalid quoted string "\u000": \u requires 4 following digits`,
   885  	},
   886  
   887  	// Bad \U escape
   888  	{
   889  		in:  `count: 42 name: "\U0000000"`,
   890  		err: `line 1.16: invalid quoted string "\U0000000": \U requires 8 following digits`,
   891  	},
   892  
   893  	// Bad \U escape
   894  	{
   895  		in:  `count: 42 name: "\xxx"`,
   896  		err: `line 1.16: invalid quoted string "\xxx": \xxx contains non-hexadecimal digits`,
   897  	},
   898  
   899  	// Number too large for int64
   900  	{
   901  		in:  "count: 1 others { key: 123456789012345678901 }",
   902  		err: "line 1.23: invalid int64: 123456789012345678901",
   903  	},
   904  
   905  	// Number too large for int32
   906  	{
   907  		in:  "count: 1234567890123",
   908  		err: "line 1.7: invalid int32: 1234567890123",
   909  	},
   910  
   911  	// Number in hexadecimal
   912  	{
   913  		in: "count: 0x2beef",
   914  		out: &pb2.MyMessage{
   915  			Count: proto.Int32(0x2beef),
   916  		},
   917  	},
   918  
   919  	// Number in octal
   920  	{
   921  		in: "count: 024601",
   922  		out: &pb2.MyMessage{
   923  			Count: proto.Int32(024601),
   924  		},
   925  	},
   926  
   927  	// Floating point number with "f" suffix
   928  	{
   929  		in: "count: 4 others:< weight: 17.0f >",
   930  		out: &pb2.MyMessage{
   931  			Count: proto.Int32(4),
   932  			Others: []*pb2.OtherMessage{
   933  				{
   934  					Weight: proto.Float32(17),
   935  				},
   936  			},
   937  		},
   938  	},
   939  
   940  	// Floating point positive infinity
   941  	{
   942  		in: "count: 4 bigfloat: inf",
   943  		out: &pb2.MyMessage{
   944  			Count:    proto.Int32(4),
   945  			Bigfloat: proto.Float64(math.Inf(1)),
   946  		},
   947  	},
   948  
   949  	// Floating point negative infinity
   950  	{
   951  		in: "count: 4 bigfloat: -inf",
   952  		out: &pb2.MyMessage{
   953  			Count:    proto.Int32(4),
   954  			Bigfloat: proto.Float64(math.Inf(-1)),
   955  		},
   956  	},
   957  
   958  	// Number too large for float32
   959  	{
   960  		in:  "others:< weight: 12345678901234567890123456789012345678901234567890 >",
   961  		err: "line 1.17: invalid float: 12345678901234567890123456789012345678901234567890",
   962  	},
   963  
   964  	// Number posing as a quoted string
   965  	{
   966  		in:  `inner: < host: 12 >` + "\n",
   967  		err: `line 1.15: invalid string: 12`,
   968  	},
   969  
   970  	// Quoted string posing as int32
   971  	{
   972  		in:  `count: "12"`,
   973  		err: `line 1.7: invalid int32: "12"`,
   974  	},
   975  
   976  	// Quoted string posing a float32
   977  	{
   978  		in:  `others:< weight: "17.4" >`,
   979  		err: `line 1.17: invalid float: "17.4"`,
   980  	},
   981  
   982  	// unclosed bracket doesn't cause infinite loop
   983  	{
   984  		in:  `[`,
   985  		err: `line 1.0: unclosed type_url or extension name`,
   986  	},
   987  
   988  	// Enum
   989  	{
   990  		in: `count:42 bikeshed: BLUE`,
   991  		out: &pb2.MyMessage{
   992  			Count:    proto.Int32(42),
   993  			Bikeshed: pb2.MyMessage_BLUE.Enum(),
   994  		},
   995  	},
   996  
   997  	// Repeated field
   998  	{
   999  		in: `count:42 pet: "horsey" pet:"bunny"`,
  1000  		out: &pb2.MyMessage{
  1001  			Count: proto.Int32(42),
  1002  			Pet:   []string{"horsey", "bunny"},
  1003  		},
  1004  	},
  1005  
  1006  	// Repeated field with list notation
  1007  	{
  1008  		in: `count:42 pet: ["horsey", "bunny"]`,
  1009  		out: &pb2.MyMessage{
  1010  			Count: proto.Int32(42),
  1011  			Pet:   []string{"horsey", "bunny"},
  1012  		},
  1013  	},
  1014  
  1015  	// Repeated message with/without colon and <>/{}
  1016  	{
  1017  		in: `count:42 others:{} others{} others:<> others:{}`,
  1018  		out: &pb2.MyMessage{
  1019  			Count: proto.Int32(42),
  1020  			Others: []*pb2.OtherMessage{
  1021  				{},
  1022  				{},
  1023  				{},
  1024  				{},
  1025  			},
  1026  		},
  1027  	},
  1028  
  1029  	// Missing colon for inner message
  1030  	{
  1031  		in: `count:42 inner < host: "cauchy.syd" >`,
  1032  		out: &pb2.MyMessage{
  1033  			Count: proto.Int32(42),
  1034  			Inner: &pb2.InnerMessage{
  1035  				Host: proto.String("cauchy.syd"),
  1036  			},
  1037  		},
  1038  	},
  1039  
  1040  	// Missing colon for string field
  1041  	{
  1042  		in:  `name "Dave"`,
  1043  		err: `line 1.5: expected ':', found "\"Dave\""`,
  1044  	},
  1045  
  1046  	// Missing colon for int32 field
  1047  	{
  1048  		in:  `count 42`,
  1049  		err: `line 1.6: expected ':', found "42"`,
  1050  	},
  1051  
  1052  	// Missing required field
  1053  	{
  1054  		in:  `name: "Pawel"`,
  1055  		err: `required field proto2_test.MyMessage.count not set`,
  1056  		out: &pb2.MyMessage{
  1057  			Name: proto.String("Pawel"),
  1058  		},
  1059  	},
  1060  
  1061  	// Missing required field in a required submessage
  1062  	{
  1063  		in:  `count: 42 we_must_go_deeper < leo_finally_won_an_oscar <> >`,
  1064  		err: `required field proto2_test.InnerMessage.host not set`,
  1065  		out: &pb2.MyMessage{
  1066  			Count:          proto.Int32(42),
  1067  			WeMustGoDeeper: &pb2.RequiredInnerMessage{LeoFinallyWonAnOscar: &pb2.InnerMessage{}},
  1068  		},
  1069  	},
  1070  
  1071  	// Repeated non-repeated field
  1072  	{
  1073  		in:  `name: "Rob" name: "Russ"`,
  1074  		err: `line 1.12: non-repeated field "name" was repeated`,
  1075  	},
  1076  
  1077  	// Group
  1078  	{
  1079  		in: `count: 17 SomeGroup { group_field: 12 }`,
  1080  		out: &pb2.MyMessage{
  1081  			Count: proto.Int32(17),
  1082  			Somegroup: &pb2.MyMessage_SomeGroup{
  1083  				GroupField: proto.Int32(12),
  1084  			},
  1085  		},
  1086  	},
  1087  
  1088  	// Semicolon between fields
  1089  	{
  1090  		in: `count:3;name:"Calvin"`,
  1091  		out: &pb2.MyMessage{
  1092  			Count: proto.Int32(3),
  1093  			Name:  proto.String("Calvin"),
  1094  		},
  1095  	},
  1096  	// Comma between fields
  1097  	{
  1098  		in: `count:4,name:"Ezekiel"`,
  1099  		out: &pb2.MyMessage{
  1100  			Count: proto.Int32(4),
  1101  			Name:  proto.String("Ezekiel"),
  1102  		},
  1103  	},
  1104  
  1105  	// Boolean false
  1106  	{
  1107  		in: `count:42 inner { host: "example.com" connected: false }`,
  1108  		out: &pb2.MyMessage{
  1109  			Count: proto.Int32(42),
  1110  			Inner: &pb2.InnerMessage{
  1111  				Host:      proto.String("example.com"),
  1112  				Connected: proto.Bool(false),
  1113  			},
  1114  		},
  1115  	},
  1116  	// Boolean true
  1117  	{
  1118  		in: `count:42 inner { host: "example.com" connected: true }`,
  1119  		out: &pb2.MyMessage{
  1120  			Count: proto.Int32(42),
  1121  			Inner: &pb2.InnerMessage{
  1122  				Host:      proto.String("example.com"),
  1123  				Connected: proto.Bool(true),
  1124  			},
  1125  		},
  1126  	},
  1127  	// Boolean 0
  1128  	{
  1129  		in: `count:42 inner { host: "example.com" connected: 0 }`,
  1130  		out: &pb2.MyMessage{
  1131  			Count: proto.Int32(42),
  1132  			Inner: &pb2.InnerMessage{
  1133  				Host:      proto.String("example.com"),
  1134  				Connected: proto.Bool(false),
  1135  			},
  1136  		},
  1137  	},
  1138  	// Boolean 1
  1139  	{
  1140  		in: `count:42 inner { host: "example.com" connected: 1 }`,
  1141  		out: &pb2.MyMessage{
  1142  			Count: proto.Int32(42),
  1143  			Inner: &pb2.InnerMessage{
  1144  				Host:      proto.String("example.com"),
  1145  				Connected: proto.Bool(true),
  1146  			},
  1147  		},
  1148  	},
  1149  	// Boolean f
  1150  	{
  1151  		in: `count:42 inner { host: "example.com" connected: f }`,
  1152  		out: &pb2.MyMessage{
  1153  			Count: proto.Int32(42),
  1154  			Inner: &pb2.InnerMessage{
  1155  				Host:      proto.String("example.com"),
  1156  				Connected: proto.Bool(false),
  1157  			},
  1158  		},
  1159  	},
  1160  	// Boolean t
  1161  	{
  1162  		in: `count:42 inner { host: "example.com" connected: t }`,
  1163  		out: &pb2.MyMessage{
  1164  			Count: proto.Int32(42),
  1165  			Inner: &pb2.InnerMessage{
  1166  				Host:      proto.String("example.com"),
  1167  				Connected: proto.Bool(true),
  1168  			},
  1169  		},
  1170  	},
  1171  	// Boolean False
  1172  	{
  1173  		in: `count:42 inner { host: "example.com" connected: False }`,
  1174  		out: &pb2.MyMessage{
  1175  			Count: proto.Int32(42),
  1176  			Inner: &pb2.InnerMessage{
  1177  				Host:      proto.String("example.com"),
  1178  				Connected: proto.Bool(false),
  1179  			},
  1180  		},
  1181  	},
  1182  	// Boolean True
  1183  	{
  1184  		in: `count:42 inner { host: "example.com" connected: True }`,
  1185  		out: &pb2.MyMessage{
  1186  			Count: proto.Int32(42),
  1187  			Inner: &pb2.InnerMessage{
  1188  				Host:      proto.String("example.com"),
  1189  				Connected: proto.Bool(true),
  1190  			},
  1191  		},
  1192  	},
  1193  
  1194  	// Extension
  1195  	buildExtStructTest(`count: 42 [proto2_test.Ext.more]:<data:"Hello, world!" >`),
  1196  	buildExtStructTest(`count: 42 [proto2_test.Ext.more] {data:"Hello, world!"}`),
  1197  	buildExtDataTest(`count: 42 [proto2_test.Ext.text]:"Hello, world!" [proto2_test.Ext.number]:1729`),
  1198  	buildExtRepStringTest(`count: 42 [proto2_test.greeting]:"bula" [proto2_test.greeting]:"hola"`),
  1199  	{
  1200  		in:  `[proto2_test.complex]:<>`,
  1201  		err: `line 1.20: extension field "proto2_test.complex" does not extend message "proto2_test.MyMessage"`,
  1202  	},
  1203  
  1204  	// Big all-in-one
  1205  	{
  1206  		in: "count:42  # Meaning\n" +
  1207  			`name:"Dave" ` +
  1208  			`quote:"\"I didn't want to go.\"" ` +
  1209  			`pet:"bunny" ` +
  1210  			`pet:"kitty" ` +
  1211  			`pet:"horsey" ` +
  1212  			`inner:<` +
  1213  			`  host:"footrest.syd" ` +
  1214  			`  port:7001 ` +
  1215  			`  connected:true ` +
  1216  			`> ` +
  1217  			`others:<` +
  1218  			`  key:3735928559 ` +
  1219  			`  value:"\x01A\a\f" ` +
  1220  			`> ` +
  1221  			`others:<` +
  1222  			"  weight:58.9  # Atomic weight of Co\n" +
  1223  			`  inner:<` +
  1224  			`    host:"lesha.mtv" ` +
  1225  			`    port:8002 ` +
  1226  			`  >` +
  1227  			`>`,
  1228  		out: &pb2.MyMessage{
  1229  			Count: proto.Int32(42),
  1230  			Name:  proto.String("Dave"),
  1231  			Quote: proto.String(`"I didn't want to go."`),
  1232  			Pet:   []string{"bunny", "kitty", "horsey"},
  1233  			Inner: &pb2.InnerMessage{
  1234  				Host:      proto.String("footrest.syd"),
  1235  				Port:      proto.Int32(7001),
  1236  				Connected: proto.Bool(true),
  1237  			},
  1238  			Others: []*pb2.OtherMessage{
  1239  				{
  1240  					Key:   proto.Int64(3735928559),
  1241  					Value: []byte{0x1, 'A', '\a', '\f'},
  1242  				},
  1243  				{
  1244  					Weight: proto.Float32(58.9),
  1245  					Inner: &pb2.InnerMessage{
  1246  						Host: proto.String("lesha.mtv"),
  1247  						Port: proto.Int32(8002),
  1248  					},
  1249  				},
  1250  			},
  1251  		},
  1252  	},
  1253  }
  1254  
  1255  func TestUnmarshalText(t *testing.T) {
  1256  	for _, test := range unmarshalTextTests {
  1257  		t.Run("", func(t *testing.T) {
  1258  			pb := new(pb2.MyMessage)
  1259  			err := proto.UnmarshalText(test.in, pb)
  1260  			if test.err == "" {
  1261  				// We don't expect failure.
  1262  				if err != nil {
  1263  					t.Errorf("proto.UnmarshalText error: %v", err)
  1264  				} else if !proto.Equal(pb, test.out) {
  1265  					t.Errorf("proto.Equal mismatch:\ngot:  %v\nwant: %v", pb, test.out)
  1266  				}
  1267  			} else {
  1268  				// We do expect failure.
  1269  				if err == nil {
  1270  					t.Errorf("proto.UnmarshalText: got nil error, want %v", test.err)
  1271  				} else if !strings.Contains(err.Error(), test.err) {
  1272  					t.Errorf("proto.UnmarshalText error mismatch:\ngot:  %v\nwant: %v", err.Error(), test.err)
  1273  				} else if _, ok := err.(*proto.RequiredNotSetError); ok && test.out != nil && !proto.Equal(pb, test.out) {
  1274  					t.Errorf("proto.Equal mismatch:\ngot  %v\nwant: %v", pb, test.out)
  1275  				}
  1276  			}
  1277  		})
  1278  	}
  1279  }
  1280  
  1281  func TestUnmarshalTextCustomMessage(t *testing.T) {
  1282  	msg := &textMessage{}
  1283  	if err := proto.UnmarshalText("custom", msg); err != nil {
  1284  		t.Errorf("proto.UnmarshalText error: %v", err)
  1285  	}
  1286  	if err := proto.UnmarshalText("not custom", msg); err == nil {
  1287  		t.Errorf("proto.UnmarshalText: got nil error, want non-nil")
  1288  	}
  1289  }
  1290  
  1291  // Regression test; this caused a panic.
  1292  func TestRepeatedEnum(t *testing.T) {
  1293  	pb := new(pb2.RepeatedEnum)
  1294  	if err := proto.UnmarshalText("color: RED", pb); err != nil {
  1295  		t.Fatal(err)
  1296  	}
  1297  	exp := &pb2.RepeatedEnum{
  1298  		Color: []pb2.RepeatedEnum_Color{pb2.RepeatedEnum_RED},
  1299  	}
  1300  	if !proto.Equal(pb, exp) {
  1301  		t.Errorf("proto.Equal mismatch:\ngot:  %v\nwant %v", pb, exp)
  1302  	}
  1303  }
  1304  
  1305  func TestProto3TextParsing(t *testing.T) {
  1306  	m := new(pb3.Message)
  1307  	const in = `name: "Wallace" true_scotsman: true`
  1308  	want := &pb3.Message{
  1309  		Name:         "Wallace",
  1310  		TrueScotsman: true,
  1311  	}
  1312  	if err := proto.UnmarshalText(in, m); err != nil {
  1313  		t.Fatal(err)
  1314  	}
  1315  	if !proto.Equal(m, want) {
  1316  		t.Errorf("proto.Equal mismatch:\ngot:  %v\nwant %v", m, want)
  1317  	}
  1318  }
  1319  
  1320  func TestMapParsing(t *testing.T) {
  1321  	m := new(pb2.MessageWithMap)
  1322  	const in = `name_mapping:<key:1234 value:"Feist"> name_mapping:<key:1 value:"Beatles">` +
  1323  		`msg_mapping:<key:-4, value:<f: 2.0>,>` + // separating commas are okay
  1324  		`msg_mapping<key:-2 value<f: 4.0>>` + // no colon after "value"
  1325  		`msg_mapping:<value:<f: 5.0>>` + // omitted key
  1326  		`byte_mapping:<key:true value:"so be it">` +
  1327  		`byte_mapping:<>` // omitted key and value
  1328  	want := &pb2.MessageWithMap{
  1329  		NameMapping: map[int32]string{
  1330  			1:    "Beatles",
  1331  			1234: "Feist",
  1332  		},
  1333  		MsgMapping: map[int64]*pb2.FloatingPoint{
  1334  			-4: {F: proto.Float64(2.0)},
  1335  			-2: {F: proto.Float64(4.0)},
  1336  			0:  {F: proto.Float64(5.0)},
  1337  		},
  1338  		ByteMapping: map[bool][]byte{
  1339  			false: nil,
  1340  			true:  []byte("so be it"),
  1341  		},
  1342  	}
  1343  	if err := proto.UnmarshalText(in, m); err != nil {
  1344  		t.Fatal(err)
  1345  	}
  1346  	if !proto.Equal(m, want) {
  1347  		t.Errorf("proto.Equal mismatch:\ngot:  %v\nwant %v", m, want)
  1348  	}
  1349  }
  1350  
  1351  func TestOneofParsing(t *testing.T) {
  1352  	const in = `name:"Shrek"`
  1353  	m := new(pb2.Communique)
  1354  	want := &pb2.Communique{Union: &pb2.Communique_Name{"Shrek"}}
  1355  	if err := proto.UnmarshalText(in, m); err != nil {
  1356  		t.Fatal(err)
  1357  	}
  1358  	if !proto.Equal(m, want) {
  1359  		t.Errorf("\n got %v\nwant %v", m, want)
  1360  	}
  1361  
  1362  	const inOverwrite = `name:"Shrek" number:42`
  1363  	m = new(pb2.Communique)
  1364  	testErr := "line 1.13: field 'number' would overwrite already parsed oneof 'union'"
  1365  	if err := proto.UnmarshalText(inOverwrite, m); err == nil {
  1366  		t.Errorf("proto.UnmarshalText: got nil error, want %v", testErr)
  1367  	} else if err.Error() != testErr {
  1368  		t.Errorf("error mismatch:\ngot:  %v\nwant: %v", err.Error(), testErr)
  1369  	}
  1370  }
  1371  

View as plain text