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 
     65 #if HAVE_ZLIB
     66 #include <google/protobuf/io/gzip_stream.h>
     67 #endif
     68 
     69 #include <google/protobuf/stubs/common.h>
     70 #include <google/protobuf/testing/googletest.h>
     71 #include <google/protobuf/testing/file.h>
     72 #include <gtest/gtest.h>
     73 
     74 namespace google {
     75 namespace protobuf {
     76 namespace io {
     77 namespace {
     78 
     79 #ifdef _WIN32
     80 #define pipe(fds) _pipe(fds, 4096, O_BINARY)
     81 #endif
     82 
     83 #ifndef O_BINARY
     84 #ifdef _O_BINARY
     85 #define O_BINARY _O_BINARY
     86 #else
     87 #define O_BINARY 0     // If this isn't defined, the platform doesn't need it.
     88 #endif
     89 #endif
     90 
     91 class IoTest : public testing::Test {
     92  protected:
     93   // Test helpers.
     94 
     95   // Helper to write an array of data to an output stream.
     96   bool WriteToOutput(ZeroCopyOutputStream* output, const void* data, int size);
     97   // Helper to read a fixed-length array of data from an input stream.
     98   int ReadFromInput(ZeroCopyInputStream* input, void* data, int size);
     99   // Write a string to the output stream.
    100   void WriteString(ZeroCopyOutputStream* output, const string& str);
    101   // Read a number of bytes equal to the size of the given string and checks
    102   // that it matches the string.
    103   void ReadString(ZeroCopyInputStream* input, const string& str);
    104   // Writes some text to the output stream in a particular order.  Returns
    105   // the number of bytes written, incase the caller needs that to set up an
    106   // input stream.
    107   int WriteStuff(ZeroCopyOutputStream* output);
    108   // Reads text from an input stream and expects it to match what
    109   // WriteStuff() writes.
    110   void ReadStuff(ZeroCopyInputStream* input);
    111 
    112   // Similar to WriteStuff, but performs more sophisticated testing.
    113   int WriteStuffLarge(ZeroCopyOutputStream* output);
    114   // Reads and tests a stream that should have been written to
    115   // via WriteStuffLarge().
    116   void ReadStuffLarge(ZeroCopyInputStream* input);
    117 
    118 #if HAVE_ZLIB
    119   string Compress(const string& data, const GzipOutputStream::Options& options);
    120   string Uncompress(const string& data);
    121 #endif
    122 
    123   static const int kBlockSizes[];
    124   static const int kBlockSizeCount;
    125 };
    126 
    127 const int IoTest::kBlockSizes[] = {-1, 1, 2, 5, 7, 10, 23, 64};
    128 const int IoTest::kBlockSizeCount = GOOGLE_ARRAYSIZE(IoTest::kBlockSizes);
    129 
    130 bool IoTest::WriteToOutput(ZeroCopyOutputStream* output,
    131                            const void* data, int size) {
    132   const uint8* in = reinterpret_cast<const uint8*>(data);
    133   int in_size = size;
    134 
    135   void* out;
    136   int out_size;
    137 
    138   while (true) {
    139     if (!output->Next(&out, &out_size)) {
    140       return false;
    141     }
    142     EXPECT_GT(out_size, 0);
    143 
    144     if (in_size <= out_size) {
    145       memcpy(out, in, in_size);
    146       output->BackUp(out_size - in_size);
    147       return true;
    148     }
    149 
    150     memcpy(out, in, out_size);
    151     in += out_size;
    152     in_size -= out_size;
    153   }
    154 }
    155 
    156 #define MAX_REPEATED_ZEROS 100
    157 
    158 int IoTest::ReadFromInput(ZeroCopyInputStream* input, void* data, int size) {
    159   uint8* out = reinterpret_cast<uint8*>(data);
    160   int out_size = size;
    161 
    162   const void* in;
    163   int in_size = 0;
    164 
    165   int repeated_zeros = 0;
    166 
    167   while (true) {
    168     if (!input->Next(&in, &in_size)) {
    169       return size - out_size;
    170     }
    171     EXPECT_GT(in_size, -1);
    172     if (in_size == 0) {
    173       repeated_zeros++;
    174     } else {
    175       repeated_zeros = 0;
    176     }
    177     EXPECT_LT(repeated_zeros, MAX_REPEATED_ZEROS);
    178 
    179     if (out_size <= in_size) {
    180       memcpy(out, in, out_size);
    181       if (in_size > out_size) {
    182         input->BackUp(in_size - out_size);
    183       }
    184       return size;  // Copied all of it.
    185     }
    186 
    187     memcpy(out, in, in_size);
    188     out += in_size;
    189     out_size -= in_size;
    190   }
    191 }
    192 
    193 void IoTest::WriteString(ZeroCopyOutputStream* output, const string& str) {
    194   EXPECT_TRUE(WriteToOutput(output, str.c_str(), str.size()));
    195 }
    196 
    197 void IoTest::ReadString(ZeroCopyInputStream* input, const string& str) {
    198   scoped_array<char> buffer(new char[str.size() + 1]);
    199   buffer[str.size()] = '\0';
    200   EXPECT_EQ(ReadFromInput(input, buffer.get(), str.size()), str.size());
    201   EXPECT_STREQ(str.c_str(), buffer.get());
    202 }
    203 
    204 int IoTest::WriteStuff(ZeroCopyOutputStream* output) {
    205   WriteString(output, "Hello world!\n");
    206   WriteString(output, "Some te");
    207   WriteString(output, "xt.  Blah blah.");
    208   WriteString(output, "abcdefg");
    209   WriteString(output, "01234567890123456789");
    210   WriteString(output, "foobar");
    211 
    212   EXPECT_EQ(output->ByteCount(), 68);
    213 
    214   int result = output->ByteCount();
    215   return result;
    216 }
    217 
    218 // Reads text from an input stream and expects it to match what WriteStuff()
    219 // writes.
    220 void IoTest::ReadStuff(ZeroCopyInputStream* input) {
    221   ReadString(input, "Hello world!\n");
    222   ReadString(input, "Some text.  ");
    223   ReadString(input, "Blah ");
    224   ReadString(input, "blah.");
    225   ReadString(input, "abcdefg");
    226   EXPECT_TRUE(input->Skip(20));
    227   ReadString(input, "foo");
    228   ReadString(input, "bar");
    229 
    230   EXPECT_EQ(input->ByteCount(), 68);
    231 
    232   uint8 byte;
    233   EXPECT_EQ(ReadFromInput(input, &byte, 1), 0);
    234 }
    235 
    236 int IoTest::WriteStuffLarge(ZeroCopyOutputStream* output) {
    237   WriteString(output, "Hello world!\n");
    238   WriteString(output, "Some te");
    239   WriteString(output, "xt.  Blah blah.");
    240   WriteString(output, string(100000, 'x'));  // A very long string
    241   WriteString(output, string(100000, 'y'));  // A very long string
    242   WriteString(output, "01234567890123456789");
    243 
    244   EXPECT_EQ(output->ByteCount(), 200055);
    245 
    246   int result = output->ByteCount();
    247   return result;
    248 }
    249 
    250 // Reads text from an input stream and expects it to match what WriteStuff()
    251 // writes.
    252 void IoTest::ReadStuffLarge(ZeroCopyInputStream* input) {
    253   ReadString(input, "Hello world!\nSome text.  ");
    254   EXPECT_TRUE(input->Skip(5));
    255   ReadString(input, "blah.");
    256   EXPECT_TRUE(input->Skip(100000 - 10));
    257   ReadString(input, string(10, 'x') + string(100000 - 20000, 'y'));
    258   EXPECT_TRUE(input->Skip(20000 - 10));
    259   ReadString(input, "yyyyyyyyyy01234567890123456789");
    260 
    261   EXPECT_EQ(input->ByteCount(), 200055);
    262 
    263   uint8 byte;
    264   EXPECT_EQ(ReadFromInput(input, &byte, 1), 0);
    265 }
    266 
    267 // ===================================================================
    268 
    269 TEST_F(IoTest, ArrayIo) {
    270   const int kBufferSize = 256;
    271   uint8 buffer[kBufferSize];
    272 
    273   for (int i = 0; i < kBlockSizeCount; i++) {
    274     for (int j = 0; j < kBlockSizeCount; j++) {
    275       int size;
    276       {
    277         ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
    278         size = WriteStuff(&output);
    279       }
    280       {
    281         ArrayInputStream input(buffer, size, kBlockSizes[j]);
    282         ReadStuff(&input);
    283       }
    284     }
    285   }
    286 }
    287 
    288 #if HAVE_ZLIB
    289 TEST_F(IoTest, GzipIo) {
    290   const int kBufferSize = 2*1024;
    291   uint8* buffer = new uint8[kBufferSize];
    292   for (int i = 0; i < kBlockSizeCount; i++) {
    293     for (int j = 0; j < kBlockSizeCount; j++) {
    294       for (int z = 0; z < kBlockSizeCount; z++) {
    295         int gzip_buffer_size = kBlockSizes[z];
    296         int size;
    297         {
    298           ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
    299           GzipOutputStream gzout(
    300               &output, GzipOutputStream::GZIP, gzip_buffer_size);
    301           WriteStuff(&gzout);
    302           gzout.Close();
    303           size = output.ByteCount();
    304         }
    305         {
    306           ArrayInputStream input(buffer, size, kBlockSizes[j]);
    307           GzipInputStream gzin(
    308               &input, GzipInputStream::GZIP, gzip_buffer_size);
    309           ReadStuff(&gzin);
    310         }
    311       }
    312     }
    313   }
    314   delete [] buffer;
    315 }
    316 
    317 TEST_F(IoTest, ZlibIo) {
    318   const int kBufferSize = 2*1024;
    319   uint8* buffer = new uint8[kBufferSize];
    320   for (int i = 0; i < kBlockSizeCount; i++) {
    321     for (int j = 0; j < kBlockSizeCount; j++) {
    322       for (int z = 0; z < kBlockSizeCount; z++) {
    323         int gzip_buffer_size = kBlockSizes[z];
    324         int size;
    325         {
    326           ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
    327           GzipOutputStream gzout(
    328               &output, GzipOutputStream::ZLIB, gzip_buffer_size);
    329           WriteStuff(&gzout);
    330           gzout.Close();
    331           size = output.ByteCount();
    332         }
    333         {
    334           ArrayInputStream input(buffer, size, kBlockSizes[j]);
    335           GzipInputStream gzin(
    336               &input, GzipInputStream::ZLIB, gzip_buffer_size);
    337           ReadStuff(&gzin);
    338         }
    339       }
    340     }
    341   }
    342   delete [] buffer;
    343 }
    344 
    345 TEST_F(IoTest, ZlibIoInputAutodetect) {
    346   const int kBufferSize = 2*1024;
    347   uint8* buffer = new uint8[kBufferSize];
    348   int size;
    349   {
    350     ArrayOutputStream output(buffer, kBufferSize);
    351     GzipOutputStream gzout(&output, GzipOutputStream::ZLIB);
    352     WriteStuff(&gzout);
    353     gzout.Close();
    354     size = output.ByteCount();
    355   }
    356   {
    357     ArrayInputStream input(buffer, size);
    358     GzipInputStream gzin(&input, GzipInputStream::AUTO);
    359     ReadStuff(&gzin);
    360   }
    361   {
    362     ArrayOutputStream output(buffer, kBufferSize);
    363     GzipOutputStream gzout(&output, GzipOutputStream::GZIP);
    364     WriteStuff(&gzout);
    365     gzout.Close();
    366     size = output.ByteCount();
    367   }
    368   {
    369     ArrayInputStream input(buffer, size);
    370     GzipInputStream gzin(&input, GzipInputStream::AUTO);
    371     ReadStuff(&gzin);
    372   }
    373   delete [] buffer;
    374 }
    375 
    376 string IoTest::Compress(const string& data,
    377                         const GzipOutputStream::Options& options) {
    378   string result;
    379   {
    380     StringOutputStream output(&result);
    381     GzipOutputStream gzout(&output, options);
    382     WriteToOutput(&gzout, data.data(), data.size());
    383   }
    384   return result;
    385 }
    386 
    387 string IoTest::Uncompress(const string& data) {
    388   string result;
    389   {
    390     ArrayInputStream input(data.data(), data.size());
    391     GzipInputStream gzin(&input);
    392     const void* buffer;
    393     int size;
    394     while (gzin.Next(&buffer, &size)) {
    395       result.append(reinterpret_cast<const char*>(buffer), size);
    396     }
    397   }
    398   return result;
    399 }
    400 
    401 TEST_F(IoTest, CompressionOptions) {
    402   // Some ad-hoc testing of compression options.
    403 
    404   string golden;
    405   File::ReadFileToStringOrDie(
    406     TestSourceDir() + "/google/protobuf/testdata/golden_message",
    407     &golden);
    408 
    409   GzipOutputStream::Options options;
    410   string gzip_compressed = Compress(golden, options);
    411 
    412   options.compression_level = 0;
    413   string not_compressed = Compress(golden, options);
    414 
    415   // Try zlib compression for fun.
    416   options = GzipOutputStream::Options();
    417   options.format = GzipOutputStream::ZLIB;
    418   string zlib_compressed = Compress(golden, options);
    419 
    420   // Uncompressed should be bigger than the original since it should have some
    421   // sort of header.
    422   EXPECT_GT(not_compressed.size(), golden.size());
    423 
    424   // Higher compression levels should result in smaller sizes.
    425   EXPECT_LT(zlib_compressed.size(), not_compressed.size());
    426 
    427   // ZLIB format should differ from GZIP format.
    428   EXPECT_TRUE(zlib_compressed != gzip_compressed);
    429 
    430   // Everything should decompress correctly.
    431   EXPECT_TRUE(Uncompress(not_compressed) == golden);
    432   EXPECT_TRUE(Uncompress(gzip_compressed) == golden);
    433   EXPECT_TRUE(Uncompress(zlib_compressed) == golden);
    434 }
    435 #endif
    436 
    437 // There is no string input, only string output.  Also, it doesn't support
    438 // explicit block sizes.  So, we'll only run one test and we'll use
    439 // ArrayInput to read back the results.
    440 TEST_F(IoTest, StringIo) {
    441   string str;
    442   {
    443     StringOutputStream output(&str);
    444     WriteStuff(&output);
    445   }
    446   {
    447     ArrayInputStream input(str.data(), str.size());
    448     ReadStuff(&input);
    449   }
    450 }
    451 
    452 
    453 // To test files, we create a temporary file, write, read, truncate, repeat.
    454 TEST_F(IoTest, FileIo) {
    455   string filename = TestTempDir() + "/zero_copy_stream_test_file";
    456 
    457   for (int i = 0; i < kBlockSizeCount; i++) {
    458     for (int j = 0; j < kBlockSizeCount; j++) {
    459       // Make a temporary file.
    460       int file =
    461         open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777);
    462       ASSERT_GE(file, 0);
    463 
    464       {
    465         FileOutputStream output(file, kBlockSizes[i]);
    466         WriteStuff(&output);
    467         EXPECT_EQ(0, output.GetErrno());
    468       }
    469 
    470       // Rewind.
    471       ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1);
    472 
    473       {
    474         FileInputStream input(file, kBlockSizes[j]);
    475         ReadStuff(&input);
    476         EXPECT_EQ(0, input.GetErrno());
    477       }
    478 
    479       close(file);
    480     }
    481   }
    482 }
    483 
    484 #if HAVE_ZLIB
    485 TEST_F(IoTest, GzipFileIo) {
    486   string filename = TestTempDir() + "/zero_copy_stream_test_file";
    487 
    488   for (int i = 0; i < kBlockSizeCount; i++) {
    489     for (int j = 0; j < kBlockSizeCount; j++) {
    490       // Make a temporary file.
    491       int file =
    492         open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777);
    493       ASSERT_GE(file, 0);
    494       {
    495         FileOutputStream output(file, kBlockSizes[i]);
    496         GzipOutputStream gzout(&output);
    497         WriteStuffLarge(&gzout);
    498         gzout.Close();
    499         output.Flush();
    500         EXPECT_EQ(0, output.GetErrno());
    501       }
    502 
    503       // Rewind.
    504       ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1);
    505 
    506       {
    507         FileInputStream input(file, kBlockSizes[j]);
    508         GzipInputStream gzin(&input);
    509         ReadStuffLarge(&gzin);
    510         EXPECT_EQ(0, input.GetErrno());
    511       }
    512 
    513       close(file);
    514     }
    515   }
    516 }
    517 #endif
    518 
    519 // MSVC raises various debugging exceptions if we try to use a file
    520 // descriptor of -1, defeating our tests below.  This class will disable
    521 // these debug assertions while in scope.
    522 class MsvcDebugDisabler {
    523  public:
    524 #if defined(_MSC_VER) && _MSC_VER >= 1400
    525   MsvcDebugDisabler() {
    526     old_handler_ = _set_invalid_parameter_handler(MyHandler);
    527     old_mode_ = _CrtSetReportMode(_CRT_ASSERT, 0);
    528   }
    529   ~MsvcDebugDisabler() {
    530     old_handler_ = _set_invalid_parameter_handler(old_handler_);
    531     old_mode_ = _CrtSetReportMode(_CRT_ASSERT, old_mode_);
    532   }
    533 
    534   static void MyHandler(const wchar_t *expr,
    535                         const wchar_t *func,
    536                         const wchar_t *file,
    537                         unsigned int line,
    538                         uintptr_t pReserved) {
    539     // do nothing
    540   }
    541 
    542   _invalid_parameter_handler old_handler_;
    543   int old_mode_;
    544 #else
    545   // Dummy constructor and destructor to ensure that GCC doesn't complain
    546   // that debug_disabler is an unused variable.
    547   MsvcDebugDisabler() {}
    548   ~MsvcDebugDisabler() {}
    549 #endif
    550 };
    551 
    552 // Test that FileInputStreams report errors correctly.
    553 TEST_F(IoTest, FileReadError) {
    554   MsvcDebugDisabler debug_disabler;
    555 
    556   // -1 = invalid file descriptor.
    557   FileInputStream input(-1);
    558 
    559   const void* buffer;
    560   int size;
    561   EXPECT_FALSE(input.Next(&buffer, &size));
    562   EXPECT_EQ(EBADF, input.GetErrno());
    563 }
    564 
    565 // Test that FileOutputStreams report errors correctly.
    566 TEST_F(IoTest, FileWriteError) {
    567   MsvcDebugDisabler debug_disabler;
    568 
    569   // -1 = invalid file descriptor.
    570   FileOutputStream input(-1);
    571 
    572   void* buffer;
    573   int size;
    574 
    575   // The first call to Next() succeeds because it doesn't have anything to
    576   // write yet.
    577   EXPECT_TRUE(input.Next(&buffer, &size));
    578 
    579   // Second call fails.
    580   EXPECT_FALSE(input.Next(&buffer, &size));
    581 
    582   EXPECT_EQ(EBADF, input.GetErrno());
    583 }
    584 
    585 // Pipes are not seekable, so File{Input,Output}Stream ends up doing some
    586 // different things to handle them.  We'll test by writing to a pipe and
    587 // reading back from it.
    588 TEST_F(IoTest, PipeIo) {
    589   int files[2];
    590 
    591   for (int i = 0; i < kBlockSizeCount; i++) {
    592     for (int j = 0; j < kBlockSizeCount; j++) {
    593       // Need to create a new pipe each time because ReadStuff() expects
    594       // to see EOF at the end.
    595       ASSERT_EQ(pipe(files), 0);
    596 
    597       {
    598         FileOutputStream output(files[1], kBlockSizes[i]);
    599         WriteStuff(&output);
    600         EXPECT_EQ(0, output.GetErrno());
    601       }
    602       close(files[1]);  // Send EOF.
    603 
    604       {
    605         FileInputStream input(files[0], kBlockSizes[j]);
    606         ReadStuff(&input);
    607         EXPECT_EQ(0, input.GetErrno());
    608       }
    609       close(files[0]);
    610     }
    611   }
    612 }
    613 
    614 // Test using C++ iostreams.
    615 TEST_F(IoTest, IostreamIo) {
    616   for (int i = 0; i < kBlockSizeCount; i++) {
    617     for (int j = 0; j < kBlockSizeCount; j++) {
    618       {
    619         stringstream stream;
    620 
    621         {
    622           OstreamOutputStream output(&stream, kBlockSizes[i]);
    623           WriteStuff(&output);
    624           EXPECT_FALSE(stream.fail());
    625         }
    626 
    627         {
    628           IstreamInputStream input(&stream, kBlockSizes[j]);
    629           ReadStuff(&input);
    630           EXPECT_TRUE(stream.eof());
    631         }
    632       }
    633 
    634       {
    635         stringstream stream;
    636 
    637         {
    638           OstreamOutputStream output(&stream, kBlockSizes[i]);
    639           WriteStuffLarge(&output);
    640           EXPECT_FALSE(stream.fail());
    641         }
    642 
    643         {
    644           IstreamInputStream input(&stream, kBlockSizes[j]);
    645           ReadStuffLarge(&input);
    646           EXPECT_TRUE(stream.eof());
    647         }
    648       }
    649     }
    650   }
    651 }
    652 
    653 // To test ConcatenatingInputStream, we create several ArrayInputStreams
    654 // covering a buffer and then concatenate them.
    655 TEST_F(IoTest, ConcatenatingInputStream) {
    656   const int kBufferSize = 256;
    657   uint8 buffer[kBufferSize];
    658 
    659   // Fill the buffer.
    660   ArrayOutputStream output(buffer, kBufferSize);
    661   WriteStuff(&output);
    662 
    663   // Now split it up into multiple streams of varying sizes.
    664   ASSERT_EQ(68, output.ByteCount());  // Test depends on this.
    665   ArrayInputStream input1(buffer     , 12);
    666   ArrayInputStream input2(buffer + 12,  7);
    667   ArrayInputStream input3(buffer + 19,  6);
    668   ArrayInputStream input4(buffer + 25, 15);
    669   ArrayInputStream input5(buffer + 40,  0);
    670   // Note:  We want to make sure we have a stream boundary somewhere between
    671   // bytes 42 and 62, which is the range that it Skip()ed by ReadStuff().  This
    672   // tests that a bug that existed in the original code for Skip() is fixed.
    673   ArrayInputStream input6(buffer + 40, 10);
    674   ArrayInputStream input7(buffer + 50, 18);  // Total = 68 bytes.
    675 
    676   ZeroCopyInputStream* streams[] =
    677     {&input1, &input2, &input3, &input4, &input5, &input6, &input7};
    678 
    679   // Create the concatenating stream and read.
    680   ConcatenatingInputStream input(streams, GOOGLE_ARRAYSIZE(streams));
    681   ReadStuff(&input);
    682 }
    683 
    684 // To test LimitingInputStream, we write our golden text to a buffer, then
    685 // create an ArrayInputStream that contains the whole buffer (not just the
    686 // bytes written), then use a LimitingInputStream to limit it just to the
    687 // bytes written.
    688 TEST_F(IoTest, LimitingInputStream) {
    689   const int kBufferSize = 256;
    690   uint8 buffer[kBufferSize];
    691 
    692   // Fill the buffer.
    693   ArrayOutputStream output(buffer, kBufferSize);
    694   WriteStuff(&output);
    695 
    696   // Set up input.
    697   ArrayInputStream array_input(buffer, kBufferSize);
    698   LimitingInputStream input(&array_input, output.ByteCount());
    699 
    700   ReadStuff(&input);
    701 }
    702 
    703 // Check that a zero-size array doesn't confuse the code.
    704 TEST(ZeroSizeArray, Input) {
    705   ArrayInputStream input(NULL, 0);
    706   const void* data;
    707   int size;
    708   EXPECT_FALSE(input.Next(&data, &size));
    709 }
    710 
    711 TEST(ZeroSizeArray, Output) {
    712   ArrayOutputStream output(NULL, 0);
    713   void* data;
    714   int size;
    715   EXPECT_FALSE(output.Next(&data, &size));
    716 }
    717 
    718 }  // namespace
    719 }  // namespace io
    720 }  // namespace protobuf
    721 }  // namespace google
    722