RandomRun.h 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. /*
  2. * Copyright (c) 2023, Martin Janiczek <martin@janiczek.cz>.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/QuickSort.h>
  8. #include <AK/Vector.h>
  9. #include <LibTest/Randomized/Chunk.h>
  10. namespace Test {
  11. namespace Randomized {
  12. // RandomRun is a record of random bits used in generation of random values.
  13. // Once a value failing user test is found, we then attempt to shrink its
  14. // RandomRun using various ShrinkCommands.
  15. //
  16. // This means that we construct new RandomRuns by saying "OK, but what if the
  17. // PRNG gave you 0 instead of 23 that time..." The runner then tries to
  18. // generate a new value from the new RandomRun; if it succeeds and the value
  19. // still fails the test, we've shrunk our counterexample some!
  20. //
  21. // RandomRun is conceptually a sequence of unsigned integers, e.g.
  22. // [5,3,10,8,0,0,1].
  23. class RandomRun {
  24. public:
  25. RandomRun() = default;
  26. RandomRun(RandomRun const& rhs) = default;
  27. RandomRun& operator=(RandomRun const& rhs) = default;
  28. explicit RandomRun(Vector<u64> const& data)
  29. : m_data(move(data))
  30. {
  31. }
  32. bool is_empty() const { return m_data.is_empty(); }
  33. bool contains_chunk(Chunk const& c) const { return c.index + c.size <= m_data.size(); }
  34. void append(u64 n) { m_data.append(n); }
  35. size_t size() const { return m_data.size(); }
  36. Optional<u64> next()
  37. {
  38. if (m_current_index < m_data.size()) {
  39. return m_data[m_current_index++];
  40. }
  41. return Optional<u64> {};
  42. }
  43. u64& operator[](size_t index) { return m_data[index]; }
  44. u64 const& operator[](size_t index) const { return m_data[index]; }
  45. Vector<u64> data() const { return m_data; }
  46. // Shortlex sorting
  47. //
  48. // This is the metric by which we try to minimize (shrink) the sequence of
  49. // random choices, from which we later generate values.
  50. //
  51. // Shorter is better; if the length is equal then lexicographic order is
  52. // used. See [paper], section 2.2.
  53. //
  54. // Examples:
  55. // [9,9,9] < [0,0,0,0] (shorter is better)
  56. // [8,9,9] < [9,0,0] (lexicographic ordering: numbers that appear earlier
  57. // are more "important" than numbers that follow them)
  58. //
  59. // [paper]: https://drops.dagstuhl.de/opus/volltexte/2020/13170/
  60. bool is_shortlex_smaller_than(RandomRun const& rhs) const
  61. {
  62. auto lhs_size = size();
  63. auto rhs_size = rhs.size();
  64. if (lhs_size < rhs_size)
  65. return true;
  66. if (lhs_size > rhs_size)
  67. return false;
  68. for (size_t i = 0; i < lhs_size; i++) {
  69. if (m_data[i] < rhs.m_data[i])
  70. return true;
  71. if (m_data[i] > rhs.m_data[i])
  72. return false;
  73. }
  74. return false;
  75. }
  76. RandomRun with_sorted(Chunk c) const
  77. {
  78. Vector<u64> new_data = m_data;
  79. AK::dual_pivot_quick_sort(
  80. new_data,
  81. c.index,
  82. c.index + c.size - 1,
  83. [](auto& a, auto& b) { return a < b; });
  84. return RandomRun(move(new_data));
  85. }
  86. RandomRun with_deleted(Chunk c) const
  87. {
  88. Vector<u64> new_data(m_data);
  89. new_data.remove(c.index, c.size);
  90. return RandomRun(move(new_data));
  91. }
  92. private:
  93. Vector<u64> m_data;
  94. size_t m_current_index = 0;
  95. };
  96. } // namespace Randomized
  97. } // namespace Test
  98. template<>
  99. struct AK::Formatter<Test::Randomized::RandomRun> : Formatter<StringView> {
  100. ErrorOr<void> format(FormatBuilder& builder, Test::Randomized::RandomRun run)
  101. {
  102. return Formatter<StringView>::format(builder, TRY(String::formatted("[{}]"sv, TRY(String::join(',', run.data(), "{}"sv)))));
  103. }
  104. };