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 struct lzma_alloc : std::allocator<char> {
lzma_alloclzma_alloc34 lzma_alloc() { }
lzma_alloclzma_alloc35 lzma_alloc(const lzma_alloc& other) { }
36 template<typename T>
lzma_alloclzma_alloc37 lzma_alloc(const std::allocator<T>& other) { }
38 };
39
compression_test()40 void compression_test()
41 {
42 text_sequence data;
43
44 // Test compression and decompression with custom allocator
45 BOOST_CHECK(
46 test_filter_pair( basic_lzma_compressor<lzma_alloc>(),
47 basic_lzma_decompressor<lzma_alloc>(),
48 std::string(data.begin(), data.end()) )
49 );
50 }
51
multiple_member_test()52 void multiple_member_test()
53 {
54 text_sequence data;
55 std::vector<char> temp, dest;
56
57 // Write compressed data to temp, twice in succession
58 filtering_ostream out;
59 out.push(lzma_compressor());
60 out.push(io::back_inserter(temp));
61 io::copy(make_iterator_range(data), out);
62 out.push(io::back_inserter(temp));
63 io::copy(make_iterator_range(data), out);
64
65 // Read compressed data from temp into dest
66 filtering_istream in;
67 in.push(lzma_decompressor());
68 in.push(array_source(&temp[0], temp.size()));
69 io::copy(in, io::back_inserter(dest));
70
71 // Check that dest consists of two copies of data
72 BOOST_REQUIRE_EQUAL(data.size() * 2, dest.size());
73 BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin()));
74 BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin() + dest.size() / 2));
75
76 dest.clear();
77 io::copy(
78 array_source(&temp[0], temp.size()),
79 io::compose(lzma_decompressor(), io::back_inserter(dest)));
80
81 // Check that dest consists of two copies of data
82 BOOST_REQUIRE_EQUAL(data.size() * 2, dest.size());
83 BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin()));
84 BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin() + dest.size() / 2));
85 }
86
array_source_test()87 void array_source_test()
88 {
89 std::string data = "simple test string.";
90 std::string encoded;
91
92 filtering_ostream out;
93 out.push(lzma_compressor());
94 out.push(io::back_inserter(encoded));
95 io::copy(make_iterator_range(data), out);
96
97 std::string res;
98 io::array_source src(encoded.data(),encoded.length());
99 io::copy(io::compose(io::lzma_decompressor(), src), io::back_inserter(res));
100
101 BOOST_CHECK_EQUAL(data, res);
102 }
103
empty_file_test()104 void empty_file_test()
105 {
106 // This test is in response to https://svn.boost.org/trac/boost/ticket/5237
107 // The previous implementation of gzip_compressor only wrote the gzip file
108 // header when the first bytes of uncompressed input were processed, causing
109 // incorrect behavior for empty files
110 BOOST_CHECK(
111 test_filter_pair( lzma_compressor(),
112 lzma_decompressor(),
113 std::string() )
114 );
115 }
116
multipart_test()117 void multipart_test()
118 {
119 // This test verifies that the lzma_decompressor properly handles a file
120 // that consists of multiple concatenated files (matches unxz behaviour)
121 static const char multipart_file[] = {
122 '\xfd', '\x37', '\x7a', '\x58', '\x5a', '\x00', '\x00', '\x04', '\xe6', '\xd6', '\xb4', '\x46',
123 '\x02', '\x00', '\x21', '\x01', '\x1c', '\x00', '\x00', '\x00', '\x10', '\xcf', '\x58', '\xcc',
124 '\xe0', '\x00', '\x14', '\x00', '\x11', '\x5d', '\x00', '\x26', '\x1a', '\x49', '\xc6', '\x67',
125 '\x41', '\x3f', '\x96', '\x8c', '\x25', '\x02', '\xb3', '\x4d', '\x16', '\xa8', '\xb4', '\x40',
126 '\x00', '\x00', '\x00', '\x00', '\xeb', '\xad', '\x3f', '\xbf', '\x8c', '\x8c', '\x72', '\x25',
127 '\x00', '\x01', '\x2d', '\x15', '\x2f', '\x0b', '\x71', '\x6d', '\x1f', '\xb6', '\xf3', '\x7d',
128 '\x01', '\x00', '\x00', '\x00', '\x00', '\x04', '\x59', '\x5a', '\xfd', '\x37', '\x7a', '\x58',
129 '\x5a', '\x00', '\x00', '\x04', '\xe6', '\xd6', '\xb4', '\x46', '\x02', '\x00', '\x21', '\x01',
130 '\x1c', '\x00', '\x00', '\x00', '\x10', '\xcf', '\x58', '\xcc', '\xe0', '\x00', '\x14', '\x00',
131 '\x11', '\x5d', '\x00', '\x26', '\x1a', '\x49', '\xc6', '\x67', '\x41', '\x4d', '\x84', '\x0c',
132 '\x25', '\x1f', '\x5e', '\x1d', '\x4a', '\x91', '\x61', '\xa0', '\x00', '\x00', '\x00', '\x00',
133 '\x56', '\x76', '\x71', '\xf0', '\x54', '\x21', '\xa2', '\x5b', '\x00', '\x01', '\x2d', '\x15',
134 '\x2f', '\x0b', '\x71', '\x6d', '\x1f', '\xb6', '\xf3', '\x7d', '\x01', '\x00', '\x00', '\x00',
135 '\x00', '\x04', '\x59', '\x5a', '\xfd', '\x37', '\x7a', '\x58', '\x5a', '\x00', '\x00', '\x04',
136 '\xe6', '\xd6', '\xb4', '\x46', '\x00', '\x00', '\x00', '\x00', '\x1c', '\xdf', '\x44', '\x21',
137 '\x1f', '\xb6', '\xf3', '\x7d', '\x01', '\x00', '\x00', '\x00', '\x00', '\x04', '\x59', '\x5a',
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', '\x5b', '\x71', '\x8c', '\x25', '\x3c', '\x08', '\xec', '\x79', '\xa7', '\x7b', '\x60',
142 '\x00', '\x00', '\x00', '\x00', '\xc7', '\x62', '\xbb', '\xaa', '\x59', '\x96', '\x2b', '\xa4',
143 '\x00', '\x01', '\x2d', '\x15', '\x2f', '\x0b', '\x71', '\x6d', '\x1f', '\xb6', '\xf3', '\x7d',
144 '\x01', '\x00', '\x00', '\x00', '\x00', '\x04', '\x59', '\x5a'
145 };
146
147 filtering_istream in;
148 std::string line;
149
150 in.push(lzma_decompressor());
151 in.push(io::array_source(multipart_file, sizeof(multipart_file)));
152
153 // First part
154 std::getline(in, line);
155 BOOST_CHECK_EQUAL("Line 1", line);
156 std::getline(in, line);
157 BOOST_CHECK_EQUAL("Line 2", line);
158 std::getline(in, line);
159 BOOST_CHECK_EQUAL("Line 3", line);
160
161 // Second part immediately follows
162 std::getline(in, line);
163 BOOST_CHECK_EQUAL("Line 4", line);
164 std::getline(in, line);
165 BOOST_CHECK_EQUAL("Line 5", line);
166 std::getline(in, line);
167 BOOST_CHECK_EQUAL("Line 6", line);
168
169 // Then an empty part, followed by one last 3-line part.
170 std::getline(in, line);
171 BOOST_CHECK_EQUAL("Line 7", line);
172 std::getline(in, line);
173 BOOST_CHECK_EQUAL("Line 8", line);
174 std::getline(in, line);
175 BOOST_CHECK_EQUAL("Line 9", line);
176
177 // Check for lzma errors too.
178 BOOST_CHECK(!in.bad());
179 }
180
multithreaded_test()181 void multithreaded_test()
182 {
183 text_sequence data;
184
185 // Get correct compressed string at level 2.
186 // Tests legacy capability of providing a single integer to the
187 // lzma_compressor constructor to be used as the "level" to initialize
188 // lzma_params.
189 std::string correct_level_2;
190 {
191 filtering_ostream out;
192 out.push(lzma_compressor(2));
193 out.push(io::back_inserter(correct_level_2));
194 io::copy(make_iterator_range(data), out);
195 }
196
197 // Tests omitting the threads parameters and arriving at same compressed data.
198 BOOST_CHECK(
199 test_output_filter( lzma_compressor(lzma_params(2)),
200 std::string(data.begin(), data.end()),
201 correct_level_2 )
202 );
203
204 // Test specifying a single thread and arriving at same compressed data.
205 BOOST_CHECK(
206 test_output_filter( lzma_compressor(lzma_params(2, 1)),
207 std::string(data.begin(), data.end()),
208 correct_level_2 )
209 );
210
211 // Test specifying multiple threads and arriving at same compressed data.
212 BOOST_CHECK(
213 test_output_filter( lzma_compressor(lzma_params(2, 4)),
214 std::string(data.begin(), data.end()),
215 correct_level_2 )
216 );
217
218 // Test specifying "0" threads, which is interpreted as
219 // using all cores, or 1 thread if such capability is missing.
220 BOOST_CHECK(
221 test_output_filter( lzma_compressor(lzma_params(2, 0)),
222 std::string(data.begin(), data.end()),
223 correct_level_2 )
224 );
225
226 // Test that decompressor works to decompress the output with various thread values.
227 // Threading shouldn't affect the decompression and, in fact, isn't
228 // threaded in current implementation of liblzma. Both the level and
229 // threads options are ignored by the decompressor.
230 BOOST_CHECK(
231 test_input_filter( lzma_decompressor(lzma_params(2, 1)),
232 correct_level_2,
233 std::string(data.begin(), data.end()) )
234 );
235 BOOST_CHECK(
236 test_input_filter( lzma_decompressor(lzma_params(2, 4)),
237 correct_level_2,
238 std::string(data.begin(), data.end()) )
239 );
240 BOOST_CHECK(
241 test_input_filter( lzma_decompressor(lzma_params(2, 0)),
242 correct_level_2,
243 std::string(data.begin(), data.end()) )
244 );
245
246 }
247
init_unit_test_suite(int,char * [])248 test_suite* init_unit_test_suite(int, char* [])
249 {
250 test_suite* test = BOOST_TEST_SUITE("lzma test");
251 test->add(BOOST_TEST_CASE(&compression_test));
252 test->add(BOOST_TEST_CASE(&multiple_member_test));
253 test->add(BOOST_TEST_CASE(&array_source_test));
254 test->add(BOOST_TEST_CASE(&empty_file_test));
255 test->add(BOOST_TEST_CASE(&multipart_test));
256 test->add(BOOST_TEST_CASE(&multithreaded_test));
257 return test;
258 }
259