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