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