mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			213 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			213 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|     This file is part of TON Blockchain Library.
 | |
| 
 | |
|     TON Blockchain Library is free software: you can redistribute it and/or modify
 | |
|     it under the terms of the GNU Lesser General Public License as published by
 | |
|     the Free Software Foundation, either version 2 of the License, or
 | |
|     (at your option) any later version.
 | |
| 
 | |
|     TON Blockchain Library is distributed in the hope that it will be useful,
 | |
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|     GNU Lesser General Public License for more details.
 | |
| 
 | |
|     You should have received a copy of the GNU Lesser General Public License
 | |
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
|     Copyright 2017-2020 Telegram Systems LLP
 | |
| */
 | |
| #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/thread.h"
 | |
| #include "td/utils/Random.h"
 | |
| #include "td/utils/Slice.h"
 | |
| #include "td/utils/Span.h"
 | |
| #include "td/utils/Status.h"
 | |
| 
 | |
| #include <atomic>
 | |
| #include <functional>
 | |
| #include <utility>
 | |
| 
 | |
| #define REGISTER_TESTS(x)                \
 | |
|   void TD_CONCAT(register_tests_, x)() { \
 | |
|   }
 | |
| #define DESC_TESTS(x) void TD_CONCAT(register_tests_, x)()
 | |
| #define LOAD_TESTS(x) TD_CONCAT(register_tests_, x)()
 | |
| 
 | |
| namespace td {
 | |
| 
 | |
| class RandomSteps {
 | |
|  public:
 | |
|   struct Step {
 | |
|     std::function<void()> func;
 | |
|     td::uint32 weight;
 | |
|   };
 | |
|   RandomSteps(std::vector<Step> steps) : steps_(std::move(steps)) {
 | |
|     for (auto &step : steps_) {
 | |
|       steps_sum_ += step.weight;
 | |
|     }
 | |
|   }
 | |
|   template <class Random>
 | |
|   void step(Random &rnd) {
 | |
|     auto w = rnd() % steps_sum_;
 | |
|     for (auto &step : steps_) {
 | |
|       if (w < step.weight) {
 | |
|         step.func();
 | |
|         break;
 | |
|       }
 | |
|       w -= step.weight;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   std::vector<Step> steps_;
 | |
|   td::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 : public TestContext {
 | |
|  public:
 | |
|   static TestsRunner &get_default();
 | |
| 
 | |
|   void add_test(string name, unique_ptr<Test> test);
 | |
|   void add_substr_filter(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_;
 | |
|   vector<std::pair<string, unique_ptr<Test>>> tests_;
 | |
|   State state_;
 | |
|   unique_ptr<RegressionTester> regression_tester_;
 | |
| 
 | |
|   Slice name() override;
 | |
|   Status verify(Slice data) override;
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| class RegisterTest {
 | |
|  public:
 | |
|   explicit RegisterTest(string name, TestsRunner &runner = TestsRunner::get_default()) {
 | |
|     runner.add_test(name, make_unique<T>());
 | |
|   }
 | |
| };
 | |
| 
 | |
| class Stage {
 | |
|  public:
 | |
|   void wait(uint64 need) {
 | |
|     value_.fetch_add(1, std::memory_order_release);
 | |
|     while (value_.load(std::memory_order_acquire) < need) {
 | |
|       td::this_thread::yield();
 | |
|     }
 | |
|   };
 | |
| 
 | |
|  private:
 | |
|   std::atomic<uint64> value_{0};
 | |
| };
 | |
| 
 | |
| inline string rand_string(int from, int to, size_t len) {
 | |
|   string res(len, '\0');
 | |
|   for (auto &c : res) {
 | |
|     c = static_cast<char>(Random::fast(from, to));
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| inline vector<string> rand_split(Slice str) {
 | |
|   vector<string> res;
 | |
|   size_t pos = 0;
 | |
|   while (pos < str.size()) {
 | |
|     size_t len;
 | |
|     if (Random::fast(0, 1) == 1) {
 | |
|       len = Random::fast(1, 10);
 | |
|     } else {
 | |
|       len = Random::fast(100, 200);
 | |
|     }
 | |
|     res.push_back(str.substr(pos, len).str());
 | |
|     pos += len;
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| template <class T1, class T2>
 | |
| void assert_eq_impl(const T1 &expected, const T2 &got, const char *file, int line) {
 | |
|   LOG_CHECK(expected == got) << tag("expected", expected) << tag("got", got) << " in " << file << " at line " << line;
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| void assert_true_impl(const T &got, const char *file, int line) {
 | |
|   LOG_CHECK(got) << "Expected true in " << file << " at line " << line;
 | |
| }
 | |
| 
 | |
| }  // namespace td
 | |
| 
 | |
| #define ASSERT_EQ(expected, got) ::td::assert_eq_impl((expected), (got), __FILE__, __LINE__)
 | |
| 
 | |
| #define ASSERT_TRUE(got) ::td::assert_true_impl((got), __FILE__, __LINE__)
 | |
| 
 | |
| #define ASSERT_STREQ(expected, got) \
 | |
|   ::td::assert_eq_impl(::td::Slice((expected)), ::td::Slice((got)), __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 : 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()
 |