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