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