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@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
75namespace google {
76namespace protobuf {
77namespace io {
78namespace {
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
92class 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
128const int IoTest::kBlockSizes[] = {-1, 1, 2, 5, 7, 10, 23, 64};
129const int IoTest::kBlockSizeCount = GOOGLE_ARRAYSIZE(IoTest::kBlockSizes);
130
131bool 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
159int 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
194void IoTest::WriteString(ZeroCopyOutputStream* output, const string& str) {
195  EXPECT_TRUE(WriteToOutput(output, str.c_str(), str.size()));
196}
197
198void 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
205int 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.
221void 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
237int 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.
253void 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
270TEST_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
289TEST_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
341TEST_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
373TEST_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
409TEST_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
438TEST_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
467TEST_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
499TEST_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
534string 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
545string 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
559TEST_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
594TEST_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.
663TEST_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.
677TEST_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
708TEST_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.
745class 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.
776TEST_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.
789TEST_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.
811TEST_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.
838TEST_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.
878TEST_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.
911TEST_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.
927TEST(ZeroSizeArray, Input) {
928  ArrayInputStream input(NULL, 0);
929  const void* data;
930  int size;
931  EXPECT_FALSE(input.Next(&data, &size));
932}
933
934TEST(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