#include "test.h" #include #include #include "fl/map.h" #include "fl/string.h" #include "fl/namespace.h" using namespace fl; // Helper function to compare map contents template bool maps_equal(const StdMap& std_map, const FlMap& fl_map) { if (std_map.size() != fl_map.size()) { return false; } auto std_it = std_map.begin(); auto fl_it = fl_map.begin(); while (std_it != std_map.end() && fl_it != fl_map.end()) { if (std_it->first != fl_it->first || std_it->second != fl_it->second) { return false; } ++std_it; ++fl_it; } return std_it == std_map.end() && fl_it == fl_map.end(); } TEST_CASE("std::map vs fl::fl_map - Basic Construction and Size") { std::map std_map; fl::fl_map fl_map; SUBCASE("Default construction") { CHECK(std_map.empty() == fl_map.empty()); CHECK(std_map.size() == fl_map.size()); CHECK(std_map.size() == 0); CHECK(fl_map.size() == 0); } } TEST_CASE("std::map vs fl::fl_map - Insert Operations") { std::map std_map; fl::fl_map fl_map; SUBCASE("Insert with pair") { auto std_result = std_map.insert({1, "one"}); auto fl_result = fl_map.insert({1, "one"}); CHECK(std_result.second == fl_result.second); CHECK(std_result.first->first == fl_result.first->first); CHECK(std_result.first->second == fl_result.first->second); CHECK(maps_equal(std_map, fl_map)); } SUBCASE("Insert duplicate key") { std_map.insert({1, "one"}); fl_map.insert({1, "one"}); auto std_result = std_map.insert({1, "ONE"}); auto fl_result = fl_map.insert({1, "ONE"}); CHECK(std_result.second == fl_result.second); CHECK(std_result.second == false); // Should not insert duplicate CHECK(maps_equal(std_map, fl_map)); } SUBCASE("Multiple inserts maintain order") { std::vector> test_data = { {3, "three"}, {1, "one"}, {4, "four"}, {2, "two"} }; for (const auto& item : test_data) { std_map.insert(item); fl_map.insert(fl::pair(item.first, item.second)); } CHECK(maps_equal(std_map, fl_map)); CHECK(std_map.size() == 4); CHECK(fl_map.size() == 4); } } TEST_CASE("std::map vs fl::fl_map - Element Access") { std::map std_map; fl::fl_map fl_map; // Insert some test data std_map[1] = "one"; std_map[2] = "two"; std_map[3] = "three"; fl_map[1] = "one"; fl_map[2] = "two"; fl_map[3] = "three"; SUBCASE("operator[] access existing keys") { CHECK(std_map[1] == fl_map[1]); CHECK(std_map[2] == fl_map[2]); CHECK(std_map[3] == fl_map[3]); } SUBCASE("operator[] creates new key with default value") { CHECK(std_map[4] == fl_map[4]); // Both should create empty string CHECK(std_map.size() == fl_map.size()); CHECK(maps_equal(std_map, fl_map)); } SUBCASE("at() method for existing keys") { CHECK(std_map.at(1) == fl_map.at(1)); CHECK(std_map.at(2) == fl_map.at(2)); CHECK(std_map.at(3) == fl_map.at(3)); } } TEST_CASE("std::map vs fl::fl_map - Find Operations") { std::map std_map; fl::fl_map fl_map; // Insert test data std_map.insert({1, "one"}); std_map.insert({2, "two"}); std_map.insert({3, "three"}); fl_map.insert({1, "one"}); fl_map.insert({2, "two"}); fl_map.insert({3, "three"}); SUBCASE("find() existing keys") { auto std_it = std_map.find(2); auto fl_it = fl_map.find(2); CHECK((std_it != std_map.end()) == (fl_it != fl_map.end())); CHECK(std_it->first == fl_it->first); CHECK(std_it->second == fl_it->second); } SUBCASE("find() non-existent keys") { auto std_it = std_map.find(99); auto fl_it = fl_map.find(99); CHECK((std_it == std_map.end()) == (fl_it == fl_map.end())); } SUBCASE("count() method") { CHECK(std_map.count(1) == fl_map.count(1)); CHECK(std_map.count(2) == fl_map.count(2)); CHECK(std_map.count(99) == fl_map.count(99)); CHECK(std_map.count(99) == 0); CHECK(fl_map.count(99) == 0); } SUBCASE("contains() method (C++20 style)") { // std::map::contains is C++20, but we can test fl_map's version CHECK(fl_map.contains(1) == true); CHECK(fl_map.contains(2) == true); CHECK(fl_map.contains(99) == false); // Compare with std::map using count CHECK((std_map.count(1) > 0) == fl_map.contains(1)); CHECK((std_map.count(99) > 0) == fl_map.contains(99)); } } TEST_CASE("std::map vs fl::fl_map - Iterator Operations") { std::map std_map; fl::fl_map fl_map; // Insert test data in different order to test sorting std::vector> test_data = { {3, "three"}, {1, "one"}, {4, "four"}, {2, "two"} }; for (const auto& item : test_data) { std_map.insert(item); fl_map.insert(fl::pair(item.first, item.second)); } SUBCASE("Forward iteration order") { std::vector std_order; std::vector fl_order; for (const auto& pair : std_map) { std_order.push_back(pair.first); } for (const auto& pair : fl_map) { fl_order.push_back(pair.first); } CHECK(std_order == fl_order); CHECK(std_order == std::vector{1, 2, 3, 4}); // Should be sorted } SUBCASE("begin() and end() iterators") { CHECK((std_map.begin() == std_map.end()) == (fl_map.begin() == fl_map.end())); CHECK(std_map.begin()->first == fl_map.begin()->first); CHECK(std_map.begin()->second == fl_map.begin()->second); } SUBCASE("Iterator increment") { auto std_it = std_map.begin(); auto fl_it = fl_map.begin(); ++std_it; ++fl_it; CHECK(std_it->first == fl_it->first); CHECK(std_it->second == fl_it->second); } } TEST_CASE("std::map vs fl::fl_map - Erase Operations") { std::map std_map; fl::fl_map fl_map; // Insert test data for (int i = 1; i <= 5; ++i) { std::string std_value = "value" + std::to_string(i); fl::string fl_value = "value"; fl_value += std::to_string(i).c_str(); std_map[i] = fl::string(std_value.c_str()); fl_map[i] = fl_value; } SUBCASE("Erase by key") { size_t std_erased = std_map.erase(3); size_t fl_erased = fl_map.erase(3); CHECK(std_erased == fl_erased); CHECK(std_erased == 1); CHECK(maps_equal(std_map, fl_map)); } SUBCASE("Erase non-existent key") { size_t std_erased = std_map.erase(99); size_t fl_erased = fl_map.erase(99); CHECK(std_erased == fl_erased); CHECK(std_erased == 0); CHECK(maps_equal(std_map, fl_map)); } SUBCASE("Erase by iterator") { auto std_it = std_map.find(2); auto fl_it = fl_map.find(2); std_map.erase(std_it); fl_map.erase(fl_it); CHECK(maps_equal(std_map, fl_map)); CHECK(std_map.find(2) == std_map.end()); CHECK(fl_map.find(2) == fl_map.end()); } } TEST_CASE("std::map vs fl::fl_map - Clear and Empty") { std::map std_map; fl::fl_map fl_map; // Insert some data std_map[1] = "one"; std_map[2] = "two"; fl_map[1] = "one"; fl_map[2] = "two"; CHECK(std_map.empty() == fl_map.empty()); CHECK(std_map.empty() == false); SUBCASE("Clear operation") { std_map.clear(); fl_map.clear(); CHECK(std_map.empty() == fl_map.empty()); CHECK(std_map.size() == fl_map.size()); CHECK(std_map.size() == 0); CHECK(maps_equal(std_map, fl_map)); } } TEST_CASE("std::map vs fl::fl_map - Bound Operations") { std::map std_map; fl::fl_map fl_map; // Insert test data: 1, 3, 5, 7, 9 for (int i = 1; i <= 9; i += 2) { std::string std_value = "value" + std::to_string(i); fl::string fl_value = "value"; fl_value += std::to_string(i).c_str(); std_map[i] = fl::string(std_value.c_str()); fl_map[i] = fl_value; } SUBCASE("lower_bound existing key") { auto std_it = std_map.lower_bound(5); auto fl_it = fl_map.lower_bound(5); CHECK(std_it->first == fl_it->first); CHECK(std_it->second == fl_it->second); CHECK(std_it->first == 5); } SUBCASE("lower_bound non-existing key") { auto std_it = std_map.lower_bound(4); auto fl_it = fl_map.lower_bound(4); CHECK(std_it->first == fl_it->first); CHECK(std_it->first == 5); // Should find next higher key } SUBCASE("upper_bound existing key") { auto std_it = std_map.upper_bound(5); auto fl_it = fl_map.upper_bound(5); CHECK(std_it->first == fl_it->first); CHECK(std_it->first == 7); // Should find next higher key } SUBCASE("equal_range") { auto std_range = std_map.equal_range(5); auto fl_range = fl_map.equal_range(5); CHECK(std_range.first->first == fl_range.first->first); CHECK(std_range.second->first == fl_range.second->first); CHECK(std_range.first->first == 5); CHECK(std_range.second->first == 7); } } TEST_CASE("std::map vs fl::fl_map - Comparison Operations") { fl::fl_map fl_map1; fl::fl_map fl_map2; SUBCASE("Empty maps equality") { CHECK(fl_map1 == fl_map2); CHECK(!(fl_map1 != fl_map2)); } SUBCASE("Equal maps") { fl_map1[1] = "one"; fl_map1[2] = "two"; fl_map2[1] = "one"; fl_map2[2] = "two"; CHECK(fl_map1 == fl_map2); CHECK(!(fl_map1 != fl_map2)); } SUBCASE("Different maps") { fl_map1[1] = "one"; fl_map2[1] = "ONE"; // Different value CHECK(!(fl_map1 == fl_map2)); CHECK(fl_map1 != fl_map2); } SUBCASE("Different sizes") { fl_map1[1] = "one"; fl_map1[2] = "two"; fl_map2[1] = "one"; CHECK(!(fl_map1 == fl_map2)); CHECK(fl_map1 != fl_map2); } } TEST_CASE("std::map vs fl::fl_map - Edge Cases") { std::map std_map; fl::fl_map fl_map; SUBCASE("Large numbers of insertions") { // Test with more elements to stress test the data structures for (int i = 100; i >= 1; --i) { // Insert in reverse order std_map[i] = i * 10; fl_map[i] = i * 10; } CHECK(std_map.size() == fl_map.size()); CHECK(std_map.size() == 100); CHECK(maps_equal(std_map, fl_map)); // Verify sorted order int prev_key = 0; for (const auto& pair : fl_map) { CHECK(pair.first > prev_key); prev_key = pair.first; } } SUBCASE("Mixed operations") { // Perform a series of mixed operations std_map[5] = 50; fl_map[5] = 50; CHECK(maps_equal(std_map, fl_map)); std_map.erase(5); fl_map.erase(5); CHECK(maps_equal(std_map, fl_map)); std_map[1] = 10; std_map[3] = 30; fl_map[1] = 10; fl_map[3] = 30; CHECK(maps_equal(std_map, fl_map)); std_map.clear(); fl_map.clear(); CHECK(maps_equal(std_map, fl_map)); CHECK(std_map.empty()); CHECK(fl_map.empty()); } } // Test with custom comparator struct DescendingInt { bool operator()(int a, int b) const { return a > b; // Reverse order } }; TEST_CASE("std::map vs fl::fl_map - Custom Comparator") { std::map std_map; fl::fl_map fl_map; std_map[1] = "one"; std_map[2] = "two"; std_map[3] = "three"; fl_map[1] = "one"; fl_map[2] = "two"; fl_map[3] = "three"; SUBCASE("Custom ordering") { std::vector std_order; std::vector fl_order; for (const auto& pair : std_map) { std_order.push_back(pair.first); } for (const auto& pair : fl_map) { fl_order.push_back(pair.first); } CHECK(std_order == fl_order); CHECK(std_order == std::vector{3, 2, 1}); // Descending order } }