blob: d0a0ebeea238afb9cf36bf1f153803ca8fcd3ecf [file] [log] [blame]
Brian Silverman9c614bc2016-02-15 20:20:02 -05001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31// Author: kenton@google.com (Kenton Varda)
32// Based on original Protocol Buffers design by
33// Sanjay Ghemawat, Jeff Dean, and others.
34
35#include <vector>
36
37#include <google/protobuf/io/printer.h>
38#include <google/protobuf/io/zero_copy_stream_impl.h>
Austin Schuh40c16522018-10-28 20:27:54 -070039#include <google/protobuf/descriptor.pb.h>
Brian Silverman9c614bc2016-02-15 20:20:02 -050040
41#include <google/protobuf/stubs/logging.h>
42#include <google/protobuf/stubs/common.h>
43#include <google/protobuf/testing/googletest.h>
44#include <gtest/gtest.h>
45
46namespace google {
47namespace protobuf {
48namespace io {
49namespace {
50
51// Each test repeats over several block sizes in order to test both cases
52// where particular writes cross a buffer boundary and cases where they do
53// not.
54
55TEST(Printer, EmptyPrinter) {
56 char buffer[8192];
57 const int block_size = 100;
58 ArrayOutputStream output(buffer, GOOGLE_ARRAYSIZE(buffer), block_size);
59 Printer printer(&output, '\0');
60 EXPECT_TRUE(!printer.failed());
61}
62
63TEST(Printer, BasicPrinting) {
64 char buffer[8192];
65
66 for (int block_size = 1; block_size < 512; block_size *= 2) {
67 ArrayOutputStream output(buffer, sizeof(buffer), block_size);
68
69 {
70 Printer printer(&output, '\0');
71
72 printer.Print("Hello World!");
73 printer.Print(" This is the same line.\n");
74 printer.Print("But this is a new one.\nAnd this is another one.");
75
76 EXPECT_FALSE(printer.failed());
77 }
78
79 buffer[output.ByteCount()] = '\0';
80
81 EXPECT_STREQ("Hello World! This is the same line.\n"
82 "But this is a new one.\n"
83 "And this is another one.",
84 buffer);
85 }
86}
87
88TEST(Printer, WriteRaw) {
89 char buffer[8192];
90
91 for (int block_size = 1; block_size < 512; block_size *= 2) {
92 ArrayOutputStream output(buffer, sizeof(buffer), block_size);
93
94 {
95 string string_obj = "From an object\n";
96 Printer printer(&output, '$');
97 printer.WriteRaw("Hello World!", 12);
98 printer.PrintRaw(" This is the same line.\n");
99 printer.PrintRaw("But this is a new one.\nAnd this is another one.");
100 printer.WriteRaw("\n", 1);
101 printer.PrintRaw(string_obj);
102 EXPECT_FALSE(printer.failed());
103 }
104
105 buffer[output.ByteCount()] = '\0';
106
107 EXPECT_STREQ("Hello World! This is the same line.\n"
108 "But this is a new one.\n"
109 "And this is another one."
110 "\n"
111 "From an object\n",
112 buffer);
113 }
114}
115
116TEST(Printer, VariableSubstitution) {
117 char buffer[8192];
118
119 for (int block_size = 1; block_size < 512; block_size *= 2) {
120 ArrayOutputStream output(buffer, sizeof(buffer), block_size);
121
122 {
123 Printer printer(&output, '$');
Austin Schuh40c16522018-10-28 20:27:54 -0700124 std::map<string, string> vars;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500125
126 vars["foo"] = "World";
127 vars["bar"] = "$foo$";
128 vars["abcdefg"] = "1234";
129
130 printer.Print(vars, "Hello $foo$!\nbar = $bar$\n");
131 printer.PrintRaw("RawBit\n");
132 printer.Print(vars, "$abcdefg$\nA literal dollar sign: $$");
133
134 vars["foo"] = "blah";
135 printer.Print(vars, "\nNow foo = $foo$.");
136
137 EXPECT_FALSE(printer.failed());
138 }
139
140 buffer[output.ByteCount()] = '\0';
141
142 EXPECT_STREQ("Hello World!\n"
143 "bar = $foo$\n"
144 "RawBit\n"
145 "1234\n"
146 "A literal dollar sign: $\n"
147 "Now foo = blah.",
148 buffer);
149 }
150}
151
152TEST(Printer, InlineVariableSubstitution) {
153 char buffer[8192];
154
155 ArrayOutputStream output(buffer, sizeof(buffer));
156
157 {
158 Printer printer(&output, '$');
159 printer.Print("Hello $foo$!\n", "foo", "World");
160 printer.PrintRaw("RawBit\n");
161 printer.Print("$foo$ $bar$\n", "foo", "one", "bar", "two");
162 EXPECT_FALSE(printer.failed());
163 }
164
165 buffer[output.ByteCount()] = '\0';
166
167 EXPECT_STREQ("Hello World!\n"
168 "RawBit\n"
169 "one two\n",
170 buffer);
171}
172
Austin Schuh40c16522018-10-28 20:27:54 -0700173// MockDescriptorFile defines only those members that Printer uses to write out
174// annotations.
175class MockDescriptorFile {
176 public:
177 explicit MockDescriptorFile(const string& file) : file_(file) {}
178
179 // The mock filename for this file.
180 const string& name() const { return file_; }
181
182 private:
183 string file_;
184};
185
186// MockDescriptor defines only those members that Printer uses to write out
187// annotations.
188class MockDescriptor {
189 public:
190 MockDescriptor(const string& file, const std::vector<int>& path)
191 : file_(file), path_(path) {}
192
193 // The mock file in which this descriptor was defined.
194 const MockDescriptorFile* file() const { return &file_; }
195
196 private:
197 // Allows access to GetLocationPath.
198 friend class ::google::protobuf::io::Printer;
199
200 // Copies the pre-stored path to output.
201 void GetLocationPath(std::vector<int>* output) const { *output = path_; }
202
203 MockDescriptorFile file_;
204 std::vector<int> path_;
205};
206
207TEST(Printer, AnnotateMap) {
208 char buffer[8192];
209 ArrayOutputStream output(buffer, sizeof(buffer));
210 GeneratedCodeInfo info;
211 AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
212 {
213 Printer printer(&output, '$', &info_collector);
214 std::map<string, string> vars;
215 vars["foo"] = "3";
216 vars["bar"] = "5";
217 printer.Print(vars, "012$foo$4$bar$\n");
218 std::vector<int> path_1;
219 path_1.push_back(33);
220 std::vector<int> path_2;
221 path_2.push_back(11);
222 path_2.push_back(22);
223 MockDescriptor descriptor_1("path_1", path_1);
224 MockDescriptor descriptor_2("path_2", path_2);
225 printer.Annotate("foo", "foo", &descriptor_1);
226 printer.Annotate("bar", "bar", &descriptor_2);
227 }
228 buffer[output.ByteCount()] = '\0';
229 EXPECT_STREQ("012345\n", buffer);
230 ASSERT_EQ(2, info.annotation_size());
231 const GeneratedCodeInfo::Annotation* foo = info.annotation(0).path_size() == 1
232 ? &info.annotation(0)
233 : &info.annotation(1);
234 const GeneratedCodeInfo::Annotation* bar = info.annotation(0).path_size() == 1
235 ? &info.annotation(1)
236 : &info.annotation(0);
237 ASSERT_EQ(1, foo->path_size());
238 ASSERT_EQ(2, bar->path_size());
239 EXPECT_EQ(33, foo->path(0));
240 EXPECT_EQ(11, bar->path(0));
241 EXPECT_EQ(22, bar->path(1));
242 EXPECT_EQ("path_1", foo->source_file());
243 EXPECT_EQ("path_2", bar->source_file());
244 EXPECT_EQ(3, foo->begin());
245 EXPECT_EQ(4, foo->end());
246 EXPECT_EQ(5, bar->begin());
247 EXPECT_EQ(6, bar->end());
248}
249
250TEST(Printer, AnnotateInline) {
251 char buffer[8192];
252 ArrayOutputStream output(buffer, sizeof(buffer));
253 GeneratedCodeInfo info;
254 AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
255 {
256 Printer printer(&output, '$', &info_collector);
257 printer.Print("012$foo$4$bar$\n", "foo", "3", "bar", "5");
258 std::vector<int> path_1;
259 path_1.push_back(33);
260 std::vector<int> path_2;
261 path_2.push_back(11);
262 path_2.push_back(22);
263 MockDescriptor descriptor_1("path_1", path_1);
264 MockDescriptor descriptor_2("path_2", path_2);
265 printer.Annotate("foo", "foo", &descriptor_1);
266 printer.Annotate("bar", "bar", &descriptor_2);
267 }
268 buffer[output.ByteCount()] = '\0';
269 EXPECT_STREQ("012345\n", buffer);
270 ASSERT_EQ(2, info.annotation_size());
271 const GeneratedCodeInfo::Annotation* foo = info.annotation(0).path_size() == 1
272 ? &info.annotation(0)
273 : &info.annotation(1);
274 const GeneratedCodeInfo::Annotation* bar = info.annotation(0).path_size() == 1
275 ? &info.annotation(1)
276 : &info.annotation(0);
277 ASSERT_EQ(1, foo->path_size());
278 ASSERT_EQ(2, bar->path_size());
279 EXPECT_EQ(33, foo->path(0));
280 EXPECT_EQ(11, bar->path(0));
281 EXPECT_EQ(22, bar->path(1));
282 EXPECT_EQ("path_1", foo->source_file());
283 EXPECT_EQ("path_2", bar->source_file());
284 EXPECT_EQ(3, foo->begin());
285 EXPECT_EQ(4, foo->end());
286 EXPECT_EQ(5, bar->begin());
287 EXPECT_EQ(6, bar->end());
288}
289
290TEST(Printer, AnnotateRange) {
291 char buffer[8192];
292 ArrayOutputStream output(buffer, sizeof(buffer));
293 GeneratedCodeInfo info;
294 AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
295 {
296 Printer printer(&output, '$', &info_collector);
297 printer.Print("012$foo$4$bar$\n", "foo", "3", "bar", "5");
298 std::vector<int> path;
299 path.push_back(33);
300 MockDescriptor descriptor("path", path);
301 printer.Annotate("foo", "bar", &descriptor);
302 }
303 buffer[output.ByteCount()] = '\0';
304 EXPECT_STREQ("012345\n", buffer);
305 ASSERT_EQ(1, info.annotation_size());
306 const GeneratedCodeInfo::Annotation* foobar = &info.annotation(0);
307 ASSERT_EQ(1, foobar->path_size());
308 EXPECT_EQ(33, foobar->path(0));
309 EXPECT_EQ("path", foobar->source_file());
310 EXPECT_EQ(3, foobar->begin());
311 EXPECT_EQ(6, foobar->end());
312}
313
314TEST(Printer, AnnotateEmptyRange) {
315 char buffer[8192];
316 ArrayOutputStream output(buffer, sizeof(buffer));
317 GeneratedCodeInfo info;
318 AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
319 {
320 Printer printer(&output, '$', &info_collector);
321 printer.Print("012$foo$4$baz$$bam$$bar$\n", "foo", "3", "bar", "5", "baz",
322 "", "bam", "");
323 std::vector<int> path;
324 path.push_back(33);
325 MockDescriptor descriptor("path", path);
326 printer.Annotate("baz", "bam", &descriptor);
327 }
328 buffer[output.ByteCount()] = '\0';
329 EXPECT_STREQ("012345\n", buffer);
330 ASSERT_EQ(1, info.annotation_size());
331 const GeneratedCodeInfo::Annotation* bazbam = &info.annotation(0);
332 ASSERT_EQ(1, bazbam->path_size());
333 EXPECT_EQ(33, bazbam->path(0));
334 EXPECT_EQ("path", bazbam->source_file());
335 EXPECT_EQ(5, bazbam->begin());
336 EXPECT_EQ(5, bazbam->end());
337}
338
339TEST(Printer, AnnotateDespiteUnrelatedMultipleUses) {
340 char buffer[8192];
341 ArrayOutputStream output(buffer, sizeof(buffer));
342 GeneratedCodeInfo info;
343 AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
344 {
345 Printer printer(&output, '$', &info_collector);
346 printer.Print("012$foo$4$foo$$bar$\n", "foo", "3", "bar", "5");
347 std::vector<int> path;
348 path.push_back(33);
349 MockDescriptor descriptor("path", path);
350 printer.Annotate("bar", "bar", &descriptor);
351 }
352 buffer[output.ByteCount()] = '\0';
353 EXPECT_STREQ("0123435\n", buffer);
354 ASSERT_EQ(1, info.annotation_size());
355 const GeneratedCodeInfo::Annotation* bar = &info.annotation(0);
356 ASSERT_EQ(1, bar->path_size());
357 EXPECT_EQ(33, bar->path(0));
358 EXPECT_EQ("path", bar->source_file());
359 EXPECT_EQ(6, bar->begin());
360 EXPECT_EQ(7, bar->end());
361}
362
363TEST(Printer, AnnotateIndent) {
364 char buffer[8192];
365 ArrayOutputStream output(buffer, sizeof(buffer));
366 GeneratedCodeInfo info;
367 AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
368 {
369 Printer printer(&output, '$', &info_collector);
370 printer.Print("0\n");
371 printer.Indent();
372 printer.Print("$foo$", "foo", "4");
373 std::vector<int> path;
374 path.push_back(44);
375 MockDescriptor descriptor("path", path);
376 printer.Annotate("foo", &descriptor);
377 printer.Print(",\n");
378 printer.Print("$bar$", "bar", "9");
379 path[0] = 99;
380 MockDescriptor descriptor_two("path", path);
381 printer.Annotate("bar", &descriptor_two);
382 printer.Print("\n${$$D$$}$\n", "{", "", "}", "", "D", "d");
383 path[0] = 1313;
384 MockDescriptor descriptor_three("path", path);
385 printer.Annotate("{", "}", &descriptor_three);
386 printer.Outdent();
387 printer.Print("\n");
388 }
389 buffer[output.ByteCount()] = '\0';
390 EXPECT_STREQ("0\n 4,\n 9\n d\n\n", buffer);
391 ASSERT_EQ(3, info.annotation_size());
392 const GeneratedCodeInfo::Annotation* foo = &info.annotation(0);
393 ASSERT_EQ(1, foo->path_size());
394 EXPECT_EQ(44, foo->path(0));
395 EXPECT_EQ("path", foo->source_file());
396 EXPECT_EQ(4, foo->begin());
397 EXPECT_EQ(5, foo->end());
398 const GeneratedCodeInfo::Annotation* bar = &info.annotation(1);
399 ASSERT_EQ(1, bar->path_size());
400 EXPECT_EQ(99, bar->path(0));
401 EXPECT_EQ("path", bar->source_file());
402 EXPECT_EQ(9, bar->begin());
403 EXPECT_EQ(10, bar->end());
404 const GeneratedCodeInfo::Annotation* braces = &info.annotation(2);
405 ASSERT_EQ(1, braces->path_size());
406 EXPECT_EQ(1313, braces->path(0));
407 EXPECT_EQ("path", braces->source_file());
408 EXPECT_EQ(13, braces->begin());
409 EXPECT_EQ(14, braces->end());
410}
411
412TEST(Printer, AnnotateIndentNewline) {
413 char buffer[8192];
414 ArrayOutputStream output(buffer, sizeof(buffer));
415 GeneratedCodeInfo info;
416 AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
417 {
418 Printer printer(&output, '$', &info_collector);
419 printer.Indent();
420 printer.Print("$A$$N$$B$C\n", "A", "", "N", "\nz", "B", "");
421 std::vector<int> path;
422 path.push_back(0);
423 MockDescriptor descriptor("path", path);
424 printer.Annotate("A", "B", &descriptor);
425 printer.Outdent();
426 printer.Print("\n");
427 }
428 buffer[output.ByteCount()] = '\0';
429 EXPECT_STREQ("\nz C\n\n", buffer);
430 ASSERT_EQ(1, info.annotation_size());
431 const GeneratedCodeInfo::Annotation* ab = &info.annotation(0);
432 ASSERT_EQ(1, ab->path_size());
433 EXPECT_EQ(0, ab->path(0));
434 EXPECT_EQ("path", ab->source_file());
435 EXPECT_EQ(0, ab->begin());
436 EXPECT_EQ(4, ab->end());
437}
438
439
Brian Silverman9c614bc2016-02-15 20:20:02 -0500440TEST(Printer, Indenting) {
441 char buffer[8192];
442
443 for (int block_size = 1; block_size < 512; block_size *= 2) {
444 ArrayOutputStream output(buffer, sizeof(buffer), block_size);
445
446 {
447 Printer printer(&output, '$');
Austin Schuh40c16522018-10-28 20:27:54 -0700448 std::map<string, string> vars;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500449
450 vars["newline"] = "\n";
451
452 printer.Print("This is not indented.\n");
453 printer.Indent();
454 printer.Print("This is indented\nAnd so is this\n");
455 printer.Outdent();
456 printer.Print("But this is not.");
457 printer.Indent();
458 printer.Print(" And this is still the same line.\n"
459 "But this is indented.\n");
460 printer.PrintRaw("RawBit has indent at start\n");
461 printer.PrintRaw("but not after a raw newline\n");
462 printer.Print(vars, "Note that a newline in a variable will break "
463 "indenting, as we see$newline$here.\n");
464 printer.Indent();
465 printer.Print("And this");
466 printer.Outdent();
467 printer.Outdent();
468 printer.Print(" is double-indented\nBack to normal.");
469
470 EXPECT_FALSE(printer.failed());
471 }
472
473 buffer[output.ByteCount()] = '\0';
474
475 EXPECT_STREQ(
476 "This is not indented.\n"
477 " This is indented\n"
478 " And so is this\n"
479 "But this is not. And this is still the same line.\n"
480 " But this is indented.\n"
481 " RawBit has indent at start\n"
482 "but not after a raw newline\n"
483 "Note that a newline in a variable will break indenting, as we see\n"
484 "here.\n"
485 " And this is double-indented\n"
486 "Back to normal.",
487 buffer);
488 }
489}
490
491// Death tests do not work on Windows as of yet.
492#ifdef PROTOBUF_HAS_DEATH_TEST
493TEST(Printer, Death) {
494 char buffer[8192];
495
496 ArrayOutputStream output(buffer, sizeof(buffer));
497 Printer printer(&output, '$');
498
499 EXPECT_DEBUG_DEATH(printer.Print("$nosuchvar$"), "Undefined variable");
500 EXPECT_DEBUG_DEATH(printer.Print("$unclosed"), "Unclosed variable name");
501 EXPECT_DEBUG_DEATH(printer.Outdent(), "without matching Indent");
502}
Austin Schuh40c16522018-10-28 20:27:54 -0700503
504TEST(Printer, AnnotateMultipleUsesDeath) {
505 char buffer[8192];
506 ArrayOutputStream output(buffer, sizeof(buffer));
507 GeneratedCodeInfo info;
508 AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
509 {
510 Printer printer(&output, '$', &info_collector);
511 printer.Print("012$foo$4$foo$\n", "foo", "3");
512 std::vector<int> path;
513 path.push_back(33);
514 MockDescriptor descriptor("path", path);
515 EXPECT_DEBUG_DEATH(printer.Annotate("foo", "foo", &descriptor), "multiple");
516 }
517}
518
519TEST(Printer, AnnotateNegativeLengthDeath) {
520 char buffer[8192];
521 ArrayOutputStream output(buffer, sizeof(buffer));
522 GeneratedCodeInfo info;
523 AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
524 {
525 Printer printer(&output, '$', &info_collector);
526 printer.Print("012$foo$4$bar$\n", "foo", "3", "bar", "5");
527 std::vector<int> path;
528 path.push_back(33);
529 MockDescriptor descriptor("path", path);
530 EXPECT_DEBUG_DEATH(printer.Annotate("bar", "foo", &descriptor), "negative");
531 }
532}
533
534TEST(Printer, AnnotateUndefinedDeath) {
535 char buffer[8192];
536 ArrayOutputStream output(buffer, sizeof(buffer));
537 GeneratedCodeInfo info;
538 AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
539 {
540 Printer printer(&output, '$', &info_collector);
541 printer.Print("012$foo$4$foo$\n", "foo", "3");
542 std::vector<int> path;
543 path.push_back(33);
544 MockDescriptor descriptor("path", path);
545 EXPECT_DEBUG_DEATH(printer.Annotate("bar", "bar", &descriptor),
546 "Undefined");
547 }
548}
Brian Silverman9c614bc2016-02-15 20:20:02 -0500549#endif // PROTOBUF_HAS_DEATH_TEST
550
551TEST(Printer, WriteFailurePartial) {
552 char buffer[17];
553
554 ArrayOutputStream output(buffer, sizeof(buffer));
555 Printer printer(&output, '$');
556
557 // Print 16 bytes to almost fill the buffer (should not fail).
558 printer.Print("0123456789abcdef");
559 EXPECT_FALSE(printer.failed());
560
561 // Try to print 2 chars. Only one fits.
562 printer.Print("<>");
563 EXPECT_TRUE(printer.failed());
564
565 // Anything else should fail too.
566 printer.Print(" ");
567 EXPECT_TRUE(printer.failed());
568 printer.Print("blah");
569 EXPECT_TRUE(printer.failed());
570
571 // Buffer should contain the first 17 bytes written.
572 EXPECT_EQ("0123456789abcdef<", string(buffer, sizeof(buffer)));
573}
574
575TEST(Printer, WriteFailureExact) {
576 char buffer[16];
577
578 ArrayOutputStream output(buffer, sizeof(buffer));
579 Printer printer(&output, '$');
580
581 // Print 16 bytes to fill the buffer exactly (should not fail).
582 printer.Print("0123456789abcdef");
583 EXPECT_FALSE(printer.failed());
584
585 // Try to print one more byte (should fail).
586 printer.Print(" ");
587 EXPECT_TRUE(printer.failed());
588
589 // Should not crash
590 printer.Print("blah");
591 EXPECT_TRUE(printer.failed());
592
593 // Buffer should contain the first 16 bytes written.
594 EXPECT_EQ("0123456789abcdef", string(buffer, sizeof(buffer)));
595}
596
597} // namespace
598} // namespace io
599} // namespace protobuf
600} // namespace google