#pragma once #include #include #include #include #include #include #ifdef CMAKE_GITHUB_CI #include #else #include #endif namespace ss { template class parser; } /* ss */ namespace { struct buffer { std::string data_; char* operator()(const std::string& data) { data_ = data; return data_.data(); } char* append(const std::string& data) { data_ += data; return data_.data(); } char* append_overwrite_last(const std::string& data, size_t size) { data_.resize(data_.size() - size); return append(data); } }; [[maybe_unused]] inline buffer buff; [[maybe_unused]] std::string time_now_rand() { srand(time(nullptr)); std::stringstream ss; auto t = std::time(nullptr); auto tm = *std::localtime(&t); ss << std::put_time(&tm, "%d%m%Y%H%M%S"); srand(time(nullptr)); return ss.str() + std::to_string(rand()); } struct unique_file_name { static inline int i = 0; std::string name; unique_file_name(const std::string& test) { do { name = "random_" + test + "_" + std::to_string(i++) + "_" + time_now_rand() + "_file.csv"; } while (std::filesystem::exists(name)); } ~unique_file_name() { std::filesystem::remove(name); } }; #define CHECK_FLOATING_CONVERSION(input, type) \ { \ auto eps = std::numeric_limits::min(); \ std::string s = #input; \ auto t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ REQUIRE(t.has_value()); \ CHECK_LT(std::abs(t.value() - type(input)), eps); \ } \ { \ /* check negative too */ \ auto eps = std::numeric_limits::min(); \ auto s = std::string("-") + #input; \ auto t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ REQUIRE(t.has_value()); \ CHECK_LT(std::abs(t.value() - type(-input)), eps); \ } #define CHECK_FLOATING_CONVERSION_LONG_NUMBER(STRING_NUMBER, TYPE, CONVERTER) \ { \ auto begin = STRING_NUMBER.c_str(); \ auto end = begin + STRING_NUMBER.size(); \ \ auto number = ss::to_num(begin, end); \ REQUIRE(number.has_value()); \ \ auto expected_number = CONVERTER(STRING_NUMBER); \ CHECK_EQ(number.value(), expected_number); \ } #define CHECK_INVALID_CONVERSION(input, type) \ { \ std::string s = input; \ auto t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ CHECK_FALSE(t.has_value()); \ } #define REQUIRE_VARIANT(var, el, type) \ { \ auto ptr = std::get_if(&var); \ REQUIRE(ptr); \ REQUIRE_EQ(el, *ptr); \ } #define CHECK_NOT_VARIANT(var, type) CHECK(!std::holds_alternative(var)); #define REQUIRE_EXCEPTION(...) \ try { \ __VA_ARGS__; \ FAIL("Expected exception"); \ } catch (ss::exception & e) { \ CHECK_FALSE(std::string{e.what()}.empty()); \ } template [[maybe_unused]] std::vector> vector_combinations( const std::vector& v, size_t n) { std::vector> ret; if (n <= 1) { for (const auto& i : v) { ret.push_back({i}); } return ret; } auto inner_combinations = vector_combinations(v, n - 1); for (const auto& i : v) { for (auto j : inner_combinations) { j.insert(j.begin(), i); ret.push_back(move(j)); } } return ret; } [[maybe_unused]] std::string make_buffer(const std::string& file_name) { std::ifstream in{file_name, std::ios::binary}; std::string tmp; std::string out; auto copy_if_whitespaces = [&] { std::string matches = "\n\r\t "; while (std::any_of(matches.begin(), matches.end(), [&](auto c) { return in.peek() == c; })) { if (in.peek() == '\r') { out += "\r\n"; in.ignore(2); } else { out += std::string{static_cast(in.peek())}; in.ignore(1); } } }; out.reserve(sizeof(out) + 1); copy_if_whitespaces(); while (in >> tmp) { out += tmp; copy_if_whitespaces(); } return out; } template [[maybe_unused]] std::tuple, std::string> make_parser( const std::string& file_name, const std::string& delim = "") { if (buffer_mode) { auto buffer = make_buffer(file_name); if (delim.empty()) { return {ss::parser{buffer.data(), buffer.size()}, std::move(buffer)}; } else { return {ss::parser{buffer.data(), buffer.size(), delim}, std::move(buffer)}; } } else { if (delim.empty()) { return {ss::parser{file_name}, std::string{}}; } else { return {ss::parser{file_name, delim}, std::string{}}; } } } } /* namespace */