Sparse Virtual File System  0.4.0
A Sparse Virtual File System.
test_svf.cpp
Go to the documentation of this file.
1 
32 #include <iostream>
33 #include <iomanip>
34 #include <thread>
35 
36 #include "test_svf.h"
37 
38 
40 void test_example_code(void) {
41  std::cout << __PRETTY_FUNCTION__ << std::endl;
42  SVFS::SparseVirtualFile svf("Some file ID", 0.0);
43  // Write six char at file position 14
44  svf.write(14, "ABCDEF", 6);
45  // Read from it
46  char read_buffer[2];
47  svf.read(16, 2, read_buffer);
48  // What do I have to do to read 24 bytes from file position 8?
49  // This returns a std::vector<std::pair<size_t, size_t>>
50  // as ((file_position, read_length), ...)
51  auto need = svf.need(8, 24);
52  // This prints ((8, 6), (20, 4),)
53  std::cout << "(";
54  for (auto &val: need) {
55  std::cout << "(" << val.first << ", " << val.second << "),";
56  }
57  std::cout << ")" << std::endl;
58  std::cout << __PRETTY_FUNCTION__ << " DONE" << std::endl;
59 }
60 
61 namespace SVFS {
62  namespace Test {
63 
64  TestCaseWrite::TestCaseWrite(const std::string &m_test_name, const t_seek_reads &m_writes,
65  const t_seek_reads &m_expected_blocks) : TestCaseABC(m_test_name, m_writes),
66  m_expected_blocks(m_expected_blocks) {}
67 
70  SparseVirtualFile svf("", 0.0);
71 
72  // Run the test
73  size_t bytes_written = 0;
74  auto time_start = std::chrono::high_resolution_clock::now();
75  try {
76  bytes_written += load_writes(svf, test_data_bytes_512);
78  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, err.message(), 0.0, 0);
79  }
80  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
81 
82  // Analyse the results
83  int result = 0;
84  std::string err;
85  auto actual_blocks = svf.blocks();
86  size_t num_bytes = 0;
87  if (m_expected_blocks.size() != actual_blocks.size()) {
88  result = 1;
89  std::ostringstream os;
90  os << "Expected " << m_expected_blocks.size() << " blocks but got " << actual_blocks.size()
91  << " blocks";
92  err = os.str();
93  return TestResult(__PRETTY_FUNCTION__, m_test_name, result, err, time_exec.count(), svf.num_bytes());
94  } else {
95  for (size_t i = 0; i < m_expected_blocks.size(); ++i) {
96  if (actual_blocks[i].first != m_expected_blocks[i].first) {
97  result = 1;
98  std::ostringstream os;
99  os << "In block " << i << " expected fpos " << m_expected_blocks[i].first;
100  os << " but got " << actual_blocks[i].first << " (other blocks not tested)";
101  err = os.str();
102  return TestResult(__PRETTY_FUNCTION__, m_test_name, result, err, time_exec.count(),
103  svf.num_bytes());
104  }
105  if (actual_blocks[i].second != m_expected_blocks[i].second) {
106  result = 1;
107  std::ostringstream os;
108  os << "In block " << i << " expected length " << m_expected_blocks[i].second;
109  os << " but got " << actual_blocks[i].second << " (other blocks not tested)";
110  err = os.str();
111  return TestResult(__PRETTY_FUNCTION__, m_test_name, result, err, time_exec.count(),
112  svf.num_bytes());
113  }
114  num_bytes += actual_blocks[i].second;
115  }
116  }
117  // Check SVF properties are correct.
118  // Blocks
119  if (svf.num_blocks() != actual_blocks.size()) {
120  result = 1;
121  std::ostringstream os;
122  os << "Found svf.num_blocks() " << svf.num_blocks() << " but expected " << actual_blocks.size();
123  err = os.str();
124  return TestResult(__PRETTY_FUNCTION__, m_test_name, result, err, time_exec.count(), svf.num_bytes());
125  }
126  // Overall bytes
127  if (svf.num_bytes() != num_bytes) {
128  result = 1;
129  std::ostringstream os;
130  os << "Found svf.num_bytes() " << svf.num_bytes() << " but expected " << num_bytes;
131  err = os.str();
132  return TestResult(__PRETTY_FUNCTION__, m_test_name, result, err, time_exec.count(), svf.num_bytes());
133  }
134  // Write properties
135  if (svf.count_write() != m_writes.size()) {
136  result = 1;
137  std::ostringstream os;
138  os << "Found svf.count_write()" << svf.count_write() << " but expected " << m_writes.size();
139  err = os.str();
140  return TestResult(__PRETTY_FUNCTION__, m_test_name, result, err, time_exec.count(), svf.num_bytes());
141  }
142  if (svf.bytes_write() != bytes_written) {
143  result = 1;
144  std::ostringstream os;
145  os << "Found svf.bytes_write() " << svf.bytes_write() << " but expected " << bytes_written;
146  err = os.str();
147  return TestResult(__PRETTY_FUNCTION__, m_test_name, result, err, time_exec.count(), svf.num_bytes());
148  }
149  // Read properties
150  if (svf.count_read() != 0) {
151  result = 1;
152  std::ostringstream os;
153  os << "Count of reads is " << svf.count_read() << " but should be 0";
154  err = os.str();
155  return TestResult(__PRETTY_FUNCTION__, m_test_name, result, err, time_exec.count(), svf.num_bytes());
156  }
157  if (svf.bytes_read() != 0) {
158  result = 1;
159  std::ostringstream os;
160  os << "Count of read bytes is " << svf.bytes_read() << " but should be 0";
161  err = os.str();
162  return TestResult(__PRETTY_FUNCTION__, m_test_name, result, err, time_exec.count(), svf.num_bytes());
163  }
164  return TestResult(__PRETTY_FUNCTION__, m_test_name, result, err, time_exec.count(), svf.num_bytes());
165  }
166 
171  const std::vector<TestCaseWrite> write_test_cases = {
172 #if 1
187  {
188  "Special (A)",
189  {
190  {0, 8},
191  {28, 74},
192  {214, 19},
193  },
194  {
195  {0, 8},
196  {28, 74},
197  {214, 19},
198  },
199  },
200 #endif
201 #if 1
202  /* Now add svf.write(3102, b'D' * 12) */
203  {
204  "Special (B)",
205  {
206  {0, 8}, /* 0-8 */
207  {28, 74}, /* 0-8, 28-102 */
208  {214, 19}, /* 0-8, 28-102, 214-233 */
209  {102, 12}, /* 0-8, 28-114, 214-233 */
210  },
211  /* assert svf.blocks() == ((0, 8), (3028, 86), (3214, 19)) */
212  {
213  {0, 8}, /* 0-8 */
214  {28, 86}, /* 28-114 */
215  {214, 19}, /* 214-233 */
216  },
217  },
218 #endif
219 #if 1
220  /* Write no blocks. */
221  {"Write no blocks", {}, {}},
222 
223  // |+++|
224  {"Write single block", {{8, 4}}, {{8, 4}},},
225 
226  //==== Old collects new and existing blocks: _write_old_append_new()
227  // ^==|
228  // |++|
229  {"Overwrite single block", {{8, 4}, {8, 4}}, {{8, 4}},},
230 
231  // ^==|
232  // |+++|
233  {"Extend single block - a", {{8, 4}, {8, 5}}, {{8, 5}},},
234 
235  // ^==|
236  // |++|
237  {"Extend single block - b", {{8, 4}, {9, 4}}, {{8, 5}},},
238 
239  // ^==|
240  // |+++|
241  {"Coalesce two blocks", {{8, 4}, {12, 5}}, {{8, 9}},},
242 
243  // |==| ^==|
244  {"Insert a previous block", {{16, 4}, {8, 4}}, {{8, 4}, {16, 4}},},
245  // ^==| |==| ^==|
246  {"Insert a new block in the middle", {{16, 4}, {2, 4}, {8, 4}}, {{2, 4}, {8, 4}, {16, 4}},},
247  // ^==| |==|
248  {"Add second block", {{8, 4}, {16, 4}}, {{8, 4}, {16, 4}},},
249  // ^==| |==|
250  // |++++++|
251  {"New joins two blocks", {{8, 4}, {16, 4}, {10, 8}}, {{8, 12}},},
252 #endif
253 
254  // ^==| |==|
255  // |++|
256 
257  {"New just fills gap between two blocks", {{8, 4}, {16, 4}, {12, 4}}, {{8, 12}},},
258 
259  // ^==| |==|
260  // |++++++++++|
261  {"New overlaps two blocks exactly", {{8, 4}, {16, 4}, {8, 12}}, {{8, 12}},},
262 
263  // ^==| |==|
264  // |++++++++|
265  {"New overlaps two blocks just short", {{8, 4}, {16, 4}, {9, 10}}, {{8, 12}},},
266 
267  // ^==| |==|
268  // |++++++++++++|
269  {"New overlaps two blocks and adds", {{8, 4}, {16, 4}, {8, 14}}, {{8, 14}},},
270 
271  //==== New collects existing blocks: _write_new_append_old()
272 
273  // ^==|
274  // |++|
275  {"New appends old[0]", {{8, 4}, {4, 4}}, {{4, 8}},},
276 
277  // ^==|
278  // |++|
279  {"New appends part of old[0]", {{8, 4}, {7, 3}}, {{7, 5}},},
280 
281  // ^==|
282  // |+++|
283  {"New overlaps end old[0] exactly", {{8, 4}, {7, 5}}, {{7, 5}},},
284 
285  // ^==|
286  // |++++|
287  {"New overlaps end old[0] and beyond", {{8, 4}, {7, 6}}, {{7, 6}},},
288 
289  // ^==| |==|
290  // |+++++|
291  {"New appends old[0] not [1] (a)", {{8, 4}, {16, 4}, {7, 7}}, {{7, 7}, {16, 4}},},
292 
293  // ^==| |==|
294  // |++++++|
295  {"New appends old[0] not [1] (b)", {{8, 4}, {16, 4}, {7, 8}}, {{7, 8}, {16, 4}},},
296 
297  // ^==| |==|
298  // |+++++++|
299  {"New appends old[0] and [1] exactly", {{8, 4}, {16, 4}, {7, 9}}, {{7, 13}},},
300 
301  // ^===| |==|
302  // |+++++++++|
303  {"New appends old[0] and [1] - just", {{8, 4}, {16, 4}, {7, 10}}, {{7, 13}},},
304 
305  // ^===| |==|
306  // |++++++++++|
307  {"New appends old[0] and [1] - one byte", {{8, 4}, {16, 4}, {7, 11}}, {{7, 13}},},
308 
309  // ^===| |==|
310  // |++++++++++++|
311  {"New appends old[0] and [1] - all", {{8, 4}, {16, 4}, {7, 13}}, {{7, 13}},},
312 
313  // ^===| |==|
314  // |+++++++++++++|
315  {"New appends old[0] and [1] overlapped", {{8, 4}, {16, 4}, {7, 14}}, {{7, 14}},},
316  };
317 
318  const std::vector<TestCaseWrite> write_test_cases_special = {
319  // ^==|
320  // |++|
321  {"New appends part of old[0]", {{8, 4}, {7, 3}}, {{7, 5}},},
322  };
323 
325  TestCount count;
326  for (const auto &test_case: write_test_cases) {
327  auto result = test_case.run();
328  count.add_result(result.result());
329  results.push_back(result);
330  }
331  for (const auto &test_case: write_test_cases_special) {
332  auto result = test_case.run();
333  count.add_result(result.result());
334  results.push_back(result);
335  }
336  return count;
337  }
338 
339 
340  TestCaseWriteThrows::TestCaseWriteThrows(const std::string &m_test_name, const t_seek_reads &m_writes,
341  t_fpos fpos, size_t len, const char *data, const std::string &message)
342  : TestCaseABC(m_test_name,
343  m_writes),
344  m_fpos(fpos),
345  m_data(data),
346  m_len(len),
347  m_message(message) {}
348 
349 
350  // Create a SVF, run the read tests and report the result.
352  SparseVirtualFile svf("", 0.0);
353 
354  try {
356  svf.write(m_fpos, m_data, m_len);
357  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, "Write test failed to throw.", 0.0, 0);
359  if (err.message() != m_message) {
360  std::ostringstream os;
361  os << "Error message \"" << err.message() << "\" expected \"" << m_message << "\"";
362  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, os.str(), 0.0, svf.num_bytes());
363  }
364  }
365  return TestResult(__PRETTY_FUNCTION__, m_test_name, 0, "", 0.0, svf.num_bytes());
366  }
367 
368  const std::vector<TestCaseWriteThrows> write_test_cases_throws = {
369  {
370  "Throws: Overwrite single block", {{65, 4}}, 65, 4, test_data_bytes_512 + 66,
371  "SparseVirtualFile::write(): Difference at position 65 'B' != 'A' Ordinal 66 != 65"
372  },
373  };
374 
376  TestCount count;
377  for (const auto &test_case: write_test_cases_throws) {
378  auto result = test_case.run();
379  count.add_result(result.result());
380  results.push_back(result);
381  }
382  return count;
383  }
384 
385  // Write test_data_bytes_512 with a overlap with diff checking on. Data is 128 blocks every 64 bytes.
386  TestCount _test_perf_write_with_diff_check(bool compare_for_diff, t_test_results &results) {
387  TestCount count;
389  config.compare_for_diff = compare_for_diff;
390  SparseVirtualFile svf("", 0.0, config);
391  size_t block_size = 256;
392  int repeat = 4000;
393 
394  auto time_start = std::chrono::high_resolution_clock::now();
395  for (int i = 0; i < repeat; ++i) {
396  svf.write(0, test_data_bytes_512, block_size);
397  }
398  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
399  std::ostringstream os;
400  os << block_size << " block size, x" << repeat << ", compare_for_diff=" << compare_for_diff;
401  auto result = TestResult(__PRETTY_FUNCTION__, std::string(os.str()), 0, "", time_exec.count(),
402  repeat * block_size);
403  count.add_result(result.result());
404  results.push_back(result);
405  return count;
406  }
407 
409  return _test_perf_write_with_diff_check(false, results);
410  }
411 
412  // Write test_data_bytes_512 with a overlap with diff checking on. Data is 128 blocks every 64 bytes.
414  return _test_perf_write_with_diff_check(true, results);
415  }
416 
417  // Simulate writing a low level RP66V1 index. Total bytes written around 1Mb.
418  // Represented file size is about 190 Mb
419  // 23831 * (4 + 10 * 4) is close to 1Mb
421  TestCount count;
422  SparseVirtualFile svf("", 0.0);
423  auto time_start = std::chrono::high_resolution_clock::now();
424 
425  for (size_t vr = 0; vr < 23831; ++vr) {
426  t_fpos fpos = 80 + vr * 8004;
427  svf.write(fpos, test_data_bytes_512, 4);
428  fpos += 4;
429  for (int lrsh = 0; lrsh < 10; ++lrsh) {
430  svf.write(fpos, test_data_bytes_512, 4);
431  fpos += 800;
432  }
433  }
434 
435  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
436  auto result = TestResult(__PRETTY_FUNCTION__, "Sim low level index", 0, "", time_exec.count(),
437  svf.num_bytes());
438  count.add_result(result.result());
439  results.push_back(result);
440  return count;
441  }
442 
443  // Write 1Mb of test_data_bytes_512 in different, equally sized, blocks that are all coalesced and report the time taken.
444  // Essentially only one block is created and all the other test_data_bytes_512 is appended.
446  TestCount count;
447  for (size_t block_size = 1; block_size <= 256; block_size *= 2) {
448  SparseVirtualFile svf("", 0.0);
449 
450  auto time_start = std::chrono::high_resolution_clock::now();
451  for (t_fpos i = 0; i < (1024 * 1024 * 1) / block_size; ++i) {
452  t_fpos fpos = i * block_size;
453  svf.write(fpos, test_data_bytes_512, block_size);
454  }
455  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
456 
457  std::ostringstream os;
458  os << "1Mb, " << std::setw(3) << block_size << " sized blocks, coalesced";
459  auto result = TestResult(__PRETTY_FUNCTION__, std::string(os.str()), 0, "", time_exec.count(),
460  svf.num_bytes());
461  count.add_result(result.result());
462  results.push_back(result);
463  }
464  return count;
465  }
466 
467  // Write 1Mb of test_data_bytes_512 in different, equally sized, blocks that are not coalesced and report the time taken.
468  // Each write creates a separate block.
470  TestCount count;
471  for (size_t block_size = 1; block_size <= 256; block_size *= 2) {
472  SparseVirtualFile svf("", 0.0);
473 
474  auto time_start = std::chrono::high_resolution_clock::now();
475  for (t_fpos i = 0; i < (1024 * 1024 * 1) / block_size; ++i) {
476  t_fpos fpos = i * block_size + i;
477  svf.write(fpos, test_data_bytes_512, block_size);
478  }
479  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
480 
481  std::ostringstream os;
482  os << "1Mb, " << std::setw(3) << block_size << " sized blocks, uncoalesced";
483  auto result = TestResult(__PRETTY_FUNCTION__, std::string(os.str()), 0, "", time_exec.count(),
484  svf.num_bytes());
485  count.add_result(result.result());
486  results.push_back(result);
487  }
488 
489  return count;
490  }
491 
492 
493  // Write 1Mb of test_data_bytes_512 in different, equally sized, blocks that are not coalesced and report the memory usage
494  // with size_of(). Each write creates a separate block.
495  // Typically 1 byte blocks are x35 times the memory. 256 byte blocks are x1.2 the memory.
497  TestCount count;
498  for (size_t block_size = 1; block_size <= 256; block_size *= 2) {
499  SparseVirtualFile svf("", 0.0);
500 
501  size_t num_blocks = (1024 * 1024 * 1) / block_size;
502 
503  auto time_start = std::chrono::high_resolution_clock::now();
504  for (t_fpos i = 0; i < num_blocks; ++i) {
505  t_fpos fpos = i * block_size + i;
506  svf.write(fpos, test_data_bytes_512, block_size);
507  }
508  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
509 
510  std::ostringstream os;
511  os << "1Mb, block size " << std::setw(3) << block_size << " sized blocks";
512  os << " num_blocks " << num_blocks;
513  os << " size_of " << svf.size_of();
514  os << " Overhead " << svf.size_of() - svf.num_bytes();
515  os << " per block " << (svf.size_of() - svf.num_bytes()) / num_blocks;
516  auto result = TestResult(__PRETTY_FUNCTION__, std::string(os.str()), 0, "", time_exec.count(),
517  svf.size_of());
518  count.add_result(result.result());
519  results.push_back(result);
520  }
521 
522  return count;
523  }
524 
525 
526  TestCaseRead::TestCaseRead(const std::string &m_test_name, const t_seek_reads &m_writes,
527  t_fpos fpos, size_t len) : TestCaseABC(m_test_name, m_writes),
528  m_fpos(fpos), m_len(len) {}
529 
530 
531  // Create a SVF, run the read tests and report the result.
533  SparseVirtualFile svf("", 0.0);
534 
535  // Load the SVF
536  try {
539  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, err.message(), 0.0, 0);
540  }
541 
542  // Analyse the results
543  int result = 0;
544  std::string err;
545 
546  char read_buffer[256];
547  // Run the test
548  auto time_start = std::chrono::high_resolution_clock::now();
549  try {
550  svf.read(m_fpos, m_len, read_buffer);
552  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, err.message(), 0.0, 0);
553  }
554  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
555 
556  // Check the result
557  for (size_t i = 0; i < m_len; ++i) {
558  if (read_buffer[i] != test_data_bytes_512[m_fpos + i]) {
559  result = 1;
560  std::ostringstream os;
561  os << "In position " << m_fpos + 1 << " expected fpos "
562  << static_cast<int>(test_data_bytes_512[m_fpos + i]);
563  os << " but got " << static_cast<int>(read_buffer[i]) << " (other test_data_bytes_512 not tested)";
564  err = os.str();
565  return TestResult(__PRETTY_FUNCTION__, m_test_name, result, err, time_exec.count(),
566  svf.num_bytes());
567  }
568  }
569  return TestResult(__PRETTY_FUNCTION__, m_test_name, result, err, time_exec.count(), svf.num_bytes());
570  }
571 
572  const std::vector<TestCaseRead> read_test_cases = {
573  // ^==|
574  // |++|
575  {"Read exactly a block", {{8, 4}}, 8, 4},
576  // ^==|
577  // |+|
578  {"Read leading part of block", {{8, 4}}, 8, 3},
579  // ^==|
580  // |+|
581  {"Read trailing part of block", {{8, 4}}, 9, 3},
582  // ^==|
583  // ||
584  {"Read mid part of block", {{8, 4}}, 9, 2},
585  };
586 
587 
588  const std::vector<TestCaseRead> read_test_cases_special = {
589  // ^==|
590  // |+|
591  {"Read trailing part of block", {{8, 4}}, 9, 3},
592  // ^==|
593  // ||
594  {"Read mid part of block", {{8, 4}}, 9, 2},
595  };
596 
597 
599  TestCount count;
600  for (const auto &test_case: read_test_cases) {
601 // for (const auto& test_case: read_test_cases_special) {
602 // std::cout << "Testing: " << test_case.test_name() << std::endl;
603  auto result = test_case.run();
604  count.add_result(result.result());
605  results.push_back(result);
606  }
607  return count;
608  }
609 
610 
611  TestCaseReadThrows::TestCaseReadThrows(const std::string &m_test_name, const t_seek_reads &m_writes,
612  t_fpos fpos, size_t len, const std::string &message) : TestCaseRead(
613  m_test_name,
614  m_writes, fpos,
615  len),
616  m_message(
617  message) {}
618 
619 
620  // Create a SVF, run the read tests and report the result.
622  SparseVirtualFile svf("", 0.0);
623 
624  // Load the SVF
625  try {
628  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, err.message(), 0.0, 0);
629  }
630  // Run the test.
631  char read_buffer[256];
632  try {
633  svf.read(m_fpos, m_len, read_buffer);
634  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, "Test failed to throw.", 0.0, 0);
636  if (err.message() != m_message) {
637  std::ostringstream os;
638  os << "Error message \"" << err.message() << "\" expected \"" << m_message << "\"";
639  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, os.str(), 0.0, svf.num_bytes());
640  }
641  }
642  return TestResult(__PRETTY_FUNCTION__, m_test_name, 0, "", 0.0, svf.num_bytes());
643  }
644 
645  const std::vector<TestCaseReadThrows> read_test_cases_throw = {
646  {"Read empty SVF throws", {}, 8, 4, "SparseVirtualFile::read(): Sparse virtual file is empty."},
653  {"Read before block throws", {{8, 4}}, 2, 4,
654  "SparseVirtualFile::read(): Requested file position 2 precedes first block at 8"},
657  {"Read prior to block throws", {{8, 4}}, 7, 4,
658  "SparseVirtualFile::read(): Requested file position 7 precedes first block at 8"},
661  {"Read beyond block throws", {{8, 4}}, 9, 4,
662  "SparseVirtualFile::read(): Requested position 9 length 4 (end 13) overruns block that starts at 8 has size 4 (end 12). Offset into block is 1 overrun is 1 bytes"},
665  {"Read beyond end throws", {{8, 4}}, 12, 4,
666  "SparseVirtualFile::read(): Requested position 12 length 4 (end 16) overruns block that starts at 8 has size 4 (end 12). Offset into block is 4 overrun is 4 bytes"},
667  };
668 
669 
671  TestCount count;
672  for (const auto &test_case: read_test_cases_throw) {
673 // std::cout << "Testing: " << test_case.test_name() << std::endl;
674  auto result = test_case.run();
675  count.add_result(result.result());
676  results.push_back(result);
677  }
678  return count;
679  }
680 
681 
682  // Write 1Mb of test_data_bytes_512 in different, equally sized, blocks that are all coalesced and report the time taken.
683  // Essentially only one block is created and all the other test_data_bytes_512 is appended.
685  const size_t SIZE = 1024 * 1024 * 1;
686  TestCount count;
687  SparseVirtualFile svf("", 0.0);
688  for (t_fpos i = 0; i < (SIZE) / 256; ++i) {
689  t_fpos fpos = i * 256;
690  svf.write(fpos, test_data_bytes_512, 256);
691  }
692 
693  char buffer[SIZE];
694  auto time_start = std::chrono::high_resolution_clock::now();
695  svf.read(0, SIZE, buffer);
696  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
697 
698  std::ostringstream os;
699  auto result = TestResult(__PRETTY_FUNCTION__, "1Mb of 256 bytes in one block", 0, "", time_exec.count(),
700  svf.num_bytes());
701  count.add_result(result.result());
702  results.push_back(result);
703  return count;
704  }
705 
706  // Read 1Mb of test_data_bytes_512 in different, equally sized, blocks that are not coalesced and report the time taken.
708  const size_t SIZE = 1024 * 1024 * 1;
709  TestCount count;
710  for (size_t block_size = 1; block_size <= 512; block_size *= 2) {
711  SparseVirtualFile svf("", 0.0);
712  for (t_fpos i = 0; i < (SIZE) / block_size; ++i) {
713  t_fpos fpos = i * 512 * 2;
714  svf.write(fpos, test_data_bytes_512, block_size);
715  }
716 
717  char buffer[SIZE];
718  auto time_start = std::chrono::high_resolution_clock::now();
719  for (t_fpos i = 0; i < (SIZE) / block_size; ++i) {
720  t_fpos fpos = i * 512 * 2;
721  svf.read(fpos, block_size, buffer);
722  }
723  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
724 
725  std::ostringstream os;
726  os << "1Mb " << block_size << " byte blocks " << svf.num_blocks() << " blocks ";
727  auto result = TestResult(__PRETTY_FUNCTION__, os.str(), 0, "", time_exec.count(),
728  svf.num_bytes());
729  count.add_result(result.result());
730  results.push_back(result);
731  }
732  return count;
733  }
734 
735  TestCaseHas::TestCaseHas(const std::string &m_test_name, const t_seek_reads &m_writes,
736  t_fpos fpos, size_t len, bool expected) : TestCaseABC(m_test_name, m_writes),
737  m_fpos(fpos), m_len(len),
738  m_expected(expected) {}
739 
740 
741  // Create a SVF, run the read tests and report the result.
743  SparseVirtualFile svf("", 0.0);
744 
745  // Load the SVF
746  try {
749  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, err.message(), 0.0, 0);
750  }
751 
752  // Analyse the results
753 
754  // Run the test
755  try {
756  auto time_start = std::chrono::high_resolution_clock::now();
757  bool result_has = svf.has(m_fpos, m_len);
758  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
759 
760  // Check the result
761  if (result_has != m_expected) {
762  std::ostringstream os;
763  os << "Expected has() in position " << m_fpos << " and length " << m_len;
764  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, os.str(), time_exec.count(),
765  svf.num_bytes());
766  }
767  return TestResult(__PRETTY_FUNCTION__, m_test_name, 0, "", time_exec.count(), svf.num_bytes());
769  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, err.message(), 0.0, 0);
770  }
771  }
772 
773  const std::vector<TestCaseHas> has_test_cases = {
774  //
775  // |++|
776  {"Has empty - false", {}, 8, 4, false},
777  // ^==|
778  // |++|
779  {"Has an exact block", {{8, 4}}, 8, 4, true},
780  // ^==|
781  // |+|
782  {"Has leading block", {{8, 4}}, 8, 3, true},
783  // ^==|
784  // |+|
785  {"Has trailing block", {{8, 4}}, 9, 3, true},
786  // ^==|
787  // ||
788  {"Has mid block", {{8, 4}}, 9, 2, true},
789  // ^==|
790  // |++|
791  {"Not has an exact block -1", {{8, 4}}, 7, 4, false},
792  // ^==|
793  // |++|
794  {"Not has an exact block +1", {{8, 4}}, 9, 4, false},
795  };
796 
797 
799  TestCount count;
800  for (const auto &test_case: has_test_cases) {
801  auto result = test_case.run();
802  count.add_result(result.result());
803  results.push_back(result);
804  }
805  return count;
806  }
807 
808  TestCaseNeed::TestCaseNeed(const std::string &m_test_name, const t_seek_reads &m_writes,
809  t_fpos fpos, size_t len, const t_seek_reads &m_need) : TestCaseABC(m_test_name,
810  m_writes),
811  m_fpos(fpos), m_len(len),
812  m_need(m_need) {}
813 
814 
815  // Create a SVF, run the read tests and report the result.
817  SparseVirtualFile svf("", 0.0);
818 
819  // Load the SVF
820  try {
823  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, err.message(), 0.0, 0);
824  }
825 
826  // Analyse the results
827  // Run the test
828  auto time_start = std::chrono::high_resolution_clock::now();
829  t_seek_reads need = svf.need(m_fpos, m_len);
830  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
831 
832  // Check the result
833  if (need.size() != m_need.size()) {
834  std::ostringstream os;
835  os << "Found " << need.size() << " need pairs but expected " << m_need.size() << " need pairs";
836  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, os.str(), time_exec.count(), svf.num_bytes());
837  }
838  for (size_t i = 0; i < need.size(); ++i) {
839  if (need[i].first != m_need[i].first || need[i].second != m_need[i].second) {
840  std::ostringstream os;
841  os << "In position " << i << " expected fpos " << m_need[i].first << " and len "
842  << m_need[i].second;
843  os << " but got fpos " << need[i].first << " and len " << need[i].second;
844  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, os.str(), time_exec.count(),
845  svf.num_bytes());
846  }
847  }
848  return TestResult(__PRETTY_FUNCTION__, m_test_name, 0, "", time_exec.count(), svf.num_bytes());
849  }
850 
851  const std::vector<TestCaseNeed> need_test_cases = {
852  //
853  // |++|
854  {"Need on empty SVF", {}, 8, 4, {{8, 4}}},
855  // ^==|
856  // |++|
857  {"Exactly one block", {{8, 4}}, 8, 4, {},},
858  // ^==|
859  // ||
860  {"Inside one block", {{8, 4}}, 9, 2, {},},
861  // ^==|
862  // |++|
863  {"All before one block", {{8, 4}}, 4, 4, {{4, 4}},},
864  // ^==|
865  // |++|
866  {"All after one block", {{8, 4}}, 12, 4, {{12, 4}},},
867  // ^==|
868  // |+++++|
869  {"Before and part of one block", {{8, 4}}, 4, 7, {{4, 4}},},
870  // ^==|
871  // |++++++|
872  {"Before and all of one block", {{8, 4}}, 4, 8, {{4, 4}},},
873  // ^==|
874  // |++++++|
875  {"Before, all of one block and after", {{8, 4}}, 4, 15, {{4, 4}, {12, 7}},},
876  // ^==|
877  // |++++++++++|
878  {"Before, all and after one block", {{8, 4}}, 4, 9, {{4, 4}, {12, 1}},},
879  // |==| |==|
880  // |++++++++|
881  {"Two blocks and in between (a)", {{8, 4}, {14, 4}}, 8, 10, {{12, 2}},},
882  // |==| |==|
883  // |+++++++|
884  {"Two blocks and in between (b)", {{8, 4}, {14, 4}}, 8, 9, {{12, 2}},},
885  // |==| |==|
886  // |+++++++|
887  {"Two blocks and in between (c)", {{8, 4}, {14, 4}}, 9, 9, {{12, 2}},},
888  // |==| |==|
889  // |++++++|
890  {"Two blocks and in between (d)", {{8, 4}, {14, 4}}, 9, 7, {{12, 2}},},
891  // |==| |==|
892  // |++++++++|
893  {"Two blocks, under-run", {{8, 4}, {14, 4}}, 7, 11, {{7, 1}, {12, 2}},},
894  // |==| |==|
895  // |+++++++++|
896  {"Two blocks, over-run", {{8, 4}, {14, 4}}, 8, 11, {{12, 2}, {18, 1}},},
897  // |==| |==|
898  // |++++++++++|
899  {"Two blocks, under/over-run", {{8, 4}, {14, 4}}, 7, 12, {{7, 1}, {12, 2}, {18, 1}},},
900  };
901 
902  const std::vector<TestCaseNeed> need_test_cases_special = {
903  // |==| |==|
904  // |++++++++|
905  {"Two blocks and in between (a)", {{8, 4}, {14, 4}}, 8, 10, {{12, 2}},},
906  };
907 
909  TestCount count;
910  for (const auto &test_case: need_test_cases) {
911  auto result = test_case.run();
912  count.add_result(result.result());
913  results.push_back(result);
914  }
915  for (const auto &test_case: need_test_cases_special) {
916  auto result = test_case.run();
917  count.add_result(result.result());
918  results.push_back(result);
919  }
920  return count;
921  }
922 
923  // Simulate writing a low level RP66V1 index and then running need on it. Total bytes written around 1Mb.
924  // Blocks are 800 bytes apart.
925  // 23831 * (4 + 10 * 4) is close to 1Mb
926  TestCount _test_perf_need_sim_index(size_t need_size, t_test_results &results) {
927  TestCount count;
928  SparseVirtualFile svf("", 0.0);
929  // Write to the SVF
930  for (size_t vr = 0; vr < 23831; ++vr) {
931  t_fpos fpos = 80 + vr * 8004;
932  svf.write(fpos, test_data_bytes_512, 4);
933  fpos += 4;
934  for (int lrsh = 0; lrsh < 10; ++lrsh) {
935  svf.write(fpos, test_data_bytes_512, 4);
936  fpos += 800;
937  }
938  }
939  // Now run need
940  size_t data_size = 0;
941  size_t num_need_blocks = 0;
942  auto time_start = std::chrono::high_resolution_clock::now();
943  for (size_t i = 0; i < svf.last_file_position(); i += need_size) {
944  auto need = svf.need(i, need_size);
945  num_need_blocks += need.size();
946  data_size += need_size;
947  }
948  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
949  std::ostringstream os;
950  os << "Sim need(" << need_size << ") on index [" << num_need_blocks << "]";
951  auto result = TestResult(__PRETTY_FUNCTION__, os.str(), 0, "", time_exec.count(), data_size);
952  count.add_result(result.result());
953  results.push_back(result);
954  return count;
955  }
956 
958  TestCount count;
959  for (size_t need_size = 32; need_size < 8 * 4096; need_size *= 2) {
960  count += _test_perf_need_sim_index(need_size, results);
961  }
962  return count;
963  }
964 
965 #pragma mark - Test need() with greedy value.
966 
968  const std::string &m_test_name, const t_seek_reads &m_writes, t_fpos fpos,
969  size_t len, size_t greedy_length, const t_seek_reads &m_need) : TestCaseABC(m_test_name, m_writes),
970  m_fpos(fpos),
971  m_len(len),
972  m_greedy_length(
973  greedy_length),
974  m_need(m_need) {
975  }
976 
978  SparseVirtualFile svf("", 0.0);
979 
980  // Load the SVF
981  try {
984  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, err.message(), 0.0, 0);
985  }
986 
987  // Analyse the results
988  // Run the test
989  auto time_start = std::chrono::high_resolution_clock::now();
991  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
992 
993  // Check the result
994  if (need.size() != m_need.size()) {
995  std::ostringstream os;
996  os << "Found " << need.size() << " need pairs but expected " << m_need.size() << " need pairs";
997  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, os.str(), time_exec.count(), svf.num_bytes());
998  }
999  for (size_t i = 0; i < need.size(); ++i) {
1000  if (need[i].first != m_need[i].first || need[i].second != m_need[i].second) {
1001  std::ostringstream os;
1002  os << "In position " << i << " expected fpos " << m_need[i].first << " and len "
1003  << m_need[i].second;
1004  os << " but got fpos " << need[i].first << " and len " << need[i].second;
1005  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, os.str(), time_exec.count(),
1006  svf.num_bytes());
1007  }
1008  }
1009  return TestResult(__PRETTY_FUNCTION__, m_test_name, 0, "", time_exec.count(), svf.num_bytes());
1010  }
1011 
1012 // clang-format off
1013 // @formatter:off
1014  const std::vector<TestCaseNeedGreedy> need_greedy_test_cases = {
1015 #if 1
1017  "Need (greedy=0) on empty SVF",
1018  {},
1019  8, 4, 0,
1020  {{8, 4}}
1021  ),
1023  "Need 32 (greedy=4) on empty SVF",
1024  {},
1025  8, 32, 4,
1026  {{8, 32}}
1027  ),
1029  "Need (greedy=32) on empty SVF",
1030  {},
1031  8, 4, 32,
1032  {{8, 32}}
1033  ),
1035  "Need (greedy=0)",
1036  {{8, 4},
1037  {16, 4},
1038  {32, 4}},
1039  8, 40, 0,
1040  {{12, 4},
1041  {20, 12},
1042  {36, 12},}
1043  ),
1045  "Need (greedy=64)",
1046  {{8, 4},
1047  {16, 4},
1048  {32, 4}},
1049  8, 40, 64,
1050  {{12, 64},}
1051  ),
1052 #endif
1053  // Test where original length of need is < greedy length.
1055  "Need with write one byte un-coalesced (greedy=8)",
1056  {
1057  {0, 1},
1058  {2, 1},
1059  {4, 1},
1060  {6, 1},
1061  {8, 1},
1062  {10, 1},
1063  {12, 1},
1064  {14, 1},
1065  {16, 1},
1066  },
1067  0, 2, 8,
1068  {{1, 8},}
1069  ),
1070  };
1071 // @formatter:on
1072 // clang-format on
1073 
1075  TestCount count;
1076  for (const auto &test_case: need_greedy_test_cases) {
1077  auto result = test_case.run();
1078  count.add_result(result.result());
1079  results.push_back(result);
1080  }
1081  return count;
1082  }
1083 
1084 
1085 #pragma mark - Test erase()
1086 
1087  TestCaseErase::TestCaseErase(const std::string &m_test_name, const t_seek_reads &m_writes,
1088  t_fpos fpos) : TestCaseABC(m_test_name, m_writes),
1089  m_fpos(fpos) {}
1090 
1091 
1092  // Create a SVF, run the read tests and report the result.
1094  SparseVirtualFile svf("", 0.0);
1095 
1096  // Load the SVF
1097  try {
1100  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, err.message(), 0.0, 0);
1101  }
1102 
1103  // Analyse the results
1104  int result = 0;
1105  std::string err;
1106 
1107  // Run the test
1108  auto time_start = std::chrono::high_resolution_clock::now();
1109  try {
1110  svf.erase(m_fpos);
1112  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, err.message(), 0.0, 0);
1113  }
1114  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1115  return TestResult(__PRETTY_FUNCTION__, m_test_name, result, err, time_exec.count(), svf.num_bytes());
1116  }
1117 
1118  const std::vector<TestCaseErase> erase_test_cases = {
1119  // ^==|
1120  // |++|
1121  {"Erase a block", {{8, 4}}, 8},
1122  };
1123 
1125  TestCount count;
1126  for (const auto &test_case: erase_test_cases) {
1127 // for (const auto& test_case: read_test_cases_special) {
1128 // std::cout << "Testing: " << test_case.test_name() << std::endl;
1129  auto result = test_case.run();
1130  count.add_result(result.result());
1131  results.push_back(result);
1132  }
1133  return count;
1134  }
1135 
1136  TestCaseEraseThrows::TestCaseEraseThrows(const std::string &m_test_name, const t_seek_reads &m_writes,
1137  t_fpos fpos, const std::string &message) : TestCaseErase(m_test_name,
1138  m_writes,
1139  fpos),
1140  m_message(message) {}
1141 
1142  // Create a SVF, run the read tests and report the result.
1144  SparseVirtualFile svf("", 0.0);
1145 
1146  // Load the SVF
1147  try {
1150  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, err.message(), 0.0, 0);
1151  }
1152  // Run the test.
1153  try {
1154  svf.erase(m_fpos);
1155  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, "Test failed to throw.", 0.0, 0);
1157  if (err.message() != m_message) {
1158  std::ostringstream os;
1159  os << "Error message \"" << err.message() << "\" expected \"" << m_message << "\"";
1160  return TestResult(__PRETTY_FUNCTION__, m_test_name, 1, os.str(), 0.0, svf.num_bytes());
1161  }
1162  }
1163  return TestResult(__PRETTY_FUNCTION__, m_test_name, 0, "", 0.0, svf.num_bytes());
1164  }
1165 
1166 
1167  const std::vector<TestCaseEraseThrows> erase_test_cases_throw = {
1168  {"Erase empty SVF throws", {}, 8, "SparseVirtualFile::erase(): Non-existent file position 8 at start of block."},
1169  // ^==|
1170  // |++|
1171  {"Erase before block throws", {{8, 4}}, 2,
1172  "SparseVirtualFile::erase(): Non-existent file position 2 at start of block."},
1173  // ^==|
1174  // |++|
1175  {"Erase within a block throws", {{8, 4}}, 9,
1176  "SparseVirtualFile::erase(): Non-existent file position 9 at start of block."},
1177  // ^==|
1178  // |++|
1179  {"Erase beyond end throws", {{8, 4}}, 12,
1180  "SparseVirtualFile::erase(): Non-existent file position 12 at start of block."},
1181  };
1182 
1183 
1185  TestCount count;
1186  for (const auto &test_case: erase_test_cases_throw) {
1187 // std::cout << "Testing: " << test_case.test_name() << std::endl;
1188  auto result = test_case.run();
1189  count.add_result(result.result());
1190  results.push_back(result);
1191  }
1192  return count;
1193  }
1194 
1196  TestCount count;
1197  size_t block_size = 256;
1198  size_t total_size = 1024 * 1024 * 1;
1199  int repeat = 1000;
1200  tSparseVirtualFileConfig config;
1201  config.overwrite_on_exit = overwrite;
1202  SparseVirtualFile svf("", 0.0, config);
1203  double time_total = 0.0;
1204  for (int r = 0; r < repeat; ++r) {
1205  for (t_fpos i = 0; i < total_size / block_size; ++i) {
1206  // Add 1 to make non-coalesced
1207  t_fpos fpos = i * block_size + 1;
1208  svf.write(fpos, test_data_bytes_512, block_size);
1209  }
1210  auto time_start = std::chrono::high_resolution_clock::now();
1211  svf.clear();
1212  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1213  time_total += time_exec.count();
1214  }
1215  std::ostringstream os;
1216  os << "1Mb, " << std::setw(3) << block_size << "block size, x" << repeat << " overwrite=" << overwrite;
1217  auto result = TestResult(__PRETTY_FUNCTION__, std::string(os.str()), 0, "", time_total, total_size);
1218  count.add_result(result.result());
1219  results.push_back(result);
1220  return count;
1221  }
1222 
1224  return _test_perf_erase_overwrite(false, results);
1225  }
1226 
1228  return _test_perf_erase_overwrite(true, results);
1229  }
1230 
1231 #ifdef SVF_THREAD_SAFE
1233 
1234  // This writes to the global SVF and is used by test_write_multithreaded_num_threads in multiple threads.
1236  try {
1237  for (size_t fpos = 0; fpos < 1024 * 1024; fpos += 8) {
1239  }
1240  }
1242  std::cout << __FUNCTION__ << "(): Fails: " << err.message() << std::endl;
1243  }
1244  }
1245 
1247  try {
1248  for (size_t fpos = 0; fpos < 1024 * 1024 * 2; fpos += 16) {
1250  }
1251  }
1253  std::cout << __FUNCTION__ << "(): Fails: " << err.message() << std::endl;
1254  }
1255  }
1256 
1257  // Launches num_threads threads and writes to a global SVF in the manner of test_perf_write_sim_index()
1258  TestCount test_write_multithreaded(int num_threads, bool is_coalesced, t_test_results &results) {
1259  TestCount count;
1260  std::vector<std::thread> threads;
1262 
1263  // Timed section
1264  auto time_start = std::chrono::high_resolution_clock::now();
1265  for (int i = 0; i < num_threads; ++i) {
1266  if (is_coalesced) {
1267  threads.push_back(std::thread(_write_multithreaded_coalesced));
1268  } else {
1269  threads.push_back(std::thread(_write_multithreaded_un_coalesced));
1270  }
1271  }
1272  for (size_t i = 0; i < threads.size(); ++i) {
1273  threads[i].join();
1274  }
1275  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1276  // END: Timed section
1277 
1278  size_t work_done = num_threads * g_svf_multithreaded.num_bytes();
1280 
1281  std::ostringstream os;
1282  os << "Multi threaded write [" << num_threads << "] Coalesced " << is_coalesced;
1283  auto result = TestResult(__PRETTY_FUNCTION__, os.str(), 0, "", time_exec.count() / num_threads, work_done);
1284  count.add_result(result.result());
1285  results.push_back(result);
1286  return count;
1287  }
1288 
1290  TestCount count;
1291  for (int num_threads = 1; num_threads < 1 << 8; num_threads *= 2) {
1292  count += test_write_multithreaded(num_threads, true, results);
1293  }
1294  return count;
1295  }
1296 
1298  TestCount count;
1299  for (int num_threads = 1; num_threads < 1 << 8; num_threads *= 2) {
1300  count += test_write_multithreaded(num_threads, false, results);
1301  }
1302  return count;
1303  }
1304 
1305 #endif
1306 
1320  SparseVirtualFile svf("", 0.0);
1321  std::string test_name(__FUNCTION__);
1322  static char data[4096]; // Random
1323 
1324  // Load the SVF
1325  svf.write(0, data, 1024);
1326  svf.write(291809396, data, 1024);
1327  auto blocks = svf.blocks();
1328 
1329  // Analyse the results
1330  int result = 0; // Success
1331  std::string err;
1332  // Run the test
1333  auto time_start = std::chrono::high_resolution_clock::now();
1334  bool has = svf.has(291810392, 2429);
1335  result |= has;
1336  auto need = svf.need(291810392, 2429, 1024);
1337  svf.write(291810420, data, 2401);
1338  blocks = svf.blocks();
1339  has = svf.has(291810392, 2429);
1340 
1341  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1342  return TestResult(__PRETTY_FUNCTION__, test_name, result, err, time_exec.count(), svf.num_bytes());
1343  }
1344 
1346  SparseVirtualFile svf("", 0.0);
1347  std::string test_name(__FUNCTION__);
1348  static char data[4096]; // Random
1349 
1350  // Load the SVF
1351  auto blocks = svf.blocks();
1352 
1353  // Analyse the results
1354  int result = 0; // Success
1355  std::string err;
1356  // Run the test
1357  auto time_start = std::chrono::high_resolution_clock::now();
1358  auto need = svf.need(0, 32, 1);
1359  svf.write(0, data, 32);
1360  blocks = svf.blocks();
1361  bool has = svf.has(0, 32);
1362  result |= has;
1363 
1364  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1365  return TestResult(__PRETTY_FUNCTION__, test_name, result, err, time_exec.count(), svf.num_bytes());
1366  }
1367 
1370  int result = 0; // Success
1371  SparseVirtualFile svf("", 0.0);
1372  std::string test_name(__FUNCTION__);
1373  static char data[1 << 16]; // 65536 greedy_length
1374 
1375  t_seek_reads needs;
1376  t_seek_reads blocks;
1377  std::string err;
1378  // Run the test
1379  auto time_start = std::chrono::high_resolution_clock::now();
1380  // Go through the sequence
1381  needs = svf.need(515'913'022, 6283);
1382  result |= needs.size() != 1;
1383  svf.write(515'913'022, data, 1 << 16);
1384  blocks = svf.blocks();
1385  result |= blocks.size() != 1;
1386  svf.read(515'913'022, 6283, data);
1387  // Second tile, already in the SVF.
1388  needs = svf.need(515'919'305, 5873);
1389  result |= needs.size() != 0;
1390  svf.read(515'919'305, 5873, data);
1391  // Third tile, not in the SVF.
1392  needs = svf.need(486'156'341, 6039);
1393  result |= needs.size() != 1;
1394  // This is where e go wrong, the two writes should be distinct but they are being coalesced.
1395  svf.write(486'156'341, data, 1 << 16);
1396  blocks = svf.blocks();
1397  result |= blocks.size() != 2;
1398 
1399  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1400  return TestResult(__PRETTY_FUNCTION__, test_name, result, err, time_exec.count(), svf.num_bytes());
1401  }
1402 
1404  std::string test_name(__FUNCTION__);
1405  int result = 0; // Success
1406  TestCount count;
1407  SparseVirtualFile svf("", 0.0);
1408  svf.write(894, test_data_bytes_512, 22);
1409  auto time_start = std::chrono::high_resolution_clock::now();
1410  result |= svf.block_size(894) != 22;
1411  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1412  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1413  svf.num_bytes());
1414  results.push_back(test_result);
1415  count.add_result(test_result.result());
1416  return count;
1417  }
1418 
1420  std::string test_name(__FUNCTION__);
1421  int result = 0; // Success
1422  std::string test_error_message = "";
1423  TestCount count;
1424  SparseVirtualFile svf("", 0.0);
1425  svf.write(894, test_data_bytes_512, 22);
1426  auto time_start = std::chrono::high_resolution_clock::now();
1427  try {
1428  svf.block_size(895);
1430  std::string expected_message = "SparseVirtualFile::block_size(): Requested file position 895 is not at the start of a block";
1431  if (err.message() != expected_message) {
1432  std::ostringstream os;
1433  os << "Error message \"" << err.message() << "\" expected \"" << expected_message << "\"";
1434  test_error_message = os.str();
1435  result = 1;
1436  }
1437  }
1438  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1439  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, test_error_message,
1440  time_exec.count(),
1441  svf.num_bytes());
1442  results.push_back(test_result);
1443  count.add_result(test_result.result());
1444  return count;
1445  }
1446 
1448  std::string test_name(__FUNCTION__);
1449  int result = 0; // Success
1450  TestCount count;
1451  SparseVirtualFile svf("", 0.0);
1452  auto time_start = std::chrono::high_resolution_clock::now();
1453  result |= svf.block_touch() != 0;
1454  // Write a block
1455  svf.write(894, test_data_bytes_512, 22);
1456  result |= svf.block_touch() != 1;
1457 
1458  t_block_touches block_touches = svf.block_touches();
1459  result |= block_touches.size() != 1;
1460 
1461  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1462  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1463  svf.num_bytes());
1464  results.push_back(test_result);
1465  count.add_result(test_result.result());
1466  return count;
1467  }
1468 
1470  std::string test_name(__FUNCTION__);
1471  int result = 0; // Success
1472  TestCount count;
1473  static char data[1 << 16]; // 65536 greedy_length
1474 
1475  SparseVirtualFile svf("", 0.0);
1476  auto time_start = std::chrono::high_resolution_clock::now();
1477  result |= svf.block_touch() != 0;
1478  // Write a block
1479  svf.write(894, test_data_bytes_512, 22);
1480  result |= svf.block_touch() != 1;
1481 
1482  t_block_touches block_touches = svf.block_touches();
1483  result |= block_touches.size() != 1;
1484  result |= block_touches.begin()->first != 0;
1485  result |= block_touches.begin()->second != 894;
1486 
1487  // Read from the block, touch should be incremented.
1488  svf.read(900, 4, data);
1489  result |= svf.block_touch() != 2;
1490  block_touches = svf.block_touches();
1491  result |= block_touches.size() != 1;
1492  result |= block_touches.begin()->first != 1;
1493  result |= block_touches.begin()->second != 894;
1494 
1495  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1496  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1497  svf.num_bytes());
1498  results.push_back(test_result);
1499  count.add_result(test_result.result());
1500  return count;
1501  }
1502 
1504  std::string test_name(__FUNCTION__);
1505  int result = 0; // Success
1506  TestCount count;
1507  SparseVirtualFile svf("", 0.0);
1508  auto time_start = std::chrono::high_resolution_clock::now();
1509  result |= svf.block_touch() != 0;
1510  // Write a block
1511  svf.write(894, test_data_bytes_512, 22);
1512  result |= svf.block_touch() != 1;
1513  // Write a block that will NOT be coalesced.
1514  svf.write(1440, test_data_bytes_512, 4);
1515  result |= svf.block_touch() != 2;
1516 
1517  t_block_touches block_touches = svf.block_touches();
1518  result |= block_touches.size() != 2;
1519 
1520  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1521  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1522  svf.num_bytes());
1523  results.push_back(test_result);
1524  count.add_result(test_result.result());
1525  return count;
1526  }
1527 
1528  // Write two blocks then coalesce them with another and check the block_touch
1530  std::string test_name(__FUNCTION__);
1531  int result = 0; // Success
1532  TestCount count;
1533  SparseVirtualFile svf("", 0.0);
1534  auto time_start = std::chrono::high_resolution_clock::now();
1535  result |= svf.block_touch() != 0;
1536  // Write two blocks
1537  svf.write(0, test_data_bytes_512, 8);
1538  result |= svf.block_touch() != 1;
1539  svf.write(12, test_data_bytes_512, 12);
1540  result |= svf.block_touch() != 2;
1541  // Write a block that will coalesce all.
1542  svf.write(0 + 8, test_data_bytes_512, 4);
1543  result |= svf.block_touch() != 3;
1544 
1545  t_block_touches block_touches = svf.block_touches();
1546  result |= block_touches.size() != 1;
1547 
1548  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1549  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1550  svf.num_bytes());
1551  results.push_back(test_result);
1552  count.add_result(test_result.result());
1553  return count;
1554  }
1555 
1557  std::string test_name(__FUNCTION__);
1558  int result = 0; // Success
1559  TestCount count;
1560  SparseVirtualFile svf("", 0.0);
1561  // Populate the SVF.
1562  size_t block_size = 128;
1563  size_t block_count = 256;
1564  t_fpos file_positon = 0;
1565  for (size_t i = 0; i < block_count; ++i) {
1566  svf.write(file_positon, test_data_bytes_512, block_size);
1567  file_positon += block_size;
1568  // + 1 so not coalesced.
1569  file_positon += 1;
1570  }
1571  // Sanity check
1572  result |= svf.num_blocks() != block_count;
1573  result |= svf.num_bytes() != (block_count * block_size);
1574  // Is this clearer?
1575  result |= svf.num_blocks() == block_count ? 0 : 1 << 0;
1576  result |= svf.num_bytes() == (block_count * block_size) ? 0 : 1 << 1;
1577 
1578  auto time_start = std::chrono::high_resolution_clock::now();
1579 
1580  size_t cache_upper_bound = 1024;
1581  result |= svf.num_bytes() < cache_upper_bound;
1582  // Is this clearer?
1583  result |= svf.num_bytes() >= cache_upper_bound ? 0 : 1 << 2;
1584  // Punt blocks.
1585  if (svf.num_blocks() > 1 && svf.num_bytes() >= cache_upper_bound) {
1586  auto touch_fpos_map = svf.block_touches();
1587  for (const auto &iter: touch_fpos_map) {
1588  if (svf.num_blocks() > 1 && svf.num_bytes() >= cache_upper_bound) {
1589  svf.erase(iter.second);
1590  } else {
1591  break;
1592  }
1593  }
1594  }
1595  result |= svf.num_bytes() >= cache_upper_bound;
1596  // Is this clearer?
1597  result |= svf.num_bytes() < cache_upper_bound ? 0 : 1 << 3;;
1598 
1599  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1600  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1601  svf.num_bytes());
1602  results.push_back(test_result);
1603  count.add_result(test_result.result());
1604  return count;
1605  }
1606 
1608  std::string test_name(__FUNCTION__);
1609  int result = 0; // Success
1610  TestCount count;
1611  SparseVirtualFile svf("", 0.0);
1612  // Populate the SVF.
1613  size_t block_size = 128;
1614  size_t block_count = 256;
1615  t_fpos file_position = 0;
1616  for (size_t i = 0; i < block_count; ++i) {
1617  svf.write(file_position, test_data_bytes_512, block_size);
1618  file_position += block_size;
1619  // + 1 so not coalesced.
1620  file_position += 1;
1621  }
1622  // Sanity check
1623  // Is this clearer?
1624  int error_bit = 0;
1625  result |= svf.num_blocks() == block_count ? 0 : 1 << error_bit;
1626  ++error_bit;
1627  result |= svf.num_bytes() == (block_count * block_size) ? 0 : 1 << error_bit;
1628  ++error_bit;
1629 
1630  auto time_start = std::chrono::high_resolution_clock::now();
1631 
1632  size_t cache_upper_bound = 1024;
1633 // auto num_bytes = svf.num_bytes();
1634  // Is this clearer?
1635  result |= svf.num_bytes() >= cache_upper_bound ? 0 : 1 << error_bit;
1636  ++error_bit;
1637  // Punt blocks.
1638  size_t punted = svf.lru_punt(cache_upper_bound);
1639 // fprintf(stdout, "XXX punted %zu\n", punted); // 31872
1640 // num_bytes = svf.num_bytes();
1641  result |= svf.num_bytes() == 7 * block_size ? 0 : 1 << error_bit;
1642  ++error_bit;
1643  result |= punted == (block_size * block_count - 896) ? 0 : 1 << error_bit;
1644  ++error_bit;
1645 
1646  // Is this clearer?
1647  result |= svf.num_bytes() < cache_upper_bound ? 0 : 1 << error_bit;
1648  ++error_bit;
1649 
1650  auto block_touches = svf.block_touches();
1651 
1652  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1653  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1654  svf.num_bytes());
1655  results.push_back(test_result);
1656  count.add_result(test_result.result());
1657  return count;
1658  }
1659 
1660  // Special case where _write_append_new_to_old() was incrementing m_block_touch wrongly
1662  TestCount count;
1663  int result_code = 0; // Success
1664  SparseVirtualFile svf("", 0.0);
1665  auto time_start = std::chrono::high_resolution_clock::now();
1666  svf.write(16, test_data_bytes_512, 8);
1667  // Add a new block that will be merged with the old one.
1668  svf.write(16 + 8, test_data_bytes_512, 8);
1669 
1670  int error_bit = 0;
1671  result_code |= svf.num_blocks() == 1 ? 0 : 1 << error_bit;
1672  ++error_bit;
1673  result_code |= svf.block_touch() == 2 ? 0 : 1 << error_bit;
1674  ++error_bit;
1675  auto block_touches = svf.block_touches();
1676  auto iter = block_touches.begin();
1677  result_code |= iter != block_touches.end() ? 0 : 1 << error_bit;
1678  ++error_bit;
1679  result_code |= iter->first == 1 ? 0 : 1 << error_bit;
1680  ++error_bit;
1681  result_code |= iter->second == 16 ? 0 : 1 << error_bit;
1682  ++error_bit;
1683 
1684  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1685  auto result = TestResult(__PRETTY_FUNCTION__, "Sim low level index", result_code, "", time_exec.count(),
1686  svf.num_bytes());
1687  count.add_result(result.result());
1688  results.push_back(result);
1689  return count;
1690  }
1691 
1692 
1693  // Test the basic operation of needs_many()
1695  std::string test_name(__FUNCTION__);
1696  int result = 0; // Success
1697  int error_bit = 0;
1698  TestCount count;
1699 
1700  // Empty SVF
1701  SparseVirtualFile svf("", 0.0);
1702  t_seek_reads seek_reads = {
1703  {0, 128},
1704  {256, 512},
1705  };
1706 
1707  auto time_start = std::chrono::high_resolution_clock::now();
1708  t_seek_reads seek_reads_result = svf.need_many(seek_reads, 0);
1709  result |= seek_reads_result.size() == 2 ? 0 : 1 << error_bit;
1710  ++error_bit;
1711  result |= seek_reads_result[0].first == 0 ? 0 : 1 << error_bit;
1712  ++error_bit;
1713  result |= seek_reads_result[0].second == 128 ? 0 : 1 << error_bit;
1714  ++error_bit;
1715  result |= seek_reads_result[1].first == 256 ? 0 : 1 << error_bit;
1716  ++error_bit;
1717  result |= seek_reads_result[1].second == 512 ? 0 : 1 << error_bit;
1718  ++error_bit;
1719  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1720 
1721  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1722  svf.num_bytes());
1723  results.push_back(test_result);
1724  count.add_result(test_result.result());
1725  return count;
1726  }
1727 
1729  std::string test_name(__FUNCTION__);
1730  int result = 0; // Success
1731  int error_bit = 0;
1732  TestCount count;
1733 
1734  // Empty SVF
1735  SparseVirtualFile svf("", 0.0);
1736  t_seek_reads seek_reads = {
1737  {0, 128},
1738  {64, 512},
1739  };
1740 
1741  auto time_start = std::chrono::high_resolution_clock::now();
1742  t_seek_reads seek_reads_result = svf.need_many(seek_reads, 0);
1743  result |= seek_reads_result.size() == 1 ? 0 : 1 << error_bit++;
1744  result |= seek_reads_result[0].first == 0 ? 0 : 1 << error_bit++;
1745  result |= seek_reads_result[0].second == 64 + 512 ? 0 : 1 << error_bit++;
1746  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1747 
1748  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1749  svf.num_bytes());
1750  results.push_back(test_result);
1751  count.add_result(test_result.result());
1752  return count;
1753  }
1754 
1756  std::string test_name(__FUNCTION__);
1757  int result = 0; // Success
1758  int error_bit = 0;
1759  TestCount count;
1760 
1761  // Empty SVF
1762  SparseVirtualFile svf("", 0.0);
1763  t_seek_reads seek_reads = {
1764  {0, 128},
1765  {256, 512},
1766  };
1767 
1768  auto time_start = std::chrono::high_resolution_clock::now();
1769  t_seek_reads seek_reads_result = svf.need_many(seek_reads, 256);
1770  result |= seek_reads_result.size() == 1 ? 0 : 1 << error_bit++;
1771  result |= seek_reads_result[0].first == 0 ? 0 : 1 << error_bit++;
1772  result |= seek_reads_result[0].second == 256 + 512 ? 0 : 1 << error_bit++;
1773  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1774 
1775  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1776  svf.num_bytes());
1777  results.push_back(test_result);
1778  count.add_result(test_result.result());
1779  return count;
1780  }
1781 
1782  // Test need_many() when there is one existing block.
1784  std::string test_name(__FUNCTION__);
1785  int result = 0; // Success
1786  int error_bit = 0;
1787  TestCount count;
1788 
1789  SparseVirtualFile svf("", 0.0);
1790  svf.write(64, test_data_bytes_512, 128);
1791  t_seek_reads seek_reads = {
1792  {0, 128},
1793  {64, 256},
1794  };
1795 
1796  auto time_start = std::chrono::high_resolution_clock::now();
1797  t_seek_reads seek_reads_result = svf.need_many(seek_reads, 0);
1798  result |= seek_reads_result.size() == 2 ? 0 : 1 << error_bit;
1799  ++error_bit;
1800  result |= seek_reads_result[0].first == 0 ? 0 : 1 << error_bit;
1801  ++error_bit;
1802  result |= seek_reads_result[0].second == 64 ? 0 : 1 << error_bit;
1803  ++error_bit;
1804  result |= seek_reads_result[1].first == 64 + 128 ? 0 : 1 << error_bit;
1805  ++error_bit;
1806  result |= seek_reads_result[1].second == (64 + 256) - (64 + 128) ? 0 : 1 << error_bit;
1807  ++error_bit;
1808  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1809 
1810  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1811  svf.num_bytes());
1812  results.push_back(test_result);
1813  count.add_result(test_result.result());
1814  return count;
1815  }
1816 
1817  // Test need_many() when there is one existing block and a greedy length.
1819  std::string test_name(__FUNCTION__);
1820  int result = 0; // Success
1821  int error_bit = 0;
1822  TestCount count;
1823 
1824  SparseVirtualFile svf("", 0.0);
1825  svf.write(64, test_data_bytes_512, 128);
1826  t_seek_reads seek_reads = {
1827  {0, 128},
1828  {64, 256},
1829  };
1830 
1831  auto time_start = std::chrono::high_resolution_clock::now();
1832  t_seek_reads seek_reads_result = svf.need_many(seek_reads, 512);
1833  result |= seek_reads_result.size() == 1 ? 0 : 1 << error_bit;
1834  ++error_bit;
1835  result |= seek_reads_result[0].first == 0 ? 0 : 1 << error_bit;
1836  ++error_bit;
1837  result |= seek_reads_result[0].second == 512 ? 0 : 1 << error_bit;
1838  ++error_bit;
1839  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1840 
1841  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1842  svf.num_bytes());
1843  results.push_back(test_result);
1844  count.add_result(test_result.result());
1845  return count;
1846  }
1847 
1848 
1850  TestCount count;
1851  std::string test_name(__FUNCTION__);
1852  int result = 0; // Success
1853  SparseVirtualFile svf("", 0.0);
1854 
1855  auto time_start = std::chrono::high_resolution_clock::now();
1856 
1857  // Values should be zero
1858  result |= svf.blocks_erased() != 0;
1859  result |= svf.bytes_erased() != 0;
1860 
1861  svf.write(64, test_data_bytes_512, 128);
1862 
1863  // Values should be zero
1864  result |= svf.blocks_erased() != 0;
1865  result |= svf.bytes_erased() != 0;
1866 
1867  // Values should not be zero
1868  svf.erase(64);
1869  result |= svf.blocks_erased() != 1;
1870  result |= svf.bytes_erased() != 128;
1871 
1872  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1873  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1874  svf.num_bytes());
1875  results.push_back(test_result);
1876  count.add_result(test_result.result());
1877  return count;
1878  }
1879 
1881  TestCount count;
1882  std::string test_name(__FUNCTION__);
1883  int result = 0; // Success
1884  SparseVirtualFile svf("", 0.0);
1885 
1886  auto time_start = std::chrono::high_resolution_clock::now();
1887 
1888  // Values should be zero
1889  result |= svf.blocks_punted() != 0;
1890  result |= svf.bytes_punted() != 0;
1891 
1892  svf.write(64, test_data_bytes_512, 128);
1893 
1894  // Values should be zero
1895  result |= svf.blocks_punted() != 0;
1896  result |= svf.bytes_punted() != 0;
1897 
1898  // Values should be zero as nothing got punted.
1899  svf.erase(64);
1900  result |= svf.blocks_punted() != 0;
1901  result |= svf.bytes_punted() != 0;
1902 
1903  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1904  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1905  svf.num_bytes());
1906  results.push_back(test_result);
1907  count.add_result(test_result.result());
1908  return count;
1909  }
1910 
1912  TestCount count;
1913  std::string test_name(__FUNCTION__);
1914  int result = 0; // Success
1915  SparseVirtualFile svf("", 0.0);
1916 
1917  auto time_start = std::chrono::high_resolution_clock::now();
1918 
1919  // Values should be zero
1920  result |= svf.blocks_punted() != 0;
1921  result |= svf.bytes_punted() != 0;
1922 
1923  svf.write(64, test_data_bytes_512, 128);
1924  svf.write(512, test_data_bytes_512, 64);
1925 
1926  // Values should be zero
1927  result |= svf.blocks_punted() != 0;
1928  result |= svf.bytes_punted() != 0;
1929  result |= svf.num_blocks() != 2;
1930 
1931  // Now punt to upperbound of 256, should leave one block.
1932  svf.lru_punt(128 + 32);
1933  result |= svf.num_blocks() != 1;
1934 
1935  result |= svf.blocks_punted() != 1;
1936  result |= svf.bytes_punted() != 128;
1937 
1938  std::chrono::duration<double> time_exec = std::chrono::high_resolution_clock::now() - time_start;
1939  TestResult test_result = TestResult(__PRETTY_FUNCTION__, test_name, result, "", time_exec.count(),
1940  svf.num_bytes());
1941  results.push_back(test_result);
1942  count.add_result(test_result.result());
1943  return count;
1944  }
1945 
1946 
1947 #define INCLUDE_TESTS 1
1948 
1950 #if INCLUDE_TESTS
1952 #endif
1953 #if INCLUDE_TESTS
1957 #endif
1958  TestCount count;
1959 #if INCLUDE_TESTS
1960  // Write
1961  count += test_write_all(results);
1962  count += test_write_all_throws(results);
1963  // write() - performance
1964  count += test_perf_write_with_diff_check(results);
1965  count += test_perf_write_without_diff_check(results);
1966  count += test_perf_write_sim_index_svf(results);
1967  count += test_perf_write_1M_coalesced(results);
1968  count += test_perf_write_1M_uncoalesced(results);
1969  count += test_perf_write_1M_uncoalesced_size_of(results);
1970  // read()
1971  count += test_read_all(results);
1972  count += test_read_throws_all(results);
1973  count += test_perf_read_1M_un_coalesced(results);
1974  count += test_perf_read_1M_coalesced(results);
1975  // has()
1976  count += test_has_all(results);
1977  // need()
1978  count += test_need_all(results);
1979  count += test_perf_need_sim_index(results);
1980 #endif
1981 #if INCLUDE_TESTS
1982  count += test_need_greedy_all(results);
1983  // erase()
1984  count += test_erase_all(results);
1985  count += test_erase_throws_all(results);
1986  count += test_perf_erase_overwrite_false(results);
1987  count += test_perf_erase_overwrite_true(results);
1988 #ifdef SVF_THREAD_SAFE
1989  count += test_write_multithreaded_coalesced(results);
1990  count += test_write_multithreaded_un_coalesced(results);
1991 #endif
1992  count += test_block_size(results);
1993  count += test_block_size_throws(results);
1994 
1995  count += test_block_touch_single_block(results);
1997  count += test_block_touch_two_blocks(results);
1998  count += test_block_touch_coalesced(results);
1999 
2000 #endif
2001 #if INCLUDE_TESTS
2002  // Block punting example
2003  count += test_lru_block_punting_a(results);
2004  count += test_lru_block_punting_b(results);
2005  count += test_lru_block_punting_c(results);
2006 #endif
2007 #if INCLUDE_TESTS
2008  count += test_needs_many_empty(results);
2009  count += test_needs_many_empty_overlap(results);
2010  count += test_needs_many_empty_greedy_length(results);
2011  count += test_needs_many_one_block(results);
2012  count += test_needs_many_one_block_greedy(results);
2013 #endif
2014 #if INCLUDE_TESTS
2015  // erase() causes m_blocks_erased and m_bytes_erased to be updated.
2016  count += test_erase_updates_counters(results);
2017  count += test_erase_updates_counters_not_punt(results);
2018  count += test_punt_updates_counters(results);
2019 #endif
2020  return count;
2021  }
2022 
2023  } // namespace Test
2024 } // namespace SVFS
Might be thrown during a erase operation where the file position is not at the exact beginning of a b...
Definition: svf.h:237
Exception specialisation for the SparseVirtualFile.
Definition: svf.h:207
const std::string & message() const
Definition: svf.h:211
Might be thrown during a write operation where the data differs.
Definition: svf.h:231
Might be thrown during a write operation which fails.
Definition: svf.h:218
Implementation of a Sparse Virtual File.
Definition: svf.h:288
size_t bytes_read() const noexcept
Count of total bytes read with read() operations.
Definition: svf.h:385
t_seek_reads need_many(t_seek_reads &seek_reads, size_t greedy_length=0) const noexcept
Given many [(file position, lengths), ...] what data do I need that I don't yet have?
Definition: svf.cpp:708
size_t size_of() const noexcept
size_of() gives best guess of total memory usage.
Definition: svf.cpp:823
size_t lru_punt(size_t cache_size_upper_bound)
Definition: svf.cpp:1021
size_t blocks_punted() const noexcept
Returns the The total count of blocks that have been erased by punting.
Definition: svf.h:392
t_block_touches block_touches() const noexcept
Returns a std::map of latest touch value key and file position value.
Definition: svf.cpp:1005
size_t num_blocks() const noexcept
Number of blocks used.
Definition: svf.h:354
size_t bytes_erased() const noexcept
Returns the The total count of bytes that have been erased either directly or by punting.
Definition: svf.h:390
size_t num_bytes() const noexcept
Gives exact number of data bytes held.
Definition: svf.h:351
t_block_touch block_touch() const noexcept
Return the latest value of the monotonically increasing block_touch value.
Definition: svf.h:411
void write(t_fpos fpos, const char *data, size_t len)
Write the data a the given file position.
Definition: svf.cpp:453
void clear() noexcept
Executes the data deletion strategy.
Definition: svf.cpp:849
size_t bytes_punted() const noexcept
Returns the The total count of bytes that have been erased by punting.
Definition: svf.h:394
size_t erase(t_fpos fpos)
Remove a particular block.
Definition: svf.cpp:905
size_t blocks_erased() const noexcept
Returns the The total count of blocks that have been erased either directly or by punting.
Definition: svf.h:388
size_t count_read() const noexcept
Count of read() operations.
Definition: svf.h:379
bool has(t_fpos fpos, size_t len) const noexcept
Do I have the data at the given file position and length?
Definition: svf.cpp:56
size_t block_size(t_fpos fpos) const
The length of the block at a specific file position.
Definition: svf.cpp:798
size_t bytes_write() const noexcept
Count of total bytes written with write() operations.
Definition: svf.h:382
size_t count_write() const noexcept
Count of write() operations.
Definition: svf.h:376
t_fpos last_file_position() const noexcept
The position of the last byte.
Definition: svf.cpp:972
void read(t_fpos fpos, size_t len, char *p)
Read data and write to the buffer provided by the caller. This is non-const as it updates the non-con...
Definition: svf.cpp:504
t_seek_reads need(t_fpos fpos, size_t len, size_t greedy_length=0) const noexcept
Create a new fragmentation list of seek/read instructions.
Definition: svf.cpp:582
t_seek_reads blocks() const noexcept
The existing blocks as a list of (file_position, size) pairs.
Definition: svf.cpp:777
Abstract base class for tests cases.
Definition: test_svf.h:49
std::string m_test_name
Definition: test_svf.h:93
size_t load_writes(SparseVirtualFile &svf, const char *data) const
Load all the specified write seek/write blocks from the data (assumed to be 512 bytes long).
Definition: test_svf.h:63
t_seek_reads m_writes
Definition: test_svf.h:94
Specialisation of a test case for erase() on a SVF.
Definition: test_svf.h:208
TestResult run() const override
Definition: test_svf.cpp:1093
TestCaseErase(const std::string &m_test_name, const t_seek_reads &m_writes, t_fpos fpos)
Definition: test_svf.cpp:1087
TestCaseEraseThrows(const std::string &m_test_name, const t_seek_reads &m_writes, t_fpos fpos, const std::string &message)
Definition: test_svf.cpp:1136
TestResult run() const override
Definition: test_svf.cpp:1143
TestCaseHas(const std::string &m_test_name, const t_seek_reads &m_writes, t_fpos fpos, size_t len, bool expected)
Definition: test_svf.cpp:735
TestResult run() const override
Definition: test_svf.cpp:742
Specialisation of a test case for a SVF need() with a greedy length.
Definition: test_svf.h:191
TestResult run() const override
Definition: test_svf.cpp:977
TestCaseNeedGreedy(const std::string &m_test_name, const t_seek_reads &m_writes, t_fpos fpos, size_t len, size_t greedy_length, const t_seek_reads &m_need)
Definition: test_svf.cpp:967
TestResult run() const override
Definition: test_svf.cpp:816
TestCaseNeed(const std::string &m_test_name, const t_seek_reads &m_writes, t_fpos fpos, size_t len, const t_seek_reads &m_need)
Definition: test_svf.cpp:808
t_seek_reads m_need
Definition: test_svf.h:187
Specialisation of a test case for writing to a SVF.
Definition: test_svf.h:130
TestCaseRead(const std::string &m_test_name, const t_seek_reads &m_writes, t_fpos fpos, size_t len)
Definition: test_svf.cpp:526
TestResult run() const override
Definition: test_svf.cpp:532
TestResult run() const override
Definition: test_svf.cpp:621
TestCaseReadThrows(const std::string &m_test_name, const t_seek_reads &m_writes, t_fpos fpos, size_t len, const std::string &message)
Definition: test_svf.cpp:611
TestCaseWrite(const std::string &m_test_name, const t_seek_reads &m_writes, const t_seek_reads &m_expected_blocks)
Definition: test_svf.cpp:64
TestResult run() const override
Create a SVF, run the write tests and report the result.
Definition: test_svf.cpp:69
t_seek_reads m_expected_blocks
Definition: test_svf.h:109
TestResult run() const override
Definition: test_svf.cpp:351
TestCaseWriteThrows(const std::string &m_test_name, const t_seek_reads &m_writes, t_fpos fpos, size_t len, const char *data, const std::string &message)
Definition: test_svf.cpp:340
Count of tests taht pass and fail.
Definition: test.h:137
void add_result(int result)
Add a tests result.
Definition: test.h:148
Result of a test.
Definition: test.h:68
int result() const
Definition: test.h:92
TestCount test_perf_erase_overwrite_true(t_test_results &results)
Definition: test_svf.cpp:1227
const std::vector< TestCaseNeed > need_test_cases_special
Definition: test_svf.cpp:902
TestCount test_block_size(t_test_results &results)
Definition: test_svf.cpp:1403
TestCount _test_perf_erase_overwrite(bool overwrite, t_test_results &results)
Definition: test_svf.cpp:1195
TestCount test_needs_many_empty_greedy_length(t_test_results &results)
Definition: test_svf.cpp:1755
const std::vector< TestCaseWrite > write_test_cases_special
Definition: test_svf.cpp:318
TestCount test_block_touch_coalesced(t_test_results &results)
Definition: test_svf.cpp:1529
const std::vector< TestCaseRead > read_test_cases_special
Definition: test_svf.cpp:588
TestCount test_needs_many_empty(t_test_results &results)
Definition: test_svf.cpp:1694
TestCount test_erase_updates_counters_not_punt(t_test_results &results)
Definition: test_svf.cpp:1880
TestCount test_perf_write_1M_uncoalesced_size_of(t_test_results &results)
Definition: test_svf.cpp:496
TestCount test_lru_block_punting_a(t_test_results &results)
Definition: test_svf.cpp:1556
std::vector< TestResult > t_test_results
Definition: test.h:128
TestCount test_svf_all(t_test_results &results)
Definition: test_svf.cpp:1949
SparseVirtualFile g_svf_multithreaded("", 0.0)
const std::vector< TestCaseNeed > need_test_cases
Definition: test_svf.cpp:851
TestCount test_perf_write_without_diff_check(t_test_results &results)
Definition: test_svf.cpp:413
TestCount test_write_multithreaded_un_coalesced(t_test_results &results)
Definition: test_svf.cpp:1297
TestCount test_perf_write_1M_coalesced(t_test_results &results)
Definition: test_svf.cpp:445
TestCount test_perf_erase_overwrite_false(t_test_results &results)
Definition: test_svf.cpp:1223
TestCount test_needs_many_empty_overlap(t_test_results &results)
Definition: test_svf.cpp:1728
const std::vector< TestCaseNeedGreedy > need_greedy_test_cases
Definition: test_svf.cpp:1014
TestCount test_needs_many_one_block(t_test_results &results)
Definition: test_svf.cpp:1783
TestCount test_perf_read_1M_un_coalesced(t_test_results &results)
Definition: test_svf.cpp:707
TestCount test_perf_write_1M_uncoalesced(t_test_results &results)
Definition: test_svf.cpp:469
TestCount test_need_greedy_all(t_test_results &results)
Definition: test_svf.cpp:1074
TestCount test_needs_many_one_block_greedy(t_test_results &results)
Definition: test_svf.cpp:1818
const std::vector< TestCaseWriteThrows > write_test_cases_throws
Definition: test_svf.cpp:368
const std::vector< TestCaseReadThrows > read_test_cases_throw
Definition: test_svf.cpp:645
TestCount test_lru_block_punting_b(t_test_results &results)
Definition: test_svf.cpp:1607
TestCount _test_perf_write_with_diff_check(bool compare_for_diff, t_test_results &results)
Definition: test_svf.cpp:386
void _write_multithreaded_un_coalesced()
Definition: test_svf.cpp:1246
TestResult test_debug_need_read_special_B()
Definition: test_svf.cpp:1345
const std::vector< TestCaseHas > has_test_cases
Definition: test_svf.cpp:773
const std::vector< TestCaseEraseThrows > erase_test_cases_throw
Definition: test_svf.cpp:1167
TestCount test_perf_need_sim_index(t_test_results &results)
Definition: test_svf.cpp:957
TestCount test_lru_block_punting_c(t_test_results &results)
Definition: test_svf.cpp:1661
TestCount test_write_multithreaded(int num_threads, bool is_coalesced, t_test_results &results)
Definition: test_svf.cpp:1258
TestCount test_read_all(t_test_results &results)
Definition: test_svf.cpp:598
TestCount test_write_all(t_test_results &results)
Definition: test_svf.cpp:324
TestCount test_perf_write_sim_index_svf(t_test_results &results)
Definition: test_svf.cpp:420
const std::vector< TestCaseErase > erase_test_cases
Definition: test_svf.cpp:1118
TestCount test_erase_all(t_test_results &results)
Definition: test_svf.cpp:1124
TestCount test_need_all(t_test_results &results)
Definition: test_svf.cpp:908
TestCount test_perf_read_1M_coalesced(t_test_results &results)
Definition: test_svf.cpp:684
TestCount test_erase_throws_all(t_test_results &results)
Definition: test_svf.cpp:1184
TestCount test_write_all_throws(t_test_results &results)
Definition: test_svf.cpp:375
const char test_data_bytes_512[]
Definition: test.cpp:51
TestCount test_block_touch_two_blocks(t_test_results &results)
Definition: test_svf.cpp:1503
const std::vector< TestCaseRead > read_test_cases
Definition: test_svf.cpp:572
TestResult test_debug_need_read_special_A()
Definition: test_svf.cpp:1319
TestCount _test_perf_need_sim_index(size_t need_size, t_test_results &results)
Definition: test_svf.cpp:926
TestCount test_erase_updates_counters(t_test_results &results)
Definition: test_svf.cpp:1849
TestCount test_perf_write_with_diff_check(t_test_results &results)
Definition: test_svf.cpp:408
TestCount test_write_multithreaded_coalesced(t_test_results &results)
Definition: test_svf.cpp:1289
void _write_multithreaded_coalesced()
Definition: test_svf.cpp:1235
TestCount test_block_touch_single_block_read_updates(t_test_results &results)
Definition: test_svf.cpp:1469
TestCount test_block_touch_single_block(t_test_results &results)
Definition: test_svf.cpp:1447
const std::vector< TestCaseWrite > write_test_cases
The actual write tests cases.
Definition: test_svf.cpp:171
TestResult test_debug_need_read_special_C()
This is special test created to check the problem reading TUPAC-TR-004.svs.
Definition: test_svf.cpp:1369
TestCount test_block_size_throws(t_test_results &results)
Definition: test_svf.cpp:1419
TestCount test_punt_updates_counters(t_test_results &results)
Definition: test_svf.cpp:1911
TestCount test_has_all(t_test_results &results)
Definition: test_svf.cpp:798
TestCount test_read_throws_all(t_test_results &results)
Definition: test_svf.cpp:670
The namespace for all svfsc code.
Definition: svf.cpp:41
std::vector< t_seek_read > t_seek_reads
Definition: svf.h:251
std::map< t_block_touch, t_fpos > t_block_touches
Definition: svf.h:255
size_t t_fpos
Definition: svf.h:247
Configuration for the Sparse Virtual File.
Definition: svf.h:262
void test_example_code(void)
Definition: test_svf.cpp:40