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 // Testing strategy:  For each type of I/O (array, string, file, etc.) we
36 // create an output stream and write some data to it, then create a
37 // corresponding input stream to read the same data back and expect it to
38 // match.  When the data is written, it is written in several small chunks
39 // of varying sizes, with a BackUp() after each chunk.  It is read back
40 // similarly, but with chunks separated at different points.  The whole
41 // process is run with a variety of block sizes for both the input and
42 // the output.
43 //
44 // TODO(kenton):  Rewrite this test to bring it up to the standards of all
45 //   the other proto2 tests.  May want to wait for gTest to implement
46 //   "parametized tests" so that one set of tests can be used on all the
47 //   implementations.
48 
49 
50 #ifndef _MSC_VER
51 #include <unistd.h>
52 #endif
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <stdlib.h>
56 #include <sys/stat.h>
57 #include <sys/types.h>
58 #include <memory>
59 #include <sstream>
60 
61 #include <google/protobuf/testing/file.h>
62 #include <google/protobuf/test_util2.h>
63 #include <google/protobuf/io/io_win32.h>
64 #include <google/protobuf/io/coded_stream.h>
65 #include <google/protobuf/io/zero_copy_stream_impl.h>
66 
67 #if HAVE_ZLIB
68 #include <google/protobuf/io/gzip_stream.h>
69 #endif
70 
71 #include <google/protobuf/stubs/common.h>
72 #include <google/protobuf/stubs/logging.h>
73 #include <google/protobuf/testing/file.h>
74 #include <google/protobuf/testing/googletest.h>
75 #include <gtest/gtest.h>
76 
77 namespace google {
78 namespace protobuf {
79 namespace io {
80 namespace {
81 
82 #ifdef _WIN32
83 #define pipe(fds) _pipe(fds, 4096, O_BINARY)
84 // DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
85 // them like we do below.
86 using google::protobuf::io::win32::access;
87 using google::protobuf::io::win32::close;
88 using google::protobuf::io::win32::mkdir;
89 using google::protobuf::io::win32::open;
90 #endif
91 
92 #ifndef O_BINARY
93 #ifdef _O_BINARY
94 #define O_BINARY _O_BINARY
95 #else
96 #define O_BINARY 0  // If this isn't defined, the platform doesn't need it.
97 #endif
98 #endif
99 
100 class IoTest : public testing::Test {
101  protected:
102   // Test helpers.
103 
104   // Helper to write an array of data to an output stream.
105   bool WriteToOutput(ZeroCopyOutputStream* output, const void* data, int size);
106   // Helper to read a fixed-length array of data from an input stream.
107   int ReadFromInput(ZeroCopyInputStream* input, void* data, int size);
108   // Write a string to the output stream.
109   void WriteString(ZeroCopyOutputStream* output, const std::string& str);
110   // Read a number of bytes equal to the size of the given string and checks
111   // that it matches the string.
112   void ReadString(ZeroCopyInputStream* input, const std::string& str);
113   // Writes some text to the output stream in a particular order.  Returns
114   // the number of bytes written, incase the caller needs that to set up an
115   // input stream.
116   int WriteStuff(ZeroCopyOutputStream* output);
117   // Reads text from an input stream and expects it to match what
118   // WriteStuff() writes.
119   void ReadStuff(ZeroCopyInputStream* input);
120 
121   // Similar to WriteStuff, but performs more sophisticated testing.
122   int WriteStuffLarge(ZeroCopyOutputStream* output);
123   // Reads and tests a stream that should have been written to
124   // via WriteStuffLarge().
125   void ReadStuffLarge(ZeroCopyInputStream* input);
126 
127 #if HAVE_ZLIB
128   std::string Compress(const std::string& data,
129                        const GzipOutputStream::Options& options);
130   std::string Uncompress(const std::string& data);
131 #endif
132 
133   static const int kBlockSizes[];
134   static const int kBlockSizeCount;
135 };
136 
137 const int IoTest::kBlockSizes[] = {-1, 1, 2, 5, 7, 10, 23, 64};
138 const int IoTest::kBlockSizeCount = GOOGLE_ARRAYSIZE(IoTest::kBlockSizes);
139 
WriteToOutput(ZeroCopyOutputStream * output,const void * data,int size)140 bool IoTest::WriteToOutput(ZeroCopyOutputStream* output, const void* data,
141                            int size) {
142   const uint8* in = reinterpret_cast<const uint8*>(data);
143   int in_size = size;
144 
145   void* out;
146   int out_size;
147 
148   while (true) {
149     if (!output->Next(&out, &out_size)) {
150       return false;
151     }
152     EXPECT_GT(out_size, 0);
153 
154     if (in_size <= out_size) {
155       memcpy(out, in, in_size);
156       output->BackUp(out_size - in_size);
157       return true;
158     }
159 
160     memcpy(out, in, out_size);
161     in += out_size;
162     in_size -= out_size;
163   }
164 }
165 
166 #define MAX_REPEATED_ZEROS 100
167 
ReadFromInput(ZeroCopyInputStream * input,void * data,int size)168 int IoTest::ReadFromInput(ZeroCopyInputStream* input, void* data, int size) {
169   uint8* out = reinterpret_cast<uint8*>(data);
170   int out_size = size;
171 
172   const void* in;
173   int in_size = 0;
174 
175   int repeated_zeros = 0;
176 
177   while (true) {
178     if (!input->Next(&in, &in_size)) {
179       return size - out_size;
180     }
181     EXPECT_GT(in_size, -1);
182     if (in_size == 0) {
183       repeated_zeros++;
184     } else {
185       repeated_zeros = 0;
186     }
187     EXPECT_LT(repeated_zeros, MAX_REPEATED_ZEROS);
188 
189     if (out_size <= in_size) {
190       memcpy(out, in, out_size);
191       if (in_size > out_size) {
192         input->BackUp(in_size - out_size);
193       }
194       return size;  // Copied all of it.
195     }
196 
197     memcpy(out, in, in_size);
198     out += in_size;
199     out_size -= in_size;
200   }
201 }
202 
WriteString(ZeroCopyOutputStream * output,const std::string & str)203 void IoTest::WriteString(ZeroCopyOutputStream* output, const std::string& str) {
204   EXPECT_TRUE(WriteToOutput(output, str.c_str(), str.size()));
205 }
206 
ReadString(ZeroCopyInputStream * input,const std::string & str)207 void IoTest::ReadString(ZeroCopyInputStream* input, const std::string& str) {
208   std::unique_ptr<char[]> buffer(new char[str.size() + 1]);
209   buffer[str.size()] = '\0';
210   EXPECT_EQ(ReadFromInput(input, buffer.get(), str.size()), str.size());
211   EXPECT_STREQ(str.c_str(), buffer.get());
212 }
213 
WriteStuff(ZeroCopyOutputStream * output)214 int IoTest::WriteStuff(ZeroCopyOutputStream* output) {
215   WriteString(output, "Hello world!\n");
216   WriteString(output, "Some te");
217   WriteString(output, "xt.  Blah blah.");
218   WriteString(output, "abcdefg");
219   WriteString(output, "01234567890123456789");
220   WriteString(output, "foobar");
221 
222   EXPECT_EQ(output->ByteCount(), 68);
223 
224   int result = output->ByteCount();
225   return result;
226 }
227 
228 // Reads text from an input stream and expects it to match what WriteStuff()
229 // writes.
ReadStuff(ZeroCopyInputStream * input)230 void IoTest::ReadStuff(ZeroCopyInputStream* input) {
231   ReadString(input, "Hello world!\n");
232   ReadString(input, "Some text.  ");
233   ReadString(input, "Blah ");
234   ReadString(input, "blah.");
235   ReadString(input, "abcdefg");
236   EXPECT_TRUE(input->Skip(20));
237   ReadString(input, "foo");
238   ReadString(input, "bar");
239 
240   EXPECT_EQ(input->ByteCount(), 68);
241 
242   uint8 byte;
243   EXPECT_EQ(ReadFromInput(input, &byte, 1), 0);
244 }
245 
WriteStuffLarge(ZeroCopyOutputStream * output)246 int IoTest::WriteStuffLarge(ZeroCopyOutputStream* output) {
247   WriteString(output, "Hello world!\n");
248   WriteString(output, "Some te");
249   WriteString(output, "xt.  Blah blah.");
250   WriteString(output, std::string(100000, 'x'));  // A very long string
251   WriteString(output, std::string(100000, 'y'));  // A very long string
252   WriteString(output, "01234567890123456789");
253 
254   EXPECT_EQ(output->ByteCount(), 200055);
255 
256   int result = output->ByteCount();
257   return result;
258 }
259 
260 // Reads text from an input stream and expects it to match what WriteStuff()
261 // writes.
ReadStuffLarge(ZeroCopyInputStream * input)262 void IoTest::ReadStuffLarge(ZeroCopyInputStream* input) {
263   ReadString(input, "Hello world!\nSome text.  ");
264   EXPECT_TRUE(input->Skip(5));
265   ReadString(input, "blah.");
266   EXPECT_TRUE(input->Skip(100000 - 10));
267   ReadString(input, std::string(10, 'x') + std::string(100000 - 20000, 'y'));
268   EXPECT_TRUE(input->Skip(20000 - 10));
269   ReadString(input, "yyyyyyyyyy01234567890123456789");
270 
271   EXPECT_EQ(input->ByteCount(), 200055);
272 
273   uint8 byte;
274   EXPECT_EQ(ReadFromInput(input, &byte, 1), 0);
275 }
276 
277 // ===================================================================
278 
TEST_F(IoTest,ArrayIo)279 TEST_F(IoTest, ArrayIo) {
280   const int kBufferSize = 256;
281   uint8 buffer[kBufferSize];
282 
283   for (int i = 0; i < kBlockSizeCount; i++) {
284     for (int j = 0; j < kBlockSizeCount; j++) {
285       int size;
286       {
287         ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
288         size = WriteStuff(&output);
289       }
290       {
291         ArrayInputStream input(buffer, size, kBlockSizes[j]);
292         ReadStuff(&input);
293       }
294     }
295   }
296 }
297 
TEST_F(IoTest,TwoSessionWrite)298 TEST_F(IoTest, TwoSessionWrite) {
299   // Test that two concatenated write sessions read correctly
300 
301   static const char* strA = "0123456789";
302   static const char* strB = "WhirledPeas";
303   const int kBufferSize = 2 * 1024;
304   uint8* buffer = new uint8[kBufferSize];
305   char* temp_buffer = new char[40];
306 
307   for (int i = 0; i < kBlockSizeCount; i++) {
308     for (int j = 0; j < kBlockSizeCount; j++) {
309       ArrayOutputStream* output =
310           new ArrayOutputStream(buffer, kBufferSize, kBlockSizes[i]);
311       CodedOutputStream* coded_output = new CodedOutputStream(output);
312       coded_output->WriteVarint32(strlen(strA));
313       coded_output->WriteRaw(strA, strlen(strA));
314       delete coded_output;  // flush
315       int64 pos = output->ByteCount();
316       delete output;
317       output = new ArrayOutputStream(buffer + pos, kBufferSize - pos,
318                                      kBlockSizes[i]);
319       coded_output = new CodedOutputStream(output);
320       coded_output->WriteVarint32(strlen(strB));
321       coded_output->WriteRaw(strB, strlen(strB));
322       delete coded_output;  // flush
323       int64 size = pos + output->ByteCount();
324       delete output;
325 
326       ArrayInputStream* input =
327           new ArrayInputStream(buffer, size, kBlockSizes[j]);
328       CodedInputStream* coded_input = new CodedInputStream(input);
329       uint32 insize;
330       EXPECT_TRUE(coded_input->ReadVarint32(&insize));
331       EXPECT_EQ(strlen(strA), insize);
332       EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize));
333       EXPECT_EQ(0, memcmp(temp_buffer, strA, insize));
334 
335       EXPECT_TRUE(coded_input->ReadVarint32(&insize));
336       EXPECT_EQ(strlen(strB), insize);
337       EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize));
338       EXPECT_EQ(0, memcmp(temp_buffer, strB, insize));
339 
340       delete coded_input;
341       delete input;
342     }
343   }
344 
345   delete[] temp_buffer;
346   delete[] buffer;
347 }
348 
349 #if HAVE_ZLIB
TEST_F(IoTest,GzipIo)350 TEST_F(IoTest, GzipIo) {
351   const int kBufferSize = 2 * 1024;
352   uint8* buffer = new uint8[kBufferSize];
353   for (int i = 0; i < kBlockSizeCount; i++) {
354     for (int j = 0; j < kBlockSizeCount; j++) {
355       for (int z = 0; z < kBlockSizeCount; z++) {
356         int gzip_buffer_size = kBlockSizes[z];
357         int size;
358         {
359           ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
360           GzipOutputStream::Options options;
361           options.format = GzipOutputStream::GZIP;
362           if (gzip_buffer_size != -1) {
363             options.buffer_size = gzip_buffer_size;
364           }
365           GzipOutputStream gzout(&output, options);
366           WriteStuff(&gzout);
367           gzout.Close();
368           size = output.ByteCount();
369         }
370         {
371           ArrayInputStream input(buffer, size, kBlockSizes[j]);
372           GzipInputStream gzin(&input, GzipInputStream::GZIP, gzip_buffer_size);
373           ReadStuff(&gzin);
374         }
375       }
376     }
377   }
378   delete[] buffer;
379 }
380 
TEST_F(IoTest,GzipIoWithFlush)381 TEST_F(IoTest, GzipIoWithFlush) {
382   const int kBufferSize = 2 * 1024;
383   uint8* buffer = new uint8[kBufferSize];
384   // We start with i = 4 as we want a block size > 6. With block size <= 6
385   // Flush() fills up the entire 2K buffer with flush markers and the test
386   // fails. See documentation for Flush() for more detail.
387   for (int i = 4; i < kBlockSizeCount; i++) {
388     for (int j = 0; j < kBlockSizeCount; j++) {
389       for (int z = 0; z < kBlockSizeCount; z++) {
390         int gzip_buffer_size = kBlockSizes[z];
391         int size;
392         {
393           ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
394           GzipOutputStream::Options options;
395           options.format = GzipOutputStream::GZIP;
396           if (gzip_buffer_size != -1) {
397             options.buffer_size = gzip_buffer_size;
398           }
399           GzipOutputStream gzout(&output, options);
400           WriteStuff(&gzout);
401           EXPECT_TRUE(gzout.Flush());
402           gzout.Close();
403           size = output.ByteCount();
404         }
405         {
406           ArrayInputStream input(buffer, size, kBlockSizes[j]);
407           GzipInputStream gzin(&input, GzipInputStream::GZIP, gzip_buffer_size);
408           ReadStuff(&gzin);
409         }
410       }
411     }
412   }
413   delete[] buffer;
414 }
415 
TEST_F(IoTest,GzipIoContiguousFlushes)416 TEST_F(IoTest, GzipIoContiguousFlushes) {
417   const int kBufferSize = 2 * 1024;
418   uint8* buffer = new uint8[kBufferSize];
419 
420   int block_size = kBlockSizes[4];
421   int gzip_buffer_size = block_size;
422   int size;
423 
424   ArrayOutputStream output(buffer, kBufferSize, block_size);
425   GzipOutputStream::Options options;
426   options.format = GzipOutputStream::GZIP;
427   if (gzip_buffer_size != -1) {
428     options.buffer_size = gzip_buffer_size;
429   }
430   GzipOutputStream gzout(&output, options);
431   WriteStuff(&gzout);
432   EXPECT_TRUE(gzout.Flush());
433   EXPECT_TRUE(gzout.Flush());
434   gzout.Close();
435   size = output.ByteCount();
436 
437   ArrayInputStream input(buffer, size, block_size);
438   GzipInputStream gzin(&input, GzipInputStream::GZIP, gzip_buffer_size);
439   ReadStuff(&gzin);
440 
441   delete[] buffer;
442 }
443 
TEST_F(IoTest,GzipIoReadAfterFlush)444 TEST_F(IoTest, GzipIoReadAfterFlush) {
445   const int kBufferSize = 2 * 1024;
446   uint8* buffer = new uint8[kBufferSize];
447 
448   int block_size = kBlockSizes[4];
449   int gzip_buffer_size = block_size;
450   int size;
451   ArrayOutputStream output(buffer, kBufferSize, block_size);
452   GzipOutputStream::Options options;
453   options.format = GzipOutputStream::GZIP;
454   if (gzip_buffer_size != -1) {
455     options.buffer_size = gzip_buffer_size;
456   }
457 
458   GzipOutputStream gzout(&output, options);
459   WriteStuff(&gzout);
460   EXPECT_TRUE(gzout.Flush());
461   size = output.ByteCount();
462 
463   ArrayInputStream input(buffer, size, block_size);
464   GzipInputStream gzin(&input, GzipInputStream::GZIP, gzip_buffer_size);
465   ReadStuff(&gzin);
466 
467   gzout.Close();
468 
469   delete[] buffer;
470 }
471 
TEST_F(IoTest,ZlibIo)472 TEST_F(IoTest, ZlibIo) {
473   const int kBufferSize = 2 * 1024;
474   uint8* buffer = new uint8[kBufferSize];
475   for (int i = 0; i < kBlockSizeCount; i++) {
476     for (int j = 0; j < kBlockSizeCount; j++) {
477       for (int z = 0; z < kBlockSizeCount; z++) {
478         int gzip_buffer_size = kBlockSizes[z];
479         int size;
480         {
481           ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
482           GzipOutputStream::Options options;
483           options.format = GzipOutputStream::ZLIB;
484           if (gzip_buffer_size != -1) {
485             options.buffer_size = gzip_buffer_size;
486           }
487           GzipOutputStream gzout(&output, options);
488           WriteStuff(&gzout);
489           gzout.Close();
490           size = output.ByteCount();
491         }
492         {
493           ArrayInputStream input(buffer, size, kBlockSizes[j]);
494           GzipInputStream gzin(&input, GzipInputStream::ZLIB, gzip_buffer_size);
495           ReadStuff(&gzin);
496         }
497       }
498     }
499   }
500   delete[] buffer;
501 }
502 
TEST_F(IoTest,ZlibIoInputAutodetect)503 TEST_F(IoTest, ZlibIoInputAutodetect) {
504   const int kBufferSize = 2 * 1024;
505   uint8* buffer = new uint8[kBufferSize];
506   int size;
507   {
508     ArrayOutputStream output(buffer, kBufferSize);
509     GzipOutputStream::Options options;
510     options.format = GzipOutputStream::ZLIB;
511     GzipOutputStream gzout(&output, options);
512     WriteStuff(&gzout);
513     gzout.Close();
514     size = output.ByteCount();
515   }
516   {
517     ArrayInputStream input(buffer, size);
518     GzipInputStream gzin(&input, GzipInputStream::AUTO);
519     ReadStuff(&gzin);
520   }
521   {
522     ArrayOutputStream output(buffer, kBufferSize);
523     GzipOutputStream::Options options;
524     options.format = GzipOutputStream::GZIP;
525     GzipOutputStream gzout(&output, options);
526     WriteStuff(&gzout);
527     gzout.Close();
528     size = output.ByteCount();
529   }
530   {
531     ArrayInputStream input(buffer, size);
532     GzipInputStream gzin(&input, GzipInputStream::AUTO);
533     ReadStuff(&gzin);
534   }
535   delete[] buffer;
536 }
537 
Compress(const std::string & data,const GzipOutputStream::Options & options)538 std::string IoTest::Compress(const std::string& data,
539                              const GzipOutputStream::Options& options) {
540   std::string result;
541   {
542     StringOutputStream output(&result);
543     GzipOutputStream gzout(&output, options);
544     WriteToOutput(&gzout, data.data(), data.size());
545   }
546   return result;
547 }
548 
Uncompress(const std::string & data)549 std::string IoTest::Uncompress(const std::string& data) {
550   std::string result;
551   {
552     ArrayInputStream input(data.data(), data.size());
553     GzipInputStream gzin(&input);
554     const void* buffer;
555     int size;
556     while (gzin.Next(&buffer, &size)) {
557       result.append(reinterpret_cast<const char*>(buffer), size);
558     }
559   }
560   return result;
561 }
562 
TEST_F(IoTest,CompressionOptions)563 TEST_F(IoTest, CompressionOptions) {
564   // Some ad-hoc testing of compression options.
565 
566   std::string golden_filename =
567       TestUtil::GetTestDataPath("net/proto2/internal/testdata/golden_message");
568   std::string golden;
569   GOOGLE_CHECK_OK(File::GetContents(golden_filename, &golden, true));
570 
571   GzipOutputStream::Options options;
572   std::string gzip_compressed = Compress(golden, options);
573 
574   options.compression_level = 0;
575   std::string not_compressed = Compress(golden, options);
576 
577   // Try zlib compression for fun.
578   options = GzipOutputStream::Options();
579   options.format = GzipOutputStream::ZLIB;
580   std::string zlib_compressed = Compress(golden, options);
581 
582   // Uncompressed should be bigger than the original since it should have some
583   // sort of header.
584   EXPECT_GT(not_compressed.size(), golden.size());
585 
586   // Higher compression levels should result in smaller sizes.
587   EXPECT_LT(zlib_compressed.size(), not_compressed.size());
588 
589   // ZLIB format should differ from GZIP format.
590   EXPECT_TRUE(zlib_compressed != gzip_compressed);
591 
592   // Everything should decompress correctly.
593   EXPECT_TRUE(Uncompress(not_compressed) == golden);
594   EXPECT_TRUE(Uncompress(gzip_compressed) == golden);
595   EXPECT_TRUE(Uncompress(zlib_compressed) == golden);
596 }
597 
TEST_F(IoTest,TwoSessionWriteGzip)598 TEST_F(IoTest, TwoSessionWriteGzip) {
599   // Test that two concatenated gzip streams can be read correctly
600 
601   static const char* strA = "0123456789";
602   static const char* strB = "QuickBrownFox";
603   const int kBufferSize = 2 * 1024;
604   uint8* buffer = new uint8[kBufferSize];
605   char* temp_buffer = new char[40];
606 
607   for (int i = 0; i < kBlockSizeCount; i++) {
608     for (int j = 0; j < kBlockSizeCount; j++) {
609       ArrayOutputStream* output =
610           new ArrayOutputStream(buffer, kBufferSize, kBlockSizes[i]);
611       GzipOutputStream* gzout = new GzipOutputStream(output);
612       CodedOutputStream* coded_output = new CodedOutputStream(gzout);
613       int32 outlen = strlen(strA) + 1;
614       coded_output->WriteVarint32(outlen);
615       coded_output->WriteRaw(strA, outlen);
616       delete coded_output;  // flush
617       delete gzout;         // flush
618       int64 pos = output->ByteCount();
619       delete output;
620       output = new ArrayOutputStream(buffer + pos, kBufferSize - pos,
621                                      kBlockSizes[i]);
622       gzout = new GzipOutputStream(output);
623       coded_output = new CodedOutputStream(gzout);
624       outlen = strlen(strB) + 1;
625       coded_output->WriteVarint32(outlen);
626       coded_output->WriteRaw(strB, outlen);
627       delete coded_output;  // flush
628       delete gzout;         // flush
629       int64 size = pos + output->ByteCount();
630       delete output;
631 
632       ArrayInputStream* input =
633           new ArrayInputStream(buffer, size, kBlockSizes[j]);
634       GzipInputStream* gzin = new GzipInputStream(input);
635       CodedInputStream* coded_input = new CodedInputStream(gzin);
636       uint32 insize;
637       EXPECT_TRUE(coded_input->ReadVarint32(&insize));
638       EXPECT_EQ(strlen(strA) + 1, insize);
639       EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize));
640       EXPECT_EQ(0, memcmp(temp_buffer, strA, insize))
641           << "strA=" << strA << " in=" << temp_buffer;
642 
643       EXPECT_TRUE(coded_input->ReadVarint32(&insize));
644       EXPECT_EQ(strlen(strB) + 1, insize);
645       EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize));
646       EXPECT_EQ(0, memcmp(temp_buffer, strB, insize))
647           << " out_block_size=" << kBlockSizes[i]
648           << " in_block_size=" << kBlockSizes[j] << " pos=" << pos
649           << " size=" << size << " strB=" << strB << " in=" << temp_buffer;
650 
651       delete coded_input;
652       delete gzin;
653       delete input;
654     }
655   }
656 
657   delete[] temp_buffer;
658   delete[] buffer;
659 }
660 
TEST_F(IoTest,GzipInputByteCountAfterClosed)661 TEST_F(IoTest, GzipInputByteCountAfterClosed) {
662   std::string golden = "abcdefghijklmnopqrstuvwxyz";
663   std::string compressed = Compress(golden, GzipOutputStream::Options());
664 
665   for (int i = 0; i < kBlockSizeCount; i++) {
666     ArrayInputStream arr_input(compressed.data(), compressed.size(),
667                                kBlockSizes[i]);
668     GzipInputStream gz_input(&arr_input);
669     const void* buffer;
670     int size;
671     while (gz_input.Next(&buffer, &size)) {
672       EXPECT_LE(gz_input.ByteCount(), golden.size());
673     }
674     EXPECT_EQ(golden.size(), gz_input.ByteCount());
675   }
676 }
677 
TEST_F(IoTest,GzipInputByteCountAfterClosedConcatenatedStreams)678 TEST_F(IoTest, GzipInputByteCountAfterClosedConcatenatedStreams) {
679   std::string golden1 = "abcdefghijklmnopqrstuvwxyz";
680   std::string golden2 = "the quick brown fox jumps over the lazy dog";
681   const size_t total_size = golden1.size() + golden2.size();
682   std::string compressed = Compress(golden1, GzipOutputStream::Options()) +
683                            Compress(golden2, GzipOutputStream::Options());
684 
685   for (int i = 0; i < kBlockSizeCount; i++) {
686     ArrayInputStream arr_input(compressed.data(), compressed.size(),
687                                kBlockSizes[i]);
688     GzipInputStream gz_input(&arr_input);
689     const void* buffer;
690     int size;
691     while (gz_input.Next(&buffer, &size)) {
692       EXPECT_LE(gz_input.ByteCount(), total_size);
693     }
694     EXPECT_EQ(total_size, gz_input.ByteCount());
695   }
696 }
697 #endif
698 
699 // There is no string input, only string output.  Also, it doesn't support
700 // explicit block sizes.  So, we'll only run one test and we'll use
701 // ArrayInput to read back the results.
TEST_F(IoTest,StringIo)702 TEST_F(IoTest, StringIo) {
703   std::string str;
704   {
705     StringOutputStream output(&str);
706     WriteStuff(&output);
707   }
708   {
709     ArrayInputStream input(str.data(), str.size());
710     ReadStuff(&input);
711   }
712 }
713 
714 
715 // To test files, we create a temporary file, write, read, truncate, repeat.
TEST_F(IoTest,FileIo)716 TEST_F(IoTest, FileIo) {
717   std::string filename = TestTempDir() + "/zero_copy_stream_test_file";
718 
719   for (int i = 0; i < kBlockSizeCount; i++) {
720     for (int j = 0; j < kBlockSizeCount; j++) {
721       // Make a temporary file.
722       int file =
723           open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777);
724       ASSERT_GE(file, 0);
725 
726       {
727         FileOutputStream output(file, kBlockSizes[i]);
728         WriteStuff(&output);
729         EXPECT_EQ(0, output.GetErrno());
730       }
731 
732       // Rewind.
733       ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1);
734 
735       {
736         FileInputStream input(file, kBlockSizes[j]);
737         ReadStuff(&input);
738         EXPECT_EQ(0, input.GetErrno());
739       }
740 
741       close(file);
742     }
743   }
744 }
745 
746 #if HAVE_ZLIB
TEST_F(IoTest,GzipFileIo)747 TEST_F(IoTest, GzipFileIo) {
748   std::string filename = TestTempDir() + "/zero_copy_stream_test_file";
749 
750   for (int i = 0; i < kBlockSizeCount; i++) {
751     for (int j = 0; j < kBlockSizeCount; j++) {
752       // Make a temporary file.
753       int file =
754           open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777);
755       ASSERT_GE(file, 0);
756       {
757         FileOutputStream output(file, kBlockSizes[i]);
758         GzipOutputStream gzout(&output);
759         WriteStuffLarge(&gzout);
760         gzout.Close();
761         output.Flush();
762         EXPECT_EQ(0, output.GetErrno());
763       }
764 
765       // Rewind.
766       ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1);
767 
768       {
769         FileInputStream input(file, kBlockSizes[j]);
770         GzipInputStream gzin(&input);
771         ReadStuffLarge(&gzin);
772         EXPECT_EQ(0, input.GetErrno());
773       }
774 
775       close(file);
776     }
777   }
778 }
779 #endif
780 
781 // MSVC raises various debugging exceptions if we try to use a file
782 // descriptor of -1, defeating our tests below.  This class will disable
783 // these debug assertions while in scope.
784 class MsvcDebugDisabler {
785  public:
786 #if defined(_MSC_VER) && _MSC_VER >= 1400
MsvcDebugDisabler()787   MsvcDebugDisabler() {
788     old_handler_ = _set_invalid_parameter_handler(MyHandler);
789     old_mode_ = _CrtSetReportMode(_CRT_ASSERT, 0);
790   }
~MsvcDebugDisabler()791   ~MsvcDebugDisabler() {
792     old_handler_ = _set_invalid_parameter_handler(old_handler_);
793     old_mode_ = _CrtSetReportMode(_CRT_ASSERT, old_mode_);
794   }
795 
MyHandler(const wchar_t * expr,const wchar_t * func,const wchar_t * file,unsigned int line,uintptr_t pReserved)796   static void MyHandler(const wchar_t* expr, const wchar_t* func,
797                         const wchar_t* file, unsigned int line,
798                         uintptr_t pReserved) {
799     // do nothing
800   }
801 
802   _invalid_parameter_handler old_handler_;
803   int old_mode_;
804 #else
805   // Dummy constructor and destructor to ensure that GCC doesn't complain
806   // that debug_disabler is an unused variable.
807   MsvcDebugDisabler() {}
808   ~MsvcDebugDisabler() {}
809 #endif
810 };
811 
812 // Test that FileInputStreams report errors correctly.
TEST_F(IoTest,FileReadError)813 TEST_F(IoTest, FileReadError) {
814   MsvcDebugDisabler debug_disabler;
815 
816   // -1 = invalid file descriptor.
817   FileInputStream input(-1);
818 
819   const void* buffer;
820   int size;
821   EXPECT_FALSE(input.Next(&buffer, &size));
822   EXPECT_EQ(EBADF, input.GetErrno());
823 }
824 
825 // Test that FileOutputStreams report errors correctly.
TEST_F(IoTest,FileWriteError)826 TEST_F(IoTest, FileWriteError) {
827   MsvcDebugDisabler debug_disabler;
828 
829   // -1 = invalid file descriptor.
830   FileOutputStream input(-1);
831 
832   void* buffer;
833   int size;
834 
835   // The first call to Next() succeeds because it doesn't have anything to
836   // write yet.
837   EXPECT_TRUE(input.Next(&buffer, &size));
838 
839   // Second call fails.
840   EXPECT_FALSE(input.Next(&buffer, &size));
841 
842   EXPECT_EQ(EBADF, input.GetErrno());
843 }
844 
845 // Pipes are not seekable, so File{Input,Output}Stream ends up doing some
846 // different things to handle them.  We'll test by writing to a pipe and
847 // reading back from it.
TEST_F(IoTest,PipeIo)848 TEST_F(IoTest, PipeIo) {
849   int files[2];
850 
851   for (int i = 0; i < kBlockSizeCount; i++) {
852     for (int j = 0; j < kBlockSizeCount; j++) {
853       // Need to create a new pipe each time because ReadStuff() expects
854       // to see EOF at the end.
855       ASSERT_EQ(pipe(files), 0);
856 
857       {
858         FileOutputStream output(files[1], kBlockSizes[i]);
859         WriteStuff(&output);
860         EXPECT_EQ(0, output.GetErrno());
861       }
862       close(files[1]);  // Send EOF.
863 
864       {
865         FileInputStream input(files[0], kBlockSizes[j]);
866         ReadStuff(&input);
867         EXPECT_EQ(0, input.GetErrno());
868       }
869       close(files[0]);
870     }
871   }
872 }
873 
874 // Test using C++ iostreams.
TEST_F(IoTest,IostreamIo)875 TEST_F(IoTest, IostreamIo) {
876   for (int i = 0; i < kBlockSizeCount; i++) {
877     for (int j = 0; j < kBlockSizeCount; j++) {
878       {
879         std::stringstream stream;
880 
881         {
882           OstreamOutputStream output(&stream, kBlockSizes[i]);
883           WriteStuff(&output);
884           EXPECT_FALSE(stream.fail());
885         }
886 
887         {
888           IstreamInputStream input(&stream, kBlockSizes[j]);
889           ReadStuff(&input);
890           EXPECT_TRUE(stream.eof());
891         }
892       }
893 
894       {
895         std::stringstream stream;
896 
897         {
898           OstreamOutputStream output(&stream, kBlockSizes[i]);
899           WriteStuffLarge(&output);
900           EXPECT_FALSE(stream.fail());
901         }
902 
903         {
904           IstreamInputStream input(&stream, kBlockSizes[j]);
905           ReadStuffLarge(&input);
906           EXPECT_TRUE(stream.eof());
907         }
908       }
909     }
910   }
911 }
912 
913 // To test ConcatenatingInputStream, we create several ArrayInputStreams
914 // covering a buffer and then concatenate them.
TEST_F(IoTest,ConcatenatingInputStream)915 TEST_F(IoTest, ConcatenatingInputStream) {
916   const int kBufferSize = 256;
917   uint8 buffer[kBufferSize];
918 
919   // Fill the buffer.
920   ArrayOutputStream output(buffer, kBufferSize);
921   WriteStuff(&output);
922 
923   // Now split it up into multiple streams of varying sizes.
924   ASSERT_EQ(68, output.ByteCount());  // Test depends on this.
925   ArrayInputStream input1(buffer, 12);
926   ArrayInputStream input2(buffer + 12, 7);
927   ArrayInputStream input3(buffer + 19, 6);
928   ArrayInputStream input4(buffer + 25, 15);
929   ArrayInputStream input5(buffer + 40, 0);
930   // Note:  We want to make sure we have a stream boundary somewhere between
931   // bytes 42 and 62, which is the range that it Skip()ed by ReadStuff().  This
932   // tests that a bug that existed in the original code for Skip() is fixed.
933   ArrayInputStream input6(buffer + 40, 10);
934   ArrayInputStream input7(buffer + 50, 18);  // Total = 68 bytes.
935 
936   ZeroCopyInputStream* streams[] = {&input1, &input2, &input3, &input4,
937                                     &input5, &input6, &input7};
938 
939   // Create the concatenating stream and read.
940   ConcatenatingInputStream input(streams, GOOGLE_ARRAYSIZE(streams));
941   ReadStuff(&input);
942 }
943 
944 // To test LimitingInputStream, we write our golden text to a buffer, then
945 // create an ArrayInputStream that contains the whole buffer (not just the
946 // bytes written), then use a LimitingInputStream to limit it just to the
947 // bytes written.
TEST_F(IoTest,LimitingInputStream)948 TEST_F(IoTest, LimitingInputStream) {
949   const int kBufferSize = 256;
950   uint8 buffer[kBufferSize];
951 
952   // Fill the buffer.
953   ArrayOutputStream output(buffer, kBufferSize);
954   WriteStuff(&output);
955 
956   // Set up input.
957   ArrayInputStream array_input(buffer, kBufferSize);
958   LimitingInputStream input(&array_input, output.ByteCount());
959 
960   ReadStuff(&input);
961 }
962 
963 // Checks that ByteCount works correctly for LimitingInputStreams where the
964 // underlying stream has already been read.
TEST_F(IoTest,LimitingInputStreamByteCount)965 TEST_F(IoTest, LimitingInputStreamByteCount) {
966   const int kHalfBufferSize = 128;
967   const int kBufferSize = kHalfBufferSize * 2;
968   uint8 buffer[kBufferSize];
969 
970   // Set up input. Only allow half to be read at once.
971   ArrayInputStream array_input(buffer, kBufferSize, kHalfBufferSize);
972   const void* data;
973   int size;
974   EXPECT_TRUE(array_input.Next(&data, &size));
975   EXPECT_EQ(kHalfBufferSize, array_input.ByteCount());
976   // kHalfBufferSize - 1 to test limiting logic as well.
977   LimitingInputStream input(&array_input, kHalfBufferSize - 1);
978   EXPECT_EQ(0, input.ByteCount());
979   EXPECT_TRUE(input.Next(&data, &size));
980   EXPECT_EQ(kHalfBufferSize - 1, input.ByteCount());
981 }
982 
983 // Check that a zero-size array doesn't confuse the code.
TEST(ZeroSizeArray,Input)984 TEST(ZeroSizeArray, Input) {
985   ArrayInputStream input(NULL, 0);
986   const void* data;
987   int size;
988   EXPECT_FALSE(input.Next(&data, &size));
989 }
990 
TEST(ZeroSizeArray,Output)991 TEST(ZeroSizeArray, Output) {
992   ArrayOutputStream output(NULL, 0);
993   void* data;
994   int size;
995   EXPECT_FALSE(output.Next(&data, &size));
996 }
997 
998 }  // namespace
999 }  // namespace io
1000 }  // namespace protobuf
1001 }  // namespace google
1002