1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/utils/common.h"
#include "td/utils/Context.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/port/sleep.h"
#include "td/utils/Slice.h"
#include "td/utils/Span.h"
#include "td/utils/Status.h"
#include <atomic>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <utility>
namespace td {
class RandomSteps {
public:
struct Step {
std::function<void()> func;
uint32 weight;
};
explicit RandomSteps(vector<Step> steps) : steps_(std::move(steps)) {
for (const auto &step : steps_) {
steps_sum_ += step.weight;
}
}
template <class Random>
auto step(Random &rnd) const {
auto w = rnd() % steps_sum_;
for (const auto &step : steps_) {
if (w < step.weight) {
return step.func();
}
w -= step.weight;
}
}
private:
vector<Step> steps_;
int32 steps_sum_ = 0;
};
class RegressionTester {
public:
virtual ~RegressionTester() = default;
static void destroy(CSlice db_path);
static unique_ptr<RegressionTester> create(string db_path, string db_cache_dir = "");
virtual Status verify_test(Slice name, Slice result) = 0;
virtual void save_db() = 0;
};
class Test {
public:
virtual ~Test() = default;
virtual void run() {
while (step()) {
}
}
virtual bool step() {
run();
return false;
}
Test() = default;
Test(const Test &) = delete;
Test &operator=(const Test &) = delete;
Test(Test &&) = delete;
Test &operator=(Test &&) = delete;
};
class TestContext : public Context<TestContext> {
public:
virtual ~TestContext() = default;
virtual Slice name() = 0;
virtual Status verify(Slice data) = 0;
};
class TestsRunner final : public TestContext {
public:
static TestsRunner &get_default();
void add_test(string name, std::function<unique_ptr<Test>()> test);
void add_substr_filter(string str);
void set_offset(string str);
void set_stress_flag(bool flag);
void run_all();
bool run_all_step();
void set_regression_tester(unique_ptr<RegressionTester> regression_tester);
private:
struct State {
size_t it{0};
bool is_running = false;
double start{0};
double start_unadjusted{0};
size_t end{0};
};
bool stress_flag_{false};
vector<string> substr_filters_;
string offset_;
struct TestInfo {
std::function<unique_ptr<Test>()> creator;
unique_ptr<Test> test;
};
vector<std::pair<string, TestInfo>> tests_;
State state_;
unique_ptr<RegressionTester> regression_tester_;
Slice name() final;
Status verify(Slice data) final;
};
template <class T>
class RegisterTest {
public:
explicit RegisterTest(string name, TestsRunner &runner = TestsRunner::get_default()) {
runner.add_test(std::move(name), [] { return make_unique<T>(); });
}
};
class StageWait {
public:
void wait(uint64 need) {
value_.fetch_add(1, std::memory_order_release);
while (value_.load(std::memory_order_acquire) < need) {
usleep_for(1);
}
};
private:
std::atomic<uint64> value_{0};
};
class StageMutex {
public:
void wait(uint64 need) {
std::unique_lock<std::mutex> lock{mutex_};
value_++;
if (value_ == need) {
cond_.notify_all();
return;
}
cond_.wait(lock, [&] { return value_ >= need; });
};
private:
std::mutex mutex_;
std::condition_variable cond_;
uint64 value_{0};
};
using Stage = StageMutex;
string rand_string(int from, int to, size_t len);
vector<string> rand_split(Slice str);
template <class T, class R>
void rand_shuffle(MutableSpan<T> v, R &rnd) {
for (size_t i = 1; i < v.size(); i++) {
auto pos = static_cast<size_t>(rnd()) % (i + 1);
using std::swap;
swap(v[i], v[pos]);
}
}
template <class T1, class T2>
void assert_eq_impl(const T1 &expected, const T2 &received, const char *file, int line) {
LOG_CHECK(expected == received) << tag("expected", expected) << tag("received", received) << " in " << file
<< " at line " << line;
}
template <class T1, class T2>
void assert_neq_impl(const T1 &expected, const T2 &received, const char *file, int line) {
LOG_CHECK(expected != received) << tag("value", expected) << " in " << file << " at line " << line;
}
template <class T>
void assert_true_impl(const T &received, const char *file, int line) {
LOG_CHECK(received) << "Expected true in " << file << " at line " << line;
}
} // namespace td
#define ASSERT_EQ(expected, received) ::td::assert_eq_impl((expected), (received), __FILE__, __LINE__)
#define ASSERT_NE(expected, received) ::td::assert_neq_impl((expected), (received), __FILE__, __LINE__)
#define ASSERT_TRUE(received) ::td::assert_true_impl((received), __FILE__, __LINE__)
#define ASSERT_FALSE(received) ::td::assert_true_impl(!(received), __FILE__, __LINE__)
#define ASSERT_STREQ(expected, received) \
::td::assert_eq_impl(::td::Slice((expected)), ::td::Slice((received)), __FILE__, __LINE__)
#define REGRESSION_VERIFY(data) ::td::TestContext::get()->verify(data).ensure()
#define TEST_NAME(test_case_name, test_name) \
TD_CONCAT(Test, TD_CONCAT(_, TD_CONCAT(test_case_name, TD_CONCAT(_, test_name))))
#define TEST(test_case_name, test_name) TEST_IMPL(TEST_NAME(test_case_name, test_name))
#define TEST_IMPL(test_name) \
class test_name final : public ::td::Test { \
public: \
using Test::Test; \
void run() final; \
}; \
::td::RegisterTest<test_name> TD_CONCAT(test_instance_, TD_CONCAT(test_name, __LINE__))(TD_DEFINE_STR(test_name)); \
void test_name::run()
|