mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-30 23:52:43 +00:00 
			
		
		
		
	 d4e4189a34
			
		
	
	
		d4e4189a34
		
	
	
	
	
		
			
			To be used later by generators and during shrinking. The generators used in randomized tests will record the drawn random bits into a RandomRun. This is a layer of indirection that will help us automatically shrink any generated value without any user input. (Having to provide manually defined shrinkers is a downside to many randomized / property-based testing libraries like QuickCheck for Haskell.)
		
			
				
	
	
		
			117 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			117 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2023, Martin Janiczek <martin@janiczek.cz>.
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| #include <AK/QuickSort.h>
 | |
| #include <AK/Vector.h>
 | |
| #include <LibTest/Randomized/Chunk.h>
 | |
| 
 | |
| namespace Test {
 | |
| namespace Randomized {
 | |
| 
 | |
| // RandomRun is a record of random bits used in generation of random values.
 | |
| // Once a value failing user test is found, we then attempt to shrink its
 | |
| // RandomRun using various ShrinkCommands.
 | |
| //
 | |
| // This means that we construct new RandomRuns by saying "OK, but what if the
 | |
| // PRNG gave you 0 instead of 23 that time..." The runner then tries to
 | |
| // generate a new value from the new RandomRun; if it succeeds and the value
 | |
| // still fails the test, we've shrunk our counterexample some!
 | |
| //
 | |
| // RandomRun is conceptually a sequence of unsigned integers, e.g.
 | |
| // [5,3,10,8,0,0,1].
 | |
| class RandomRun {
 | |
| public:
 | |
|     RandomRun() = default;
 | |
|     RandomRun(RandomRun const& rhs) = default;
 | |
|     RandomRun& operator=(RandomRun const& rhs) = default;
 | |
|     explicit RandomRun(Vector<u32> const& data)
 | |
|         : m_data(move(data))
 | |
|     {
 | |
|     }
 | |
|     bool is_empty() const { return m_data.is_empty(); }
 | |
|     bool contains_chunk(Chunk const& c) const { return c.index + c.size <= m_data.size(); }
 | |
|     void append(u32 n) { m_data.append(n); }
 | |
|     size_t size() const { return m_data.size(); }
 | |
|     Optional<u32> next()
 | |
|     {
 | |
|         if (m_current_index < m_data.size()) {
 | |
|             return m_data[m_current_index++];
 | |
|         }
 | |
|         return Optional<u32> {};
 | |
|     }
 | |
|     u32& operator[](size_t index) { return m_data[index]; }
 | |
|     u32 const& operator[](size_t index) const { return m_data[index]; }
 | |
|     Vector<u32> data() const { return m_data; }
 | |
| 
 | |
|     // Shortlex sorting
 | |
|     //
 | |
|     // This is the metric by which we try to minimize (shrink) the sequence of
 | |
|     // random choices, from which we later generate values.
 | |
|     //
 | |
|     // Shorter is better; if the length is equal then lexicographic order is
 | |
|     // used. See [paper], section 2.2.
 | |
|     //
 | |
|     // Examples:
 | |
|     // [9,9,9] < [0,0,0,0] (shorter is better)
 | |
|     // [8,9,9] < [9,0,0] (lexicographic ordering: numbers that appear earlier
 | |
|     //                    are more "important" than numbers that follow them)
 | |
|     //
 | |
|     // [paper]: https://drops.dagstuhl.de/opus/volltexte/2020/13170/
 | |
|     bool is_shortlex_smaller_than(RandomRun const& rhs) const
 | |
|     {
 | |
|         auto lhs_size = size();
 | |
|         auto rhs_size = rhs.size();
 | |
| 
 | |
|         if (lhs_size < rhs_size)
 | |
|             return true;
 | |
| 
 | |
|         if (lhs_size > rhs_size)
 | |
|             return false;
 | |
| 
 | |
|         for (size_t i = 0; i < lhs_size; i++) {
 | |
|             if (m_data[i] < rhs.m_data[i])
 | |
|                 return true;
 | |
| 
 | |
|             if (m_data[i] > rhs.m_data[i])
 | |
|                 return false;
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     RandomRun with_sorted(Chunk c) const
 | |
|     {
 | |
|         Vector<u32> new_data = m_data;
 | |
|         AK::dual_pivot_quick_sort(
 | |
|             new_data,
 | |
|             c.index,
 | |
|             c.index + c.size - 1,
 | |
|             [](auto& a, auto& b) { return a < b; });
 | |
|         return RandomRun(move(new_data));
 | |
|     }
 | |
|     RandomRun with_deleted(Chunk c) const
 | |
|     {
 | |
|         Vector<u32> new_data(m_data);
 | |
|         new_data.remove(c.index, c.size);
 | |
|         return RandomRun(move(new_data));
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     Vector<u32> m_data;
 | |
|     size_t m_current_index = 0;
 | |
| };
 | |
| 
 | |
| } // namespace Randomized
 | |
| } // namespace Test
 | |
| 
 | |
| template<>
 | |
| struct AK::Formatter<Test::Randomized::RandomRun> : Formatter<StringView> {
 | |
|     ErrorOr<void> format(FormatBuilder& builder, Test::Randomized::RandomRun run)
 | |
|     {
 | |
|         return Formatter<StringView>::format(builder, TRY(String::formatted("[{}]"sv, TRY(String::join(',', run.data(), "{}"sv)))));
 | |
|     }
 | |
| };
 |