Home | History | Annotate | Download | only in io
      1 // Protocol Buffers - Google's data interchange format
      2 // Copyright 2008 Google Inc.  All rights reserved.
      3 // http://code.google.com/p/protobuf/
      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 (at) 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 
    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 
    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 
    194 void IoTest::WriteString(ZeroCopyOutputStream* output, const string& str) {
    195   EXPECT_TRUE(WriteToOutput(output, str.c_str(), str.size()));
    196 }
    197 
    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 
    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.
    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 
    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.
    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 
    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 
    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
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    559 TEST_F(IoTest, CompressionOptions) {
    560   // Some ad-hoc testing of compression options.
    561 
    562   string golden;
    563   File::ReadFileToStringOrDie(
    564     TestSourceDir() + "/google/protobuf/testdata/golden_message",
    565     &golden);
    566 
    567   GzipOutputStream::Options options;
    568   string gzip_compressed = Compress(golden, options);
    569 
    570   options.compression_level = 0;
    571   string not_compressed = Compress(golden, options);
    572 
    573   // Try zlib compression for fun.
    574   options = GzipOutputStream::Options();
    575   options.format = GzipOutputStream::ZLIB;
    576   string zlib_compressed = Compress(golden, options);
    577 
    578   // Uncompressed should be bigger than the original since it should have some
    579   // sort of header.
    580   EXPECT_GT(not_compressed.size(), golden.size());
    581 
    582   // Higher compression levels should result in smaller sizes.
    583   EXPECT_LT(zlib_compressed.size(), not_compressed.size());
    584 
    585   // ZLIB format should differ from GZIP format.
    586   EXPECT_TRUE(zlib_compressed != gzip_compressed);
    587 
    588   // Everything should decompress correctly.
    589   EXPECT_TRUE(Uncompress(not_compressed) == golden);
    590   EXPECT_TRUE(Uncompress(gzip_compressed) == golden);
    591   EXPECT_TRUE(Uncompress(zlib_compressed) == golden);
    592 }
    593 
    594 TEST_F(IoTest, TwoSessionWriteGzip) {
    595   // Test that two concatenated gzip streams can be read correctly
    596 
    597   static const char* strA = "0123456789";
    598   static const char* strB = "QuickBrownFox";
    599   const int kBufferSize = 2*1024;
    600   uint8* buffer = new uint8[kBufferSize];
    601   char* temp_buffer = new char[40];
    602 
    603   for (int i = 0; i < kBlockSizeCount; i++) {
    604     for (int j = 0; j < kBlockSizeCount; j++) {
    605       ArrayOutputStream* output =
    606           new ArrayOutputStream(buffer, kBufferSize, kBlockSizes[i]);
    607       GzipOutputStream* gzout = new GzipOutputStream(output);
    608       CodedOutputStream* coded_output = new CodedOutputStream(gzout);
    609       int32 outlen = strlen(strA) + 1;
    610       coded_output->WriteVarint32(outlen);
    611       coded_output->WriteRaw(strA, outlen);
    612       delete coded_output;  // flush
    613       delete gzout;  // flush
    614       int64 pos = output->ByteCount();
    615       delete output;
    616       output = new ArrayOutputStream(
    617           buffer + pos, kBufferSize - pos, kBlockSizes[i]);
    618       gzout = new GzipOutputStream(output);
    619       coded_output = new CodedOutputStream(gzout);
    620       outlen = strlen(strB) + 1;
    621       coded_output->WriteVarint32(outlen);
    622       coded_output->WriteRaw(strB, outlen);
    623       delete coded_output;  // flush
    624       delete gzout;  // flush
    625       int64 size = pos + output->ByteCount();
    626       delete output;
    627 
    628       ArrayInputStream* input =
    629           new ArrayInputStream(buffer, size, kBlockSizes[j]);
    630       GzipInputStream* gzin = new GzipInputStream(input);
    631       CodedInputStream* coded_input = new CodedInputStream(gzin);
    632       uint32 insize;
    633       EXPECT_TRUE(coded_input->ReadVarint32(&insize));
    634       EXPECT_EQ(strlen(strA) + 1, insize);
    635       EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize));
    636       EXPECT_EQ(0, memcmp(temp_buffer, strA, insize))
    637           << "strA=" << strA << " in=" << temp_buffer;
    638 
    639       EXPECT_TRUE(coded_input->ReadVarint32(&insize));
    640       EXPECT_EQ(strlen(strB) + 1, insize);
    641       EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize));
    642       EXPECT_EQ(0, memcmp(temp_buffer, strB, insize))
    643           << " out_block_size=" << kBlockSizes[i]
    644           << " in_block_size=" << kBlockSizes[j]
    645           << " pos=" << pos
    646           << " size=" << size
    647           << " strB=" << strB << " in=" << temp_buffer;
    648 
    649       delete coded_input;
    650       delete gzin;
    651       delete input;
    652     }
    653   }
    654 
    655   delete [] temp_buffer;
    656   delete [] buffer;
    657 }
    658 #endif
    659 
    660 // There is no string input, only string output.  Also, it doesn't support
    661 // explicit block sizes.  So, we'll only run one test and we'll use
    662 // ArrayInput to read back the results.
    663 TEST_F(IoTest, StringIo) {
    664   string str;
    665   {
    666     StringOutputStream output(&str);
    667     WriteStuff(&output);
    668   }
    669   {
    670     ArrayInputStream input(str.data(), str.size());
    671     ReadStuff(&input);
    672   }
    673 }
    674 
    675 
    676 // To test files, we create a temporary file, write, read, truncate, repeat.
    677 TEST_F(IoTest, FileIo) {
    678   string filename = TestTempDir() + "/zero_copy_stream_test_file";
    679 
    680   for (int i = 0; i < kBlockSizeCount; i++) {
    681     for (int j = 0; j < kBlockSizeCount; j++) {
    682       // Make a temporary file.
    683       int file =
    684         open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777);
    685       ASSERT_GE(file, 0);
    686 
    687       {
    688         FileOutputStream output(file, kBlockSizes[i]);
    689         WriteStuff(&output);
    690         EXPECT_EQ(0, output.GetErrno());
    691       }
    692 
    693       // Rewind.
    694       ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1);
    695 
    696       {
    697         FileInputStream input(file, kBlockSizes[j]);
    698         ReadStuff(&input);
    699         EXPECT_EQ(0, input.GetErrno());
    700       }
    701 
    702       close(file);
    703     }
    704   }
    705 }
    706 
    707 #if HAVE_ZLIB
    708 TEST_F(IoTest, GzipFileIo) {
    709   string filename = TestTempDir() + "/zero_copy_stream_test_file";
    710 
    711   for (int i = 0; i < kBlockSizeCount; i++) {
    712     for (int j = 0; j < kBlockSizeCount; j++) {
    713       // Make a temporary file.
    714       int file =
    715         open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777);
    716       ASSERT_GE(file, 0);
    717       {
    718         FileOutputStream output(file, kBlockSizes[i]);
    719         GzipOutputStream gzout(&output);
    720         WriteStuffLarge(&gzout);
    721         gzout.Close();
    722         output.Flush();
    723         EXPECT_EQ(0, output.GetErrno());
    724       }
    725 
    726       // Rewind.
    727       ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1);
    728 
    729       {
    730         FileInputStream input(file, kBlockSizes[j]);
    731         GzipInputStream gzin(&input);
    732         ReadStuffLarge(&gzin);
    733         EXPECT_EQ(0, input.GetErrno());
    734       }
    735 
    736       close(file);
    737     }
    738   }
    739 }
    740 #endif
    741 
    742 // MSVC raises various debugging exceptions if we try to use a file
    743 // descriptor of -1, defeating our tests below.  This class will disable
    744 // these debug assertions while in scope.
    745 class MsvcDebugDisabler {
    746  public:
    747 #if defined(_MSC_VER) && _MSC_VER >= 1400
    748   MsvcDebugDisabler() {
    749     old_handler_ = _set_invalid_parameter_handler(MyHandler);
    750     old_mode_ = _CrtSetReportMode(_CRT_ASSERT, 0);
    751   }
    752   ~MsvcDebugDisabler() {
    753     old_handler_ = _set_invalid_parameter_handler(old_handler_);
    754     old_mode_ = _CrtSetReportMode(_CRT_ASSERT, old_mode_);
    755   }
    756 
    757   static void MyHandler(const wchar_t *expr,
    758                         const wchar_t *func,
    759                         const wchar_t *file,
    760                         unsigned int line,
    761                         uintptr_t pReserved) {
    762     // do nothing
    763   }
    764 
    765   _invalid_parameter_handler old_handler_;
    766   int old_mode_;
    767 #else
    768   // Dummy constructor and destructor to ensure that GCC doesn't complain
    769   // that debug_disabler is an unused variable.
    770   MsvcDebugDisabler() {}
    771   ~MsvcDebugDisabler() {}
    772 #endif
    773 };
    774 
    775 // Test that FileInputStreams report errors correctly.
    776 TEST_F(IoTest, FileReadError) {
    777   MsvcDebugDisabler debug_disabler;
    778 
    779   // -1 = invalid file descriptor.
    780   FileInputStream input(-1);
    781 
    782   const void* buffer;
    783   int size;
    784   EXPECT_FALSE(input.Next(&buffer, &size));
    785   EXPECT_EQ(EBADF, input.GetErrno());
    786 }
    787 
    788 // Test that FileOutputStreams report errors correctly.
    789 TEST_F(IoTest, FileWriteError) {
    790   MsvcDebugDisabler debug_disabler;
    791 
    792   // -1 = invalid file descriptor.
    793   FileOutputStream input(-1);
    794 
    795   void* buffer;
    796   int size;
    797 
    798   // The first call to Next() succeeds because it doesn't have anything to
    799   // write yet.
    800   EXPECT_TRUE(input.Next(&buffer, &size));
    801 
    802   // Second call fails.
    803   EXPECT_FALSE(input.Next(&buffer, &size));
    804 
    805   EXPECT_EQ(EBADF, input.GetErrno());
    806 }
    807 
    808 // Pipes are not seekable, so File{Input,Output}Stream ends up doing some
    809 // different things to handle them.  We'll test by writing to a pipe and
    810 // reading back from it.
    811 TEST_F(IoTest, PipeIo) {
    812   int files[2];
    813 
    814   for (int i = 0; i < kBlockSizeCount; i++) {
    815     for (int j = 0; j < kBlockSizeCount; j++) {
    816       // Need to create a new pipe each time because ReadStuff() expects
    817       // to see EOF at the end.
    818       ASSERT_EQ(pipe(files), 0);
    819 
    820       {
    821         FileOutputStream output(files[1], kBlockSizes[i]);
    822         WriteStuff(&output);
    823         EXPECT_EQ(0, output.GetErrno());
    824       }
    825       close(files[1]);  // Send EOF.
    826 
    827       {
    828         FileInputStream input(files[0], kBlockSizes[j]);
    829         ReadStuff(&input);
    830         EXPECT_EQ(0, input.GetErrno());
    831       }
    832       close(files[0]);
    833     }
    834   }
    835 }
    836 
    837 // Test using C++ iostreams.
    838 TEST_F(IoTest, IostreamIo) {
    839   for (int i = 0; i < kBlockSizeCount; i++) {
    840     for (int j = 0; j < kBlockSizeCount; j++) {
    841       {
    842         stringstream stream;
    843 
    844         {
    845           OstreamOutputStream output(&stream, kBlockSizes[i]);
    846           WriteStuff(&output);
    847           EXPECT_FALSE(stream.fail());
    848         }
    849 
    850         {
    851           IstreamInputStream input(&stream, kBlockSizes[j]);
    852           ReadStuff(&input);
    853           EXPECT_TRUE(stream.eof());
    854         }
    855       }
    856 
    857       {
    858         stringstream stream;
    859 
    860         {
    861           OstreamOutputStream output(&stream, kBlockSizes[i]);
    862           WriteStuffLarge(&output);
    863           EXPECT_FALSE(stream.fail());
    864         }
    865 
    866         {
    867           IstreamInputStream input(&stream, kBlockSizes[j]);
    868           ReadStuffLarge(&input);
    869           EXPECT_TRUE(stream.eof());
    870         }
    871       }
    872     }
    873   }
    874 }
    875 
    876 // To test ConcatenatingInputStream, we create several ArrayInputStreams
    877 // covering a buffer and then concatenate them.
    878 TEST_F(IoTest, ConcatenatingInputStream) {
    879   const int kBufferSize = 256;
    880   uint8 buffer[kBufferSize];
    881 
    882   // Fill the buffer.
    883   ArrayOutputStream output(buffer, kBufferSize);
    884   WriteStuff(&output);
    885 
    886   // Now split it up into multiple streams of varying sizes.
    887   ASSERT_EQ(68, output.ByteCount());  // Test depends on this.
    888   ArrayInputStream input1(buffer     , 12);
    889   ArrayInputStream input2(buffer + 12,  7);
    890   ArrayInputStream input3(buffer + 19,  6);
    891   ArrayInputStream input4(buffer + 25, 15);
    892   ArrayInputStream input5(buffer + 40,  0);
    893   // Note:  We want to make sure we have a stream boundary somewhere between
    894   // bytes 42 and 62, which is the range that it Skip()ed by ReadStuff().  This
    895   // tests that a bug that existed in the original code for Skip() is fixed.
    896   ArrayInputStream input6(buffer + 40, 10);
    897   ArrayInputStream input7(buffer + 50, 18);  // Total = 68 bytes.
    898 
    899   ZeroCopyInputStream* streams[] =
    900     {&input1, &input2, &input3, &input4, &input5, &input6, &input7};
    901 
    902   // Create the concatenating stream and read.
    903   ConcatenatingInputStream input(streams, GOOGLE_ARRAYSIZE(streams));
    904   ReadStuff(&input);
    905 }
    906 
    907 // To test LimitingInputStream, we write our golden text to a buffer, then
    908 // create an ArrayInputStream that contains the whole buffer (not just the
    909 // bytes written), then use a LimitingInputStream to limit it just to the
    910 // bytes written.
    911 TEST_F(IoTest, LimitingInputStream) {
    912   const int kBufferSize = 256;
    913   uint8 buffer[kBufferSize];
    914 
    915   // Fill the buffer.
    916   ArrayOutputStream output(buffer, kBufferSize);
    917   WriteStuff(&output);
    918 
    919   // Set up input.
    920   ArrayInputStream array_input(buffer, kBufferSize);
    921   LimitingInputStream input(&array_input, output.ByteCount());
    922 
    923   ReadStuff(&input);
    924 }
    925 
    926 // Check that a zero-size array doesn't confuse the code.
    927 TEST(ZeroSizeArray, Input) {
    928   ArrayInputStream input(NULL, 0);
    929   const void* data;
    930   int size;
    931   EXPECT_FALSE(input.Next(&data, &size));
    932 }
    933 
    934 TEST(ZeroSizeArray, Output) {
    935   ArrayOutputStream output(NULL, 0);
    936   void* data;
    937   int size;
    938   EXPECT_FALSE(output.Next(&data, &size));
    939 }
    940 
    941 }  // namespace
    942 }  // namespace io
    943 }  // namespace protobuf
    944 }  // namespace google
    945