github.com/schwarzm/garden-linux@v0.0.0-20150507151835-33bca2147c47/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto/text_test.go (about) 1 // Go support for Protocol Buffers - Google's data interchange format 2 // 3 // Copyright 2010 The Go Authors. All rights reserved. 4 // http://code.google.com/p/goprotobuf/ 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions are 8 // met: 9 // 10 // * Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // * Redistributions in binary form must reproduce the above 13 // copyright notice, this list of conditions and the following disclaimer 14 // in the documentation and/or other materials provided with the 15 // distribution. 16 // * Neither the name of Google Inc. nor the names of its 17 // contributors may be used to endorse or promote products derived from 18 // this software without specific prior written permission. 19 // 20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 32 package proto_test 33 34 import ( 35 "bytes" 36 "errors" 37 "io/ioutil" 38 "math" 39 "strings" 40 "testing" 41 42 "code.google.com/p/gogoprotobuf/proto" 43 44 pb "./testdata" 45 ) 46 47 // textMessage implements the methods that allow it to marshal and unmarshal 48 // itself as text. 49 type textMessage struct { 50 } 51 52 func (*textMessage) MarshalText() ([]byte, error) { 53 return []byte("custom"), nil 54 } 55 56 func (*textMessage) UnmarshalText(bytes []byte) error { 57 if string(bytes) != "custom" { 58 return errors.New("expected 'custom'") 59 } 60 return nil 61 } 62 63 func (*textMessage) Reset() {} 64 func (*textMessage) String() string { return "" } 65 func (*textMessage) ProtoMessage() {} 66 67 func newTestMessage() *pb.MyMessage { 68 msg := &pb.MyMessage{ 69 Count: proto.Int32(42), 70 Name: proto.String("Dave"), 71 Quote: proto.String(`"I didn't want to go."`), 72 Pet: []string{"bunny", "kitty", "horsey"}, 73 Inner: &pb.InnerMessage{ 74 Host: proto.String("footrest.syd"), 75 Port: proto.Int32(7001), 76 Connected: proto.Bool(true), 77 }, 78 Others: []*pb.OtherMessage{ 79 { 80 Key: proto.Int64(0xdeadbeef), 81 Value: []byte{1, 65, 7, 12}, 82 }, 83 { 84 Weight: proto.Float32(6.022), 85 Inner: &pb.InnerMessage{ 86 Host: proto.String("lesha.mtv"), 87 Port: proto.Int32(8002), 88 }, 89 }, 90 }, 91 Bikeshed: pb.MyMessage_BLUE.Enum(), 92 Somegroup: &pb.MyMessage_SomeGroup{ 93 GroupField: proto.Int32(8), 94 }, 95 // One normally wouldn't do this. 96 // This is an undeclared tag 13, as a varint (wire type 0) with value 4. 97 XXX_unrecognized: []byte{13<<3 | 0, 4}, 98 } 99 ext := &pb.Ext{ 100 Data: proto.String("Big gobs for big rats"), 101 } 102 if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil { 103 panic(err) 104 } 105 greetings := []string{"adg", "easy", "cow"} 106 if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil { 107 panic(err) 108 } 109 110 // Add an unknown extension. We marshal a pb.Ext, and fake the ID. 111 b, err := proto.Marshal(&pb.Ext{Data: proto.String("3G skiing")}) 112 if err != nil { 113 panic(err) 114 } 115 b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...) 116 proto.SetRawExtension(msg, 201, b) 117 118 // Extensions can be plain fields, too, so let's test that. 119 b = append(proto.EncodeVarint(202<<3|proto.WireVarint), 19) 120 proto.SetRawExtension(msg, 202, b) 121 122 return msg 123 } 124 125 const text = `count: 42 126 name: "Dave" 127 quote: "\"I didn't want to go.\"" 128 pet: "bunny" 129 pet: "kitty" 130 pet: "horsey" 131 inner: < 132 host: "footrest.syd" 133 port: 7001 134 connected: true 135 > 136 others: < 137 key: 3735928559 138 value: "\001A\007\014" 139 > 140 others: < 141 weight: 6.022 142 inner: < 143 host: "lesha.mtv" 144 port: 8002 145 > 146 > 147 bikeshed: BLUE 148 SomeGroup { 149 group_field: 8 150 } 151 /* 2 unknown bytes */ 152 13: 4 153 [testdata.Ext.more]: < 154 data: "Big gobs for big rats" 155 > 156 [testdata.greeting]: "adg" 157 [testdata.greeting]: "easy" 158 [testdata.greeting]: "cow" 159 /* 13 unknown bytes */ 160 201: "\t3G skiing" 161 /* 3 unknown bytes */ 162 202: 19 163 ` 164 165 func TestMarshalText(t *testing.T) { 166 buf := new(bytes.Buffer) 167 if err := proto.MarshalText(buf, newTestMessage()); err != nil { 168 t.Fatalf("proto.MarshalText: %v", err) 169 } 170 s := buf.String() 171 if s != text { 172 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text) 173 } 174 } 175 176 func TestMarshalTextCustomMessage(t *testing.T) { 177 buf := new(bytes.Buffer) 178 if err := proto.MarshalText(buf, &textMessage{}); err != nil { 179 t.Fatalf("proto.MarshalText: %v", err) 180 } 181 s := buf.String() 182 if s != "custom" { 183 t.Errorf("Got %q, expected %q", s, "custom") 184 } 185 } 186 func TestMarshalTextNil(t *testing.T) { 187 want := "<nil>" 188 tests := []proto.Message{nil, (*pb.MyMessage)(nil)} 189 for i, test := range tests { 190 buf := new(bytes.Buffer) 191 if err := proto.MarshalText(buf, test); err != nil { 192 t.Fatal(err) 193 } 194 if got := buf.String(); got != want { 195 t.Errorf("%d: got %q want %q", i, got, want) 196 } 197 } 198 } 199 200 func TestMarshalTextUnknownEnum(t *testing.T) { 201 // The Color enum only specifies values 0-2. 202 m := &pb.MyMessage{Bikeshed: pb.MyMessage_Color(3).Enum()} 203 got := m.String() 204 const want = `bikeshed:3 ` 205 if got != want { 206 t.Errorf("\n got %q\nwant %q", got, want) 207 } 208 } 209 210 func BenchmarkMarshalTextBuffered(b *testing.B) { 211 buf := new(bytes.Buffer) 212 m := newTestMessage() 213 for i := 0; i < b.N; i++ { 214 buf.Reset() 215 proto.MarshalText(buf, m) 216 } 217 } 218 219 func BenchmarkMarshalTextUnbuffered(b *testing.B) { 220 w := ioutil.Discard 221 m := newTestMessage() 222 for i := 0; i < b.N; i++ { 223 proto.MarshalText(w, m) 224 } 225 } 226 227 func compact(src string) string { 228 // s/[ \n]+/ /g; s/ $//; 229 dst := make([]byte, len(src)) 230 space, comment := false, false 231 j := 0 232 for i := 0; i < len(src); i++ { 233 if strings.HasPrefix(src[i:], "/*") { 234 comment = true 235 i++ 236 continue 237 } 238 if comment && strings.HasPrefix(src[i:], "*/") { 239 comment = false 240 i++ 241 continue 242 } 243 if comment { 244 continue 245 } 246 c := src[i] 247 if c == ' ' || c == '\n' { 248 space = true 249 continue 250 } 251 if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') { 252 space = false 253 } 254 if c == '{' { 255 space = false 256 } 257 if space { 258 dst[j] = ' ' 259 j++ 260 space = false 261 } 262 dst[j] = c 263 j++ 264 } 265 if space { 266 dst[j] = ' ' 267 j++ 268 } 269 return string(dst[0:j]) 270 } 271 272 var compactText = compact(text) 273 274 func TestCompactText(t *testing.T) { 275 s := proto.CompactTextString(newTestMessage()) 276 if s != compactText { 277 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v\n===\n", s, compactText) 278 } 279 } 280 281 func TestStringEscaping(t *testing.T) { 282 testCases := []struct { 283 in *pb.Strings 284 out string 285 }{ 286 { 287 // Test data from C++ test (TextFormatTest.StringEscape). 288 // Single divergence: we don't escape apostrophes. 289 &pb.Strings{StringField: proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces")}, 290 "string_field: \"\\\"A string with ' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"\n", 291 }, 292 { 293 // Test data from the same C++ test. 294 &pb.Strings{StringField: proto.String("\350\260\267\346\255\214")}, 295 "string_field: \"\\350\\260\\267\\346\\255\\214\"\n", 296 }, 297 { 298 // Some UTF-8. 299 &pb.Strings{StringField: proto.String("\x00\x01\xff\x81")}, 300 `string_field: "\000\001\377\201"` + "\n", 301 }, 302 } 303 304 for i, tc := range testCases { 305 var buf bytes.Buffer 306 if err := proto.MarshalText(&buf, tc.in); err != nil { 307 t.Errorf("proto.MarsalText: %v", err) 308 continue 309 } 310 s := buf.String() 311 if s != tc.out { 312 t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out) 313 continue 314 } 315 316 // Check round-trip. 317 pb := new(pb.Strings) 318 if err := proto.UnmarshalText(s, pb); err != nil { 319 t.Errorf("#%d: UnmarshalText: %v", i, err) 320 continue 321 } 322 if !proto.Equal(pb, tc.in) { 323 t.Errorf("#%d: Round-trip failed:\nstart: %v\n end: %v", i, tc.in, pb) 324 } 325 } 326 } 327 328 // A limitedWriter accepts some output before it fails. 329 // This is a proxy for something like a nearly-full or imminently-failing disk, 330 // or a network connection that is about to die. 331 type limitedWriter struct { 332 b bytes.Buffer 333 limit int 334 } 335 336 var outOfSpace = errors.New("proto: insufficient space") 337 338 func (w *limitedWriter) Write(p []byte) (n int, err error) { 339 var avail = w.limit - w.b.Len() 340 if avail <= 0 { 341 return 0, outOfSpace 342 } 343 if len(p) <= avail { 344 return w.b.Write(p) 345 } 346 n, _ = w.b.Write(p[:avail]) 347 return n, outOfSpace 348 } 349 350 func TestMarshalTextFailing(t *testing.T) { 351 // Try lots of different sizes to exercise more error code-paths. 352 for lim := 0; lim < len(text); lim++ { 353 buf := new(limitedWriter) 354 buf.limit = lim 355 err := proto.MarshalText(buf, newTestMessage()) 356 // We expect a certain error, but also some partial results in the buffer. 357 if err != outOfSpace { 358 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", err, outOfSpace) 359 } 360 s := buf.b.String() 361 x := text[:buf.limit] 362 if s != x { 363 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, x) 364 } 365 } 366 } 367 368 func TestFloats(t *testing.T) { 369 tests := []struct { 370 f float64 371 want string 372 }{ 373 {0, "0"}, 374 {4.7, "4.7"}, 375 {math.Inf(1), "inf"}, 376 {math.Inf(-1), "-inf"}, 377 {math.NaN(), "nan"}, 378 } 379 for _, test := range tests { 380 msg := &pb.FloatingPoint{F: &test.f} 381 got := strings.TrimSpace(msg.String()) 382 want := `f:` + test.want 383 if got != want { 384 t.Errorf("f=%f: got %q, want %q", test.f, got, want) 385 } 386 } 387 } 388 389 func TestRepeatedNilText(t *testing.T) { 390 m := &pb.MessageList{ 391 Message: []*pb.MessageList_Message{ 392 nil, 393 { 394 Name: proto.String("Horse"), 395 }, 396 nil, 397 }, 398 } 399 want := `Message <nil> 400 Message { 401 name: "Horse" 402 } 403 Message <nil> 404 ` 405 if s := proto.MarshalTextString(m); s != want { 406 t.Errorf(" got: %s\nwant: %s", s, want) 407 } 408 }