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