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