1// (C) COPYRIGHT 2018 Reimar Döffinger
2// Based on zstd_test.cpp by:
3// (C) COPYRIGHT 2017 ARM Limited
4// (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com)
5// (C) Copyright 2004-2007 Jonathan Turkanis
6// Distributed under the Boost Software License, Version 1.0. (See accompanying
7// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
8
9// See http://www.boost.org/libs/iostreams for documentation.
10
11// Note: basically a copy-paste of the gzip test
12
13#include <cstddef>
14#include <string>
15#include <boost/iostreams/copy.hpp>
16#include <boost/iostreams/device/array.hpp>
17#include <boost/iostreams/device/back_inserter.hpp>
18#include <boost/iostreams/filter/zstd.hpp>
19#include <boost/iostreams/filter/test.hpp>
20#include <boost/iostreams/filtering_stream.hpp>
21#include <boost/ref.hpp>
22#include <boost/range/iterator_range.hpp>
23#include <boost/test/test_tools.hpp>
24#include <boost/test/unit_test.hpp>
25#include "detail/sequence.hpp"
26#include "detail/verification.hpp"
27
28using namespace boost;
29using namespace boost::iostreams;
30using namespace boost::iostreams::test;
31namespace io = boost::iostreams;
32using boost::unit_test::test_suite;
33
34template<class T> struct basic_test_alloc: std::allocator<T>
35{
36 basic_test_alloc()
37 {
38 }
39
40 basic_test_alloc( basic_test_alloc const& /*other*/ )
41 {
42 }
43
44 template<class U>
45 basic_test_alloc( basic_test_alloc<U> const & /*other*/ )
46 {
47 }
48
49 template<class U> struct rebind
50 {
51 typedef basic_test_alloc<U> other;
52 };
53};
54
55typedef basic_test_alloc<char> zstd_alloc;
56
57void compression_test()
58{
59 text_sequence data;
60
61 // Test compression and decompression with custom allocator
62 BOOST_CHECK(
63 test_filter_pair( basic_zstd_compressor<zstd_alloc>(),
64 basic_zstd_decompressor<zstd_alloc>(),
65 std::string(data.begin(), data.end()) )
66 );
67}
68
69void multiple_member_test()
70{
71 text_sequence data;
72 std::vector<char> temp, dest;
73
74 // Write compressed data to temp, twice in succession
75 filtering_ostream out;
76 out.push(t: zstd_compressor());
77 out.push(t: io::back_inserter(cnt&: temp));
78 io::copy(src: make_iterator_range(r&: data), snk&: out);
79 out.push(t: io::back_inserter(cnt&: temp));
80 io::copy(src: make_iterator_range(r&: data), snk&: out);
81 BOOST_CHECK(std::equal(temp.begin(), temp.begin() + temp.size()/2, temp.begin() + temp.size()/2));
82
83 // Read compressed data from temp into dest
84 filtering_istream in;
85 in.push(t: zstd_decompressor());
86 in.push(t: array_source(&temp[0], temp.size()));
87 io::copy(src&: in, snk: io::back_inserter(cnt&: dest));
88
89 // Check that dest consists of two copies of data
90 BOOST_REQUIRE_EQUAL(data.size() * 2, dest.size());
91 BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin()));
92 BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin() + dest.size() / 2));
93
94 dest.clear();
95 io::copy(
96 src: array_source(&temp[0], temp.size()),
97 snk: io::compose(filter: zstd_decompressor(), fod: io::back_inserter(cnt&: dest)));
98
99 // Check that dest consists of two copies of data
100 BOOST_REQUIRE_EQUAL(data.size() * 2, dest.size());
101 BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin()));
102 BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin() + dest.size() / 2));
103}
104
105void array_source_test()
106{
107 std::string data = "simple test string.";
108 std::string encoded;
109
110 filtering_ostream out;
111 out.push(t: zstd_compressor());
112 out.push(t: io::back_inserter(cnt&: encoded));
113 io::copy(src: make_iterator_range(r&: data), snk&: out);
114
115 std::string res;
116 io::array_source src(encoded.data(),encoded.length());
117 io::copy(src: io::compose(filter: io::zstd_decompressor(), fod: src), snk: io::back_inserter(cnt&: res));
118
119 BOOST_CHECK_EQUAL(data, res);
120}
121
122void empty_file_test()
123{
124 // This test is in response to https://svn.boost.org/trac/boost/ticket/5237
125 // The previous implementation of gzip_compressor only wrote the gzip file
126 // header when the first bytes of uncompressed input were processed, causing
127 // incorrect behavior for empty files
128 BOOST_CHECK(
129 test_filter_pair( zstd_compressor(),
130 zstd_decompressor(),
131 std::string() )
132 );
133}
134
135void multipart_test()
136{
137 // This test verifies that the zstd_decompressor properly handles a file
138 // that consists of multiple concatenated files (matches unzstd behaviour)
139 static const char multipart_file[] = {
140 '\x28', '\xb5', '\x2f', '\xfd', '\x24', '\x15', '\x95', '\x00', '\x00', '\x50', '\x4c', '\x69',
141 '\x6e', '\x65', '\x20', '\x31', '\x0a', '\x32', '\x33', '\x0a', '\x02', '\x00', '\x60', '\x84',
142 '\xae', '\x62', '\x04', '\x19', '\xf8', '\xe1', '\x2d', '\x28', '\xb5', '\x2f', '\xfd', '\x24',
143 '\x15', '\x95', '\x00', '\x00', '\x50', '\x4c', '\x69', '\x6e', '\x65', '\x20', '\x34', '\x0a',
144 '\x35', '\x36', '\x0a', '\x02', '\x00', '\x60', '\x84', '\xae', '\x62', '\x04', '\x5f', '\xcf',
145 '\xd5', '\xb8', '\x28', '\xb5', '\x2f', '\xfd', '\x24', '\x00', '\x01', '\x00', '\x00', '\x99',
146 '\xe9', '\xd8', '\x51', '\x28', '\xb5', '\x2f', '\xfd', '\x24', '\x15', '\x95', '\x00', '\x00',
147 '\x50', '\x4c', '\x69', '\x6e', '\x65', '\x20', '\x37', '\x0a', '\x38', '\x39', '\x0a', '\x02',
148 '\x00', '\x60', '\x84', '\xae', '\x62', '\x04', '\x94', '\x13', '\xdb', '\xae'
149 };
150
151 filtering_istream in;
152 std::string line;
153
154 in.push(t: zstd_decompressor());
155 in.push(t: io::array_source(multipart_file, sizeof(multipart_file)));
156
157 // First part
158 std::getline(is&: in, str&: line);
159 BOOST_CHECK_EQUAL("Line 1", line);
160 std::getline(is&: in, str&: line);
161 BOOST_CHECK_EQUAL("Line 2", line);
162 std::getline(is&: in, str&: line);
163 BOOST_CHECK_EQUAL("Line 3", line);
164
165 // Second part immediately follows
166 std::getline(is&: in, str&: line);
167 BOOST_CHECK_EQUAL("Line 4", line);
168 std::getline(is&: in, str&: line);
169 BOOST_CHECK_EQUAL("Line 5", line);
170 std::getline(is&: in, str&: line);
171 BOOST_CHECK_EQUAL("Line 6", line);
172
173 // Then an empty part, followed by one last 3-line part.
174 std::getline(is&: in, str&: line);
175 BOOST_CHECK_EQUAL("Line 7", line);
176 std::getline(is&: in, str&: line);
177 BOOST_CHECK_EQUAL("Line 8", line);
178 std::getline(is&: in, str&: line);
179 BOOST_CHECK_EQUAL("Line 9", line);
180
181 // Check for zstd errors too.
182 BOOST_CHECK(!in.bad());
183}
184
185test_suite* init_unit_test_suite(int, char* [])
186{
187 test_suite* test = BOOST_TEST_SUITE("zstd test");
188 test->add(BOOST_TEST_CASE(&compression_test));
189 test->add(BOOST_TEST_CASE(&multiple_member_test));
190 test->add(BOOST_TEST_CASE(&array_source_test));
191 test->add(BOOST_TEST_CASE(&empty_file_test));
192 test->add(BOOST_TEST_CASE(&multipart_test));
193 return test;
194}
195

source code of boost/libs/iostreams/test/zstd_test.cpp