1 | // (C) COPYRIGHT 2017 ARM Limited |
2 | // Based on gzip_test.cpp by: |
3 | // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com) |
4 | // (C) Copyright 2004-2007 Jonathan Turkanis |
5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying |
6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) |
7 | |
8 | // See http://www.boost.org/libs/iostreams for documentation. |
9 | |
10 | // Note: basically a copy-paste of the gzip test |
11 | |
12 | #include <cstddef> |
13 | #include <string> |
14 | #include <boost/iostreams/copy.hpp> |
15 | #include <boost/iostreams/device/array.hpp> |
16 | #include <boost/iostreams/device/back_inserter.hpp> |
17 | #include <boost/iostreams/filter/lzma.hpp> |
18 | #include <boost/iostreams/filter/test.hpp> |
19 | #include <boost/iostreams/filtering_stream.hpp> |
20 | #include <boost/ref.hpp> |
21 | #include <boost/range/iterator_range.hpp> |
22 | #include <boost/test/test_tools.hpp> |
23 | #include <boost/test/unit_test.hpp> |
24 | #include "detail/sequence.hpp" |
25 | #include "detail/verification.hpp" |
26 | |
27 | using namespace boost; |
28 | using namespace boost::iostreams; |
29 | using namespace boost::iostreams::test; |
30 | namespace io = boost::iostreams; |
31 | using boost::unit_test::test_suite; |
32 | |
33 | template<class T> struct basic_test_alloc: std::allocator<T> |
34 | { |
35 | basic_test_alloc() |
36 | { |
37 | } |
38 | |
39 | basic_test_alloc( basic_test_alloc const& /*other*/ ) |
40 | { |
41 | } |
42 | |
43 | template<class U> |
44 | basic_test_alloc( basic_test_alloc<U> const & /*other*/ ) |
45 | { |
46 | } |
47 | |
48 | template<class U> struct rebind |
49 | { |
50 | typedef basic_test_alloc<U> other; |
51 | }; |
52 | }; |
53 | |
54 | typedef basic_test_alloc<char> lzma_alloc; |
55 | |
56 | void compression_test() |
57 | { |
58 | text_sequence data; |
59 | |
60 | // Test compression and decompression with custom allocator |
61 | BOOST_CHECK( |
62 | test_filter_pair( basic_lzma_compressor<lzma_alloc>(), |
63 | basic_lzma_decompressor<lzma_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(t: lzma_compressor()); |
76 | out.push(t: io::back_inserter(cnt&: temp)); |
77 | io::copy(src: make_iterator_range(r&: data), snk&: out); |
78 | out.push(t: io::back_inserter(cnt&: temp)); |
79 | io::copy(src: make_iterator_range(r&: data), snk&: out); |
80 | |
81 | // Read compressed data from temp into dest |
82 | filtering_istream in; |
83 | in.push(t: lzma_decompressor()); |
84 | in.push(t: array_source(&temp[0], temp.size())); |
85 | io::copy(src&: in, snk: io::back_inserter(cnt&: 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 | src: array_source(&temp[0], temp.size()), |
95 | snk: io::compose(filter: lzma_decompressor(), fod: io::back_inserter(cnt&: 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(t: lzma_compressor()); |
110 | out.push(t: io::back_inserter(cnt&: encoded)); |
111 | io::copy(src: make_iterator_range(r&: data), snk&: out); |
112 | |
113 | std::string res; |
114 | io::array_source src(encoded.data(),encoded.length()); |
115 | io::copy(src: io::compose(filter: io::lzma_decompressor(), fod: src), snk: io::back_inserter(cnt&: res)); |
116 | |
117 | BOOST_CHECK_EQUAL(data, res); |
118 | } |
119 | |
120 | void empty_file_test() |
121 | { |
122 | // This test is in response to https://svn.boost.org/trac/boost/ticket/5237 |
123 | // The previous implementation of gzip_compressor only wrote the gzip file |
124 | // header when the first bytes of uncompressed input were processed, causing |
125 | // incorrect behavior for empty files |
126 | BOOST_CHECK( |
127 | test_filter_pair( lzma_compressor(), |
128 | lzma_decompressor(), |
129 | std::string() ) |
130 | ); |
131 | } |
132 | |
133 | void multipart_test() |
134 | { |
135 | // This test verifies that the lzma_decompressor properly handles a file |
136 | // that consists of multiple concatenated files (matches unxz behaviour) |
137 | static const char multipart_file[] = { |
138 | '\xfd', '\x37', '\x7a', '\x58', '\x5a', '\x00', '\x00', '\x04', '\xe6', '\xd6', '\xb4', '\x46', |
139 | '\x02', '\x00', '\x21', '\x01', '\x1c', '\x00', '\x00', '\x00', '\x10', '\xcf', '\x58', '\xcc', |
140 | '\xe0', '\x00', '\x14', '\x00', '\x11', '\x5d', '\x00', '\x26', '\x1a', '\x49', '\xc6', '\x67', |
141 | '\x41', '\x3f', '\x96', '\x8c', '\x25', '\x02', '\xb3', '\x4d', '\x16', '\xa8', '\xb4', '\x40', |
142 | '\x00', '\x00', '\x00', '\x00', '\xeb', '\xad', '\x3f', '\xbf', '\x8c', '\x8c', '\x72', '\x25', |
143 | '\x00', '\x01', '\x2d', '\x15', '\x2f', '\x0b', '\x71', '\x6d', '\x1f', '\xb6', '\xf3', '\x7d', |
144 | '\x01', '\x00', '\x00', '\x00', '\x00', '\x04', '\x59', '\x5a', '\xfd', '\x37', '\x7a', '\x58', |
145 | '\x5a', '\x00', '\x00', '\x04', '\xe6', '\xd6', '\xb4', '\x46', '\x02', '\x00', '\x21', '\x01', |
146 | '\x1c', '\x00', '\x00', '\x00', '\x10', '\xcf', '\x58', '\xcc', '\xe0', '\x00', '\x14', '\x00', |
147 | '\x11', '\x5d', '\x00', '\x26', '\x1a', '\x49', '\xc6', '\x67', '\x41', '\x4d', '\x84', '\x0c', |
148 | '\x25', '\x1f', '\x5e', '\x1d', '\x4a', '\x91', '\x61', '\xa0', '\x00', '\x00', '\x00', '\x00', |
149 | '\x56', '\x76', '\x71', '\xf0', '\x54', '\x21', '\xa2', '\x5b', '\x00', '\x01', '\x2d', '\x15', |
150 | '\x2f', '\x0b', '\x71', '\x6d', '\x1f', '\xb6', '\xf3', '\x7d', '\x01', '\x00', '\x00', '\x00', |
151 | '\x00', '\x04', '\x59', '\x5a', '\xfd', '\x37', '\x7a', '\x58', '\x5a', '\x00', '\x00', '\x04', |
152 | '\xe6', '\xd6', '\xb4', '\x46', '\x00', '\x00', '\x00', '\x00', '\x1c', '\xdf', '\x44', '\x21', |
153 | '\x1f', '\xb6', '\xf3', '\x7d', '\x01', '\x00', '\x00', '\x00', '\x00', '\x04', '\x59', '\x5a', |
154 | '\xfd', '\x37', '\x7a', '\x58', '\x5a', '\x00', '\x00', '\x04', '\xe6', '\xd6', '\xb4', '\x46', |
155 | '\x02', '\x00', '\x21', '\x01', '\x1c', '\x00', '\x00', '\x00', '\x10', '\xcf', '\x58', '\xcc', |
156 | '\xe0', '\x00', '\x14', '\x00', '\x11', '\x5d', '\x00', '\x26', '\x1a', '\x49', '\xc6', '\x67', |
157 | '\x41', '\x5b', '\x71', '\x8c', '\x25', '\x3c', '\x08', '\xec', '\x79', '\xa7', '\x7b', '\x60', |
158 | '\x00', '\x00', '\x00', '\x00', '\xc7', '\x62', '\xbb', '\xaa', '\x59', '\x96', '\x2b', '\xa4', |
159 | '\x00', '\x01', '\x2d', '\x15', '\x2f', '\x0b', '\x71', '\x6d', '\x1f', '\xb6', '\xf3', '\x7d', |
160 | '\x01', '\x00', '\x00', '\x00', '\x00', '\x04', '\x59', '\x5a' |
161 | }; |
162 | |
163 | filtering_istream in; |
164 | std::string line; |
165 | |
166 | in.push(t: lzma_decompressor()); |
167 | in.push(t: io::array_source(multipart_file, sizeof(multipart_file))); |
168 | |
169 | // First part |
170 | std::getline(is&: in, str&: line); |
171 | BOOST_CHECK_EQUAL("Line 1" , line); |
172 | std::getline(is&: in, str&: line); |
173 | BOOST_CHECK_EQUAL("Line 2" , line); |
174 | std::getline(is&: in, str&: line); |
175 | BOOST_CHECK_EQUAL("Line 3" , line); |
176 | |
177 | // Second part immediately follows |
178 | std::getline(is&: in, str&: line); |
179 | BOOST_CHECK_EQUAL("Line 4" , line); |
180 | std::getline(is&: in, str&: line); |
181 | BOOST_CHECK_EQUAL("Line 5" , line); |
182 | std::getline(is&: in, str&: line); |
183 | BOOST_CHECK_EQUAL("Line 6" , line); |
184 | |
185 | // Then an empty part, followed by one last 3-line part. |
186 | std::getline(is&: in, str&: line); |
187 | BOOST_CHECK_EQUAL("Line 7" , line); |
188 | std::getline(is&: in, str&: line); |
189 | BOOST_CHECK_EQUAL("Line 8" , line); |
190 | std::getline(is&: in, str&: line); |
191 | BOOST_CHECK_EQUAL("Line 9" , line); |
192 | |
193 | // Check for lzma errors too. |
194 | BOOST_CHECK(!in.bad()); |
195 | } |
196 | |
197 | void multithreaded_test() |
198 | { |
199 | text_sequence data; |
200 | |
201 | // Get correct compressed string at level 2. |
202 | // Tests legacy capability of providing a single integer to the |
203 | // lzma_compressor constructor to be used as the "level" to initialize |
204 | // lzma_params. |
205 | std::string correct_level_2; |
206 | { |
207 | filtering_ostream out; |
208 | out.push(t: lzma_compressor(2)); |
209 | out.push(t: io::back_inserter(cnt&: correct_level_2)); |
210 | io::copy(src: make_iterator_range(r&: data), snk&: out); |
211 | } |
212 | |
213 | // Tests omitting the threads parameters and arriving at same compressed data. |
214 | BOOST_CHECK( |
215 | test_output_filter( lzma_compressor(lzma_params(2)), |
216 | std::string(data.begin(), data.end()), |
217 | correct_level_2 ) |
218 | ); |
219 | |
220 | // Test specifying a single thread and arriving at same compressed data. |
221 | BOOST_CHECK( |
222 | test_output_filter( lzma_compressor(lzma_params(2, 1)), |
223 | std::string(data.begin(), data.end()), |
224 | correct_level_2 ) |
225 | ); |
226 | |
227 | // Test specifying multiple threads and arriving at same compressed data. |
228 | BOOST_CHECK( |
229 | test_output_filter( lzma_compressor(lzma_params(2, 4)), |
230 | std::string(data.begin(), data.end()), |
231 | correct_level_2 ) |
232 | ); |
233 | |
234 | // Test specifying "0" threads, which is interpreted as |
235 | // using all cores, or 1 thread if such capability is missing. |
236 | BOOST_CHECK( |
237 | test_output_filter( lzma_compressor(lzma_params(2, 0)), |
238 | std::string(data.begin(), data.end()), |
239 | correct_level_2 ) |
240 | ); |
241 | |
242 | // Test that decompressor works to decompress the output with various thread values. |
243 | // Threading shouldn't affect the decompression and, in fact, isn't |
244 | // threaded in current implementation of liblzma. Both the level and |
245 | // threads options are ignored by the decompressor. |
246 | BOOST_CHECK( |
247 | test_input_filter( lzma_decompressor(lzma_params(2, 1)), |
248 | correct_level_2, |
249 | std::string(data.begin(), data.end()) ) |
250 | ); |
251 | BOOST_CHECK( |
252 | test_input_filter( lzma_decompressor(lzma_params(2, 4)), |
253 | correct_level_2, |
254 | std::string(data.begin(), data.end()) ) |
255 | ); |
256 | BOOST_CHECK( |
257 | test_input_filter( lzma_decompressor(lzma_params(2, 0)), |
258 | correct_level_2, |
259 | std::string(data.begin(), data.end()) ) |
260 | ); |
261 | |
262 | } |
263 | |
264 | test_suite* init_unit_test_suite(int, char* []) |
265 | { |
266 | test_suite* test = BOOST_TEST_SUITE("lzma test" ); |
267 | test->add(BOOST_TEST_CASE(&compression_test)); |
268 | test->add(BOOST_TEST_CASE(&multiple_member_test)); |
269 | test->add(BOOST_TEST_CASE(&array_source_test)); |
270 | test->add(BOOST_TEST_CASE(&empty_file_test)); |
271 | test->add(BOOST_TEST_CASE(&multipart_test)); |
272 | test->add(BOOST_TEST_CASE(&multithreaded_test)); |
273 | return test; |
274 | } |
275 | |