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 |