Line data Source code
1 : // Copyright (c) 2012-2015 The Bitcoin Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include "random.h"
6 : #include "streams.h"
7 : #include "test/test_pivx.h"
8 :
9 : #include <boost/test/unit_test.hpp>
10 :
11 : BOOST_FIXTURE_TEST_SUITE(streams_tests, BasicTestingSetup)
12 :
13 2 : BOOST_AUTO_TEST_CASE(streams_vector_writer)
14 : {
15 1 : unsigned char a(1);
16 1 : unsigned char b(2);
17 1 : unsigned char bytes[] = { 3, 4, 5, 6 };
18 1 : std::vector<unsigned char> vch;
19 :
20 : // Each test runs twice. Serializing a second time at the same starting
21 : // point should yield the same results, even if the first test grew the
22 : // vector.
23 :
24 1 : CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, a, b);
25 3 : BOOST_CHECK((vch == std::vector<unsigned char>{{1, 2}}));
26 1 : CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, a, b);
27 3 : BOOST_CHECK((vch == std::vector<unsigned char>{{1, 2}}));
28 1 : vch.clear();
29 :
30 1 : CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b);
31 3 : BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2}}));
32 1 : CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b);
33 3 : BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2}}));
34 1 : vch.clear();
35 :
36 1 : vch.resize(5, 0);
37 1 : CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b);
38 3 : BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2, 0}}));
39 1 : CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b);
40 3 : BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2, 0}}));
41 1 : vch.clear();
42 :
43 1 : vch.resize(4, 0);
44 1 : CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 3, a, b);
45 3 : BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 1, 2}}));
46 1 : CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 3, a, b);
47 3 : BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 1, 2}}));
48 1 : vch.clear();
49 :
50 1 : vch.resize(4, 0);
51 1 : CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 4, a, b);
52 3 : BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}}));
53 1 : CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 4, a, b);
54 3 : BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}}));
55 1 : vch.clear();
56 :
57 1 : CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, bytes);
58 3 : BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}}));
59 1 : CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, bytes);
60 3 : BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}}));
61 1 : vch.clear();
62 :
63 1 : vch.resize(4, 8);
64 1 : CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, bytes, b);
65 3 : BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}}));
66 1 : CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, bytes, b);
67 3 : BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}}));
68 2 : vch.clear();
69 1 : }
70 :
71 2 : BOOST_AUTO_TEST_CASE(streams_buffered_file)
72 : {
73 1 : FILE* file = fsbridge::fopen("streams_test_tmp", "w+b");
74 : // The value at each offset is the offset.
75 41 : for (uint8_t j = 0; j < 40; ++j) {
76 40 : fwrite(&j, 1, 1, file);
77 : }
78 1 : rewind(file);
79 :
80 : // The buffer size (second arg) must be greater than the rewind
81 : // amount (third arg).
82 1 : try {
83 1 : CBufferedFile bfbad(file, 25, 25, 222, 333);
84 0 : BOOST_CHECK(false);
85 2 : } catch (const std::exception& e) {
86 2 : BOOST_CHECK(strstr(e.what(),
87 : "Rewind limit must be less than buffer size") != nullptr);
88 : }
89 :
90 : // The buffer is 25 bytes, allow rewinding 10 bytes.
91 1 : CBufferedFile bf(file, 25, 10, 222, 333);
92 3 : BOOST_CHECK(!bf.eof());
93 :
94 1 : uint8_t i;
95 1 : bf >> i;
96 1 : BOOST_CHECK_EQUAL(i, 0);
97 1 : bf >> i;
98 1 : BOOST_CHECK_EQUAL(i, 1);
99 :
100 : // After reading bytes 0 and 1, we're positioned at 2.
101 1 : BOOST_CHECK_EQUAL(bf.GetPos(), 2);
102 :
103 : // Rewind to offset 0, ok (within the 10 byte window).
104 3 : BOOST_CHECK(bf.SetPos(0));
105 1 : bf >> i;
106 1 : BOOST_CHECK_EQUAL(i, 0);
107 :
108 : // We can go forward to where we've been, but beyond may fail.
109 3 : BOOST_CHECK(bf.SetPos(2));
110 1 : bf >> i;
111 1 : BOOST_CHECK_EQUAL(i, 2);
112 :
113 : // If you know the maximum number of bytes that should be
114 : // read to deserialize the variable, you can limit the read
115 : // extent. The current file offset is 3, so the following
116 : // SetLimit() allows zero bytes to be read.
117 3 : BOOST_CHECK(bf.SetLimit(3));
118 1 : try {
119 1 : bf >> i;
120 1 : BOOST_CHECK(false);
121 2 : } catch (const std::exception& e) {
122 2 : BOOST_CHECK(strstr(e.what(),
123 : "Read attempted past buffer limit") != nullptr);
124 : }
125 : // The default argument removes the limit completely.
126 2 : BOOST_CHECK(bf.SetLimit());
127 : // The read position should still be at 3 (no change).
128 1 : BOOST_CHECK_EQUAL(bf.GetPos(), 3);
129 :
130 : // Read from current offset, 3, forward until position 10.
131 8 : for (uint8_t j = 3; j < 10; ++j) {
132 7 : bf >> i;
133 7 : BOOST_CHECK_EQUAL(i, j);
134 : }
135 1 : BOOST_CHECK_EQUAL(bf.GetPos(), 10);
136 :
137 : // We're guaranteed (just barely) to be able to rewind to zero.
138 3 : BOOST_CHECK(bf.SetPos(0));
139 1 : BOOST_CHECK_EQUAL(bf.GetPos(), 0);
140 1 : bf >> i;
141 1 : BOOST_CHECK_EQUAL(i, 0);
142 :
143 : // We can set the position forward again up to the farthest
144 : // into the stream we've been, but no farther. (Attempting
145 : // to go farther may succeed, but it's not guaranteed.)
146 3 : BOOST_CHECK(bf.SetPos(10));
147 1 : bf >> i;
148 1 : BOOST_CHECK_EQUAL(i, 10);
149 1 : BOOST_CHECK_EQUAL(bf.GetPos(), 11);
150 :
151 : // Now it's only guaranteed that we can rewind to offset 1
152 : // (current read position, 11, minus rewind amount, 10).
153 3 : BOOST_CHECK(bf.SetPos(1));
154 1 : BOOST_CHECK_EQUAL(bf.GetPos(), 1);
155 1 : bf >> i;
156 1 : BOOST_CHECK_EQUAL(i, 1);
157 :
158 : // We can stream into large variables, even larger than
159 : // the buffer size.
160 3 : BOOST_CHECK(bf.SetPos(11));
161 1 : {
162 1 : uint8_t a[40 - 11];
163 1 : bf >> a;
164 30 : for (uint8_t j = 0; j < sizeof(a); ++j) {
165 29 : BOOST_CHECK_EQUAL(a[j], 11 + j);
166 : }
167 : }
168 1 : BOOST_CHECK_EQUAL(bf.GetPos(), 40);
169 :
170 : // We've read the entire file, the next read should throw.
171 1 : try {
172 1 : bf >> i;
173 1 : BOOST_CHECK(false);
174 2 : } catch (const std::exception& e) {
175 2 : BOOST_CHECK(strstr(e.what(),
176 : "CBufferedFile::Fill: end of file") != nullptr);
177 : }
178 : // Attempting to read beyond the end sets the EOF indicator.
179 3 : BOOST_CHECK(bf.eof());
180 :
181 : // Still at offset 40, we can go back 10, to 30.
182 1 : BOOST_CHECK_EQUAL(bf.GetPos(), 40);
183 3 : BOOST_CHECK(bf.SetPos(30));
184 1 : bf >> i;
185 1 : BOOST_CHECK_EQUAL(i, 30);
186 1 : BOOST_CHECK_EQUAL(bf.GetPos(), 31);
187 :
188 : // We're too far to rewind to position zero.
189 3 : BOOST_CHECK(!bf.SetPos(0));
190 : // But we should now be positioned at least as far back as allowed
191 : // by the rewind window (relative to our farthest read position, 40).
192 2 : BOOST_CHECK(bf.GetPos() <= 30);
193 :
194 : // We can explicitly close the file, or the destructor will do it.
195 1 : bf.fclose();
196 :
197 2 : fs::remove("streams_test_tmp");
198 1 : }
199 :
200 2 : BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
201 : {
202 : // Make this test deterministic.
203 1 : SeedInsecureRand(SeedRand::ZEROS);
204 :
205 51 : for (int rep = 0; rep < 50; ++rep) {
206 50 : FILE* file = fsbridge::fopen("streams_test_tmp", "w+b");
207 50 : size_t fileSize = InsecureRandRange(256);
208 5796 : for (uint8_t i = 0; i < fileSize; ++i) {
209 5746 : fwrite(&i, 1, 1, file);
210 : }
211 50 : rewind(file);
212 :
213 76 : size_t bufSize = InsecureRandRange(300) + 1;
214 50 : size_t rewindSize = InsecureRandRange(bufSize);
215 100 : CBufferedFile bf(file, bufSize, rewindSize, 222, 333);
216 50 : size_t currentPos = 0;
217 50 : size_t maxPos = 0;
218 3722 : for (int step = 0; step < 100; ++step) {
219 3694 : if (currentPos >= fileSize)
220 : break;
221 :
222 : // We haven't read to the end of the file yet.
223 7500 : BOOST_CHECK(!bf.eof());
224 3672 : BOOST_CHECK_EQUAL(bf.GetPos(), currentPos);
225 :
226 : // Pretend the file consists of a series of objects of varying
227 : // sizes; the boundaries of the objects can interact arbitrarily
228 : // with the CBufferFile's internal buffer. These first three
229 : // cases simulate objects of various sizes (1, 2, 5 bytes).
230 7344 : switch (InsecureRandRange(5)) {
231 757 : case 0: {
232 757 : uint8_t a[1];
233 757 : if (currentPos + 1 > fileSize)
234 0 : continue;
235 757 : bf.SetLimit(currentPos + 1);
236 757 : bf >> a;
237 1514 : for (uint8_t i = 0; i < 1; ++i) {
238 757 : BOOST_CHECK_EQUAL(a[i], currentPos);
239 757 : currentPos++;
240 : }
241 757 : break;
242 : }
243 723 : case 1: {
244 723 : uint8_t a[2];
245 723 : if (currentPos + 2 > fileSize)
246 3 : continue;
247 720 : bf.SetLimit(currentPos + 2);
248 720 : bf >> a;
249 2160 : for (uint8_t i = 0; i < 2; ++i) {
250 1440 : BOOST_CHECK_EQUAL(a[i], currentPos);
251 1440 : currentPos++;
252 : }
253 720 : break;
254 : }
255 756 : case 2: {
256 756 : uint8_t a[5];
257 756 : if (currentPos + 5 > fileSize)
258 11 : continue;
259 745 : bf.SetLimit(currentPos + 5);
260 745 : bf >> a;
261 4470 : for (uint8_t i = 0; i < 5; ++i) {
262 3725 : BOOST_CHECK_EQUAL(a[i], currentPos);
263 3725 : currentPos++;
264 : }
265 745 : break;
266 : }
267 : case 3: {
268 : // Find a byte value (that is at or ahead of the current position).
269 746 : size_t find = currentPos + InsecureRandRange(8);
270 746 : if (find >= fileSize)
271 5 : find = fileSize - 1;
272 746 : bf.FindByte(static_cast<char>(find));
273 : // The value at each offset is the offset.
274 746 : BOOST_CHECK_EQUAL(bf.GetPos(), find);
275 746 : currentPos = find;
276 :
277 746 : bf.SetLimit(currentPos + 1);
278 746 : uint8_t i;
279 746 : bf >> i;
280 746 : BOOST_CHECK_EQUAL(i, currentPos);
281 746 : currentPos++;
282 746 : break;
283 : }
284 690 : case 4: {
285 690 : size_t requestPos = InsecureRandRange(maxPos + 4);
286 690 : bool okay = bf.SetPos(requestPos);
287 : // The new position may differ from the requested position
288 : // because we may not be able to rewind beyond the rewind
289 : // window, and we may not be able to move forward beyond the
290 : // farthest position we've reached so far.
291 690 : currentPos = bf.GetPos();
292 690 : BOOST_CHECK_EQUAL(okay, currentPos == requestPos);
293 : // Check that we can position within the rewind window.
294 690 : if (requestPos <= maxPos &&
295 690 : maxPos > rewindSize &&
296 236 : requestPos >= maxPos - rewindSize) {
297 : // We requested a position within the rewind window.
298 220 : BOOST_CHECK(okay);
299 : }
300 690 : break;
301 : }
302 : }
303 3658 : if (maxPos < currentPos)
304 1384 : maxPos = currentPos;
305 : }
306 : }
307 1 : fs::remove("streams_test_tmp");
308 1 : }
309 :
310 : BOOST_AUTO_TEST_SUITE_END()
|