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