PIVX Core  5.6.99
P2P Digital Currency
streams_tests.cpp
Go to the documentation of this file.
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 
12 
13 BOOST_AUTO_TEST_CASE(streams_vector_writer)
14 {
15  unsigned char a(1);
16  unsigned char b(2);
17  unsigned char bytes[] = { 3, 4, 5, 6 };
18  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  CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, a, b);
25  BOOST_CHECK((vch == std::vector<unsigned char>{{1, 2}}));
26  CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, a, b);
27  BOOST_CHECK((vch == std::vector<unsigned char>{{1, 2}}));
28  vch.clear();
29 
30  CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b);
31  BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2}}));
32  CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b);
33  BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2}}));
34  vch.clear();
35 
36  vch.resize(5, 0);
37  CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b);
38  BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2, 0}}));
39  CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b);
40  BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2, 0}}));
41  vch.clear();
42 
43  vch.resize(4, 0);
44  CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 3, a, b);
45  BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 1, 2}}));
46  CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 3, a, b);
47  BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 1, 2}}));
48  vch.clear();
49 
50  vch.resize(4, 0);
51  CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 4, a, b);
52  BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}}));
53  CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 4, a, b);
54  BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}}));
55  vch.clear();
56 
57  CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, bytes);
58  BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}}));
59  CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, bytes);
60  BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}}));
61  vch.clear();
62 
63  vch.resize(4, 8);
64  CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, bytes, b);
65  BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}}));
66  CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, bytes, b);
67  BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}}));
68  vch.clear();
69 }
70 
71 BOOST_AUTO_TEST_CASE(streams_buffered_file)
72 {
73  FILE* file = fsbridge::fopen("streams_test_tmp", "w+b");
74  // The value at each offset is the offset.
75  for (uint8_t j = 0; j < 40; ++j) {
76  fwrite(&j, 1, 1, file);
77  }
78  rewind(file);
79 
80  // The buffer size (second arg) must be greater than the rewind
81  // amount (third arg).
82  try {
83  CBufferedFile bfbad(file, 25, 25, 222, 333);
84  BOOST_CHECK(false);
85  } catch (const std::exception& e) {
86  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  CBufferedFile bf(file, 25, 10, 222, 333);
92  BOOST_CHECK(!bf.eof());
93 
94  uint8_t i;
95  bf >> i;
96  BOOST_CHECK_EQUAL(i, 0);
97  bf >> i;
98  BOOST_CHECK_EQUAL(i, 1);
99 
100  // After reading bytes 0 and 1, we're positioned at 2.
101  BOOST_CHECK_EQUAL(bf.GetPos(), 2);
102 
103  // Rewind to offset 0, ok (within the 10 byte window).
104  BOOST_CHECK(bf.SetPos(0));
105  bf >> i;
106  BOOST_CHECK_EQUAL(i, 0);
107 
108  // We can go forward to where we've been, but beyond may fail.
109  BOOST_CHECK(bf.SetPos(2));
110  bf >> i;
111  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  BOOST_CHECK(bf.SetLimit(3));
118  try {
119  bf >> i;
120  BOOST_CHECK(false);
121  } catch (const std::exception& e) {
122  BOOST_CHECK(strstr(e.what(),
123  "Read attempted past buffer limit") != nullptr);
124  }
125  // The default argument removes the limit completely.
126  BOOST_CHECK(bf.SetLimit());
127  // The read position should still be at 3 (no change).
128  BOOST_CHECK_EQUAL(bf.GetPos(), 3);
129 
130  // Read from current offset, 3, forward until position 10.
131  for (uint8_t j = 3; j < 10; ++j) {
132  bf >> i;
133  BOOST_CHECK_EQUAL(i, j);
134  }
135  BOOST_CHECK_EQUAL(bf.GetPos(), 10);
136 
137  // We're guaranteed (just barely) to be able to rewind to zero.
138  BOOST_CHECK(bf.SetPos(0));
139  BOOST_CHECK_EQUAL(bf.GetPos(), 0);
140  bf >> i;
141  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  BOOST_CHECK(bf.SetPos(10));
147  bf >> i;
148  BOOST_CHECK_EQUAL(i, 10);
149  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  BOOST_CHECK(bf.SetPos(1));
154  BOOST_CHECK_EQUAL(bf.GetPos(), 1);
155  bf >> i;
156  BOOST_CHECK_EQUAL(i, 1);
157 
158  // We can stream into large variables, even larger than
159  // the buffer size.
160  BOOST_CHECK(bf.SetPos(11));
161  {
162  uint8_t a[40 - 11];
163  bf >> a;
164  for (uint8_t j = 0; j < sizeof(a); ++j) {
165  BOOST_CHECK_EQUAL(a[j], 11 + j);
166  }
167  }
168  BOOST_CHECK_EQUAL(bf.GetPos(), 40);
169 
170  // We've read the entire file, the next read should throw.
171  try {
172  bf >> i;
173  BOOST_CHECK(false);
174  } catch (const std::exception& e) {
175  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  BOOST_CHECK(bf.eof());
180 
181  // Still at offset 40, we can go back 10, to 30.
182  BOOST_CHECK_EQUAL(bf.GetPos(), 40);
183  BOOST_CHECK(bf.SetPos(30));
184  bf >> i;
185  BOOST_CHECK_EQUAL(i, 30);
186  BOOST_CHECK_EQUAL(bf.GetPos(), 31);
187 
188  // We're too far to rewind to position zero.
189  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  BOOST_CHECK(bf.GetPos() <= 30);
193 
194  // We can explicitly close the file, or the destructor will do it.
195  bf.fclose();
196 
197  fs::remove("streams_test_tmp");
198 }
199 
200 BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
201 {
202  // Make this test deterministic.
203  SeedInsecureRand(SeedRand::ZEROS);
204 
205  for (int rep = 0; rep < 50; ++rep) {
206  FILE* file = fsbridge::fopen("streams_test_tmp", "w+b");
207  size_t fileSize = InsecureRandRange(256);
208  for (uint8_t i = 0; i < fileSize; ++i) {
209  fwrite(&i, 1, 1, file);
210  }
211  rewind(file);
212 
213  size_t bufSize = InsecureRandRange(300) + 1;
214  size_t rewindSize = InsecureRandRange(bufSize);
215  CBufferedFile bf(file, bufSize, rewindSize, 222, 333);
216  size_t currentPos = 0;
217  size_t maxPos = 0;
218  for (int step = 0; step < 100; ++step) {
219  if (currentPos >= fileSize)
220  break;
221 
222  // We haven't read to the end of the file yet.
223  BOOST_CHECK(!bf.eof());
224  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  switch (InsecureRandRange(5)) {
231  case 0: {
232  uint8_t a[1];
233  if (currentPos + 1 > fileSize)
234  continue;
235  bf.SetLimit(currentPos + 1);
236  bf >> a;
237  for (uint8_t i = 0; i < 1; ++i) {
238  BOOST_CHECK_EQUAL(a[i], currentPos);
239  currentPos++;
240  }
241  break;
242  }
243  case 1: {
244  uint8_t a[2];
245  if (currentPos + 2 > fileSize)
246  continue;
247  bf.SetLimit(currentPos + 2);
248  bf >> a;
249  for (uint8_t i = 0; i < 2; ++i) {
250  BOOST_CHECK_EQUAL(a[i], currentPos);
251  currentPos++;
252  }
253  break;
254  }
255  case 2: {
256  uint8_t a[5];
257  if (currentPos + 5 > fileSize)
258  continue;
259  bf.SetLimit(currentPos + 5);
260  bf >> a;
261  for (uint8_t i = 0; i < 5; ++i) {
262  BOOST_CHECK_EQUAL(a[i], currentPos);
263  currentPos++;
264  }
265  break;
266  }
267  case 3: {
268  // Find a byte value (that is at or ahead of the current position).
269  size_t find = currentPos + InsecureRandRange(8);
270  if (find >= fileSize)
271  find = fileSize - 1;
272  bf.FindByte(static_cast<char>(find));
273  // The value at each offset is the offset.
274  BOOST_CHECK_EQUAL(bf.GetPos(), find);
275  currentPos = find;
276 
277  bf.SetLimit(currentPos + 1);
278  uint8_t i;
279  bf >> i;
280  BOOST_CHECK_EQUAL(i, currentPos);
281  currentPos++;
282  break;
283  }
284  case 4: {
285  size_t requestPos = InsecureRandRange(maxPos + 4);
286  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  currentPos = bf.GetPos();
292  BOOST_CHECK_EQUAL(okay, currentPos == requestPos);
293  // Check that we can position within the rewind window.
294  if (requestPos <= maxPos &&
295  maxPos > rewindSize &&
296  requestPos >= maxPos - rewindSize) {
297  // We requested a position within the rewind window.
298  BOOST_CHECK(okay);
299  }
300  break;
301  }
302  }
303  if (maxPos < currentPos)
304  maxPos = currentPos;
305  }
306  }
307  fs::remove("streams_test_tmp");
308 }
309 
Non-refcounted RAII wrapper around a FILE* that implements a ring buffer to deserialize from.
Definition: streams.h:567
bool SetLimit(uint64_t nPos=std::numeric_limits< uint64_t >::max())
prevent reading beyond a certain position no argument removes the limit
Definition: streams.h:679
void FindByte(char ch)
Definition: streams.h:694
bool SetPos(uint64_t nPos)
rewind to a given reading position
Definition: streams.h:661
void fclose()
Definition: streams.h:619
bool eof() const
Definition: streams.h:628
uint64_t GetPos()
Definition: streams.h:655
BOOST_AUTO_TEST_SUITE_END()
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:23
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
@ SER_NETWORK
Definition: serialize.h:174
BOOST_AUTO_TEST_CASE(streams_vector_writer)
Basic testing setup.
Definition: test_pivx.h:51
@ ZEROS
Seed with a compile time constant of zeros.