github.com/matm/etcd@v0.3.1-0.20140328024009-5b4a473f1453/third_party/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 "github.com/coreos/etcd/third_party/code.google.com/p/gogoprotobuf/proto" 43 44 pb "./testdata" 45 ) 46 47 func newTestMessage() *pb.MyMessage { 48 msg := &pb.MyMessage{ 49 Count: proto.Int32(42), 50 Name: proto.String("Dave"), 51 Quote: proto.String(`"I didn't want to go."`), 52 Pet: []string{"bunny", "kitty", "horsey"}, 53 Inner: &pb.InnerMessage{ 54 Host: proto.String("footrest.syd"), 55 Port: proto.Int32(7001), 56 Connected: proto.Bool(true), 57 }, 58 Others: []*pb.OtherMessage{ 59 { 60 Key: proto.Int64(0xdeadbeef), 61 Value: []byte{1, 65, 7, 12}, 62 }, 63 { 64 Weight: proto.Float32(6.022), 65 Inner: &pb.InnerMessage{ 66 Host: proto.String("lesha.mtv"), 67 Port: proto.Int32(8002), 68 }, 69 }, 70 }, 71 Bikeshed: pb.MyMessage_BLUE.Enum(), 72 Somegroup: &pb.MyMessage_SomeGroup{ 73 GroupField: proto.Int32(8), 74 }, 75 // One normally wouldn't do this. 76 // This is an undeclared tag 13, as a varint (wire type 0) with value 4. 77 XXX_unrecognized: []byte{13<<3 | 0, 4}, 78 } 79 ext := &pb.Ext{ 80 Data: proto.String("Big gobs for big rats"), 81 } 82 if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil { 83 panic(err) 84 } 85 greetings := []string{"adg", "easy", "cow"} 86 if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil { 87 panic(err) 88 } 89 90 // Add an unknown extension. We marshal a pb.Ext, and fake the ID. 91 b, err := proto.Marshal(&pb.Ext{Data: proto.String("3G skiing")}) 92 if err != nil { 93 panic(err) 94 } 95 b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...) 96 proto.SetRawExtension(msg, 201, b) 97 98 // Extensions can be plain fields, too, so let's test that. 99 b = append(proto.EncodeVarint(202<<3|proto.WireVarint), 19) 100 proto.SetRawExtension(msg, 202, b) 101 102 return msg 103 } 104 105 const text = `count: 42 106 name: "Dave" 107 quote: "\"I didn't want to go.\"" 108 pet: "bunny" 109 pet: "kitty" 110 pet: "horsey" 111 inner: < 112 host: "footrest.syd" 113 port: 7001 114 connected: true 115 > 116 others: < 117 key: 3735928559 118 value: "\001A\007\014" 119 > 120 others: < 121 weight: 6.022 122 inner: < 123 host: "lesha.mtv" 124 port: 8002 125 > 126 > 127 bikeshed: BLUE 128 SomeGroup { 129 group_field: 8 130 } 131 /* 2 unknown bytes */ 132 13: 4 133 [testdata.Ext.more]: < 134 data: "Big gobs for big rats" 135 > 136 [testdata.greeting]: "adg" 137 [testdata.greeting]: "easy" 138 [testdata.greeting]: "cow" 139 /* 13 unknown bytes */ 140 201: "\t3G skiing" 141 /* 3 unknown bytes */ 142 202: 19 143 ` 144 145 func TestMarshalText(t *testing.T) { 146 buf := new(bytes.Buffer) 147 if err := proto.MarshalText(buf, newTestMessage()); err != nil { 148 t.Fatalf("proto.MarshalText: %v", err) 149 } 150 s := buf.String() 151 if s != text { 152 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text) 153 } 154 } 155 156 func TestMarshalTextNil(t *testing.T) { 157 want := "<nil>" 158 tests := []proto.Message{nil, (*pb.MyMessage)(nil)} 159 for i, test := range tests { 160 buf := new(bytes.Buffer) 161 if err := proto.MarshalText(buf, test); err != nil { 162 t.Fatal(err) 163 } 164 if got := buf.String(); got != want { 165 t.Errorf("%d: got %q want %q", i, got, want) 166 } 167 } 168 } 169 170 func TestMarshalTextUnknownEnum(t *testing.T) { 171 // The Color enum only specifies values 0-2. 172 m := &pb.MyMessage{Bikeshed: pb.MyMessage_Color(3).Enum()} 173 got := m.String() 174 const want = `bikeshed:3 ` 175 if got != want { 176 t.Errorf("\n got %q\nwant %q", got, want) 177 } 178 } 179 180 func BenchmarkMarshalTextBuffered(b *testing.B) { 181 buf := new(bytes.Buffer) 182 m := newTestMessage() 183 for i := 0; i < b.N; i++ { 184 buf.Reset() 185 proto.MarshalText(buf, m) 186 } 187 } 188 189 func BenchmarkMarshalTextUnbuffered(b *testing.B) { 190 w := ioutil.Discard 191 m := newTestMessage() 192 for i := 0; i < b.N; i++ { 193 proto.MarshalText(w, m) 194 } 195 } 196 197 func compact(src string) string { 198 // s/[ \n]+/ /g; s/ $//; 199 dst := make([]byte, len(src)) 200 space, comment := false, false 201 j := 0 202 for i := 0; i < len(src); i++ { 203 if strings.HasPrefix(src[i:], "/*") { 204 comment = true 205 i++ 206 continue 207 } 208 if comment && strings.HasPrefix(src[i:], "*/") { 209 comment = false 210 i++ 211 continue 212 } 213 if comment { 214 continue 215 } 216 c := src[i] 217 if c == ' ' || c == '\n' { 218 space = true 219 continue 220 } 221 if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') { 222 space = false 223 } 224 if c == '{' { 225 space = false 226 } 227 if space { 228 dst[j] = ' ' 229 j++ 230 space = false 231 } 232 dst[j] = c 233 j++ 234 } 235 if space { 236 dst[j] = ' ' 237 j++ 238 } 239 return string(dst[0:j]) 240 } 241 242 var compactText = compact(text) 243 244 func TestCompactText(t *testing.T) { 245 s := proto.CompactTextString(newTestMessage()) 246 if s != compactText { 247 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v\n===\n", s, compactText) 248 } 249 } 250 251 func TestStringEscaping(t *testing.T) { 252 testCases := []struct { 253 in *pb.Strings 254 out string 255 }{ 256 { 257 // Test data from C++ test (TextFormatTest.StringEscape). 258 // Single divergence: we don't escape apostrophes. 259 &pb.Strings{StringField: proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces")}, 260 "string_field: \"\\\"A string with ' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"\n", 261 }, 262 { 263 // Test data from the same C++ test. 264 &pb.Strings{StringField: proto.String("\350\260\267\346\255\214")}, 265 "string_field: \"\\350\\260\\267\\346\\255\\214\"\n", 266 }, 267 { 268 // Some UTF-8. 269 &pb.Strings{StringField: proto.String("\x00\x01\xff\x81")}, 270 `string_field: "\000\001\377\201"` + "\n", 271 }, 272 } 273 274 for i, tc := range testCases { 275 var buf bytes.Buffer 276 if err := proto.MarshalText(&buf, tc.in); err != nil { 277 t.Errorf("proto.MarsalText: %v", err) 278 continue 279 } 280 s := buf.String() 281 if s != tc.out { 282 t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out) 283 continue 284 } 285 286 // Check round-trip. 287 pb := new(pb.Strings) 288 if err := proto.UnmarshalText(s, pb); err != nil { 289 t.Errorf("#%d: UnmarshalText: %v", i, err) 290 continue 291 } 292 if !proto.Equal(pb, tc.in) { 293 t.Errorf("#%d: Round-trip failed:\nstart: %v\n end: %v", i, tc.in, pb) 294 } 295 } 296 } 297 298 // A limitedWriter accepts some output before it fails. 299 // This is a proxy for something like a nearly-full or imminently-failing disk, 300 // or a network connection that is about to die. 301 type limitedWriter struct { 302 b bytes.Buffer 303 limit int 304 } 305 306 var outOfSpace = errors.New("proto: insufficient space") 307 308 func (w *limitedWriter) Write(p []byte) (n int, err error) { 309 var avail = w.limit - w.b.Len() 310 if avail <= 0 { 311 return 0, outOfSpace 312 } 313 if len(p) <= avail { 314 return w.b.Write(p) 315 } 316 n, _ = w.b.Write(p[:avail]) 317 return n, outOfSpace 318 } 319 320 func TestMarshalTextFailing(t *testing.T) { 321 // Try lots of different sizes to exercise more error code-paths. 322 for lim := 0; lim < len(text); lim++ { 323 buf := new(limitedWriter) 324 buf.limit = lim 325 err := proto.MarshalText(buf, newTestMessage()) 326 // We expect a certain error, but also some partial results in the buffer. 327 if err != outOfSpace { 328 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", err, outOfSpace) 329 } 330 s := buf.b.String() 331 x := text[:buf.limit] 332 if s != x { 333 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, x) 334 } 335 } 336 } 337 338 func TestFloats(t *testing.T) { 339 tests := []struct { 340 f float64 341 want string 342 }{ 343 {0, "0"}, 344 {4.7, "4.7"}, 345 {math.Inf(1), "inf"}, 346 {math.Inf(-1), "-inf"}, 347 {math.NaN(), "nan"}, 348 } 349 for _, test := range tests { 350 msg := &pb.FloatingPoint{F: &test.f} 351 got := strings.TrimSpace(msg.String()) 352 want := `f:` + test.want 353 if got != want { 354 t.Errorf("f=%f: got %q, want %q", test.f, got, want) 355 } 356 } 357 }