1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // http://code.google.com/p/protobuf/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 // Author: kenton (at) 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 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 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 194 void IoTest::WriteString(ZeroCopyOutputStream* output, const string& str) { 195 EXPECT_TRUE(WriteToOutput(output, str.c_str(), str.size())); 196 } 197 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 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. 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 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. 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 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 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 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 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 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 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 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 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 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 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 559 TEST_F(IoTest, CompressionOptions) { 560 // Some ad-hoc testing of compression options. 561 562 string golden; 563 File::ReadFileToStringOrDie( 564 TestSourceDir() + "/google/protobuf/testdata/golden_message", 565 &golden); 566 567 GzipOutputStream::Options options; 568 string gzip_compressed = Compress(golden, options); 569 570 options.compression_level = 0; 571 string not_compressed = Compress(golden, options); 572 573 // Try zlib compression for fun. 574 options = GzipOutputStream::Options(); 575 options.format = GzipOutputStream::ZLIB; 576 string zlib_compressed = Compress(golden, options); 577 578 // Uncompressed should be bigger than the original since it should have some 579 // sort of header. 580 EXPECT_GT(not_compressed.size(), golden.size()); 581 582 // Higher compression levels should result in smaller sizes. 583 EXPECT_LT(zlib_compressed.size(), not_compressed.size()); 584 585 // ZLIB format should differ from GZIP format. 586 EXPECT_TRUE(zlib_compressed != gzip_compressed); 587 588 // Everything should decompress correctly. 589 EXPECT_TRUE(Uncompress(not_compressed) == golden); 590 EXPECT_TRUE(Uncompress(gzip_compressed) == golden); 591 EXPECT_TRUE(Uncompress(zlib_compressed) == golden); 592 } 593 594 TEST_F(IoTest, TwoSessionWriteGzip) { 595 // Test that two concatenated gzip streams can be read correctly 596 597 static const char* strA = "0123456789"; 598 static const char* strB = "QuickBrownFox"; 599 const int kBufferSize = 2*1024; 600 uint8* buffer = new uint8[kBufferSize]; 601 char* temp_buffer = new char[40]; 602 603 for (int i = 0; i < kBlockSizeCount; i++) { 604 for (int j = 0; j < kBlockSizeCount; j++) { 605 ArrayOutputStream* output = 606 new ArrayOutputStream(buffer, kBufferSize, kBlockSizes[i]); 607 GzipOutputStream* gzout = new GzipOutputStream(output); 608 CodedOutputStream* coded_output = new CodedOutputStream(gzout); 609 int32 outlen = strlen(strA) + 1; 610 coded_output->WriteVarint32(outlen); 611 coded_output->WriteRaw(strA, outlen); 612 delete coded_output; // flush 613 delete gzout; // flush 614 int64 pos = output->ByteCount(); 615 delete output; 616 output = new ArrayOutputStream( 617 buffer + pos, kBufferSize - pos, kBlockSizes[i]); 618 gzout = new GzipOutputStream(output); 619 coded_output = new CodedOutputStream(gzout); 620 outlen = strlen(strB) + 1; 621 coded_output->WriteVarint32(outlen); 622 coded_output->WriteRaw(strB, outlen); 623 delete coded_output; // flush 624 delete gzout; // flush 625 int64 size = pos + output->ByteCount(); 626 delete output; 627 628 ArrayInputStream* input = 629 new ArrayInputStream(buffer, size, kBlockSizes[j]); 630 GzipInputStream* gzin = new GzipInputStream(input); 631 CodedInputStream* coded_input = new CodedInputStream(gzin); 632 uint32 insize; 633 EXPECT_TRUE(coded_input->ReadVarint32(&insize)); 634 EXPECT_EQ(strlen(strA) + 1, insize); 635 EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize)); 636 EXPECT_EQ(0, memcmp(temp_buffer, strA, insize)) 637 << "strA=" << strA << " in=" << temp_buffer; 638 639 EXPECT_TRUE(coded_input->ReadVarint32(&insize)); 640 EXPECT_EQ(strlen(strB) + 1, insize); 641 EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize)); 642 EXPECT_EQ(0, memcmp(temp_buffer, strB, insize)) 643 << " out_block_size=" << kBlockSizes[i] 644 << " in_block_size=" << kBlockSizes[j] 645 << " pos=" << pos 646 << " size=" << size 647 << " strB=" << strB << " in=" << temp_buffer; 648 649 delete coded_input; 650 delete gzin; 651 delete input; 652 } 653 } 654 655 delete [] temp_buffer; 656 delete [] buffer; 657 } 658 #endif 659 660 // There is no string input, only string output. Also, it doesn't support 661 // explicit block sizes. So, we'll only run one test and we'll use 662 // ArrayInput to read back the results. 663 TEST_F(IoTest, StringIo) { 664 string str; 665 { 666 StringOutputStream output(&str); 667 WriteStuff(&output); 668 } 669 { 670 ArrayInputStream input(str.data(), str.size()); 671 ReadStuff(&input); 672 } 673 } 674 675 676 // To test files, we create a temporary file, write, read, truncate, repeat. 677 TEST_F(IoTest, FileIo) { 678 string filename = TestTempDir() + "/zero_copy_stream_test_file"; 679 680 for (int i = 0; i < kBlockSizeCount; i++) { 681 for (int j = 0; j < kBlockSizeCount; j++) { 682 // Make a temporary file. 683 int file = 684 open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777); 685 ASSERT_GE(file, 0); 686 687 { 688 FileOutputStream output(file, kBlockSizes[i]); 689 WriteStuff(&output); 690 EXPECT_EQ(0, output.GetErrno()); 691 } 692 693 // Rewind. 694 ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1); 695 696 { 697 FileInputStream input(file, kBlockSizes[j]); 698 ReadStuff(&input); 699 EXPECT_EQ(0, input.GetErrno()); 700 } 701 702 close(file); 703 } 704 } 705 } 706 707 #if HAVE_ZLIB 708 TEST_F(IoTest, GzipFileIo) { 709 string filename = TestTempDir() + "/zero_copy_stream_test_file"; 710 711 for (int i = 0; i < kBlockSizeCount; i++) { 712 for (int j = 0; j < kBlockSizeCount; j++) { 713 // Make a temporary file. 714 int file = 715 open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777); 716 ASSERT_GE(file, 0); 717 { 718 FileOutputStream output(file, kBlockSizes[i]); 719 GzipOutputStream gzout(&output); 720 WriteStuffLarge(&gzout); 721 gzout.Close(); 722 output.Flush(); 723 EXPECT_EQ(0, output.GetErrno()); 724 } 725 726 // Rewind. 727 ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1); 728 729 { 730 FileInputStream input(file, kBlockSizes[j]); 731 GzipInputStream gzin(&input); 732 ReadStuffLarge(&gzin); 733 EXPECT_EQ(0, input.GetErrno()); 734 } 735 736 close(file); 737 } 738 } 739 } 740 #endif 741 742 // MSVC raises various debugging exceptions if we try to use a file 743 // descriptor of -1, defeating our tests below. This class will disable 744 // these debug assertions while in scope. 745 class MsvcDebugDisabler { 746 public: 747 #if defined(_MSC_VER) && _MSC_VER >= 1400 748 MsvcDebugDisabler() { 749 old_handler_ = _set_invalid_parameter_handler(MyHandler); 750 old_mode_ = _CrtSetReportMode(_CRT_ASSERT, 0); 751 } 752 ~MsvcDebugDisabler() { 753 old_handler_ = _set_invalid_parameter_handler(old_handler_); 754 old_mode_ = _CrtSetReportMode(_CRT_ASSERT, old_mode_); 755 } 756 757 static void MyHandler(const wchar_t *expr, 758 const wchar_t *func, 759 const wchar_t *file, 760 unsigned int line, 761 uintptr_t pReserved) { 762 // do nothing 763 } 764 765 _invalid_parameter_handler old_handler_; 766 int old_mode_; 767 #else 768 // Dummy constructor and destructor to ensure that GCC doesn't complain 769 // that debug_disabler is an unused variable. 770 MsvcDebugDisabler() {} 771 ~MsvcDebugDisabler() {} 772 #endif 773 }; 774 775 // Test that FileInputStreams report errors correctly. 776 TEST_F(IoTest, FileReadError) { 777 MsvcDebugDisabler debug_disabler; 778 779 // -1 = invalid file descriptor. 780 FileInputStream input(-1); 781 782 const void* buffer; 783 int size; 784 EXPECT_FALSE(input.Next(&buffer, &size)); 785 EXPECT_EQ(EBADF, input.GetErrno()); 786 } 787 788 // Test that FileOutputStreams report errors correctly. 789 TEST_F(IoTest, FileWriteError) { 790 MsvcDebugDisabler debug_disabler; 791 792 // -1 = invalid file descriptor. 793 FileOutputStream input(-1); 794 795 void* buffer; 796 int size; 797 798 // The first call to Next() succeeds because it doesn't have anything to 799 // write yet. 800 EXPECT_TRUE(input.Next(&buffer, &size)); 801 802 // Second call fails. 803 EXPECT_FALSE(input.Next(&buffer, &size)); 804 805 EXPECT_EQ(EBADF, input.GetErrno()); 806 } 807 808 // Pipes are not seekable, so File{Input,Output}Stream ends up doing some 809 // different things to handle them. We'll test by writing to a pipe and 810 // reading back from it. 811 TEST_F(IoTest, PipeIo) { 812 int files[2]; 813 814 for (int i = 0; i < kBlockSizeCount; i++) { 815 for (int j = 0; j < kBlockSizeCount; j++) { 816 // Need to create a new pipe each time because ReadStuff() expects 817 // to see EOF at the end. 818 ASSERT_EQ(pipe(files), 0); 819 820 { 821 FileOutputStream output(files[1], kBlockSizes[i]); 822 WriteStuff(&output); 823 EXPECT_EQ(0, output.GetErrno()); 824 } 825 close(files[1]); // Send EOF. 826 827 { 828 FileInputStream input(files[0], kBlockSizes[j]); 829 ReadStuff(&input); 830 EXPECT_EQ(0, input.GetErrno()); 831 } 832 close(files[0]); 833 } 834 } 835 } 836 837 // Test using C++ iostreams. 838 TEST_F(IoTest, IostreamIo) { 839 for (int i = 0; i < kBlockSizeCount; i++) { 840 for (int j = 0; j < kBlockSizeCount; j++) { 841 { 842 stringstream stream; 843 844 { 845 OstreamOutputStream output(&stream, kBlockSizes[i]); 846 WriteStuff(&output); 847 EXPECT_FALSE(stream.fail()); 848 } 849 850 { 851 IstreamInputStream input(&stream, kBlockSizes[j]); 852 ReadStuff(&input); 853 EXPECT_TRUE(stream.eof()); 854 } 855 } 856 857 { 858 stringstream stream; 859 860 { 861 OstreamOutputStream output(&stream, kBlockSizes[i]); 862 WriteStuffLarge(&output); 863 EXPECT_FALSE(stream.fail()); 864 } 865 866 { 867 IstreamInputStream input(&stream, kBlockSizes[j]); 868 ReadStuffLarge(&input); 869 EXPECT_TRUE(stream.eof()); 870 } 871 } 872 } 873 } 874 } 875 876 // To test ConcatenatingInputStream, we create several ArrayInputStreams 877 // covering a buffer and then concatenate them. 878 TEST_F(IoTest, ConcatenatingInputStream) { 879 const int kBufferSize = 256; 880 uint8 buffer[kBufferSize]; 881 882 // Fill the buffer. 883 ArrayOutputStream output(buffer, kBufferSize); 884 WriteStuff(&output); 885 886 // Now split it up into multiple streams of varying sizes. 887 ASSERT_EQ(68, output.ByteCount()); // Test depends on this. 888 ArrayInputStream input1(buffer , 12); 889 ArrayInputStream input2(buffer + 12, 7); 890 ArrayInputStream input3(buffer + 19, 6); 891 ArrayInputStream input4(buffer + 25, 15); 892 ArrayInputStream input5(buffer + 40, 0); 893 // Note: We want to make sure we have a stream boundary somewhere between 894 // bytes 42 and 62, which is the range that it Skip()ed by ReadStuff(). This 895 // tests that a bug that existed in the original code for Skip() is fixed. 896 ArrayInputStream input6(buffer + 40, 10); 897 ArrayInputStream input7(buffer + 50, 18); // Total = 68 bytes. 898 899 ZeroCopyInputStream* streams[] = 900 {&input1, &input2, &input3, &input4, &input5, &input6, &input7}; 901 902 // Create the concatenating stream and read. 903 ConcatenatingInputStream input(streams, GOOGLE_ARRAYSIZE(streams)); 904 ReadStuff(&input); 905 } 906 907 // To test LimitingInputStream, we write our golden text to a buffer, then 908 // create an ArrayInputStream that contains the whole buffer (not just the 909 // bytes written), then use a LimitingInputStream to limit it just to the 910 // bytes written. 911 TEST_F(IoTest, LimitingInputStream) { 912 const int kBufferSize = 256; 913 uint8 buffer[kBufferSize]; 914 915 // Fill the buffer. 916 ArrayOutputStream output(buffer, kBufferSize); 917 WriteStuff(&output); 918 919 // Set up input. 920 ArrayInputStream array_input(buffer, kBufferSize); 921 LimitingInputStream input(&array_input, output.ByteCount()); 922 923 ReadStuff(&input); 924 } 925 926 // Check that a zero-size array doesn't confuse the code. 927 TEST(ZeroSizeArray, Input) { 928 ArrayInputStream input(NULL, 0); 929 const void* data; 930 int size; 931 EXPECT_FALSE(input.Next(&data, &size)); 932 } 933 934 TEST(ZeroSizeArray, Output) { 935 ArrayOutputStream output(NULL, 0); 936 void* data; 937 int size; 938 EXPECT_FALSE(output.Next(&data, &size)); 939 } 940 941 } // namespace 942 } // namespace io 943 } // namespace protobuf 944 } // namespace google 945