Imported Upstream version 1.64.0
[platform/upstream/boost.git] / libs / iostreams / test / gzip_test.cpp
1 // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com)
2 // (C) Copyright 2004-2007 Jonathan Turkanis
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
5
6 // See http://www.boost.org/libs/iostreams for documentation.
7
8 #include <cstddef>
9 #include <string>
10 #include <boost/iostreams/copy.hpp>
11 #include <boost/iostreams/device/array.hpp>
12 #include <boost/iostreams/device/back_inserter.hpp>
13 #include <boost/iostreams/filter/gzip.hpp>
14 #include <boost/iostreams/filter/test.hpp>
15 #include <boost/iostreams/filtering_stream.hpp>
16 #include <boost/ref.hpp>
17 #include <boost/range/iterator_range.hpp>
18 #include <boost/test/test_tools.hpp>
19 #include <boost/test/unit_test.hpp>
20 #include "detail/sequence.hpp"
21 #include "detail/verification.hpp"
22
23 using namespace boost;
24 using namespace boost::iostreams;
25 using namespace boost::iostreams::test;
26 namespace io = boost::iostreams;
27 using boost::unit_test::test_suite;     
28
29 struct gzip_alloc : std::allocator<char> {
30     gzip_alloc() { }
31     gzip_alloc(const gzip_alloc& other) { }
32     template<typename T>
33     gzip_alloc(const std::allocator<T>& other) { }
34 };
35
36 void compression_test()
37 {
38     text_sequence      data;
39
40     // Test compression and decompression with metadata
41     for (int i = 0; i < 4; ++i) {
42         gzip_params params;
43         if (i & 1) {
44             params.file_name = "original file name";
45         }
46         if (i & 2) {
47             params.comment = "detailed file description";
48         }
49         gzip_compressor    out(params);
50         gzip_decompressor  in;
51         BOOST_CHECK(
52             test_filter_pair( boost::ref(out), 
53                               boost::ref(in), 
54                               std::string(data.begin(), data.end()) )
55         );
56         BOOST_CHECK(in.file_name() == params.file_name);
57         BOOST_CHECK(in.comment() == params.comment);
58     }
59
60     // Test compression and decompression with custom allocator
61     BOOST_CHECK(
62         test_filter_pair( basic_gzip_compressor<gzip_alloc>(), 
63                           basic_gzip_decompressor<gzip_alloc>(), 
64                           std::string(data.begin(), data.end()) )
65     );
66 }
67
68 void multiple_member_test()
69 {
70     text_sequence      data;
71     std::vector<char>  temp, dest;
72
73     // Write compressed data to temp, twice in succession
74     filtering_ostream out;
75     out.push(gzip_compressor());
76     out.push(io::back_inserter(temp));
77     io::copy(make_iterator_range(data), out);
78     out.push(io::back_inserter(temp));
79     io::copy(make_iterator_range(data), out);
80
81     // Read compressed data from temp into dest
82     filtering_istream in;
83     in.push(gzip_decompressor());
84     in.push(array_source(&temp[0], temp.size()));
85     io::copy(in, io::back_inserter(dest));
86
87     // Check that dest consists of two copies of data
88     BOOST_REQUIRE_EQUAL(data.size() * 2, dest.size());
89     BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin()));
90     BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin() + dest.size() / 2));
91
92     dest.clear();
93     io::copy(
94         array_source(&temp[0], temp.size()),
95         io::compose(gzip_decompressor(), io::back_inserter(dest)));
96
97     // Check that dest consists of two copies of data
98     BOOST_REQUIRE_EQUAL(data.size() * 2, dest.size());
99     BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin()));
100     BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin() + dest.size() / 2));
101 }
102
103 void array_source_test()
104 {
105     std::string data = "simple test string.";
106     std::string encoded;
107
108     filtering_ostream out;
109     out.push(gzip_compressor());
110     out.push(io::back_inserter(encoded));
111     io::copy(make_iterator_range(data), out);
112
113     std::string res;
114     io::array_source src(encoded.data(),encoded.length());
115     io::copy(io::compose(io::gzip_decompressor(), src), io::back_inserter(res));
116     
117     BOOST_CHECK_EQUAL(data, res);
118 }
119
120 #if defined(BOOST_MSVC)
121 # pragma warning(push)
122 # pragma warning(disable:4309)  // Truncation of constant value
123 #endif
124
125 void header_test()
126 {
127     // This test is in response to https://svn.boost.org/trac/boost/ticket/5908
128     // which describes a problem parsing gzip headers with extra fields as
129     // defined in RFC 1952 (http://www.ietf.org/rfc/rfc1952.txt).
130     // The extra field data used here is characteristic of the tabix file
131     // format (http://samtools.sourceforge.net/tabix.shtml).
132     const char header_bytes[] = {
133         static_cast<char>(gzip::magic::id1),
134         static_cast<char>(gzip::magic::id2),
135         gzip::method::deflate, // Compression Method: deflate
136         gzip::flags::extra | gzip::flags::name | gzip::flags::comment, // flags
137         '\x22', '\x9c', '\xf3', '\x4e', // 4 byte modification time (little endian)
138         gzip::extra_flags::best_compression, // XFL
139         gzip::os_unix, // OS
140         6, 0, // 2 byte length of extra field (little endian, 6 bytes)
141         'B', 'C', 2, 0, 0, 0, // 6 bytes worth of extra field data
142         'a', 'b', 'c', 0, // original filename, null terminated
143         'n', 'o', ' ', 'c', 'o', 'm', 'm', 'e', 'n', 't', 0, // comment
144     };
145     size_t sz = sizeof(header_bytes)/sizeof(header_bytes[0]);
146
147     boost::iostreams::detail::gzip_header hdr;
148     for (size_t i = 0; i < sz; ++i) {
149         hdr.process(header_bytes[i]);
150
151         // Require that we are done at the last byte, not before.
152         if (i == sz-1)
153             BOOST_REQUIRE(hdr.done());
154         else
155             BOOST_REQUIRE(!hdr.done());
156     }
157
158     BOOST_CHECK_EQUAL("abc", hdr.file_name());
159     BOOST_CHECK_EQUAL("no comment", hdr.comment());
160     BOOST_CHECK_EQUAL(0x4ef39c22, hdr.mtime());
161     BOOST_CHECK_EQUAL(gzip::os_unix, hdr.os());
162 }
163
164 #if defined(BOOST_MSVC)
165 # pragma warning(pop)
166 #endif
167
168 void empty_file_test()
169 {
170     // This test is in response to https://svn.boost.org/trac/boost/ticket/5237
171     // The previous implementation of gzip_compressor only wrote the gzip file
172     // header when the first bytes of uncompressed input were processed, causing
173     // incorrect behavior for empty files
174     BOOST_CHECK(
175         test_filter_pair( gzip_compressor(),
176                           gzip_decompressor(),
177                           std::string() )
178     );
179 }
180
181 void multipart_test()
182 {
183     // This test verifies that the gzip_decompressor properly handles a file
184     // that was written in multiple parts using Z_FULL_FLUSH, and in particular
185     // handles the CRC properly when one of those parts is empty.
186     const char multipart_file[] = {
187         '\x1f', '\x8b', '\x08', '\x00', '\x00', '\x00', '\x00', '\x00', '\x02', '\xff', '\xf2', '\xc9',
188         '\xcc', '\x4b', '\x55', '\x30', '\xe4', '\xf2', '\x01', '\x51', '\x46', '\x10', '\xca', '\x98',
189         '\x0b', '\x00', '\x00', '\x00', '\xff', '\xff', '\x03', '\x00', '\xdb', '\xa7', '\x83', '\xc9',
190         '\x15', '\x00', '\x00', '\x00', '\x1f', '\x8b', '\x08', '\x00', '\x00', '\x00', '\x00', '\x00',
191         '\x02', '\xff', '\xf2', '\xc9', '\xcc', '\x4b', '\x55', '\x30', '\xe1', '\xf2', '\x01', '\x51',
192         '\xa6', '\x10', '\xca', '\x8c', '\x0b', '\x00', '\x00', '\x00', '\xff', '\xff', '\x03', '\x00',
193         '\x41', '\xe3', '\xcc', '\xaa', '\x15', '\x00', '\x00', '\x00', '\x1f', '\x8b', '\x08', '\x00',
194         '\x00', '\x00', '\x00', '\x00', '\x02', '\xff', '\x02', '\x00', '\x00', '\x00', '\xff', '\xff',
195         '\x03', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x1f', '\x8b',
196         '\x08', '\x00', '\x00', '\x00', '\x00', '\x00', '\x02', '\xff', '\xf2', '\xc9', '\xcc', '\x4b',
197         '\x55', '\x30', '\xe7', '\xf2', '\x01', '\x51', '\x16', '\x10', '\xca', '\x92', '\x0b', '\x00',
198         '\x00', '\x00', '\xff', '\xff', '\x03', '\x00', '\x2b', '\xac', '\xd3', '\xf5', '\x15', '\x00',
199         '\x00', '\x00'
200     };
201
202     filtering_istream in;
203     std::string line;
204
205     in.push(gzip_decompressor());
206     in.push(io::array_source(multipart_file, sizeof(multipart_file)));
207
208     // First part
209     std::getline(in, line);
210     BOOST_CHECK_EQUAL("Line 1", line);
211     std::getline(in, line);
212     BOOST_CHECK_EQUAL("Line 2", line);
213     std::getline(in, line);
214     BOOST_CHECK_EQUAL("Line 3", line);
215
216     // Second part immediately follows
217     std::getline(in, line);
218     BOOST_CHECK_EQUAL("Line 4", line);
219     std::getline(in, line);
220     BOOST_CHECK_EQUAL("Line 5", line);
221     std::getline(in, line);
222     BOOST_CHECK_EQUAL("Line 6", line);
223
224     // Then an empty part, followed by one last 3-line part.
225     std::getline(in, line);
226     BOOST_CHECK_EQUAL("Line 7", line);
227     std::getline(in, line);
228     BOOST_CHECK_EQUAL("Line 8", line);
229     std::getline(in, line);
230     BOOST_CHECK_EQUAL("Line 9", line);
231
232     // Check for gzip errors too.
233     BOOST_CHECK(!in.bad());
234 }
235
236 test_suite* init_unit_test_suite(int, char* []) 
237 {
238     test_suite* test = BOOST_TEST_SUITE("gzip test");
239     test->add(BOOST_TEST_CASE(&compression_test));
240     test->add(BOOST_TEST_CASE(&multiple_member_test));
241     test->add(BOOST_TEST_CASE(&array_source_test));
242     test->add(BOOST_TEST_CASE(&header_test));
243     test->add(BOOST_TEST_CASE(&empty_file_test));
244     test->add(BOOST_TEST_CASE(&multipart_test));
245     return test;
246 }