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