| // Go support for Protocol Buffers - Google's data interchange format | 
 | // | 
 | // Copyright 2010 The Go Authors.  All rights reserved. | 
 | // https://github.com/golang/protobuf | 
 | // | 
 | // Redistribution and use in source and binary forms, with or without | 
 | // modification, are permitted provided that the following conditions are | 
 | // met: | 
 | // | 
 | //     * Redistributions of source code must retain the above copyright | 
 | // notice, this list of conditions and the following disclaimer. | 
 | //     * Redistributions in binary form must reproduce the above | 
 | // copyright notice, this list of conditions and the following disclaimer | 
 | // in the documentation and/or other materials provided with the | 
 | // distribution. | 
 | //     * Neither the name of Google Inc. nor the names of its | 
 | // contributors may be used to endorse or promote products derived from | 
 | // this software without specific prior written permission. | 
 | // | 
 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  | 
 | package proto_test | 
 |  | 
 | import ( | 
 | 	"bytes" | 
 | 	"errors" | 
 | 	"io/ioutil" | 
 | 	"math" | 
 | 	"strings" | 
 | 	"testing" | 
 |  | 
 | 	"github.com/golang/protobuf/proto" | 
 |  | 
 | 	proto3pb "github.com/golang/protobuf/proto/proto3_proto" | 
 | 	pb "github.com/golang/protobuf/proto/testdata" | 
 | ) | 
 |  | 
 | // textMessage implements the methods that allow it to marshal and unmarshal | 
 | // itself as text. | 
 | type textMessage struct { | 
 | } | 
 |  | 
 | func (*textMessage) MarshalText() ([]byte, error) { | 
 | 	return []byte("custom"), nil | 
 | } | 
 |  | 
 | func (*textMessage) UnmarshalText(bytes []byte) error { | 
 | 	if string(bytes) != "custom" { | 
 | 		return errors.New("expected 'custom'") | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | func (*textMessage) Reset()         {} | 
 | func (*textMessage) String() string { return "" } | 
 | func (*textMessage) ProtoMessage()  {} | 
 |  | 
 | func newTestMessage() *pb.MyMessage { | 
 | 	msg := &pb.MyMessage{ | 
 | 		Count: proto.Int32(42), | 
 | 		Name:  proto.String("Dave"), | 
 | 		Quote: proto.String(`"I didn't want to go."`), | 
 | 		Pet:   []string{"bunny", "kitty", "horsey"}, | 
 | 		Inner: &pb.InnerMessage{ | 
 | 			Host:      proto.String("footrest.syd"), | 
 | 			Port:      proto.Int32(7001), | 
 | 			Connected: proto.Bool(true), | 
 | 		}, | 
 | 		Others: []*pb.OtherMessage{ | 
 | 			{ | 
 | 				Key:   proto.Int64(0xdeadbeef), | 
 | 				Value: []byte{1, 65, 7, 12}, | 
 | 			}, | 
 | 			{ | 
 | 				Weight: proto.Float32(6.022), | 
 | 				Inner: &pb.InnerMessage{ | 
 | 					Host: proto.String("lesha.mtv"), | 
 | 					Port: proto.Int32(8002), | 
 | 				}, | 
 | 			}, | 
 | 		}, | 
 | 		Bikeshed: pb.MyMessage_BLUE.Enum(), | 
 | 		Somegroup: &pb.MyMessage_SomeGroup{ | 
 | 			GroupField: proto.Int32(8), | 
 | 		}, | 
 | 		// One normally wouldn't do this. | 
 | 		// This is an undeclared tag 13, as a varint (wire type 0) with value 4. | 
 | 		XXX_unrecognized: []byte{13<<3 | 0, 4}, | 
 | 	} | 
 | 	ext := &pb.Ext{ | 
 | 		Data: proto.String("Big gobs for big rats"), | 
 | 	} | 
 | 	if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil { | 
 | 		panic(err) | 
 | 	} | 
 | 	greetings := []string{"adg", "easy", "cow"} | 
 | 	if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil { | 
 | 		panic(err) | 
 | 	} | 
 |  | 
 | 	// Add an unknown extension. We marshal a pb.Ext, and fake the ID. | 
 | 	b, err := proto.Marshal(&pb.Ext{Data: proto.String("3G skiing")}) | 
 | 	if err != nil { | 
 | 		panic(err) | 
 | 	} | 
 | 	b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...) | 
 | 	proto.SetRawExtension(msg, 201, b) | 
 |  | 
 | 	// Extensions can be plain fields, too, so let's test that. | 
 | 	b = append(proto.EncodeVarint(202<<3|proto.WireVarint), 19) | 
 | 	proto.SetRawExtension(msg, 202, b) | 
 |  | 
 | 	return msg | 
 | } | 
 |  | 
 | const text = `count: 42 | 
 | name: "Dave" | 
 | quote: "\"I didn't want to go.\"" | 
 | pet: "bunny" | 
 | pet: "kitty" | 
 | pet: "horsey" | 
 | inner: < | 
 |   host: "footrest.syd" | 
 |   port: 7001 | 
 |   connected: true | 
 | > | 
 | others: < | 
 |   key: 3735928559 | 
 |   value: "\001A\007\014" | 
 | > | 
 | others: < | 
 |   weight: 6.022 | 
 |   inner: < | 
 |     host: "lesha.mtv" | 
 |     port: 8002 | 
 |   > | 
 | > | 
 | bikeshed: BLUE | 
 | SomeGroup { | 
 |   group_field: 8 | 
 | } | 
 | /* 2 unknown bytes */ | 
 | 13: 4 | 
 | [testdata.Ext.more]: < | 
 |   data: "Big gobs for big rats" | 
 | > | 
 | [testdata.greeting]: "adg" | 
 | [testdata.greeting]: "easy" | 
 | [testdata.greeting]: "cow" | 
 | /* 13 unknown bytes */ | 
 | 201: "\t3G skiing" | 
 | /* 3 unknown bytes */ | 
 | 202: 19 | 
 | ` | 
 |  | 
 | func TestMarshalText(t *testing.T) { | 
 | 	buf := new(bytes.Buffer) | 
 | 	if err := proto.MarshalText(buf, newTestMessage()); err != nil { | 
 | 		t.Fatalf("proto.MarshalText: %v", err) | 
 | 	} | 
 | 	s := buf.String() | 
 | 	if s != text { | 
 | 		t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text) | 
 | 	} | 
 | } | 
 |  | 
 | func TestMarshalTextCustomMessage(t *testing.T) { | 
 | 	buf := new(bytes.Buffer) | 
 | 	if err := proto.MarshalText(buf, &textMessage{}); err != nil { | 
 | 		t.Fatalf("proto.MarshalText: %v", err) | 
 | 	} | 
 | 	s := buf.String() | 
 | 	if s != "custom" { | 
 | 		t.Errorf("Got %q, expected %q", s, "custom") | 
 | 	} | 
 | } | 
 | func TestMarshalTextNil(t *testing.T) { | 
 | 	want := "<nil>" | 
 | 	tests := []proto.Message{nil, (*pb.MyMessage)(nil)} | 
 | 	for i, test := range tests { | 
 | 		buf := new(bytes.Buffer) | 
 | 		if err := proto.MarshalText(buf, test); err != nil { | 
 | 			t.Fatal(err) | 
 | 		} | 
 | 		if got := buf.String(); got != want { | 
 | 			t.Errorf("%d: got %q want %q", i, got, want) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func TestMarshalTextUnknownEnum(t *testing.T) { | 
 | 	// The Color enum only specifies values 0-2. | 
 | 	m := &pb.MyMessage{Bikeshed: pb.MyMessage_Color(3).Enum()} | 
 | 	got := m.String() | 
 | 	const want = `bikeshed:3 ` | 
 | 	if got != want { | 
 | 		t.Errorf("\n got %q\nwant %q", got, want) | 
 | 	} | 
 | } | 
 |  | 
 | func TestTextOneof(t *testing.T) { | 
 | 	tests := []struct { | 
 | 		m    proto.Message | 
 | 		want string | 
 | 	}{ | 
 | 		// zero message | 
 | 		{&pb.Communique{}, ``}, | 
 | 		// scalar field | 
 | 		{&pb.Communique{Union: &pb.Communique_Number{4}}, `number:4`}, | 
 | 		// message field | 
 | 		{&pb.Communique{Union: &pb.Communique_Msg{ | 
 | 			&pb.Strings{StringField: proto.String("why hello!")}, | 
 | 		}}, `msg:<string_field:"why hello!" >`}, | 
 | 		// bad oneof (should not panic) | 
 | 		{&pb.Communique{Union: &pb.Communique_Msg{nil}}, `msg:/* nil */`}, | 
 | 	} | 
 | 	for _, test := range tests { | 
 | 		got := strings.TrimSpace(test.m.String()) | 
 | 		if got != test.want { | 
 | 			t.Errorf("\n got %s\nwant %s", got, test.want) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func BenchmarkMarshalTextBuffered(b *testing.B) { | 
 | 	buf := new(bytes.Buffer) | 
 | 	m := newTestMessage() | 
 | 	for i := 0; i < b.N; i++ { | 
 | 		buf.Reset() | 
 | 		proto.MarshalText(buf, m) | 
 | 	} | 
 | } | 
 |  | 
 | func BenchmarkMarshalTextUnbuffered(b *testing.B) { | 
 | 	w := ioutil.Discard | 
 | 	m := newTestMessage() | 
 | 	for i := 0; i < b.N; i++ { | 
 | 		proto.MarshalText(w, m) | 
 | 	} | 
 | } | 
 |  | 
 | func compact(src string) string { | 
 | 	// s/[ \n]+/ /g; s/ $//; | 
 | 	dst := make([]byte, len(src)) | 
 | 	space, comment := false, false | 
 | 	j := 0 | 
 | 	for i := 0; i < len(src); i++ { | 
 | 		if strings.HasPrefix(src[i:], "/*") { | 
 | 			comment = true | 
 | 			i++ | 
 | 			continue | 
 | 		} | 
 | 		if comment && strings.HasPrefix(src[i:], "*/") { | 
 | 			comment = false | 
 | 			i++ | 
 | 			continue | 
 | 		} | 
 | 		if comment { | 
 | 			continue | 
 | 		} | 
 | 		c := src[i] | 
 | 		if c == ' ' || c == '\n' { | 
 | 			space = true | 
 | 			continue | 
 | 		} | 
 | 		if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') { | 
 | 			space = false | 
 | 		} | 
 | 		if c == '{' { | 
 | 			space = false | 
 | 		} | 
 | 		if space { | 
 | 			dst[j] = ' ' | 
 | 			j++ | 
 | 			space = false | 
 | 		} | 
 | 		dst[j] = c | 
 | 		j++ | 
 | 	} | 
 | 	if space { | 
 | 		dst[j] = ' ' | 
 | 		j++ | 
 | 	} | 
 | 	return string(dst[0:j]) | 
 | } | 
 |  | 
 | var compactText = compact(text) | 
 |  | 
 | func TestCompactText(t *testing.T) { | 
 | 	s := proto.CompactTextString(newTestMessage()) | 
 | 	if s != compactText { | 
 | 		t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v\n===\n", s, compactText) | 
 | 	} | 
 | } | 
 |  | 
 | func TestStringEscaping(t *testing.T) { | 
 | 	testCases := []struct { | 
 | 		in  *pb.Strings | 
 | 		out string | 
 | 	}{ | 
 | 		{ | 
 | 			// Test data from C++ test (TextFormatTest.StringEscape). | 
 | 			// Single divergence: we don't escape apostrophes. | 
 | 			&pb.Strings{StringField: proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and  multiple   spaces")}, | 
 | 			"string_field: \"\\\"A string with ' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and  multiple   spaces\"\n", | 
 | 		}, | 
 | 		{ | 
 | 			// Test data from the same C++ test. | 
 | 			&pb.Strings{StringField: proto.String("\350\260\267\346\255\214")}, | 
 | 			"string_field: \"\\350\\260\\267\\346\\255\\214\"\n", | 
 | 		}, | 
 | 		{ | 
 | 			// Some UTF-8. | 
 | 			&pb.Strings{StringField: proto.String("\x00\x01\xff\x81")}, | 
 | 			`string_field: "\000\001\377\201"` + "\n", | 
 | 		}, | 
 | 	} | 
 |  | 
 | 	for i, tc := range testCases { | 
 | 		var buf bytes.Buffer | 
 | 		if err := proto.MarshalText(&buf, tc.in); err != nil { | 
 | 			t.Errorf("proto.MarsalText: %v", err) | 
 | 			continue | 
 | 		} | 
 | 		s := buf.String() | 
 | 		if s != tc.out { | 
 | 			t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out) | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		// Check round-trip. | 
 | 		pb := new(pb.Strings) | 
 | 		if err := proto.UnmarshalText(s, pb); err != nil { | 
 | 			t.Errorf("#%d: UnmarshalText: %v", i, err) | 
 | 			continue | 
 | 		} | 
 | 		if !proto.Equal(pb, tc.in) { | 
 | 			t.Errorf("#%d: Round-trip failed:\nstart: %v\n  end: %v", i, tc.in, pb) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // A limitedWriter accepts some output before it fails. | 
 | // This is a proxy for something like a nearly-full or imminently-failing disk, | 
 | // or a network connection that is about to die. | 
 | type limitedWriter struct { | 
 | 	b     bytes.Buffer | 
 | 	limit int | 
 | } | 
 |  | 
 | var outOfSpace = errors.New("proto: insufficient space") | 
 |  | 
 | func (w *limitedWriter) Write(p []byte) (n int, err error) { | 
 | 	var avail = w.limit - w.b.Len() | 
 | 	if avail <= 0 { | 
 | 		return 0, outOfSpace | 
 | 	} | 
 | 	if len(p) <= avail { | 
 | 		return w.b.Write(p) | 
 | 	} | 
 | 	n, _ = w.b.Write(p[:avail]) | 
 | 	return n, outOfSpace | 
 | } | 
 |  | 
 | func TestMarshalTextFailing(t *testing.T) { | 
 | 	// Try lots of different sizes to exercise more error code-paths. | 
 | 	for lim := 0; lim < len(text); lim++ { | 
 | 		buf := new(limitedWriter) | 
 | 		buf.limit = lim | 
 | 		err := proto.MarshalText(buf, newTestMessage()) | 
 | 		// We expect a certain error, but also some partial results in the buffer. | 
 | 		if err != outOfSpace { | 
 | 			t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", err, outOfSpace) | 
 | 		} | 
 | 		s := buf.b.String() | 
 | 		x := text[:buf.limit] | 
 | 		if s != x { | 
 | 			t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, x) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func TestFloats(t *testing.T) { | 
 | 	tests := []struct { | 
 | 		f    float64 | 
 | 		want string | 
 | 	}{ | 
 | 		{0, "0"}, | 
 | 		{4.7, "4.7"}, | 
 | 		{math.Inf(1), "inf"}, | 
 | 		{math.Inf(-1), "-inf"}, | 
 | 		{math.NaN(), "nan"}, | 
 | 	} | 
 | 	for _, test := range tests { | 
 | 		msg := &pb.FloatingPoint{F: &test.f} | 
 | 		got := strings.TrimSpace(msg.String()) | 
 | 		want := `f:` + test.want | 
 | 		if got != want { | 
 | 			t.Errorf("f=%f: got %q, want %q", test.f, got, want) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func TestRepeatedNilText(t *testing.T) { | 
 | 	m := &pb.MessageList{ | 
 | 		Message: []*pb.MessageList_Message{ | 
 | 			nil, | 
 | 			&pb.MessageList_Message{ | 
 | 				Name: proto.String("Horse"), | 
 | 			}, | 
 | 			nil, | 
 | 		}, | 
 | 	} | 
 | 	want := `Message <nil> | 
 | Message { | 
 |   name: "Horse" | 
 | } | 
 | Message <nil> | 
 | ` | 
 | 	if s := proto.MarshalTextString(m); s != want { | 
 | 		t.Errorf(" got: %s\nwant: %s", s, want) | 
 | 	} | 
 | } | 
 |  | 
 | func TestProto3Text(t *testing.T) { | 
 | 	tests := []struct { | 
 | 		m    proto.Message | 
 | 		want string | 
 | 	}{ | 
 | 		// zero message | 
 | 		{&proto3pb.Message{}, ``}, | 
 | 		// zero message except for an empty byte slice | 
 | 		{&proto3pb.Message{Data: []byte{}}, ``}, | 
 | 		// trivial case | 
 | 		{&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`}, | 
 | 		// empty map | 
 | 		{&pb.MessageWithMap{}, ``}, | 
 | 		// non-empty map; map format is the same as a repeated struct, | 
 | 		// and they are sorted by key (numerically for numeric keys). | 
 | 		{ | 
 | 			&pb.MessageWithMap{NameMapping: map[int32]string{ | 
 | 				-1:      "Negatory", | 
 | 				7:       "Lucky", | 
 | 				1234:    "Feist", | 
 | 				6345789: "Otis", | 
 | 			}}, | 
 | 			`name_mapping:<key:-1 value:"Negatory" > ` + | 
 | 				`name_mapping:<key:7 value:"Lucky" > ` + | 
 | 				`name_mapping:<key:1234 value:"Feist" > ` + | 
 | 				`name_mapping:<key:6345789 value:"Otis" >`, | 
 | 		}, | 
 | 		// map with nil value; not well-defined, but we shouldn't crash | 
 | 		{ | 
 | 			&pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{7: nil}}, | 
 | 			`msg_mapping:<key:7 >`, | 
 | 		}, | 
 | 	} | 
 | 	for _, test := range tests { | 
 | 		got := strings.TrimSpace(test.m.String()) | 
 | 		if got != test.want { | 
 | 			t.Errorf("\n got %s\nwant %s", got, test.want) | 
 | 		} | 
 | 	} | 
 | } |