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, in case 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 // Verifies that outputs up to kint32max can be created.
TEST_F(IoTest,LargeOutput)716 TEST_F(IoTest, LargeOutput) {
717 std::string str;
718 StringOutputStream output(&str);
719 void* unused_data;
720 int size;
721 // Repeatedly calling Next should eventually grow the buffer to kint32max.
722 do {
723 EXPECT_TRUE(output.Next(&unused_data, &size));
724 } while (str.size() < std::numeric_limits<int>::max());
725 // Further increases should be possible.
726 output.Next(&unused_data, &size);
727 EXPECT_GT(size, 0);
728 }
729
730
731 // To test files, we create a temporary file, write, read, truncate, repeat.
TEST_F(IoTest,FileIo)732 TEST_F(IoTest, FileIo) {
733 std::string filename = TestTempDir() + "/zero_copy_stream_test_file";
734
735 for (int i = 0; i < kBlockSizeCount; i++) {
736 for (int j = 0; j < kBlockSizeCount; j++) {
737 // Make a temporary file.
738 int file =
739 open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777);
740 ASSERT_GE(file, 0);
741
742 {
743 FileOutputStream output(file, kBlockSizes[i]);
744 WriteStuff(&output);
745 EXPECT_EQ(0, output.GetErrno());
746 }
747
748 // Rewind.
749 ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1);
750
751 {
752 FileInputStream input(file, kBlockSizes[j]);
753 ReadStuff(&input);
754 EXPECT_EQ(0, input.GetErrno());
755 }
756
757 close(file);
758 }
759 }
760 }
761
762 #if HAVE_ZLIB
TEST_F(IoTest,GzipFileIo)763 TEST_F(IoTest, GzipFileIo) {
764 std::string filename = TestTempDir() + "/zero_copy_stream_test_file";
765
766 for (int i = 0; i < kBlockSizeCount; i++) {
767 for (int j = 0; j < kBlockSizeCount; j++) {
768 // Make a temporary file.
769 int file =
770 open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777);
771 ASSERT_GE(file, 0);
772 {
773 FileOutputStream output(file, kBlockSizes[i]);
774 GzipOutputStream gzout(&output);
775 WriteStuffLarge(&gzout);
776 gzout.Close();
777 output.Flush();
778 EXPECT_EQ(0, output.GetErrno());
779 }
780
781 // Rewind.
782 ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1);
783
784 {
785 FileInputStream input(file, kBlockSizes[j]);
786 GzipInputStream gzin(&input);
787 ReadStuffLarge(&gzin);
788 EXPECT_EQ(0, input.GetErrno());
789 }
790
791 close(file);
792 }
793 }
794 }
795 #endif
796
797 // MSVC raises various debugging exceptions if we try to use a file
798 // descriptor of -1, defeating our tests below. This class will disable
799 // these debug assertions while in scope.
800 class MsvcDebugDisabler {
801 public:
802 #if defined(_MSC_VER) && _MSC_VER >= 1400
MsvcDebugDisabler()803 MsvcDebugDisabler() {
804 old_handler_ = _set_invalid_parameter_handler(MyHandler);
805 old_mode_ = _CrtSetReportMode(_CRT_ASSERT, 0);
806 }
~MsvcDebugDisabler()807 ~MsvcDebugDisabler() {
808 old_handler_ = _set_invalid_parameter_handler(old_handler_);
809 old_mode_ = _CrtSetReportMode(_CRT_ASSERT, old_mode_);
810 }
811
MyHandler(const wchar_t * expr,const wchar_t * func,const wchar_t * file,unsigned int line,uintptr_t pReserved)812 static void MyHandler(const wchar_t* expr, const wchar_t* func,
813 const wchar_t* file, unsigned int line,
814 uintptr_t pReserved) {
815 // do nothing
816 }
817
818 _invalid_parameter_handler old_handler_;
819 int old_mode_;
820 #else
821 // Dummy constructor and destructor to ensure that GCC doesn't complain
822 // that debug_disabler is an unused variable.
823 MsvcDebugDisabler() {}
824 ~MsvcDebugDisabler() {}
825 #endif
826 };
827
828 // Test that FileInputStreams report errors correctly.
TEST_F(IoTest,FileReadError)829 TEST_F(IoTest, FileReadError) {
830 MsvcDebugDisabler debug_disabler;
831
832 // -1 = invalid file descriptor.
833 FileInputStream input(-1);
834
835 const void* buffer;
836 int size;
837 EXPECT_FALSE(input.Next(&buffer, &size));
838 EXPECT_EQ(EBADF, input.GetErrno());
839 }
840
841 // Test that FileOutputStreams report errors correctly.
TEST_F(IoTest,FileWriteError)842 TEST_F(IoTest, FileWriteError) {
843 MsvcDebugDisabler debug_disabler;
844
845 // -1 = invalid file descriptor.
846 FileOutputStream input(-1);
847
848 void* buffer;
849 int size;
850
851 // The first call to Next() succeeds because it doesn't have anything to
852 // write yet.
853 EXPECT_TRUE(input.Next(&buffer, &size));
854
855 // Second call fails.
856 EXPECT_FALSE(input.Next(&buffer, &size));
857
858 EXPECT_EQ(EBADF, input.GetErrno());
859 }
860
861 // Pipes are not seekable, so File{Input,Output}Stream ends up doing some
862 // different things to handle them. We'll test by writing to a pipe and
863 // reading back from it.
TEST_F(IoTest,PipeIo)864 TEST_F(IoTest, PipeIo) {
865 int files[2];
866
867 for (int i = 0; i < kBlockSizeCount; i++) {
868 for (int j = 0; j < kBlockSizeCount; j++) {
869 // Need to create a new pipe each time because ReadStuff() expects
870 // to see EOF at the end.
871 ASSERT_EQ(pipe(files), 0);
872
873 {
874 FileOutputStream output(files[1], kBlockSizes[i]);
875 WriteStuff(&output);
876 EXPECT_EQ(0, output.GetErrno());
877 }
878 close(files[1]); // Send EOF.
879
880 {
881 FileInputStream input(files[0], kBlockSizes[j]);
882 ReadStuff(&input);
883 EXPECT_EQ(0, input.GetErrno());
884 }
885 close(files[0]);
886 }
887 }
888 }
889
890 // Test using C++ iostreams.
TEST_F(IoTest,IostreamIo)891 TEST_F(IoTest, IostreamIo) {
892 for (int i = 0; i < kBlockSizeCount; i++) {
893 for (int j = 0; j < kBlockSizeCount; j++) {
894 {
895 std::stringstream stream;
896
897 {
898 OstreamOutputStream output(&stream, kBlockSizes[i]);
899 WriteStuff(&output);
900 EXPECT_FALSE(stream.fail());
901 }
902
903 {
904 IstreamInputStream input(&stream, kBlockSizes[j]);
905 ReadStuff(&input);
906 EXPECT_TRUE(stream.eof());
907 }
908 }
909
910 {
911 std::stringstream stream;
912
913 {
914 OstreamOutputStream output(&stream, kBlockSizes[i]);
915 WriteStuffLarge(&output);
916 EXPECT_FALSE(stream.fail());
917 }
918
919 {
920 IstreamInputStream input(&stream, kBlockSizes[j]);
921 ReadStuffLarge(&input);
922 EXPECT_TRUE(stream.eof());
923 }
924 }
925 }
926 }
927 }
928
929 // To test ConcatenatingInputStream, we create several ArrayInputStreams
930 // covering a buffer and then concatenate them.
TEST_F(IoTest,ConcatenatingInputStream)931 TEST_F(IoTest, ConcatenatingInputStream) {
932 const int kBufferSize = 256;
933 uint8 buffer[kBufferSize];
934
935 // Fill the buffer.
936 ArrayOutputStream output(buffer, kBufferSize);
937 WriteStuff(&output);
938
939 // Now split it up into multiple streams of varying sizes.
940 ASSERT_EQ(68, output.ByteCount()); // Test depends on this.
941 ArrayInputStream input1(buffer, 12);
942 ArrayInputStream input2(buffer + 12, 7);
943 ArrayInputStream input3(buffer + 19, 6);
944 ArrayInputStream input4(buffer + 25, 15);
945 ArrayInputStream input5(buffer + 40, 0);
946 // Note: We want to make sure we have a stream boundary somewhere between
947 // bytes 42 and 62, which is the range that it Skip()ed by ReadStuff(). This
948 // tests that a bug that existed in the original code for Skip() is fixed.
949 ArrayInputStream input6(buffer + 40, 10);
950 ArrayInputStream input7(buffer + 50, 18); // Total = 68 bytes.
951
952 ZeroCopyInputStream* streams[] = {&input1, &input2, &input3, &input4,
953 &input5, &input6, &input7};
954
955 // Create the concatenating stream and read.
956 ConcatenatingInputStream input(streams, GOOGLE_ARRAYSIZE(streams));
957 ReadStuff(&input);
958 }
959
960 // To test LimitingInputStream, we write our golden text to a buffer, then
961 // create an ArrayInputStream that contains the whole buffer (not just the
962 // bytes written), then use a LimitingInputStream to limit it just to the
963 // bytes written.
TEST_F(IoTest,LimitingInputStream)964 TEST_F(IoTest, LimitingInputStream) {
965 const int kBufferSize = 256;
966 uint8 buffer[kBufferSize];
967
968 // Fill the buffer.
969 ArrayOutputStream output(buffer, kBufferSize);
970 WriteStuff(&output);
971
972 // Set up input.
973 ArrayInputStream array_input(buffer, kBufferSize);
974 LimitingInputStream input(&array_input, output.ByteCount());
975
976 ReadStuff(&input);
977 }
978
979 // Checks that ByteCount works correctly for LimitingInputStreams where the
980 // underlying stream has already been read.
TEST_F(IoTest,LimitingInputStreamByteCount)981 TEST_F(IoTest, LimitingInputStreamByteCount) {
982 const int kHalfBufferSize = 128;
983 const int kBufferSize = kHalfBufferSize * 2;
984 uint8 buffer[kBufferSize];
985
986 // Set up input. Only allow half to be read at once.
987 ArrayInputStream array_input(buffer, kBufferSize, kHalfBufferSize);
988 const void* data;
989 int size;
990 EXPECT_TRUE(array_input.Next(&data, &size));
991 EXPECT_EQ(kHalfBufferSize, array_input.ByteCount());
992 // kHalfBufferSize - 1 to test limiting logic as well.
993 LimitingInputStream input(&array_input, kHalfBufferSize - 1);
994 EXPECT_EQ(0, input.ByteCount());
995 EXPECT_TRUE(input.Next(&data, &size));
996 EXPECT_EQ(kHalfBufferSize - 1, input.ByteCount());
997 }
998
999 // Check that a zero-size array doesn't confuse the code.
TEST(ZeroSizeArray,Input)1000 TEST(ZeroSizeArray, Input) {
1001 ArrayInputStream input(NULL, 0);
1002 const void* data;
1003 int size;
1004 EXPECT_FALSE(input.Next(&data, &size));
1005 }
1006
TEST(ZeroSizeArray,Output)1007 TEST(ZeroSizeArray, Output) {
1008 ArrayOutputStream output(NULL, 0);
1009 void* data;
1010 int size;
1011 EXPECT_FALSE(output.Next(&data, &size));
1012 }
1013
1014 } // namespace
1015 } // namespace io
1016 } // namespace protobuf
1017 } // namespace google
1018