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 
40 #include <google/protobuf/stubs/common.h>
41 #include <google/protobuf/testing/googletest.h>
42 #include <gtest/gtest.h>
43 
44 namespace google {
45 namespace protobuf {
46 namespace io {
47 namespace {
48 
49 // Each test repeats over several block sizes in order to test both cases
50 // where particular writes cross a buffer boundary and cases where they do
51 // not.
52 
TEST(Printer,EmptyPrinter)53 TEST(Printer, EmptyPrinter) {
54   char buffer[8192];
55   const int block_size = 100;
56   ArrayOutputStream output(buffer, GOOGLE_ARRAYSIZE(buffer), block_size);
57   Printer printer(&output, '\0');
58   EXPECT_TRUE(!printer.failed());
59 }
60 
TEST(Printer,BasicPrinting)61 TEST(Printer, BasicPrinting) {
62   char buffer[8192];
63 
64   for (int block_size = 1; block_size < 512; block_size *= 2) {
65     ArrayOutputStream output(buffer, sizeof(buffer), block_size);
66 
67     {
68       Printer printer(&output, '\0');
69 
70       printer.Print("Hello World!");
71       printer.Print("  This is the same line.\n");
72       printer.Print("But this is a new one.\nAnd this is another one.");
73 
74       EXPECT_FALSE(printer.failed());
75     }
76 
77     buffer[output.ByteCount()] = '\0';
78 
79     EXPECT_STREQ("Hello World!  This is the same line.\n"
80                  "But this is a new one.\n"
81                  "And this is another one.",
82                  buffer);
83   }
84 }
85 
TEST(Printer,WriteRaw)86 TEST(Printer, WriteRaw) {
87   char buffer[8192];
88 
89   for (int block_size = 1; block_size < 512; block_size *= 2) {
90     ArrayOutputStream output(buffer, sizeof(buffer), block_size);
91 
92     {
93       string string_obj = "From an object\n";
94       Printer printer(&output, '$');
95       printer.WriteRaw("Hello World!", 12);
96       printer.PrintRaw("  This is the same line.\n");
97       printer.PrintRaw("But this is a new one.\nAnd this is another one.");
98       printer.WriteRaw("\n", 1);
99       printer.PrintRaw(string_obj);
100       EXPECT_FALSE(printer.failed());
101     }
102 
103     buffer[output.ByteCount()] = '\0';
104 
105     EXPECT_STREQ("Hello World!  This is the same line.\n"
106                  "But this is a new one.\n"
107                  "And this is another one."
108                  "\n"
109                  "From an object\n",
110                  buffer);
111   }
112 }
113 
TEST(Printer,VariableSubstitution)114 TEST(Printer, VariableSubstitution) {
115   char buffer[8192];
116 
117   for (int block_size = 1; block_size < 512; block_size *= 2) {
118     ArrayOutputStream output(buffer, sizeof(buffer), block_size);
119 
120     {
121       Printer printer(&output, '$');
122       map<string, string> vars;
123 
124       vars["foo"] = "World";
125       vars["bar"] = "$foo$";
126       vars["abcdefg"] = "1234";
127 
128       printer.Print(vars, "Hello $foo$!\nbar = $bar$\n");
129       printer.PrintRaw("RawBit\n");
130       printer.Print(vars, "$abcdefg$\nA literal dollar sign:  $$");
131 
132       vars["foo"] = "blah";
133       printer.Print(vars, "\nNow foo = $foo$.");
134 
135       EXPECT_FALSE(printer.failed());
136     }
137 
138     buffer[output.ByteCount()] = '\0';
139 
140     EXPECT_STREQ("Hello World!\n"
141                  "bar = $foo$\n"
142                  "RawBit\n"
143                  "1234\n"
144                  "A literal dollar sign:  $\n"
145                  "Now foo = blah.",
146                  buffer);
147   }
148 }
149 
TEST(Printer,InlineVariableSubstitution)150 TEST(Printer, InlineVariableSubstitution) {
151   char buffer[8192];
152 
153   ArrayOutputStream output(buffer, sizeof(buffer));
154 
155   {
156     Printer printer(&output, '$');
157     printer.Print("Hello $foo$!\n", "foo", "World");
158     printer.PrintRaw("RawBit\n");
159     printer.Print("$foo$ $bar$\n", "foo", "one", "bar", "two");
160     EXPECT_FALSE(printer.failed());
161   }
162 
163   buffer[output.ByteCount()] = '\0';
164 
165   EXPECT_STREQ("Hello World!\n"
166                "RawBit\n"
167                "one two\n",
168                buffer);
169 }
170 
TEST(Printer,Indenting)171 TEST(Printer, Indenting) {
172   char buffer[8192];
173 
174   for (int block_size = 1; block_size < 512; block_size *= 2) {
175     ArrayOutputStream output(buffer, sizeof(buffer), block_size);
176 
177     {
178       Printer printer(&output, '$');
179       map<string, string> vars;
180 
181       vars["newline"] = "\n";
182 
183       printer.Print("This is not indented.\n");
184       printer.Indent();
185       printer.Print("This is indented\nAnd so is this\n");
186       printer.Outdent();
187       printer.Print("But this is not.");
188       printer.Indent();
189       printer.Print("  And this is still the same line.\n"
190                     "But this is indented.\n");
191       printer.PrintRaw("RawBit has indent at start\n");
192       printer.PrintRaw("but not after a raw newline\n");
193       printer.Print(vars, "Note that a newline in a variable will break "
194                     "indenting, as we see$newline$here.\n");
195       printer.Indent();
196       printer.Print("And this");
197       printer.Outdent();
198       printer.Outdent();
199       printer.Print(" is double-indented\nBack to normal.");
200 
201       EXPECT_FALSE(printer.failed());
202     }
203 
204     buffer[output.ByteCount()] = '\0';
205 
206     EXPECT_STREQ(
207       "This is not indented.\n"
208       "  This is indented\n"
209       "  And so is this\n"
210       "But this is not.  And this is still the same line.\n"
211       "  But this is indented.\n"
212       "  RawBit has indent at start\n"
213       "but not after a raw newline\n"
214       "Note that a newline in a variable will break indenting, as we see\n"
215       "here.\n"
216       "    And this is double-indented\n"
217       "Back to normal.",
218       buffer);
219   }
220 }
221 
222 // Death tests do not work on Windows as of yet.
223 #ifdef PROTOBUF_HAS_DEATH_TEST
TEST(Printer,Death)224 TEST(Printer, Death) {
225   char buffer[8192];
226 
227   ArrayOutputStream output(buffer, sizeof(buffer));
228   Printer printer(&output, '$');
229 
230   EXPECT_DEBUG_DEATH(printer.Print("$nosuchvar$"), "Undefined variable");
231   EXPECT_DEBUG_DEATH(printer.Print("$unclosed"), "Unclosed variable name");
232   EXPECT_DEBUG_DEATH(printer.Outdent(), "without matching Indent");
233 }
234 #endif  // PROTOBUF_HAS_DEATH_TEST
235 
TEST(Printer,WriteFailurePartial)236 TEST(Printer, WriteFailurePartial) {
237   char buffer[17];
238 
239   ArrayOutputStream output(buffer, sizeof(buffer));
240   Printer printer(&output, '$');
241 
242   // Print 16 bytes to almost fill the buffer (should not fail).
243   printer.Print("0123456789abcdef");
244   EXPECT_FALSE(printer.failed());
245 
246   // Try to print 2 chars. Only one fits.
247   printer.Print("<>");
248   EXPECT_TRUE(printer.failed());
249 
250   // Anything else should fail too.
251   printer.Print(" ");
252   EXPECT_TRUE(printer.failed());
253   printer.Print("blah");
254   EXPECT_TRUE(printer.failed());
255 
256   // Buffer should contain the first 17 bytes written.
257   EXPECT_EQ("0123456789abcdef<", string(buffer, sizeof(buffer)));
258 }
259 
TEST(Printer,WriteFailureExact)260 TEST(Printer, WriteFailureExact) {
261   char buffer[16];
262 
263   ArrayOutputStream output(buffer, sizeof(buffer));
264   Printer printer(&output, '$');
265 
266   // Print 16 bytes to fill the buffer exactly (should not fail).
267   printer.Print("0123456789abcdef");
268   EXPECT_FALSE(printer.failed());
269 
270   // Try to print one more byte (should fail).
271   printer.Print(" ");
272   EXPECT_TRUE(printer.failed());
273 
274   // Should not crash
275   printer.Print("blah");
276   EXPECT_TRUE(printer.failed());
277 
278   // Buffer should contain the first 16 bytes written.
279   EXPECT_EQ("0123456789abcdef", string(buffer, sizeof(buffer)));
280 }
281 
282 }  // namespace
283 }  // namespace io
284 }  // namespace protobuf
285 }  // namespace google
286