PageRenderTime 73ms CodeModel.GetById 14ms app.highlight 52ms RepoModel.GetById 1ms app.codeStats 1ms

/thirdparty/breakpad/third_party/protobuf/protobuf/src/google/protobuf/io/zero_copy_stream_unittest.cc

http://github.com/tomahawk-player/tomahawk
C++ | 850 lines | 615 code | 119 blank | 116 comment | 40 complexity | 4d160cc9c0f7ea6420dfd93e9763fc22 MD5 | raw file
  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, ZlibIo) {
374  const int kBufferSize = 2*1024;
375  uint8* buffer = new uint8[kBufferSize];
376  for (int i = 0; i < kBlockSizeCount; i++) {
377    for (int j = 0; j < kBlockSizeCount; j++) {
378      for (int z = 0; z < kBlockSizeCount; z++) {
379        int gzip_buffer_size = kBlockSizes[z];
380        int size;
381        {
382          ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
383          GzipOutputStream::Options options;
384          options.format = GzipOutputStream::ZLIB;
385          if (gzip_buffer_size != -1) {
386            options.buffer_size = gzip_buffer_size;
387          }
388          GzipOutputStream gzout(&output, options);
389          WriteStuff(&gzout);
390          gzout.Close();
391          size = output.ByteCount();
392        }
393        {
394          ArrayInputStream input(buffer, size, kBlockSizes[j]);
395          GzipInputStream gzin(
396              &input, GzipInputStream::ZLIB, gzip_buffer_size);
397          ReadStuff(&gzin);
398        }
399      }
400    }
401  }
402  delete [] buffer;
403}
404
405TEST_F(IoTest, ZlibIoInputAutodetect) {
406  const int kBufferSize = 2*1024;
407  uint8* buffer = new uint8[kBufferSize];
408  int size;
409  {
410    ArrayOutputStream output(buffer, kBufferSize);
411    GzipOutputStream::Options options;
412    options.format = GzipOutputStream::ZLIB;
413    GzipOutputStream gzout(&output, options);
414    WriteStuff(&gzout);
415    gzout.Close();
416    size = output.ByteCount();
417  }
418  {
419    ArrayInputStream input(buffer, size);
420    GzipInputStream gzin(&input, GzipInputStream::AUTO);
421    ReadStuff(&gzin);
422  }
423  {
424    ArrayOutputStream output(buffer, kBufferSize);
425    GzipOutputStream::Options options;
426    options.format = GzipOutputStream::GZIP;
427    GzipOutputStream gzout(&output, options);
428    WriteStuff(&gzout);
429    gzout.Close();
430    size = output.ByteCount();
431  }
432  {
433    ArrayInputStream input(buffer, size);
434    GzipInputStream gzin(&input, GzipInputStream::AUTO);
435    ReadStuff(&gzin);
436  }
437  delete [] buffer;
438}
439
440string IoTest::Compress(const string& data,
441                        const GzipOutputStream::Options& options) {
442  string result;
443  {
444    StringOutputStream output(&result);
445    GzipOutputStream gzout(&output, options);
446    WriteToOutput(&gzout, data.data(), data.size());
447  }
448  return result;
449}
450
451string IoTest::Uncompress(const string& data) {
452  string result;
453  {
454    ArrayInputStream input(data.data(), data.size());
455    GzipInputStream gzin(&input);
456    const void* buffer;
457    int size;
458    while (gzin.Next(&buffer, &size)) {
459      result.append(reinterpret_cast<const char*>(buffer), size);
460    }
461  }
462  return result;
463}
464
465TEST_F(IoTest, CompressionOptions) {
466  // Some ad-hoc testing of compression options.
467
468  string golden;
469  File::ReadFileToStringOrDie(
470    TestSourceDir() + "/google/protobuf/testdata/golden_message",
471    &golden);
472
473  GzipOutputStream::Options options;
474  string gzip_compressed = Compress(golden, options);
475
476  options.compression_level = 0;
477  string not_compressed = Compress(golden, options);
478
479  // Try zlib compression for fun.
480  options = GzipOutputStream::Options();
481  options.format = GzipOutputStream::ZLIB;
482  string zlib_compressed = Compress(golden, options);
483
484  // Uncompressed should be bigger than the original since it should have some
485  // sort of header.
486  EXPECT_GT(not_compressed.size(), golden.size());
487
488  // Higher compression levels should result in smaller sizes.
489  EXPECT_LT(zlib_compressed.size(), not_compressed.size());
490
491  // ZLIB format should differ from GZIP format.
492  EXPECT_TRUE(zlib_compressed != gzip_compressed);
493
494  // Everything should decompress correctly.
495  EXPECT_TRUE(Uncompress(not_compressed) == golden);
496  EXPECT_TRUE(Uncompress(gzip_compressed) == golden);
497  EXPECT_TRUE(Uncompress(zlib_compressed) == golden);
498}
499
500TEST_F(IoTest, TwoSessionWriteGzip) {
501  // Test that two concatenated gzip streams can be read correctly
502
503  static const char* strA = "0123456789";
504  static const char* strB = "QuickBrownFox";
505  const int kBufferSize = 2*1024;
506  uint8* buffer = new uint8[kBufferSize];
507  char* temp_buffer = new char[40];
508
509  for (int i = 0; i < kBlockSizeCount; i++) {
510    for (int j = 0; j < kBlockSizeCount; j++) {
511      ArrayOutputStream* output =
512          new ArrayOutputStream(buffer, kBufferSize, kBlockSizes[i]);
513      GzipOutputStream* gzout = new GzipOutputStream(output);
514      CodedOutputStream* coded_output = new CodedOutputStream(gzout);
515      int32 outlen = strlen(strA) + 1;
516      coded_output->WriteVarint32(outlen);
517      coded_output->WriteRaw(strA, outlen);
518      delete coded_output;  // flush
519      delete gzout;  // flush
520      int64 pos = output->ByteCount();
521      delete output;
522      output = new ArrayOutputStream(
523          buffer + pos, kBufferSize - pos, kBlockSizes[i]);
524      gzout = new GzipOutputStream(output);
525      coded_output = new CodedOutputStream(gzout);
526      outlen = strlen(strB) + 1;
527      coded_output->WriteVarint32(outlen);
528      coded_output->WriteRaw(strB, outlen);
529      delete coded_output;  // flush
530      delete gzout;  // flush
531      int64 size = pos + output->ByteCount();
532      delete output;
533
534      ArrayInputStream* input =
535          new ArrayInputStream(buffer, size, kBlockSizes[j]);
536      GzipInputStream* gzin = new GzipInputStream(input);
537      CodedInputStream* coded_input = new CodedInputStream(gzin);
538      uint32 insize;
539      EXPECT_TRUE(coded_input->ReadVarint32(&insize));
540      EXPECT_EQ(strlen(strA) + 1, insize);
541      EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize));
542      EXPECT_EQ(0, memcmp(temp_buffer, strA, insize))
543          << "strA=" << strA << " in=" << temp_buffer;
544
545      EXPECT_TRUE(coded_input->ReadVarint32(&insize));
546      EXPECT_EQ(strlen(strB) + 1, insize);
547      EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize));
548      EXPECT_EQ(0, memcmp(temp_buffer, strB, insize))
549          << " out_block_size=" << kBlockSizes[i]
550          << " in_block_size=" << kBlockSizes[j]
551          << " pos=" << pos
552          << " size=" << size
553          << " strB=" << strB << " in=" << temp_buffer;
554
555      delete coded_input;
556      delete gzin;
557      delete input;
558    }
559  }
560
561  delete [] temp_buffer;
562  delete [] buffer;
563}
564#endif
565
566// There is no string input, only string output.  Also, it doesn't support
567// explicit block sizes.  So, we'll only run one test and we'll use
568// ArrayInput to read back the results.
569TEST_F(IoTest, StringIo) {
570  string str;
571  {
572    StringOutputStream output(&str);
573    WriteStuff(&output);
574  }
575  {
576    ArrayInputStream input(str.data(), str.size());
577    ReadStuff(&input);
578  }
579}
580
581
582// To test files, we create a temporary file, write, read, truncate, repeat.
583TEST_F(IoTest, FileIo) {
584  string filename = TestTempDir() + "/zero_copy_stream_test_file";
585
586  for (int i = 0; i < kBlockSizeCount; i++) {
587    for (int j = 0; j < kBlockSizeCount; j++) {
588      // Make a temporary file.
589      int file =
590        open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777);
591      ASSERT_GE(file, 0);
592
593      {
594        FileOutputStream output(file, kBlockSizes[i]);
595        WriteStuff(&output);
596        EXPECT_EQ(0, output.GetErrno());
597      }
598
599      // Rewind.
600      ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1);
601
602      {
603        FileInputStream input(file, kBlockSizes[j]);
604        ReadStuff(&input);
605        EXPECT_EQ(0, input.GetErrno());
606      }
607
608      close(file);
609    }
610  }
611}
612
613#if HAVE_ZLIB
614TEST_F(IoTest, GzipFileIo) {
615  string filename = TestTempDir() + "/zero_copy_stream_test_file";
616
617  for (int i = 0; i < kBlockSizeCount; i++) {
618    for (int j = 0; j < kBlockSizeCount; j++) {
619      // Make a temporary file.
620      int file =
621        open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777);
622      ASSERT_GE(file, 0);
623      {
624        FileOutputStream output(file, kBlockSizes[i]);
625        GzipOutputStream gzout(&output);
626        WriteStuffLarge(&gzout);
627        gzout.Close();
628        output.Flush();
629        EXPECT_EQ(0, output.GetErrno());
630      }
631
632      // Rewind.
633      ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1);
634
635      {
636        FileInputStream input(file, kBlockSizes[j]);
637        GzipInputStream gzin(&input);
638        ReadStuffLarge(&gzin);
639        EXPECT_EQ(0, input.GetErrno());
640      }
641
642      close(file);
643    }
644  }
645}
646#endif
647
648// MSVC raises various debugging exceptions if we try to use a file
649// descriptor of -1, defeating our tests below.  This class will disable
650// these debug assertions while in scope.
651class MsvcDebugDisabler {
652 public:
653#if defined(_MSC_VER) && _MSC_VER >= 1400
654  MsvcDebugDisabler() {
655    old_handler_ = _set_invalid_parameter_handler(MyHandler);
656    old_mode_ = _CrtSetReportMode(_CRT_ASSERT, 0);
657  }
658  ~MsvcDebugDisabler() {
659    old_handler_ = _set_invalid_parameter_handler(old_handler_);
660    old_mode_ = _CrtSetReportMode(_CRT_ASSERT, old_mode_);
661  }
662
663  static void MyHandler(const wchar_t *expr,
664                        const wchar_t *func,
665                        const wchar_t *file,
666                        unsigned int line,
667                        uintptr_t pReserved) {
668    // do nothing
669  }
670
671  _invalid_parameter_handler old_handler_;
672  int old_mode_;
673#else
674  // Dummy constructor and destructor to ensure that GCC doesn't complain
675  // that debug_disabler is an unused variable.
676  MsvcDebugDisabler() {}
677  ~MsvcDebugDisabler() {}
678#endif
679};
680
681// Test that FileInputStreams report errors correctly.
682TEST_F(IoTest, FileReadError) {
683  MsvcDebugDisabler debug_disabler;
684
685  // -1 = invalid file descriptor.
686  FileInputStream input(-1);
687
688  const void* buffer;
689  int size;
690  EXPECT_FALSE(input.Next(&buffer, &size));
691  EXPECT_EQ(EBADF, input.GetErrno());
692}
693
694// Test that FileOutputStreams report errors correctly.
695TEST_F(IoTest, FileWriteError) {
696  MsvcDebugDisabler debug_disabler;
697
698  // -1 = invalid file descriptor.
699  FileOutputStream input(-1);
700
701  void* buffer;
702  int size;
703
704  // The first call to Next() succeeds because it doesn't have anything to
705  // write yet.
706  EXPECT_TRUE(input.Next(&buffer, &size));
707
708  // Second call fails.
709  EXPECT_FALSE(input.Next(&buffer, &size));
710
711  EXPECT_EQ(EBADF, input.GetErrno());
712}
713
714// Pipes are not seekable, so File{Input,Output}Stream ends up doing some
715// different things to handle them.  We'll test by writing to a pipe and
716// reading back from it.
717TEST_F(IoTest, PipeIo) {
718  int files[2];
719
720  for (int i = 0; i < kBlockSizeCount; i++) {
721    for (int j = 0; j < kBlockSizeCount; j++) {
722      // Need to create a new pipe each time because ReadStuff() expects
723      // to see EOF at the end.
724      ASSERT_EQ(pipe(files), 0);
725
726      {
727        FileOutputStream output(files[1], kBlockSizes[i]);
728        WriteStuff(&output);
729        EXPECT_EQ(0, output.GetErrno());
730      }
731      close(files[1]);  // Send EOF.
732
733      {
734        FileInputStream input(files[0], kBlockSizes[j]);
735        ReadStuff(&input);
736        EXPECT_EQ(0, input.GetErrno());
737      }
738      close(files[0]);
739    }
740  }
741}
742
743// Test using C++ iostreams.
744TEST_F(IoTest, IostreamIo) {
745  for (int i = 0; i < kBlockSizeCount; i++) {
746    for (int j = 0; j < kBlockSizeCount; j++) {
747      {
748        stringstream stream;
749
750        {
751          OstreamOutputStream output(&stream, kBlockSizes[i]);
752          WriteStuff(&output);
753          EXPECT_FALSE(stream.fail());
754        }
755
756        {
757          IstreamInputStream input(&stream, kBlockSizes[j]);
758          ReadStuff(&input);
759          EXPECT_TRUE(stream.eof());
760        }
761      }
762
763      {
764        stringstream stream;
765
766        {
767          OstreamOutputStream output(&stream, kBlockSizes[i]);
768          WriteStuffLarge(&output);
769          EXPECT_FALSE(stream.fail());
770        }
771
772        {
773          IstreamInputStream input(&stream, kBlockSizes[j]);
774          ReadStuffLarge(&input);
775          EXPECT_TRUE(stream.eof());
776        }
777      }
778    }
779  }
780}
781
782// To test ConcatenatingInputStream, we create several ArrayInputStreams
783// covering a buffer and then concatenate them.
784TEST_F(IoTest, ConcatenatingInputStream) {
785  const int kBufferSize = 256;
786  uint8 buffer[kBufferSize];
787
788  // Fill the buffer.
789  ArrayOutputStream output(buffer, kBufferSize);
790  WriteStuff(&output);
791
792  // Now split it up into multiple streams of varying sizes.
793  ASSERT_EQ(68, output.ByteCount());  // Test depends on this.
794  ArrayInputStream input1(buffer     , 12);
795  ArrayInputStream input2(buffer + 12,  7);
796  ArrayInputStream input3(buffer + 19,  6);
797  ArrayInputStream input4(buffer + 25, 15);
798  ArrayInputStream input5(buffer + 40,  0);
799  // Note:  We want to make sure we have a stream boundary somewhere between
800  // bytes 42 and 62, which is the range that it Skip()ed by ReadStuff().  This
801  // tests that a bug that existed in the original code for Skip() is fixed.
802  ArrayInputStream input6(buffer + 40, 10);
803  ArrayInputStream input7(buffer + 50, 18);  // Total = 68 bytes.
804
805  ZeroCopyInputStream* streams[] =
806    {&input1, &input2, &input3, &input4, &input5, &input6, &input7};
807
808  // Create the concatenating stream and read.
809  ConcatenatingInputStream input(streams, GOOGLE_ARRAYSIZE(streams));
810  ReadStuff(&input);
811}
812
813// To test LimitingInputStream, we write our golden text to a buffer, then
814// create an ArrayInputStream that contains the whole buffer (not just the
815// bytes written), then use a LimitingInputStream to limit it just to the
816// bytes written.
817TEST_F(IoTest, LimitingInputStream) {
818  const int kBufferSize = 256;
819  uint8 buffer[kBufferSize];
820
821  // Fill the buffer.
822  ArrayOutputStream output(buffer, kBufferSize);
823  WriteStuff(&output);
824
825  // Set up input.
826  ArrayInputStream array_input(buffer, kBufferSize);
827  LimitingInputStream input(&array_input, output.ByteCount());
828
829  ReadStuff(&input);
830}
831
832// Check that a zero-size array doesn't confuse the code.
833TEST(ZeroSizeArray, Input) {
834  ArrayInputStream input(NULL, 0);
835  const void* data;
836  int size;
837  EXPECT_FALSE(input.Next(&data, &size));
838}
839
840TEST(ZeroSizeArray, Output) {
841  ArrayOutputStream output(NULL, 0);
842  void* data;
843  int size;
844  EXPECT_FALSE(output.Next(&data, &size));
845}
846
847}  // namespace
848}  // namespace io
849}  // namespace protobuf
850}  // namespace google