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 65 #if HAVE_ZLIB 66 #include <google/protobuf/io/gzip_stream.h> 67 #endif 68 69 #include <google/protobuf/stubs/common.h> 70 #include <google/protobuf/testing/googletest.h> 71 #include <google/protobuf/testing/file.h> 72 #include <gtest/gtest.h> 73 74 namespace google { 75 namespace protobuf { 76 namespace io { 77 namespace { 78 79 #ifdef _WIN32 80 #define pipe(fds) _pipe(fds, 4096, O_BINARY) 81 #endif 82 83 #ifndef O_BINARY 84 #ifdef _O_BINARY 85 #define O_BINARY _O_BINARY 86 #else 87 #define O_BINARY 0 // If this isn't defined, the platform doesn't need it. 88 #endif 89 #endif 90 91 class IoTest : public testing::Test { 92 protected: 93 // Test helpers. 94 95 // Helper to write an array of data to an output stream. 96 bool WriteToOutput(ZeroCopyOutputStream* output, const void* data, int size); 97 // Helper to read a fixed-length array of data from an input stream. 98 int ReadFromInput(ZeroCopyInputStream* input, void* data, int size); 99 // Write a string to the output stream. 100 void WriteString(ZeroCopyOutputStream* output, const string& str); 101 // Read a number of bytes equal to the size of the given string and checks 102 // that it matches the string. 103 void ReadString(ZeroCopyInputStream* input, const string& str); 104 // Writes some text to the output stream in a particular order. Returns 105 // the number of bytes written, incase the caller needs that to set up an 106 // input stream. 107 int WriteStuff(ZeroCopyOutputStream* output); 108 // Reads text from an input stream and expects it to match what 109 // WriteStuff() writes. 110 void ReadStuff(ZeroCopyInputStream* input); 111 112 // Similar to WriteStuff, but performs more sophisticated testing. 113 int WriteStuffLarge(ZeroCopyOutputStream* output); 114 // Reads and tests a stream that should have been written to 115 // via WriteStuffLarge(). 116 void ReadStuffLarge(ZeroCopyInputStream* input); 117 118 #if HAVE_ZLIB 119 string Compress(const string& data, const GzipOutputStream::Options& options); 120 string Uncompress(const string& data); 121 #endif 122 123 static const int kBlockSizes[]; 124 static const int kBlockSizeCount; 125 }; 126 127 const int IoTest::kBlockSizes[] = {-1, 1, 2, 5, 7, 10, 23, 64}; 128 const int IoTest::kBlockSizeCount = GOOGLE_ARRAYSIZE(IoTest::kBlockSizes); 129 130 bool IoTest::WriteToOutput(ZeroCopyOutputStream* output, 131 const void* data, int size) { 132 const uint8* in = reinterpret_cast<const uint8*>(data); 133 int in_size = size; 134 135 void* out; 136 int out_size; 137 138 while (true) { 139 if (!output->Next(&out, &out_size)) { 140 return false; 141 } 142 EXPECT_GT(out_size, 0); 143 144 if (in_size <= out_size) { 145 memcpy(out, in, in_size); 146 output->BackUp(out_size - in_size); 147 return true; 148 } 149 150 memcpy(out, in, out_size); 151 in += out_size; 152 in_size -= out_size; 153 } 154 } 155 156 #define MAX_REPEATED_ZEROS 100 157 158 int IoTest::ReadFromInput(ZeroCopyInputStream* input, void* data, int size) { 159 uint8* out = reinterpret_cast<uint8*>(data); 160 int out_size = size; 161 162 const void* in; 163 int in_size = 0; 164 165 int repeated_zeros = 0; 166 167 while (true) { 168 if (!input->Next(&in, &in_size)) { 169 return size - out_size; 170 } 171 EXPECT_GT(in_size, -1); 172 if (in_size == 0) { 173 repeated_zeros++; 174 } else { 175 repeated_zeros = 0; 176 } 177 EXPECT_LT(repeated_zeros, MAX_REPEATED_ZEROS); 178 179 if (out_size <= in_size) { 180 memcpy(out, in, out_size); 181 if (in_size > out_size) { 182 input->BackUp(in_size - out_size); 183 } 184 return size; // Copied all of it. 185 } 186 187 memcpy(out, in, in_size); 188 out += in_size; 189 out_size -= in_size; 190 } 191 } 192 193 void IoTest::WriteString(ZeroCopyOutputStream* output, const string& str) { 194 EXPECT_TRUE(WriteToOutput(output, str.c_str(), str.size())); 195 } 196 197 void IoTest::ReadString(ZeroCopyInputStream* input, const string& str) { 198 scoped_array<char> buffer(new char[str.size() + 1]); 199 buffer[str.size()] = '\0'; 200 EXPECT_EQ(ReadFromInput(input, buffer.get(), str.size()), str.size()); 201 EXPECT_STREQ(str.c_str(), buffer.get()); 202 } 203 204 int IoTest::WriteStuff(ZeroCopyOutputStream* output) { 205 WriteString(output, "Hello world!\n"); 206 WriteString(output, "Some te"); 207 WriteString(output, "xt. Blah blah."); 208 WriteString(output, "abcdefg"); 209 WriteString(output, "01234567890123456789"); 210 WriteString(output, "foobar"); 211 212 EXPECT_EQ(output->ByteCount(), 68); 213 214 int result = output->ByteCount(); 215 return result; 216 } 217 218 // Reads text from an input stream and expects it to match what WriteStuff() 219 // writes. 220 void IoTest::ReadStuff(ZeroCopyInputStream* input) { 221 ReadString(input, "Hello world!\n"); 222 ReadString(input, "Some text. "); 223 ReadString(input, "Blah "); 224 ReadString(input, "blah."); 225 ReadString(input, "abcdefg"); 226 EXPECT_TRUE(input->Skip(20)); 227 ReadString(input, "foo"); 228 ReadString(input, "bar"); 229 230 EXPECT_EQ(input->ByteCount(), 68); 231 232 uint8 byte; 233 EXPECT_EQ(ReadFromInput(input, &byte, 1), 0); 234 } 235 236 int IoTest::WriteStuffLarge(ZeroCopyOutputStream* output) { 237 WriteString(output, "Hello world!\n"); 238 WriteString(output, "Some te"); 239 WriteString(output, "xt. Blah blah."); 240 WriteString(output, string(100000, 'x')); // A very long string 241 WriteString(output, string(100000, 'y')); // A very long string 242 WriteString(output, "01234567890123456789"); 243 244 EXPECT_EQ(output->ByteCount(), 200055); 245 246 int result = output->ByteCount(); 247 return result; 248 } 249 250 // Reads text from an input stream and expects it to match what WriteStuff() 251 // writes. 252 void IoTest::ReadStuffLarge(ZeroCopyInputStream* input) { 253 ReadString(input, "Hello world!\nSome text. "); 254 EXPECT_TRUE(input->Skip(5)); 255 ReadString(input, "blah."); 256 EXPECT_TRUE(input->Skip(100000 - 10)); 257 ReadString(input, string(10, 'x') + string(100000 - 20000, 'y')); 258 EXPECT_TRUE(input->Skip(20000 - 10)); 259 ReadString(input, "yyyyyyyyyy01234567890123456789"); 260 261 EXPECT_EQ(input->ByteCount(), 200055); 262 263 uint8 byte; 264 EXPECT_EQ(ReadFromInput(input, &byte, 1), 0); 265 } 266 267 // =================================================================== 268 269 TEST_F(IoTest, ArrayIo) { 270 const int kBufferSize = 256; 271 uint8 buffer[kBufferSize]; 272 273 for (int i = 0; i < kBlockSizeCount; i++) { 274 for (int j = 0; j < kBlockSizeCount; j++) { 275 int size; 276 { 277 ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]); 278 size = WriteStuff(&output); 279 } 280 { 281 ArrayInputStream input(buffer, size, kBlockSizes[j]); 282 ReadStuff(&input); 283 } 284 } 285 } 286 } 287 288 #if HAVE_ZLIB 289 TEST_F(IoTest, GzipIo) { 290 const int kBufferSize = 2*1024; 291 uint8* buffer = new uint8[kBufferSize]; 292 for (int i = 0; i < kBlockSizeCount; i++) { 293 for (int j = 0; j < kBlockSizeCount; j++) { 294 for (int z = 0; z < kBlockSizeCount; z++) { 295 int gzip_buffer_size = kBlockSizes[z]; 296 int size; 297 { 298 ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]); 299 GzipOutputStream gzout( 300 &output, GzipOutputStream::GZIP, gzip_buffer_size); 301 WriteStuff(&gzout); 302 gzout.Close(); 303 size = output.ByteCount(); 304 } 305 { 306 ArrayInputStream input(buffer, size, kBlockSizes[j]); 307 GzipInputStream gzin( 308 &input, GzipInputStream::GZIP, gzip_buffer_size); 309 ReadStuff(&gzin); 310 } 311 } 312 } 313 } 314 delete [] buffer; 315 } 316 317 TEST_F(IoTest, ZlibIo) { 318 const int kBufferSize = 2*1024; 319 uint8* buffer = new uint8[kBufferSize]; 320 for (int i = 0; i < kBlockSizeCount; i++) { 321 for (int j = 0; j < kBlockSizeCount; j++) { 322 for (int z = 0; z < kBlockSizeCount; z++) { 323 int gzip_buffer_size = kBlockSizes[z]; 324 int size; 325 { 326 ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]); 327 GzipOutputStream gzout( 328 &output, GzipOutputStream::ZLIB, gzip_buffer_size); 329 WriteStuff(&gzout); 330 gzout.Close(); 331 size = output.ByteCount(); 332 } 333 { 334 ArrayInputStream input(buffer, size, kBlockSizes[j]); 335 GzipInputStream gzin( 336 &input, GzipInputStream::ZLIB, gzip_buffer_size); 337 ReadStuff(&gzin); 338 } 339 } 340 } 341 } 342 delete [] buffer; 343 } 344 345 TEST_F(IoTest, ZlibIoInputAutodetect) { 346 const int kBufferSize = 2*1024; 347 uint8* buffer = new uint8[kBufferSize]; 348 int size; 349 { 350 ArrayOutputStream output(buffer, kBufferSize); 351 GzipOutputStream gzout(&output, GzipOutputStream::ZLIB); 352 WriteStuff(&gzout); 353 gzout.Close(); 354 size = output.ByteCount(); 355 } 356 { 357 ArrayInputStream input(buffer, size); 358 GzipInputStream gzin(&input, GzipInputStream::AUTO); 359 ReadStuff(&gzin); 360 } 361 { 362 ArrayOutputStream output(buffer, kBufferSize); 363 GzipOutputStream gzout(&output, GzipOutputStream::GZIP); 364 WriteStuff(&gzout); 365 gzout.Close(); 366 size = output.ByteCount(); 367 } 368 { 369 ArrayInputStream input(buffer, size); 370 GzipInputStream gzin(&input, GzipInputStream::AUTO); 371 ReadStuff(&gzin); 372 } 373 delete [] buffer; 374 } 375 376 string IoTest::Compress(const string& data, 377 const GzipOutputStream::Options& options) { 378 string result; 379 { 380 StringOutputStream output(&result); 381 GzipOutputStream gzout(&output, options); 382 WriteToOutput(&gzout, data.data(), data.size()); 383 } 384 return result; 385 } 386 387 string IoTest::Uncompress(const string& data) { 388 string result; 389 { 390 ArrayInputStream input(data.data(), data.size()); 391 GzipInputStream gzin(&input); 392 const void* buffer; 393 int size; 394 while (gzin.Next(&buffer, &size)) { 395 result.append(reinterpret_cast<const char*>(buffer), size); 396 } 397 } 398 return result; 399 } 400 401 TEST_F(IoTest, CompressionOptions) { 402 // Some ad-hoc testing of compression options. 403 404 string golden; 405 File::ReadFileToStringOrDie( 406 TestSourceDir() + "/google/protobuf/testdata/golden_message", 407 &golden); 408 409 GzipOutputStream::Options options; 410 string gzip_compressed = Compress(golden, options); 411 412 options.compression_level = 0; 413 string not_compressed = Compress(golden, options); 414 415 // Try zlib compression for fun. 416 options = GzipOutputStream::Options(); 417 options.format = GzipOutputStream::ZLIB; 418 string zlib_compressed = Compress(golden, options); 419 420 // Uncompressed should be bigger than the original since it should have some 421 // sort of header. 422 EXPECT_GT(not_compressed.size(), golden.size()); 423 424 // Higher compression levels should result in smaller sizes. 425 EXPECT_LT(zlib_compressed.size(), not_compressed.size()); 426 427 // ZLIB format should differ from GZIP format. 428 EXPECT_TRUE(zlib_compressed != gzip_compressed); 429 430 // Everything should decompress correctly. 431 EXPECT_TRUE(Uncompress(not_compressed) == golden); 432 EXPECT_TRUE(Uncompress(gzip_compressed) == golden); 433 EXPECT_TRUE(Uncompress(zlib_compressed) == golden); 434 } 435 #endif 436 437 // There is no string input, only string output. Also, it doesn't support 438 // explicit block sizes. So, we'll only run one test and we'll use 439 // ArrayInput to read back the results. 440 TEST_F(IoTest, StringIo) { 441 string str; 442 { 443 StringOutputStream output(&str); 444 WriteStuff(&output); 445 } 446 { 447 ArrayInputStream input(str.data(), str.size()); 448 ReadStuff(&input); 449 } 450 } 451 452 453 // To test files, we create a temporary file, write, read, truncate, repeat. 454 TEST_F(IoTest, FileIo) { 455 string filename = TestTempDir() + "/zero_copy_stream_test_file"; 456 457 for (int i = 0; i < kBlockSizeCount; i++) { 458 for (int j = 0; j < kBlockSizeCount; j++) { 459 // Make a temporary file. 460 int file = 461 open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777); 462 ASSERT_GE(file, 0); 463 464 { 465 FileOutputStream output(file, kBlockSizes[i]); 466 WriteStuff(&output); 467 EXPECT_EQ(0, output.GetErrno()); 468 } 469 470 // Rewind. 471 ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1); 472 473 { 474 FileInputStream input(file, kBlockSizes[j]); 475 ReadStuff(&input); 476 EXPECT_EQ(0, input.GetErrno()); 477 } 478 479 close(file); 480 } 481 } 482 } 483 484 #if HAVE_ZLIB 485 TEST_F(IoTest, GzipFileIo) { 486 string filename = TestTempDir() + "/zero_copy_stream_test_file"; 487 488 for (int i = 0; i < kBlockSizeCount; i++) { 489 for (int j = 0; j < kBlockSizeCount; j++) { 490 // Make a temporary file. 491 int file = 492 open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777); 493 ASSERT_GE(file, 0); 494 { 495 FileOutputStream output(file, kBlockSizes[i]); 496 GzipOutputStream gzout(&output); 497 WriteStuffLarge(&gzout); 498 gzout.Close(); 499 output.Flush(); 500 EXPECT_EQ(0, output.GetErrno()); 501 } 502 503 // Rewind. 504 ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1); 505 506 { 507 FileInputStream input(file, kBlockSizes[j]); 508 GzipInputStream gzin(&input); 509 ReadStuffLarge(&gzin); 510 EXPECT_EQ(0, input.GetErrno()); 511 } 512 513 close(file); 514 } 515 } 516 } 517 #endif 518 519 // MSVC raises various debugging exceptions if we try to use a file 520 // descriptor of -1, defeating our tests below. This class will disable 521 // these debug assertions while in scope. 522 class MsvcDebugDisabler { 523 public: 524 #if defined(_MSC_VER) && _MSC_VER >= 1400 525 MsvcDebugDisabler() { 526 old_handler_ = _set_invalid_parameter_handler(MyHandler); 527 old_mode_ = _CrtSetReportMode(_CRT_ASSERT, 0); 528 } 529 ~MsvcDebugDisabler() { 530 old_handler_ = _set_invalid_parameter_handler(old_handler_); 531 old_mode_ = _CrtSetReportMode(_CRT_ASSERT, old_mode_); 532 } 533 534 static void MyHandler(const wchar_t *expr, 535 const wchar_t *func, 536 const wchar_t *file, 537 unsigned int line, 538 uintptr_t pReserved) { 539 // do nothing 540 } 541 542 _invalid_parameter_handler old_handler_; 543 int old_mode_; 544 #else 545 // Dummy constructor and destructor to ensure that GCC doesn't complain 546 // that debug_disabler is an unused variable. 547 MsvcDebugDisabler() {} 548 ~MsvcDebugDisabler() {} 549 #endif 550 }; 551 552 // Test that FileInputStreams report errors correctly. 553 TEST_F(IoTest, FileReadError) { 554 MsvcDebugDisabler debug_disabler; 555 556 // -1 = invalid file descriptor. 557 FileInputStream input(-1); 558 559 const void* buffer; 560 int size; 561 EXPECT_FALSE(input.Next(&buffer, &size)); 562 EXPECT_EQ(EBADF, input.GetErrno()); 563 } 564 565 // Test that FileOutputStreams report errors correctly. 566 TEST_F(IoTest, FileWriteError) { 567 MsvcDebugDisabler debug_disabler; 568 569 // -1 = invalid file descriptor. 570 FileOutputStream input(-1); 571 572 void* buffer; 573 int size; 574 575 // The first call to Next() succeeds because it doesn't have anything to 576 // write yet. 577 EXPECT_TRUE(input.Next(&buffer, &size)); 578 579 // Second call fails. 580 EXPECT_FALSE(input.Next(&buffer, &size)); 581 582 EXPECT_EQ(EBADF, input.GetErrno()); 583 } 584 585 // Pipes are not seekable, so File{Input,Output}Stream ends up doing some 586 // different things to handle them. We'll test by writing to a pipe and 587 // reading back from it. 588 TEST_F(IoTest, PipeIo) { 589 int files[2]; 590 591 for (int i = 0; i < kBlockSizeCount; i++) { 592 for (int j = 0; j < kBlockSizeCount; j++) { 593 // Need to create a new pipe each time because ReadStuff() expects 594 // to see EOF at the end. 595 ASSERT_EQ(pipe(files), 0); 596 597 { 598 FileOutputStream output(files[1], kBlockSizes[i]); 599 WriteStuff(&output); 600 EXPECT_EQ(0, output.GetErrno()); 601 } 602 close(files[1]); // Send EOF. 603 604 { 605 FileInputStream input(files[0], kBlockSizes[j]); 606 ReadStuff(&input); 607 EXPECT_EQ(0, input.GetErrno()); 608 } 609 close(files[0]); 610 } 611 } 612 } 613 614 // Test using C++ iostreams. 615 TEST_F(IoTest, IostreamIo) { 616 for (int i = 0; i < kBlockSizeCount; i++) { 617 for (int j = 0; j < kBlockSizeCount; j++) { 618 { 619 stringstream stream; 620 621 { 622 OstreamOutputStream output(&stream, kBlockSizes[i]); 623 WriteStuff(&output); 624 EXPECT_FALSE(stream.fail()); 625 } 626 627 { 628 IstreamInputStream input(&stream, kBlockSizes[j]); 629 ReadStuff(&input); 630 EXPECT_TRUE(stream.eof()); 631 } 632 } 633 634 { 635 stringstream stream; 636 637 { 638 OstreamOutputStream output(&stream, kBlockSizes[i]); 639 WriteStuffLarge(&output); 640 EXPECT_FALSE(stream.fail()); 641 } 642 643 { 644 IstreamInputStream input(&stream, kBlockSizes[j]); 645 ReadStuffLarge(&input); 646 EXPECT_TRUE(stream.eof()); 647 } 648 } 649 } 650 } 651 } 652 653 // To test ConcatenatingInputStream, we create several ArrayInputStreams 654 // covering a buffer and then concatenate them. 655 TEST_F(IoTest, ConcatenatingInputStream) { 656 const int kBufferSize = 256; 657 uint8 buffer[kBufferSize]; 658 659 // Fill the buffer. 660 ArrayOutputStream output(buffer, kBufferSize); 661 WriteStuff(&output); 662 663 // Now split it up into multiple streams of varying sizes. 664 ASSERT_EQ(68, output.ByteCount()); // Test depends on this. 665 ArrayInputStream input1(buffer , 12); 666 ArrayInputStream input2(buffer + 12, 7); 667 ArrayInputStream input3(buffer + 19, 6); 668 ArrayInputStream input4(buffer + 25, 15); 669 ArrayInputStream input5(buffer + 40, 0); 670 // Note: We want to make sure we have a stream boundary somewhere between 671 // bytes 42 and 62, which is the range that it Skip()ed by ReadStuff(). This 672 // tests that a bug that existed in the original code for Skip() is fixed. 673 ArrayInputStream input6(buffer + 40, 10); 674 ArrayInputStream input7(buffer + 50, 18); // Total = 68 bytes. 675 676 ZeroCopyInputStream* streams[] = 677 {&input1, &input2, &input3, &input4, &input5, &input6, &input7}; 678 679 // Create the concatenating stream and read. 680 ConcatenatingInputStream input(streams, GOOGLE_ARRAYSIZE(streams)); 681 ReadStuff(&input); 682 } 683 684 // To test LimitingInputStream, we write our golden text to a buffer, then 685 // create an ArrayInputStream that contains the whole buffer (not just the 686 // bytes written), then use a LimitingInputStream to limit it just to the 687 // bytes written. 688 TEST_F(IoTest, LimitingInputStream) { 689 const int kBufferSize = 256; 690 uint8 buffer[kBufferSize]; 691 692 // Fill the buffer. 693 ArrayOutputStream output(buffer, kBufferSize); 694 WriteStuff(&output); 695 696 // Set up input. 697 ArrayInputStream array_input(buffer, kBufferSize); 698 LimitingInputStream input(&array_input, output.ByteCount()); 699 700 ReadStuff(&input); 701 } 702 703 // Check that a zero-size array doesn't confuse the code. 704 TEST(ZeroSizeArray, Input) { 705 ArrayInputStream input(NULL, 0); 706 const void* data; 707 int size; 708 EXPECT_FALSE(input.Next(&data, &size)); 709 } 710 711 TEST(ZeroSizeArray, Output) { 712 ArrayOutputStream output(NULL, 0); 713 void* data; 714 int size; 715 EXPECT_FALSE(output.Next(&data, &size)); 716 } 717 718 } // namespace 719 } // namespace io 720 } // namespace protobuf 721 } // namespace google 722