451 lines
13 KiB
C++
451 lines
13 KiB
C++
#include "test.h"
|
|
#include <map>
|
|
#include <string>
|
|
#include "fl/map.h"
|
|
#include "fl/string.h"
|
|
#include "fl/namespace.h"
|
|
|
|
using namespace fl;
|
|
|
|
// Helper function to compare map contents
|
|
template<typename StdMap, typename FlMap>
|
|
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<int, int> std_map;
|
|
fl::fl_map<int, int> 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<int, fl::string> std_map;
|
|
fl::fl_map<int, fl::string> 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<std::pair<int, fl::string>> 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<int, fl::string>(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<int, fl::string> std_map;
|
|
fl::fl_map<int, fl::string> 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<int, fl::string> std_map;
|
|
fl::fl_map<int, fl::string> 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<int, fl::string> std_map;
|
|
fl::fl_map<int, fl::string> fl_map;
|
|
|
|
// Insert test data in different order to test sorting
|
|
std::vector<std::pair<int, fl::string>> 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<int, fl::string>(item.first, item.second));
|
|
}
|
|
|
|
SUBCASE("Forward iteration order") {
|
|
std::vector<int> std_order;
|
|
std::vector<int> 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<int>{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<int, fl::string> std_map;
|
|
fl::fl_map<int, fl::string> 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<int, fl::string> std_map;
|
|
fl::fl_map<int, fl::string> 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<int, fl::string> std_map;
|
|
fl::fl_map<int, fl::string> 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<int, fl::string> fl_map1;
|
|
fl::fl_map<int, fl::string> 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<int, int> std_map;
|
|
fl::fl_map<int, int> 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<int, fl::string, DescendingInt> std_map;
|
|
fl::fl_map<int, fl::string, DescendingInt> 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<int> std_order;
|
|
std::vector<int> 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<int>{3, 2, 1}); // Descending order
|
|
}
|
|
}
|