diff --git a/tests/specmicp_common/misc.cpp b/tests/specmicp_common/misc.cpp index 0efa09d..e906359 100644 --- a/tests/specmicp_common/misc.cpp +++ b/tests/specmicp_common/misc.cpp @@ -1,626 +1,626 @@ #include "catch.hpp" #include "specmicp_common/log.hpp" #include "specmicp_common/timer.hpp" #include "specmicp_common/physics/maths.hpp" #include "specmicp_common/moving_average.hpp" #include "specmicp_common/perfs_handler.hpp" #include "specmicp_common/options_handler.hpp" #include "specmicp_common/scope_guard.hpp" #include "specmicp_common/pimpl_ptr.hpp" #include "specmicp_common/cached_vector.hpp" #include "specmicp_common/string_algorithms.hpp" #include "specmicp_common/filesystem.hpp" #include using namespace specmicp; // TOC // ======================= // // 1 - Logger // 2 - Timer // 3 - Average // 4 - Moving Average // 5 - Performance handler // 6 - Options handler // 7 - Scope guard // 8 - Pimpl pointer // 9 - Cached Vector // 10 - Named Cached Vector // 11 - String algorithms // 12 - Filesystem // Logger // ======================= TEST_CASE("Logger", "[io],[log]") { SECTION("Non init logging") { init_logger(nullptr, logger::LogLevel::Warning); // compilation test, and runtime test of not failling nullptr SPAM << "Spam"; DEBUG << "Debug"; INFO << "Info"; WARNING << "Warning"; ERROR << "Error"; CRITICAL << "Critical"; } SECTION("Level test") { std::stringstream stream_log; init_logger(&stream_log, logger::LogLevel::Warning); SPAM << "Spam"; REQUIRE(stream_log.str().size() == 0); DEBUG << "Debug"; REQUIRE(stream_log.str().size() == 0); INFO << "Info"; REQUIRE(stream_log.str().size() == 0); WARNING << "Warning"; REQUIRE(stream_log.str().size() != 0); stream_log.str(""); ERROR << "Error"; REQUIRE(stream_log.str().size() != 0); stream_log.str(""); CRITICAL << "Critical"; REQUIRE(stream_log.str().size() != 0); stream_log.str(""); init_logger(&stream_log, logger::LogLevel::Debug); SPAM << "Spam"; REQUIRE(stream_log.str().size() == 0); DEBUG << "Debug"; #ifdef NDEBUG REQUIRE(stream_log.str().size() == 0); #else REQUIRE(stream_log.str().size() != 0); #endif stream_log.str(""); INFO << "Info"; #ifdef NDEBUG REQUIRE(stream_log.str().size() == 0); #else REQUIRE(stream_log.str().size() != 0); #endif stream_log.str(""); WARNING << "Warning"; REQUIRE(stream_log.str().size() != 0); stream_log.str(""); ERROR << "Error"; REQUIRE(stream_log.str().size() != 0); stream_log.str(""); CRITICAL << "Critical"; REQUIRE(stream_log.str().size() != 0); stream_log.str(""); } } // Timer // ======================= TEST_CASE("Timer", "[time],[CPU]") { SECTION("Timer test") { Timer timer; timer.stop(); CHECK(timer.elapsed_time() >= 0.0); timer.start(); timer.stop(); CHECK(timer.elapsed_time() >= 0.0); CHECK(timer.get_stop() >= timer.get_start()); CHECK(timer.get_ctime_start() != nullptr); CHECK(timer.get_ctime_stop() != nullptr); } } // Average // ==================== #define test_average(method, a, b, sol) \ CHECK(average(a, b) == Approx(sol).epsilon(1e-10)); #define test_average_vector(method, vector, sol) \ CHECK(average(vector) == Approx(sol).epsilon(1e-10)); TEST_CASE("Average", "[average],[maths]") { Vector test_vector(4); test_vector << 1.0, 2.0, 3.0, 4.0; SECTION("Arithmetic average") { test_average(Average::arithmetic, 1.0, 1.0, 1.0); test_average(Average::arithmetic, 2.0, 1.0, 1.5); test_average(Average::arithmetic, -1.0, 1.0, 0.0); test_average_vector(Average::arithmetic, test_vector, 10.0/4); } SECTION("Harmonic average") { test_average(Average::harmonic, 1.0, 1.0, 1.0); test_average(Average::harmonic, 2.0, 1.0, 2.0/(1+0.5)); test_average_vector(Average::harmonic, test_vector, 1.92); } SECTION("Geometric average") { test_average(Average::geometric, 1.0, 1.0, 1.0); test_average(Average::geometric, 1.0, 2.0, std::sqrt(2.0)); test_average_vector(Average::geometric, test_vector, std::pow(24.0, 1.0/4.0)); } } #undef test_average #undef test_average_vector // Moving Average // ======================= TEST_CASE("Moving average", "[average],[timestep]") { SECTION("Moving average test") { utils::ExponentialMovingAverage moving_average(0.1, 1.0); REQUIRE(moving_average.current_value() == 1.0); CHECK(moving_average.add_point(1.0) == 1.0); - CHECK(moving_average.add_point(2.0) == 1.1); - REQUIRE(moving_average.add_point(3.0) == 0.9*1.1+0.3); + CHECK(moving_average.add_point(2.0) == Approx(1.1).epsilon(1e-8)); + REQUIRE(moving_average.add_point(3.0) == Approx(0.9*1.1+0.3).epsilon(1e-8)); moving_average.reset(1.0); REQUIRE(moving_average.current_value() == 1.0); moving_average.set_alpha(0.2); REQUIRE(moving_average.add_point(2.0) == Approx(0.8+0.4)); } } // Performance handler // ======================= struct MockPerf { scalar_t residuals {-1}; index_t nb_iterations {-1}; }; class MockSolverPerf: public PerformanceHandler { public: MockSolverPerf() {} void do_stuff() { get_perfs().residuals = 1e-6; get_perfs().nb_iterations = 10; } void reset_solver() { reset_perfs(); } }; TEST_CASE("PerformanceHandler", "[performance],[base]") { SECTION("Performance handler test") { auto my_solver = MockSolverPerf(); const auto& perfs = my_solver.get_perfs(); CHECK(perfs.nb_iterations == -1); CHECK(perfs.residuals == -1); my_solver.do_stuff(); CHECK(perfs.nb_iterations == 10); CHECK(perfs.residuals == 1e-6); my_solver.reset_solver(); CHECK(perfs.nb_iterations == -1); CHECK(perfs.residuals == -1); } } // Options handler // ======================= struct MockOptions { MockOptions() {} MockOptions(scalar_t res, index_t max_iter): residuals(res), max_iterations(max_iter) {} scalar_t residuals {1e-6}; index_t max_iterations {100}; }; class MockSolverOptions: public OptionsHandler { public: MockSolverOptions() {} MockSolverOptions(scalar_t res, index_t max_iter): OptionsHandler(res, max_iter) {} }; TEST_CASE("Options handler", "[options],[base]") { SECTION("Options handler test") { auto my_solver_default = MockSolverOptions(); const auto& ro_options = my_solver_default.get_options(); CHECK(ro_options.max_iterations == 100); CHECK(ro_options.residuals == 1e-6); auto& rw_options = my_solver_default.get_options(); rw_options.max_iterations = 10; rw_options.residuals = 1e-4; CHECK(ro_options.max_iterations == 10); CHECK(ro_options.residuals == 1e-4); } SECTION("Options handler - non default initialization") { auto my_solver = MockSolverOptions(1e-4, 10); const auto& ro_options = my_solver.get_options(); CHECK(ro_options.max_iterations == 10); CHECK(ro_options.residuals == 1e-4); } } // ScopeGuard // ========== static void will_fail(bool& hop) { auto guard = utils::make_scope_guard([&hop]{hop=true;}); throw std::runtime_error("fail on purpose"); guard.release(); } static void dont_fail(bool& hop) { auto guard = utils::make_scope_guard([&hop]{hop=true;}); hop = false; guard.release(); } TEST_CASE("Scope guard", "[utils]") { SECTION("Catch error") { bool hop = false; REQUIRE_THROWS_AS(will_fail(hop), std::runtime_error); CHECK(hop == true); dont_fail(hop); CHECK(hop == false); } } // pimpl_ptr // ========= // simple Interface + Implementation to test pimpl_ptr class MockImplementation { public: MockImplementation(double a): m_a(a) {} double add(double b) const {return m_a + b;} // Compilation should failed if this one is activated //double add(double b) {return m_a + b;} void set(double new_a) { m_a = new_a;} private: double m_a; }; class MockInterface { public: MockInterface(double a): m_impl(utils::make_pimpl(a)) {} double add(double b) const {return m_impl->add(b);} void set(double new_a) {return m_impl->set(new_a);} private: utils::pimpl_ptr m_impl; }; TEST_CASE("pimpl_ptr", "[utils],[pointer],[const]") { SECTION("Test mock") { auto hop = MockImplementation(1.0); CHECK(hop.add(2.0) == Approx(3.0)); hop.set(2.0); CHECK(hop.add(2.0) == Approx(4.0)); } //SECTION("compilation fail") { // const MockInterface fail1(2.0); // fail1.set(3.0); //} } // Cached Vector // TEST_CASE("Cached vector", "[utils],[container],[cache]") { SECTION("Default") { utils::CachedVector cached; cached.push_back("value0"); cached.push_back(0); CHECK(cached.size() == 2); CHECK(cached.size_cache() == 1); CHECK(cached[0] == "value0"); CHECK(cached[1] == "value0"); for (auto& it: cached) { CHECK(it == "value0"); } cached.emplace_back("truc"); CHECK(cached[2] == "truc"); cached.emplace_back("other"); CHECK(cached[3] == "other"); cached.push_back(2); CHECK(cached.at(4) == "other"); CHECK(cached.size() == 5); CHECK(cached.size_cache() == 3); auto& new_val = cached.fork(1); new_val = "plop"; CHECK(cached[1] == "plop"); for (auto it=cached.begin_index(); it!=cached.cend_index();++it) { CHECK(*it < cached.size()); CHECK(*it >= 0); } //! it's possible, absolutely not a good idea for (auto& value: cached) { value = "reinit"; } CHECK(cached[2] == "reinit"); } SECTION("Default constructor") { static_assert(std::is_default_constructible::value, "String is default constructible" ); utils::CachedVector cached(5); CHECK(cached[4] == ""); } } // Named Cached Vector TEST_CASE("NamedCachedVector", "[utils],[container],[cache]") { SECTION("Default") { utils::NameCachedVector cached( 5, "default_name", "default_value"); CHECK(cached.size_cache() == 1); CHECK(cached.size() == 5); CHECK(cached.get("default_name") == "default_value"); auto& forked = cached.fork(2, "new name"); forked = "new value"; CHECK(cached.size_cache() == 2); CHECK(cached.size() == 5); CHECK(cached[2] == "new value"); CHECK(cached.get("new name") == "new value"); CHECK(cached.has_value("new name")); CHECK(not cached.has_value("not a name")); cached.push_back_cache("another name", "another value"); CHECK(cached.get("another name") == "another value"); CHECK(cached.size_cache() == 3); CHECK(cached.size() == 5); for (auto& value: cached) { CHECK(value != "another value"); } } SECTION("Default constructor") { static_assert(std::is_default_constructible::value, "String is default constructible" ); utils::NameCachedVector cached(5, "default"); CHECK(cached[4] == ""); } } // String Algorithms // ================= TEST_CASE("String algorithms", "[utils],[string],[algorithms]") { SECTION("split") { std::string test1 = "a"; CHECK(utils::split(test1, ',') == std::vector({"a", })); std::string test2 = "a,b,c"; CHECK(utils::split(test2, ',') == std::vector({"a", "b", "c"})); std::string test3 = "a,b,c,"; CHECK(utils::split(test3, ',') == std::vector({"a", "b", "c"})); } SECTION("strip") { CHECK(utils::strip("hop") == "hop"); CHECK(utils::strip(" hop") == "hop"); CHECK(utils::strip("hop ") == "hop"); CHECK(utils::strip(" hop ") == "hop"); CHECK(utils::strip(" hop hop ") == "hop hop"); CHECK(utils::strip(" ") == ""); } SECTION("range_indices") { CHECK(utils::range_indices("1") == std::vector({1, })); CHECK(utils::range_indices("-1:3") == std::vector({-1, 0, 1, 2, 3})); CHECK(utils::range_indices("1,5") == std::vector({1, 5})); CHECK(utils::range_indices("1,3:5") == std::vector({1, 3, 4, 5})); CHECK(utils::range_indices("1,5,") == std::vector({1, 5})); CHECK(utils::range_indices("1, 3:5, 2") == std::vector({1, 2, 3, 4, 5,})); CHECK(utils::range_indices("1,3:5") == std::vector({1, 3, 4, 5})); CHECK_THROWS_AS(utils::range_indices("1,-3:5"), std::invalid_argument); CHECK_THROWS_AS(utils::range_indices("-2"), std::invalid_argument); } SECTION("parse expr") { CHECK(utils::parse_expression("2.0") == 2.0); CHECK(utils::parse_expression("-2.0") == -2.0); CHECK(utils::parse_expression("2.0 +2.0") == 4.0); CHECK(utils::parse_expression("2.0/ 2.0") == 1.0); CHECK(utils::parse_expression("3 + 2.0 / 2.0") == 4.0); CHECK(utils::parse_expression("1e-6") == 1e-6); CHECK(utils::parse_expression("2*1e-6") == 2e-6); CHECK(utils::parse_expression("1e-6-2") == 1e-6-2); CHECK(utils::parse_expression("1e-6/1e-3*1e-2") == 1e-5); CHECK(utils::parse_expression("3 + a / 2.0", {{"a", 2}}) == 4.0); CHECK(utils::parse_expression("a + b / c", {{"a", 2,}, {"b", 3.0}, {"c", 6.0}}) == 2.5); CHECK_THROWS(utils::parse_expression("234.53as")); CHECK_THROWS(utils::parse_expression("3 + a / 2.0", {{"b", 2}})); CHECK_THROWS(utils::parse_expression("3 + a/ / 2.0", {{"a", 2}})); CHECK_THROWS(utils::parse_expression("3 + a // 2.0", {{"a", 2}})); } SECTION("string to bool") { CHECK(utils::string_to_bool("True") == true); CHECK(utils::string_to_bool(" true") == true); CHECK(utils::string_to_bool("1") == true); CHECK(utils::string_to_bool("yes ") == true); CHECK(utils::string_to_bool("Yes") == true); CHECK(utils::string_to_bool(" Y ") == true); CHECK(utils::string_to_bool("False") == false); CHECK(utils::string_to_bool("false ") == false); CHECK(utils::string_to_bool(" 0 ") == false); CHECK(utils::string_to_bool(" no ") == false); CHECK(utils::string_to_bool(" No") == false); CHECK(utils::string_to_bool("N ") == false); CHECK_THROWS_AS(utils::string_to_bool("plop"), std::invalid_argument); CHECK_THROWS_AS(utils::string_to_bool(" plop"), std::invalid_argument); CHECK_THROWS_AS(utils::string_to_bool("Faolse"), std::invalid_argument); } } // Filesystem // =========== TEST_CASE("filesystem", "[io],[dir],[filesystem]") { SECTION("is directory") { CHECK(utils::is_directory(CURRENT_DIRECTORY)); CHECK_FALSE(utils::is_directory("test_common")); CHECK_FALSE(utils::is_directory("mouahahahah")); } SECTION("is directory") { CHECK_FALSE(utils::is_file(CURRENT_DIRECTORY)); CHECK(utils::is_file("test_common")); CHECK(utils::is_file(utils::complete_path( CURRENT_DIRECTORY, "test_common"))); } SECTION("is absolute") { CHECK(utils::is_path_absolute("/abs/path")); CHECK_FALSE(utils::is_path_absolute("not/abs")); CHECK_FALSE(utils::is_path_absolute("../not/abs")); } SECTION("Current directory") { CHECK(utils::get_current_directory() == CURRENT_DIRECTORY); } SECTION("Complete path") { CHECK(utils::complete_path("/usr/include/", "dirent.h") == "/usr/include/dirent.h"); CHECK(utils::complete_path("/usr/include", "dirent.h") == "/usr/include/dirent.h"); } SECTION("Absolute path") { std::string error = ""; CHECK(utils::relative_to_absolute("./test_common", error) == utils::complete_path(CURRENT_DIRECTORY, "test_common")); CHECK(error == ""); CHECK(utils::relative_to_absolute("../../platypus.duck", error) == ""); CHECK(error != ""); } SECTION("Find path") { CHECK(utils::find_path("dirent.h", {"/usr/include"}) == "/usr/include/dirent.h"); CHECK(utils::find_path("not_found_becuase_.h", {"/usr/include"}) == ""); } SECTION("has_env") { CHECK(utils::has_env("USER")); CHECK_FALSE(utils::has_env("platypus_platypus_platypys_35897037289589")); } SECTION("get_env") { CHECK(utils::get_env("USER") != ""); CHECK(utils::get_env("platypus_platypus_platypys_35897037289589") == ""); } }