| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <fstream> | |
| 6 #include <iostream> | |
| 7 | |
| 8 #if defined(USE_SYSTEM_LIBBZ2) | |
| 9 #include <bzlib.h> | |
| 10 #else | |
| 11 #include "third_party/bzip2/bzlib.h" | |
| 12 #endif | |
| 13 | |
| 14 #include "base/file_util.h" | |
| 15 #include "base/path_service.h" | |
| 16 #include "base/scoped_ptr.h" | |
| 17 #include "net/base/bzip2_filter.h" | |
| 18 #include "net/base/filter_unittest.h" | |
| 19 #include "net/base/io_buffer.h" | |
| 20 #include "testing/gtest/include/gtest/gtest.h" | |
| 21 #include "testing/platform_test.h" | |
| 22 | |
| 23 namespace { | |
| 24 | |
| 25 const char* kExtraData = "Test Data, More Test Data, Even More Data of Test"; | |
| 26 const int kExtraDataBufferSize = 49; | |
| 27 const int kDefaultBufferSize = 4096; | |
| 28 const int kSmallBufferSize = 128; | |
| 29 const int kMaxBufferSize = 1048576; // 1048576 == 2^20 == 1 MB | |
| 30 | |
| 31 const char kApplicationOctetStream[] = "application/octet-stream"; | |
| 32 | |
| 33 // These tests use the path service, which uses autoreleased objects on the | |
| 34 // Mac, so this needs to be a PlatformTest. | |
| 35 class BZip2FilterUnitTest : public PlatformTest { | |
| 36 protected: | |
| 37 virtual void SetUp() { | |
| 38 PlatformTest::SetUp(); | |
| 39 | |
| 40 bzip2_encode_buffer_ = NULL; | |
| 41 | |
| 42 // Get the path of source data file. | |
| 43 FilePath file_path; | |
| 44 PathService::Get(base::DIR_SOURCE_ROOT, &file_path); | |
| 45 file_path = file_path.AppendASCII("net"); | |
| 46 file_path = file_path.AppendASCII("data"); | |
| 47 file_path = file_path.AppendASCII("filter_unittests"); | |
| 48 file_path = file_path.AppendASCII("google.txt"); | |
| 49 | |
| 50 // Read data from the file into buffer. | |
| 51 ASSERT_TRUE(file_util::ReadFileToString(file_path, &source_buffer_)); | |
| 52 | |
| 53 // Append the extra data to end of source | |
| 54 source_buffer_.append(kExtraData, kExtraDataBufferSize); | |
| 55 | |
| 56 // Encode the whole data with bzip2 for next testing | |
| 57 bzip2_data_stream_.reset(new bz_stream); | |
| 58 ASSERT_TRUE(bzip2_data_stream_.get()); | |
| 59 memset(bzip2_data_stream_.get(), 0, sizeof(bz_stream)); | |
| 60 | |
| 61 int result = BZ2_bzCompressInit(bzip2_data_stream_.get(), | |
| 62 9, // 900k block size | |
| 63 0, // quiet | |
| 64 0); // default work factor | |
| 65 ASSERT_EQ(BZ_OK, result); | |
| 66 | |
| 67 bzip2_encode_buffer_ = new char[kDefaultBufferSize]; | |
| 68 ASSERT_TRUE(bzip2_encode_buffer_ != NULL); | |
| 69 bzip2_encode_len_ = kDefaultBufferSize; | |
| 70 | |
| 71 bzip2_data_stream_->next_in = const_cast<char*>(source_buffer()); | |
| 72 bzip2_data_stream_->avail_in = source_len(); | |
| 73 bzip2_data_stream_->next_out = bzip2_encode_buffer_; | |
| 74 bzip2_data_stream_->avail_out = bzip2_encode_len_; | |
| 75 do { | |
| 76 result = BZ2_bzCompress(bzip2_data_stream_.get(), BZ_FINISH); | |
| 77 } while (result == BZ_FINISH_OK); | |
| 78 | |
| 79 ASSERT_EQ(BZ_STREAM_END, result); | |
| 80 result = BZ2_bzCompressEnd(bzip2_data_stream_.get()); | |
| 81 ASSERT_EQ(BZ_OK, result); | |
| 82 bzip2_encode_len_ = bzip2_data_stream_->total_out_lo32; | |
| 83 | |
| 84 // Make sure we wrote something; otherwise not sure what to expect | |
| 85 ASSERT_GT(bzip2_encode_len_, 0); | |
| 86 ASSERT_LE(bzip2_encode_len_, kDefaultBufferSize); | |
| 87 } | |
| 88 | |
| 89 virtual void TearDown() { | |
| 90 delete[] bzip2_encode_buffer_; | |
| 91 bzip2_encode_buffer_ = NULL; | |
| 92 | |
| 93 PlatformTest::TearDown(); | |
| 94 } | |
| 95 | |
| 96 // Use filter to decode compressed data, and compare the decoding result with | |
| 97 // the orginal Data. | |
| 98 // Parameters: Source and source_len are original data and its size. | |
| 99 // Encoded_source and encoded_source_len are compressed data and its size. | |
| 100 // Output_buffer_size specifies the size of buffer to read out data from | |
| 101 // filter. | |
| 102 // get_extra_data specifies whether get the extra data because maybe some | |
| 103 // server might send extra data after finish sending compress data. | |
| 104 void DecodeAndCompareWithFilter(Filter* filter, | |
| 105 const char* source, | |
| 106 int source_len, | |
| 107 const char* encoded_source, | |
| 108 int encoded_source_len, | |
| 109 int output_buffer_size, | |
| 110 bool get_extra_data) { | |
| 111 // Make sure we have enough space to hold the decoding output. | |
| 112 ASSERT_LE(source_len, kDefaultBufferSize); | |
| 113 ASSERT_LE(output_buffer_size, kDefaultBufferSize); | |
| 114 | |
| 115 int total_output_len = kDefaultBufferSize; | |
| 116 if (get_extra_data) | |
| 117 total_output_len += kExtraDataBufferSize; | |
| 118 char decode_buffer[kDefaultBufferSize + kExtraDataBufferSize]; | |
| 119 char* decode_next = decode_buffer; | |
| 120 int decode_avail_size = total_output_len; | |
| 121 | |
| 122 const char* encode_next = encoded_source; | |
| 123 int encode_avail_size = encoded_source_len; | |
| 124 | |
| 125 Filter::FilterStatus code = Filter::FILTER_OK; | |
| 126 while (code != Filter::FILTER_DONE) { | |
| 127 int encode_data_len; | |
| 128 if (get_extra_data && !encode_avail_size) | |
| 129 break; | |
| 130 encode_data_len = std::min(encode_avail_size, | |
| 131 filter->stream_buffer_size()); | |
| 132 memcpy(filter->stream_buffer()->data(), encode_next, encode_data_len); | |
| 133 filter->FlushStreamBuffer(encode_data_len); | |
| 134 encode_next += encode_data_len; | |
| 135 encode_avail_size -= encode_data_len; | |
| 136 | |
| 137 while (1) { | |
| 138 int decode_data_len = std::min(decode_avail_size, output_buffer_size); | |
| 139 | |
| 140 code = filter->ReadData(decode_next, &decode_data_len); | |
| 141 decode_next += decode_data_len; | |
| 142 decode_avail_size -= decode_data_len; | |
| 143 | |
| 144 ASSERT_TRUE(code != Filter::FILTER_ERROR); | |
| 145 | |
| 146 if (code == Filter::FILTER_NEED_MORE_DATA || | |
| 147 code == Filter::FILTER_DONE) { | |
| 148 if (code == Filter::FILTER_DONE && get_extra_data) | |
| 149 code = Filter::FILTER_OK; | |
| 150 else | |
| 151 break; | |
| 152 } | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 // Compare the decoding result with source data | |
| 157 int decode_total_data_len = total_output_len - decode_avail_size; | |
| 158 EXPECT_TRUE(decode_total_data_len == source_len); | |
| 159 EXPECT_EQ(memcmp(source, decode_buffer, source_len), 0); | |
| 160 } | |
| 161 | |
| 162 // Unsafe function to use filter to decode compressed data. | |
| 163 // Parameters: Source and source_len are compressed data and its size. | |
| 164 // Dest is the buffer for decoding results. Upon entry, *dest_len is the size | |
| 165 // of the dest buffer. Upon exit, *dest_len is the number of chars written | |
| 166 // into the buffer. | |
| 167 Filter::FilterStatus DecodeAllWithFilter(Filter* filter, | |
| 168 const char* source, | |
| 169 int source_len, | |
| 170 char* dest, | |
| 171 int* dest_len) { | |
| 172 memcpy(filter->stream_buffer()->data(), source, source_len); | |
| 173 filter->FlushStreamBuffer(source_len); | |
| 174 return filter->ReadData(dest, dest_len); | |
| 175 } | |
| 176 | |
| 177 const char* source_buffer() const { return source_buffer_.data(); } | |
| 178 int source_len() const { | |
| 179 return static_cast<int>(source_buffer_.size()) - kExtraDataBufferSize; | |
| 180 } | |
| 181 | |
| 182 std::string source_buffer_; | |
| 183 | |
| 184 scoped_ptr<bz_stream> bzip2_data_stream_; | |
| 185 char* bzip2_encode_buffer_; | |
| 186 int bzip2_encode_len_; | |
| 187 }; | |
| 188 | |
| 189 // Basic scenario: decoding bzip2 data with big enough buffer. | |
| 190 TEST_F(BZip2FilterUnitTest, DecodeBZip2) { | |
| 191 // Decode the compressed data with filter | |
| 192 std::vector<Filter::FilterType> filter_types; | |
| 193 filter_types.push_back(Filter::FILTER_TYPE_BZIP2); | |
| 194 MockFilterContext filter_context(kDefaultBufferSize); | |
| 195 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); | |
| 196 ASSERT_TRUE(filter.get()); | |
| 197 memcpy(filter->stream_buffer()->data(), bzip2_encode_buffer_, | |
| 198 bzip2_encode_len_); | |
| 199 filter->FlushStreamBuffer(bzip2_encode_len_); | |
| 200 | |
| 201 char bzip2_decode_buffer[kDefaultBufferSize]; | |
| 202 int bzip2_decode_size = kDefaultBufferSize; | |
| 203 Filter::FilterStatus result = | |
| 204 filter->ReadData(bzip2_decode_buffer, &bzip2_decode_size); | |
| 205 ASSERT_EQ(Filter::FILTER_DONE, result); | |
| 206 | |
| 207 // Compare the decoding result with source data | |
| 208 EXPECT_TRUE(bzip2_decode_size == source_len()); | |
| 209 EXPECT_EQ(memcmp(source_buffer(), bzip2_decode_buffer, source_len()), 0); | |
| 210 } | |
| 211 | |
| 212 // Tests we can call filter repeatedly to get all the data decoded. | |
| 213 // To do that, we create a filter with a small buffer that can not hold all | |
| 214 // the input data. | |
| 215 TEST_F(BZip2FilterUnitTest, DecodeWithSmallInputBuffer) { | |
| 216 std::vector<Filter::FilterType> filter_types; | |
| 217 filter_types.push_back(Filter::FILTER_TYPE_BZIP2); | |
| 218 MockFilterContext filter_context(kSmallBufferSize); | |
| 219 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); | |
| 220 ASSERT_TRUE(filter.get()); | |
| 221 DecodeAndCompareWithFilter(filter.get(), source_buffer(), source_len(), | |
| 222 bzip2_encode_buffer_, bzip2_encode_len_, | |
| 223 kDefaultBufferSize, false); | |
| 224 } | |
| 225 | |
| 226 // Tests we can decode when caller has small buffer to read out from filter. | |
| 227 TEST_F(BZip2FilterUnitTest, DecodeWithSmallOutputBuffer) { | |
| 228 std::vector<Filter::FilterType> filter_types; | |
| 229 filter_types.push_back(Filter::FILTER_TYPE_BZIP2); | |
| 230 MockFilterContext filter_context(kDefaultBufferSize); | |
| 231 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); | |
| 232 ASSERT_TRUE(filter.get()); | |
| 233 DecodeAndCompareWithFilter(filter.get(), source_buffer(), source_len(), | |
| 234 bzip2_encode_buffer_, bzip2_encode_len_, | |
| 235 kSmallBufferSize, false); | |
| 236 } | |
| 237 | |
| 238 // Tests we can still decode with just 1 byte buffer in the filter. | |
| 239 // The purpose of this tests are two: (1) Verify filter can parse partial BZip2 | |
| 240 // header correctly. (2) Sometimes the filter will consume input without | |
| 241 // generating output. Verify filter can handle it correctly. | |
| 242 TEST_F(BZip2FilterUnitTest, DecodeWithOneByteInputBuffer) { | |
| 243 std::vector<Filter::FilterType> filter_types; | |
| 244 filter_types.push_back(Filter::FILTER_TYPE_BZIP2); | |
| 245 MockFilterContext filter_context(1); | |
| 246 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); | |
| 247 ASSERT_TRUE(filter.get()); | |
| 248 DecodeAndCompareWithFilter(filter.get(), source_buffer(), source_len(), | |
| 249 bzip2_encode_buffer_, bzip2_encode_len_, | |
| 250 kDefaultBufferSize, false); | |
| 251 } | |
| 252 | |
| 253 // Tests we can still decode with just 1 byte buffer in the filter and just 1 | |
| 254 // byte buffer in the caller. | |
| 255 TEST_F(BZip2FilterUnitTest, DecodeWithOneByteInputAndOutputBuffer) { | |
| 256 std::vector<Filter::FilterType> filter_types; | |
| 257 filter_types.push_back(Filter::FILTER_TYPE_BZIP2); | |
| 258 MockFilterContext filter_context(1); | |
| 259 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); | |
| 260 ASSERT_TRUE(filter.get()); | |
| 261 DecodeAndCompareWithFilter(filter.get(), source_buffer(), source_len(), | |
| 262 bzip2_encode_buffer_, bzip2_encode_len_, 1, false); | |
| 263 } | |
| 264 | |
| 265 // Decoding bzip2 stream with corrupted data. | |
| 266 TEST_F(BZip2FilterUnitTest, DecodeCorruptedData) { | |
| 267 char corrupt_data[kDefaultBufferSize]; | |
| 268 int corrupt_data_len = bzip2_encode_len_; | |
| 269 memcpy(corrupt_data, bzip2_encode_buffer_, bzip2_encode_len_); | |
| 270 | |
| 271 char corrupt_decode_buffer[kDefaultBufferSize]; | |
| 272 int corrupt_decode_size = kDefaultBufferSize; | |
| 273 | |
| 274 // Decode the correct data with filter | |
| 275 std::vector<Filter::FilterType> filter_types; | |
| 276 filter_types.push_back(Filter::FILTER_TYPE_BZIP2); | |
| 277 MockFilterContext filter_context(kDefaultBufferSize); | |
| 278 scoped_ptr<Filter> filter1(Filter::Factory(filter_types, filter_context)); | |
| 279 ASSERT_TRUE(filter1.get()); | |
| 280 | |
| 281 Filter::FilterStatus code = DecodeAllWithFilter(filter1.get(), | |
| 282 corrupt_data, | |
| 283 corrupt_data_len, | |
| 284 corrupt_decode_buffer, | |
| 285 &corrupt_decode_size); | |
| 286 | |
| 287 // Expect failures | |
| 288 EXPECT_TRUE(code == Filter::FILTER_DONE); | |
| 289 | |
| 290 // Decode the corrupted data with filter | |
| 291 scoped_ptr<Filter> filter2(Filter::Factory(filter_types, filter_context)); | |
| 292 ASSERT_TRUE(filter2.get()); | |
| 293 | |
| 294 int pos = corrupt_data_len / 2; | |
| 295 corrupt_data[pos] = !corrupt_data[pos]; | |
| 296 | |
| 297 code = DecodeAllWithFilter(filter2.get(), | |
| 298 corrupt_data, | |
| 299 corrupt_data_len, | |
| 300 corrupt_decode_buffer, | |
| 301 &corrupt_decode_size); | |
| 302 | |
| 303 // Expect failures | |
| 304 EXPECT_TRUE(code != Filter::FILTER_DONE); | |
| 305 } | |
| 306 | |
| 307 // Decoding bzip2 stream with missing data. | |
| 308 TEST_F(BZip2FilterUnitTest, DecodeMissingData) { | |
| 309 char corrupt_data[kDefaultBufferSize]; | |
| 310 int corrupt_data_len = bzip2_encode_len_; | |
| 311 memcpy(corrupt_data, bzip2_encode_buffer_, bzip2_encode_len_); | |
| 312 | |
| 313 int pos = corrupt_data_len / 2; | |
| 314 int len = corrupt_data_len - pos - 1; | |
| 315 memmove(&corrupt_data[pos], &corrupt_data[pos+1], len); | |
| 316 --corrupt_data_len; | |
| 317 | |
| 318 // Decode the corrupted data with filter | |
| 319 std::vector<Filter::FilterType> filter_types; | |
| 320 filter_types.push_back(Filter::FILTER_TYPE_BZIP2); | |
| 321 MockFilterContext filter_context(kDefaultBufferSize); | |
| 322 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); | |
| 323 ASSERT_TRUE(filter.get()); | |
| 324 char corrupt_decode_buffer[kDefaultBufferSize]; | |
| 325 int corrupt_decode_size = kDefaultBufferSize; | |
| 326 | |
| 327 Filter::FilterStatus code = DecodeAllWithFilter(filter.get(), | |
| 328 corrupt_data, | |
| 329 corrupt_data_len, | |
| 330 corrupt_decode_buffer, | |
| 331 &corrupt_decode_size); | |
| 332 // Expect failures | |
| 333 EXPECT_TRUE(code != Filter::FILTER_DONE); | |
| 334 } | |
| 335 | |
| 336 // Decoding bzip2 stream with corrupted header. | |
| 337 TEST_F(BZip2FilterUnitTest, DecodeCorruptedHeader) { | |
| 338 char corrupt_data[kDefaultBufferSize]; | |
| 339 int corrupt_data_len = bzip2_encode_len_; | |
| 340 memcpy(corrupt_data, bzip2_encode_buffer_, bzip2_encode_len_); | |
| 341 | |
| 342 corrupt_data[2] = !corrupt_data[2]; | |
| 343 | |
| 344 // Decode the corrupted data with filter | |
| 345 std::vector<Filter::FilterType> filter_types; | |
| 346 filter_types.push_back(Filter::FILTER_TYPE_BZIP2); | |
| 347 MockFilterContext filter_context(kDefaultBufferSize); | |
| 348 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); | |
| 349 ASSERT_TRUE(filter.get()); | |
| 350 char corrupt_decode_buffer[kDefaultBufferSize]; | |
| 351 int corrupt_decode_size = kDefaultBufferSize; | |
| 352 | |
| 353 Filter::FilterStatus code = DecodeAllWithFilter(filter.get(), | |
| 354 corrupt_data, | |
| 355 corrupt_data_len, | |
| 356 corrupt_decode_buffer, | |
| 357 &corrupt_decode_size); | |
| 358 | |
| 359 // Expect failures | |
| 360 EXPECT_TRUE(code == Filter::FILTER_ERROR); | |
| 361 } | |
| 362 | |
| 363 // Tests we can decode all compress data and get extra data which is | |
| 364 // appended to compress data stream by some server when it finish | |
| 365 // sending compress data. | |
| 366 TEST_F(BZip2FilterUnitTest, DecodeWithExtraDataAndSmallOutputBuffer) { | |
| 367 char more_data[kDefaultBufferSize + kExtraDataBufferSize]; | |
| 368 int more_data_len = bzip2_encode_len_ + kExtraDataBufferSize; | |
| 369 memcpy(more_data, bzip2_encode_buffer_, bzip2_encode_len_); | |
| 370 memcpy(more_data + bzip2_encode_len_, kExtraData, kExtraDataBufferSize); | |
| 371 | |
| 372 std::vector<Filter::FilterType> filter_types; | |
| 373 filter_types.push_back(Filter::FILTER_TYPE_BZIP2); | |
| 374 MockFilterContext filter_context(kDefaultBufferSize); | |
| 375 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); | |
| 376 ASSERT_TRUE(filter.get()); | |
| 377 DecodeAndCompareWithFilter(filter.get(), | |
| 378 source_buffer(), | |
| 379 source_len() + kExtraDataBufferSize, | |
| 380 more_data, | |
| 381 more_data_len, | |
| 382 kSmallBufferSize, | |
| 383 true); | |
| 384 } | |
| 385 | |
| 386 TEST_F(BZip2FilterUnitTest, DecodeWithExtraDataAndSmallInputBuffer) { | |
| 387 char more_data[kDefaultBufferSize + kExtraDataBufferSize]; | |
| 388 int more_data_len = bzip2_encode_len_ + kExtraDataBufferSize; | |
| 389 memcpy(more_data, bzip2_encode_buffer_, bzip2_encode_len_); | |
| 390 memcpy(more_data + bzip2_encode_len_, kExtraData, kExtraDataBufferSize); | |
| 391 | |
| 392 std::vector<Filter::FilterType> filter_types; | |
| 393 filter_types.push_back(Filter::FILTER_TYPE_BZIP2); | |
| 394 MockFilterContext filter_context(kSmallBufferSize); | |
| 395 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); | |
| 396 ASSERT_TRUE(filter.get()); | |
| 397 DecodeAndCompareWithFilter(filter.get(), | |
| 398 source_buffer(), | |
| 399 source_len() + kExtraDataBufferSize, | |
| 400 more_data, | |
| 401 more_data_len, | |
| 402 kDefaultBufferSize, | |
| 403 true); | |
| 404 } | |
| 405 | |
| 406 } // namespace | |
| OLD | NEW |