userver: /data/code/userver/libraries/multi-index-lru/src/main_test.cpp Source File
Loading...
Searching...
No Matches
main_test.cpp
1#include <userver/multi-index-lru/container.hpp>
2
3#include <string>
4
5#include <gtest/gtest.h>
6#include <boost/multi_index/member.hpp>
7
8USERVER_NAMESPACE_BEGIN
9
10namespace {
11
12class LRUUsersTest : public ::testing::Test {
13protected:
14 void SetUp() override {}
15
16 struct IdTag {};
17 struct EmailTag {};
18 struct NameTag {};
19
20 struct User {
21 int id;
22 std::string email;
23 std::string name;
24
25 bool operator==(const User& other) const {
26 return id == other.id && email == other.email && name == other.name;
27 }
28 };
29
30 using UserCache = multi_index_lru::Container<
31 User,
32 boost::multi_index::indexed_by<
33 boost::multi_index::ordered_unique<
34 boost::multi_index::tag<IdTag>,
35 boost::multi_index::member<User, int, &User::id>>,
36 boost::multi_index::ordered_unique<
37 boost::multi_index::tag<EmailTag>,
38 boost::multi_index::member<User, std::string, &User::email>>,
39 boost::multi_index::ordered_non_unique<
40 boost::multi_index::tag<NameTag>,
41 boost::multi_index::member<User, std::string, &User::name>>>>;
42};
43
44TEST_F(LRUUsersTest, BasicOperations) {
45 UserCache cache(3); // capacity == 3
46
47 // Test insertion
48 cache.emplace(User{1, "alice@test.com", "Alice"});
49 cache.emplace(User{2, "bob@test.com", "Bob"});
50 cache.emplace(User{3, "charlie@test.com", "Charlie"});
51
52 EXPECT_EQ(cache.size(), 3);
53
54 // Test find by id
55 auto by_id = cache.find<IdTag, int>(1);
56 ASSERT_NE(by_id, cache.end<IdTag>());
57 EXPECT_EQ(by_id->name, "Alice");
58
59 // Test find by email
60 auto by_email = cache.find<EmailTag, std::string>("bob@test.com");
61 ASSERT_NE(by_email, cache.end<EmailTag>());
62 EXPECT_EQ(by_email->id, 2);
63
64 // Test find by name
65 auto by_name = cache.find<NameTag, std::string>("Charlie");
66 ASSERT_NE(by_name, cache.end<NameTag>());
67 EXPECT_EQ(by_name->email, "charlie@test.com");
68
69 // Test template find method
70 auto it = cache.find<EmailTag, std::string>("alice@test.com");
71 EXPECT_NE(it, cache.end<EmailTag>());
72}
73
74TEST_F(LRUUsersTest, LRUEviction) {
75 UserCache cache(3);
76
77 cache.emplace(User{1, "alice@test.com", "Alice"});
78 cache.emplace(User{2, "bob@test.com", "Bob"});
79 cache.emplace(User{3, "charlie@test.com", "Charlie"});
80
81 // Access Alice and Charlie to make them recently used
82 cache.find<IdTag>(1);
83 cache.find<IdTag>(3);
84
85 // Add fourth element - Bob should be evicted
86 cache.emplace(User{4, "david@test.com", "David"});
87
88 EXPECT_FALSE((cache.contains<IdTag>(2))); // Bob evicted
89 EXPECT_TRUE((cache.contains<IdTag>(1))); // Alice remains
90 EXPECT_TRUE((cache.contains<IdTag>(3))); // Charlie remains
91 EXPECT_TRUE((cache.contains<IdTag>(4))); // David added
92}
93
94class ProductsTest : public ::testing::Test {
95protected:
96 struct SkuTag {};
97 struct NameTag {};
98
99 struct Product {
100 std::string sku;
101 std::string name;
102 double price;
103
104 bool operator==(const Product& other) const {
105 return sku == other.sku && name == other.name && price == other.price;
106 }
107 };
108
109 using ProductCache = multi_index_lru::Container<
110 Product,
111 boost::multi_index::indexed_by<
112 boost::multi_index::ordered_unique<
113 boost::multi_index::tag<SkuTag>,
114 boost::multi_index::member<Product, std::string, &Product::sku>>,
115 boost::multi_index::ordered_unique<
116 boost::multi_index::tag<NameTag>,
117 boost::multi_index::member<Product, std::string, &Product::name>>>>;
118};
119
120TEST_F(ProductsTest, BasicProductOperations) {
121 ProductCache cache(2);
122
123 cache.emplace(Product{"A1", "Laptop", 999.99});
124 cache.emplace(Product{"A2", "Mouse", 29.99});
125
126 auto laptop = cache.find<SkuTag, std::string>("A1");
127 ASSERT_NE(laptop, cache.end<SkuTag>());
128 EXPECT_EQ(laptop->name, "Laptop");
129}
130
131TEST_F(ProductsTest, ProductEviction) {
132 ProductCache cache(2);
133
134 cache.emplace(Product{"A1", "Laptop", 999.99});
135 cache.emplace(Product{"A2", "Mouse", 29.99});
136
137 // A1 was used, so A2 should be ousted when adding A3
138 cache.find<SkuTag>("A1");
139 cache.emplace(Product{"A3", "Keyboard", 79.99});
140
141 EXPECT_TRUE((cache.contains<SkuTag, std::string>("A1"))); // used
142 EXPECT_TRUE((cache.contains<SkuTag, std::string>("A3"))); // new
143 EXPECT_FALSE((cache.contains<SkuTag, std::string>("A2"))); // ousted
144
145 EXPECT_NE(cache.find<NameTag>("Keyboard"), cache.end<NameTag>());
146 EXPECT_EQ(cache.find<NameTag>("Mouse"), cache.end<NameTag>());
147}
148
149} // namespace
150
151USERVER_NAMESPACE_END