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