forked from lthn/blockchain
coretests: redesigned test environment to allow individual tests to be conducted against various activated hardforks
This commit is contained in:
parent
30a54790a6
commit
363d1bc316
3 changed files with 168 additions and 7 deletions
|
|
@ -255,11 +255,14 @@ public:
|
|||
void set_core_proxy(std::shared_ptr<tools::i_core_proxy>) { /* do nothing */ }
|
||||
uint64_t get_tx_version_from_events(const std::vector<test_event_entry> &events) const;
|
||||
|
||||
virtual void on_test_constructed() {} // called right after test class is constructed by the chaingen
|
||||
void on_test_generator_created(test_generator& generator) const; // tests can override this for special initialization
|
||||
|
||||
currency::core_runtime_config get_runtime_info_for_core() const; // tests can override this for special initialization
|
||||
|
||||
void set_hardforks_for_old_tests();
|
||||
currency::hard_forks_descriptor& get_hardforks() { return m_hardforks; }
|
||||
const currency::hard_forks_descriptor& get_hardforks() const { return m_hardforks; }
|
||||
|
||||
private:
|
||||
callbacks_map m_callbacks;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ namespace
|
|||
test_generator::set_test_gentime_settings_default(); \
|
||||
std::vector<test_event_entry> events; \
|
||||
genclass g; \
|
||||
g.on_test_constructed(); \
|
||||
g.generate(events); \
|
||||
if (!tools::serialize_obj_to_file(events, filename)) \
|
||||
{ \
|
||||
|
|
@ -54,6 +55,95 @@ namespace
|
|||
return 1; \
|
||||
}
|
||||
|
||||
|
||||
std::vector<size_t> parse_hardfork_str_mask(std::string s /* intentionally passing by value */)
|
||||
{
|
||||
// "*" -> 0, 1, 2, ..., ZANO_HARDFORKS_TOTAL-1
|
||||
// "2-4,0" -> 2, 3, 4, 0
|
||||
// "1, 3-*" -> 1, 3, 4, ..., ZANO_HARDFORKS_TOTAL-1
|
||||
|
||||
std::vector<size_t> result;
|
||||
|
||||
auto error_result = []() -> std::vector<size_t> { return std::vector<size_t>(); };
|
||||
auto all_hardforks = []() -> std::vector<size_t> { std::vector<size_t> r; for(size_t i = 0; i < ZANO_HARDFORKS_TOTAL; ++i) r.push_back(i); return r; };
|
||||
|
||||
// remove all spaces
|
||||
s.erase(std::remove_if(s.begin(), s.end(), [](char c){ return std::isspace(static_cast<unsigned char>(c)); }), s.end());
|
||||
std::vector<std::string> ranges;
|
||||
boost::split(ranges, s, boost::is_any_of(","));
|
||||
for(auto& range : ranges)
|
||||
{
|
||||
std::vector<std::string> segments;
|
||||
boost::split(segments, range, boost::is_any_of("-"));
|
||||
if (segments.size() == 1)
|
||||
{
|
||||
if (segments.front() == "*")
|
||||
return all_hardforks();
|
||||
size_t hfid = SIZE_MAX;
|
||||
if (!epee::string_tools::string_to_num_fast(segments.front(), (int64_t&)hfid) || hfid == SIZE_MAX || hfid >= ZANO_HARDFORKS_TOTAL)
|
||||
return error_result();
|
||||
result.push_back(hfid);
|
||||
}
|
||||
else if (segments.size() == 2)
|
||||
{
|
||||
if (segments.front() == "*")
|
||||
return all_hardforks();
|
||||
|
||||
size_t hfid_a = SIZE_MAX;
|
||||
if (!epee::string_tools::string_to_num_fast(segments.front(), (int64_t&)hfid_a) || hfid_a == SIZE_MAX || hfid_a >= ZANO_HARDFORKS_TOTAL)
|
||||
return error_result();
|
||||
|
||||
size_t hfid_b = SIZE_MAX;
|
||||
if (segments.back() == "*")
|
||||
hfid_b = ZANO_HARDFORKS_TOTAL - 1;
|
||||
else
|
||||
{
|
||||
if (!epee::string_tools::string_to_num_fast(segments.back(), (int64_t&)hfid_b))
|
||||
hfid_b = SIZE_MAX;
|
||||
}
|
||||
if (hfid_b == SIZE_MAX || hfid_b >= ZANO_HARDFORKS_TOTAL || hfid_b < hfid_a)
|
||||
return error_result();
|
||||
for(size_t i = hfid_a; i <= hfid_b; ++i)
|
||||
result.push_back(i);
|
||||
if (segments.back() == "*")
|
||||
return result; // don't keep parsing if the last range was ?-*
|
||||
}
|
||||
else // i.e. segments.size() == 0 || segments.size() > 2
|
||||
return error_result();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool test_parse_hardfork_str_mask()
|
||||
{
|
||||
static_assert(ZANO_HARDFORKS_TOTAL >= 5, "this test was made in assumption that this condition holds");
|
||||
auto v_range = [](size_t a, size_t b) -> std::vector<size_t> { std::vector<size_t> r; for(size_t i = a; i <= b; ++i) r.push_back(i); return r; };
|
||||
auto v_concat = [](const std::vector<size_t>& a, const std::vector<size_t>& b) -> std::vector<size_t> { std::vector<size_t> r = a; r.insert(r.end(), b.begin(), b.end()); };
|
||||
const std::vector<size_t> res_empty;
|
||||
const std::vector<size_t> res_all_hf = v_range(0, ZANO_HARDFORKS_TOTAL - 1);
|
||||
std::string hf_total_num_str_m_1 = epee::string_tools::num_to_string_fast(ZANO_HARDFORKS_TOTAL - 1);
|
||||
std::string hf_total_num_str = epee::string_tools::num_to_string_fast(ZANO_HARDFORKS_TOTAL);
|
||||
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask("") == res_empty, false, "");
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask("*") == res_all_hf, false, "");
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask("1,2") == v_range(1, 2), false, "");
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask("0,*, 3") == res_all_hf, false, "");
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask("1-4") == v_range(1, 4), false, "");
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask("4-1") == res_empty, false, "");
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask("3-1,2") == res_empty, false, "");
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask("1,2-1") == res_empty, false, "");
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask("0, 2 - 2") == std::vector<size_t>({0, 2}), false, "");
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask("2-*") == v_range(2, ZANO_HARDFORKS_TOTAL - 1), false, "");
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask(" 2-*, 1") == v_range(2, ZANO_HARDFORKS_TOTAL - 1), false, "");
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask("2- *,3") == v_range(2, ZANO_HARDFORKS_TOTAL - 1), false, "");
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask("1,3,2") == std::vector<size_t>({1, 3, 2}), false, "");
|
||||
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask(hf_total_num_str_m_1) == std::vector<size_t>({ZANO_HARDFORKS_TOTAL - 1}), false, "");
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask(hf_total_num_str) == res_empty, false, "");
|
||||
CHECK_AND_ASSERT_MES(parse_hardfork_str_mask(std::string("2-") + hf_total_num_str) == res_empty, false, "");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool clean_data_directory()
|
||||
{
|
||||
std::string config_folder = command_line::get_arg(g_vm, command_line::arg_data_dir);
|
||||
|
|
@ -102,7 +192,7 @@ bool clean_data_directory()
|
|||
}
|
||||
|
||||
template<class genclass>
|
||||
bool generate_and_play(const char* const genclass_name)
|
||||
bool generate_and_play(const char* const genclass_name, size_t hardfork_id = SIZE_MAX)
|
||||
{
|
||||
std::vector<test_event_entry> events;
|
||||
bool generated = false;
|
||||
|
|
@ -117,11 +207,37 @@ bool generate_and_play(const char* const genclass_name)
|
|||
test_core_time::init();
|
||||
test_generator::set_test_gentime_settings_default();
|
||||
genclass g;
|
||||
|
||||
static const test_chain_unit_base tcub;
|
||||
bool has_non_default_hardforks = g.get_hardforks() != tcub.get_hardforks(); // compare with the defaults
|
||||
currency::hard_forks_descriptor hfd_local{};
|
||||
if (hardfork_id != SIZE_MAX)
|
||||
{
|
||||
static test_chain_unit_base tcub;
|
||||
if (has_non_default_hardforks) // compare with the defaults
|
||||
{
|
||||
LOG_ERROR(ENDL << "hardforks setting have been changed in ctor of " << genclass_name << " test" << ENDL << ENDL);
|
||||
return false;
|
||||
}
|
||||
g.get_hardforks().clear();
|
||||
g.get_hardforks().set_hardfork_height(hardfork_id, 1 /* <- height_the_hardfork_is_active_after */);
|
||||
}
|
||||
hfd_local = g.get_hardforks(); // save a copy for the safety checking at the end
|
||||
|
||||
g.on_test_constructed();
|
||||
|
||||
try
|
||||
{
|
||||
generated = g.generate(events);
|
||||
if (generated)
|
||||
{
|
||||
std::cout << concolor::normal << events.size() << " events generated successfully" << std::endl;
|
||||
if (has_non_default_hardforks || g.get_hardforks() != tcub.get_hardforks())
|
||||
{
|
||||
size_t configure_core_events_count = std::count_if(events.begin(), events.end(), [](auto& ev){ return ev.type() == typeid(callback_entry) && boost::get<callback_entry>(ev).callback_name == "configure_core"; });
|
||||
CHECK_AND_ASSERT_THROW_MES(configure_core_events_count != 0, "Test " << genclass_name << " has non-default hardfork settings and therefore must use 'configure_core' callback");
|
||||
}
|
||||
|
||||
std::cout << concolor::bright_white << std::string(100, '=') << std::endl <<
|
||||
"#TEST# >>>> " << genclass_name << " <<<< start replaying events" << std::endl <<
|
||||
std::string(100, '=') << concolor::normal << std::endl;
|
||||
|
|
@ -131,11 +247,18 @@ bool generate_and_play(const char* const genclass_name)
|
|||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
LOG_ERROR("got an exception during " << genclass_name << (generated ? " replaying: " : " generation: ") << ex.what());
|
||||
LOG_ERROR(">>>>> got an exception during " << genclass_name << (generated ? " replaying: " : " generation: ") << ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_ERROR("got an unknown exception during " << genclass_name << (generated ? " replaying" : " generation"));
|
||||
LOG_ERROR(">>>>> got an unknown exception during " << genclass_name << (generated ? " replaying" : " generation"));
|
||||
}
|
||||
|
||||
if (hardfork_id != SIZE_MAX && g.get_hardforks() != hfd_local)
|
||||
{
|
||||
// conflict detected: hardfork settings are either controlled by the test itself or by the chaingen
|
||||
LOG_ERROR("hardforks setting have been changed during generation or execution of test " << genclass_name);
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (result)
|
||||
|
|
@ -181,6 +304,7 @@ bool gen_and_play_intermitted_by_blockchain_saveload(const char* const genclass_
|
|||
test_core_time::init();
|
||||
test_generator::set_test_gentime_settings_default();
|
||||
genclass g;
|
||||
g.on_test_constructed();
|
||||
try
|
||||
{
|
||||
r = g.generate(events);
|
||||
|
|
@ -294,6 +418,34 @@ bool gen_and_play_intermitted_by_blockchain_saveload(const char* const genclass_
|
|||
tests_running_time.push_back(std::make_pair(testname, t)); \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define GENERATE_AND_PLAY_HF(genclass, hardfork_str_mask) \
|
||||
if((!postponed_tests.count(#genclass) && run_single_test.empty()) || (!run_single_test.empty() && std::string::npos != std::string(#genclass).find(run_single_test))) \
|
||||
{ \
|
||||
std::vector<size_t> hardforks = parse_hardfork_str_mask(hardfork_str_mask); \
|
||||
CHECK_AND_ASSERT_MES(!hardforks.empty(), false, "invalid hardforks mask: " << hardfork_str_mask); \
|
||||
for(size_t hfid : hardforks) \
|
||||
{ \
|
||||
std::string tns = std::string(#genclass) + " @ HF " + epee::string_tools::num_to_string_fast(hfid); \
|
||||
const char* testname = tns.c_str(); \
|
||||
TIME_MEASURE_START_MS(t); \
|
||||
++tests_count; \
|
||||
if (!generate_and_play<genclass>(testname, hfid)) \
|
||||
{ \
|
||||
failed_tests.insert(testname); \
|
||||
LOCAL_ASSERT(false); \
|
||||
if (stop_on_first_fail) \
|
||||
return 1; \
|
||||
} \
|
||||
TIME_MEASURE_FINISH_MS(t); \
|
||||
tests_running_time.push_back(std::make_pair(testname, t)); \
|
||||
} \
|
||||
++unique_tests_count; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
//#define GENERATE_AND_PLAY(genclass) GENERATE_AND_PLAY_INTERMITTED_BY_BLOCKCHAIN_SAVELOAD(genclass)
|
||||
|
||||
|
||||
|
|
@ -738,6 +890,7 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
|
||||
size_t tests_count = 0;
|
||||
size_t unique_tests_count = 0;
|
||||
size_t serious_failures_count = 0;
|
||||
std::set<std::string> failed_tests;
|
||||
std::string tests_folder = command_line::get_arg(g_vm, arg_test_data_path);
|
||||
|
|
@ -774,6 +927,7 @@ int main(int argc, char* argv[])
|
|||
CALL_TEST("check_allowed_types_in_variant_container() test", check_allowed_types_in_variant_container_test);
|
||||
CALL_TEST("check_u8_str_case_funcs", check_u8_str_case_funcs);
|
||||
CALL_TEST("chec_u8_str_matching", chec_u8_str_matching);
|
||||
CALL_TEST("test_parse_hardfork_str_mask", test_parse_hardfork_str_mask);
|
||||
}
|
||||
|
||||
//CALL_TEST("check_hash_and_difficulty_monte_carlo_test", check_hash_and_difficulty_monte_carlo_test); // it's rather an experiment with unclean results than a solid test, for further research...
|
||||
|
|
@ -1134,10 +1288,12 @@ int main(int argc, char* argv[])
|
|||
|
||||
std::cout << (serious_failures_count == 0 ? concolor::green : concolor::magenta);
|
||||
std::cout << "\nREPORT:\n";
|
||||
std::cout << " Test run: " << tests_count << std::endl;
|
||||
std::cout << " Failures: " << serious_failures_count << " (postponed failures: " << failed_postponed_tests_count << ")" << std::endl;
|
||||
std::cout << " Postponed: " << postponed_tests.size() << std::endl;
|
||||
std::cout << " Total time: " << total_time / 1000 << " s. (" << (tests_count > 0 ? total_time / tests_count : 0) << " ms per test in average)" << std::endl;
|
||||
std::cout << " Unique tests run: " << unique_tests_count << std::endl;
|
||||
std::cout << " Total tests run: " << tests_count << std::endl;
|
||||
|
||||
std::cout << " Failures: " << serious_failures_count << " (postponed failures: " << failed_postponed_tests_count << ")" << std::endl;
|
||||
std::cout << " Postponed: " << postponed_tests.size() << std::endl;
|
||||
std::cout << " Total time: " << total_time / 1000 << " s. (" << (tests_count > 0 ? total_time / tests_count : 0) << " ms per test in average)" << std::endl;
|
||||
if (!failed_tests.empty())
|
||||
{
|
||||
std::cout << "FAILED/POSTPONED TESTS:\n";
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ struct wallet_test : virtual public test_chain_unit_enchanced
|
|||
bool check_balance_via_build_wallets(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
bool check_balance(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
|
||||
void on_test_constructed() override { on_test_generator_created(this->generator); }
|
||||
|
||||
static std::string get_test_account_name_by_id(size_t acc_id);
|
||||
|
||||
protected:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue