1 // 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>
39 #include <google/protobuf/descriptor.pb.h>
40
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
46 namespace google {
47 namespace protobuf {
48 namespace io {
49 namespace {
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
TEST(Printer,EmptyPrinter)55 TEST(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
TEST(Printer,BasicPrinting)63 TEST(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
TEST(Printer,WriteRaw)88 TEST(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
TEST(Printer,VariableSubstitution)116 TEST(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, '$');
124 map<string, string> vars;
125
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
TEST(Printer,InlineVariableSubstitution)152 TEST(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
173 // MockDescriptorFile defines only those members that Printer uses to write out
174 // annotations.
175 class MockDescriptorFile {
176 public:
MockDescriptorFile(const string & file)177 explicit MockDescriptorFile(const string& file) : file_(file) {}
178
179 // The mock filename for this file.
name() const180 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.
188 class MockDescriptor {
189 public:
MockDescriptor(const string & file,const vector<int> & path)190 MockDescriptor(const string& file, const vector<int>& path)
191 : file_(file), path_(path) {}
192
193 // The mock file in which this descriptor was defined.
file() const194 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.
GetLocationPath(std::vector<int> * output) const201 void GetLocationPath(std::vector<int>* output) const { *output = path_; }
202
203 MockDescriptorFile file_;
204 vector<int> path_;
205 };
206
TEST(Printer,AnnotateMap)207 TEST(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 map<string, string> vars;
215 vars["foo"] = "3";
216 vars["bar"] = "5";
217 printer.Print(vars, "012$foo$4$bar$\n");
218 vector<int> path_1;
219 path_1.push_back(33);
220 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
TEST(Printer,AnnotateInline)250 TEST(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 vector<int> path_1;
259 path_1.push_back(33);
260 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
TEST(Printer,AnnotateRange)290 TEST(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 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
TEST(Printer,AnnotateEmptyRange)314 TEST(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 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
TEST(Printer,AnnotateDespiteUnrelatedMultipleUses)339 TEST(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 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
TEST(Printer,Indenting)363 TEST(Printer, Indenting) {
364 char buffer[8192];
365
366 for (int block_size = 1; block_size < 512; block_size *= 2) {
367 ArrayOutputStream output(buffer, sizeof(buffer), block_size);
368
369 {
370 Printer printer(&output, '$');
371 map<string, string> vars;
372
373 vars["newline"] = "\n";
374
375 printer.Print("This is not indented.\n");
376 printer.Indent();
377 printer.Print("This is indented\nAnd so is this\n");
378 printer.Outdent();
379 printer.Print("But this is not.");
380 printer.Indent();
381 printer.Print(" And this is still the same line.\n"
382 "But this is indented.\n");
383 printer.PrintRaw("RawBit has indent at start\n");
384 printer.PrintRaw("but not after a raw newline\n");
385 printer.Print(vars, "Note that a newline in a variable will break "
386 "indenting, as we see$newline$here.\n");
387 printer.Indent();
388 printer.Print("And this");
389 printer.Outdent();
390 printer.Outdent();
391 printer.Print(" is double-indented\nBack to normal.");
392
393 EXPECT_FALSE(printer.failed());
394 }
395
396 buffer[output.ByteCount()] = '\0';
397
398 EXPECT_STREQ(
399 "This is not indented.\n"
400 " This is indented\n"
401 " And so is this\n"
402 "But this is not. And this is still the same line.\n"
403 " But this is indented.\n"
404 " RawBit has indent at start\n"
405 "but not after a raw newline\n"
406 "Note that a newline in a variable will break indenting, as we see\n"
407 "here.\n"
408 " And this is double-indented\n"
409 "Back to normal.",
410 buffer);
411 }
412 }
413
414 // Death tests do not work on Windows as of yet.
415 #ifdef PROTOBUF_HAS_DEATH_TEST
TEST(Printer,Death)416 TEST(Printer, Death) {
417 char buffer[8192];
418
419 ArrayOutputStream output(buffer, sizeof(buffer));
420 Printer printer(&output, '$');
421
422 EXPECT_DEBUG_DEATH(printer.Print("$nosuchvar$"), "Undefined variable");
423 EXPECT_DEBUG_DEATH(printer.Print("$unclosed"), "Unclosed variable name");
424 EXPECT_DEBUG_DEATH(printer.Outdent(), "without matching Indent");
425 }
426
TEST(Printer,AnnotateMultipleUsesDeath)427 TEST(Printer, AnnotateMultipleUsesDeath) {
428 char buffer[8192];
429 ArrayOutputStream output(buffer, sizeof(buffer));
430 GeneratedCodeInfo info;
431 AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
432 {
433 Printer printer(&output, '$', &info_collector);
434 printer.Print("012$foo$4$foo$\n", "foo", "3");
435 vector<int> path;
436 path.push_back(33);
437 MockDescriptor descriptor("path", path);
438 EXPECT_DEBUG_DEATH(printer.Annotate("foo", "foo", &descriptor), "multiple");
439 }
440 }
441
TEST(Printer,AnnotateNegativeLengthDeath)442 TEST(Printer, AnnotateNegativeLengthDeath) {
443 char buffer[8192];
444 ArrayOutputStream output(buffer, sizeof(buffer));
445 GeneratedCodeInfo info;
446 AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
447 {
448 Printer printer(&output, '$', &info_collector);
449 printer.Print("012$foo$4$bar$\n", "foo", "3", "bar", "5");
450 vector<int> path;
451 path.push_back(33);
452 MockDescriptor descriptor("path", path);
453 EXPECT_DEBUG_DEATH(printer.Annotate("bar", "foo", &descriptor), "negative");
454 }
455 }
456
TEST(Printer,AnnotateUndefinedDeath)457 TEST(Printer, AnnotateUndefinedDeath) {
458 char buffer[8192];
459 ArrayOutputStream output(buffer, sizeof(buffer));
460 GeneratedCodeInfo info;
461 AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
462 {
463 Printer printer(&output, '$', &info_collector);
464 printer.Print("012$foo$4$foo$\n", "foo", "3");
465 vector<int> path;
466 path.push_back(33);
467 MockDescriptor descriptor("path", path);
468 EXPECT_DEBUG_DEATH(printer.Annotate("bar", "bar", &descriptor),
469 "Undefined");
470 }
471 }
472 #endif // PROTOBUF_HAS_DEATH_TEST
473
TEST(Printer,WriteFailurePartial)474 TEST(Printer, WriteFailurePartial) {
475 char buffer[17];
476
477 ArrayOutputStream output(buffer, sizeof(buffer));
478 Printer printer(&output, '$');
479
480 // Print 16 bytes to almost fill the buffer (should not fail).
481 printer.Print("0123456789abcdef");
482 EXPECT_FALSE(printer.failed());
483
484 // Try to print 2 chars. Only one fits.
485 printer.Print("<>");
486 EXPECT_TRUE(printer.failed());
487
488 // Anything else should fail too.
489 printer.Print(" ");
490 EXPECT_TRUE(printer.failed());
491 printer.Print("blah");
492 EXPECT_TRUE(printer.failed());
493
494 // Buffer should contain the first 17 bytes written.
495 EXPECT_EQ("0123456789abcdef<", string(buffer, sizeof(buffer)));
496 }
497
TEST(Printer,WriteFailureExact)498 TEST(Printer, WriteFailureExact) {
499 char buffer[16];
500
501 ArrayOutputStream output(buffer, sizeof(buffer));
502 Printer printer(&output, '$');
503
504 // Print 16 bytes to fill the buffer exactly (should not fail).
505 printer.Print("0123456789abcdef");
506 EXPECT_FALSE(printer.failed());
507
508 // Try to print one more byte (should fail).
509 printer.Print(" ");
510 EXPECT_TRUE(printer.failed());
511
512 // Should not crash
513 printer.Print("blah");
514 EXPECT_TRUE(printer.failed());
515
516 // Buffer should contain the first 16 bytes written.
517 EXPECT_EQ("0123456789abcdef", string(buffer, sizeof(buffer)));
518 }
519
520 } // namespace
521 } // namespace io
522 } // namespace protobuf
523 } // namespace google
524