151c0b2f7Stbbdev /*
2*cb88c51cSIlya Isaev     Copyright (c) 2005-2022 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1751c0b2f7Stbbdev #define TBB_PREVIEW_CONCURRENT_LRU_CACHE 1
1851c0b2f7Stbbdev 
19b15aabb3Stbbdev #if __INTEL_COMPILER && _MSC_VER
20b15aabb3Stbbdev #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
21b15aabb3Stbbdev #endif
22b15aabb3Stbbdev 
2351c0b2f7Stbbdev #include "common/test.h"
24*cb88c51cSIlya Isaev #include "common/utils.h"
2551c0b2f7Stbbdev #include <tbb/concurrent_lru_cache.h>
2651c0b2f7Stbbdev #include <common/concurrent_lru_cache_common.h>
2751c0b2f7Stbbdev 
2851c0b2f7Stbbdev //! \file test_concurrent_lru_cache.cpp
2951c0b2f7Stbbdev //! \brief Test for [preview] functionality
3051c0b2f7Stbbdev 
3151c0b2f7Stbbdev //-----------------------------------------------------------------------------
3251c0b2f7Stbbdev // Concurrent LRU Cache Tests: Cache Test Cases
3351c0b2f7Stbbdev //-----------------------------------------------------------------------------
3451c0b2f7Stbbdev 
3551c0b2f7Stbbdev //! \brief \ref error_guessing
3651c0b2f7Stbbdev TEST_CASE("basic test for return value") {
3751c0b2f7Stbbdev     using preset = concurrent_lru_cache_presets::preset_default<int, int>;
3851c0b2f7Stbbdev 
__anoncd5487920102(int ) 3951c0b2f7Stbbdev     auto dummy_f = [](int /*key*/) -> int { return 0xDEADBEEF; };
4051c0b2f7Stbbdev     std::size_t number_of_lru_history_items = 8;
4151c0b2f7Stbbdev 
4251c0b2f7Stbbdev     preset preset_object{dummy_f, number_of_lru_history_items};
4351c0b2f7Stbbdev     preset::cache_type& cache = preset_object.cache;
4451c0b2f7Stbbdev 
4551c0b2f7Stbbdev     int dummy_key = 1;
4651c0b2f7Stbbdev     REQUIRE_MESSAGE(
4751c0b2f7Stbbdev         dummy_f(dummy_key) == cache[dummy_key].value(),
4851c0b2f7Stbbdev         "cache operator() must return only values obtained from value function");
4951c0b2f7Stbbdev }
5051c0b2f7Stbbdev 
5151c0b2f7Stbbdev //! \brief \ref error_guessing
5251c0b2f7Stbbdev TEST_CASE("basic test for unused objects") {
5351c0b2f7Stbbdev     using preset = concurrent_lru_cache_presets::preset_instance_count;
5451c0b2f7Stbbdev     preset preset_object{};
5551c0b2f7Stbbdev 
5651c0b2f7Stbbdev     for (std::size_t i = 0; i < preset_object.number_of_lru_history_items; ++i)
5751c0b2f7Stbbdev         preset_object.cache[i];
5851c0b2f7Stbbdev 
5951c0b2f7Stbbdev     REQUIRE_MESSAGE(
6051c0b2f7Stbbdev         preset_object.source.instances_count() > 1,
6151c0b2f7Stbbdev         "cache should store some unused objects");
6251c0b2f7Stbbdev }
6351c0b2f7Stbbdev 
6451c0b2f7Stbbdev //! \brief \ref error_guessing
6551c0b2f7Stbbdev TEST_CASE("basic test for unused object limit") {
6651c0b2f7Stbbdev     using preset = concurrent_lru_cache_presets::preset_instance_count;
6751c0b2f7Stbbdev     preset preset_object{};
6851c0b2f7Stbbdev 
6951c0b2f7Stbbdev     for (std::size_t i = 0; i < preset_object.number_of_lru_history_items + 1; ++i)
7051c0b2f7Stbbdev         preset_object.cache[i];
7151c0b2f7Stbbdev 
7251c0b2f7Stbbdev     REQUIRE_MESSAGE(
7351c0b2f7Stbbdev         preset_object.source.instances_count() == preset_object.number_of_lru_history_items + 1,
7451c0b2f7Stbbdev         "cache should respect number of stored unused objects to number passed in constructor");
7551c0b2f7Stbbdev }
7651c0b2f7Stbbdev 
7751c0b2f7Stbbdev //! \brief \ref error_guessing
7851c0b2f7Stbbdev TEST_CASE("basic test for eviction order") {
7951c0b2f7Stbbdev     using preset = concurrent_lru_cache_presets::preset_map_instance_count;
8051c0b2f7Stbbdev     preset preset_object{};
8151c0b2f7Stbbdev 
8251c0b2f7Stbbdev     REQUIRE_MESSAGE(
8351c0b2f7Stbbdev         preset_object.number_of_lru_history_items > 2,
8451c0b2f7Stbbdev         "incorrect test setup");
8551c0b2f7Stbbdev 
8651c0b2f7Stbbdev     preset_object.fill_up_cache(0, preset_object.number_of_lru_history_items);
8751c0b2f7Stbbdev 
8851c0b2f7Stbbdev     // heat up first element
8951c0b2f7Stbbdev     preset_object.cache[0];
9051c0b2f7Stbbdev 
9151c0b2f7Stbbdev     // cause eviction;
9251c0b2f7Stbbdev     preset_object.cache[preset_object.number_of_lru_history_items];
9351c0b2f7Stbbdev 
9451c0b2f7Stbbdev     bool is_correct = preset_object.is_evicted(1) && !preset_object.is_evicted(0);
9551c0b2f7Stbbdev     REQUIRE_MESSAGE(is_correct, "cache should evict items in lru order");
9651c0b2f7Stbbdev }
9751c0b2f7Stbbdev 
9851c0b2f7Stbbdev //! \brief \ref error_guessing
9951c0b2f7Stbbdev TEST_CASE("basic test for eviction of only unused items") {
10051c0b2f7Stbbdev     using preset = concurrent_lru_cache_presets::preset_map_instance_count;
10151c0b2f7Stbbdev     preset preset_object{};
10251c0b2f7Stbbdev 
10351c0b2f7Stbbdev     preset::handle_type h = preset_object.cache[0];
10451c0b2f7Stbbdev 
10551c0b2f7Stbbdev     //cause eviction
10651c0b2f7Stbbdev     preset_object.fill_up_cache(1, preset_object.number_of_lru_history_items+2);
10751c0b2f7Stbbdev 
10851c0b2f7Stbbdev     bool is_correct = preset_object.is_evicted(1) && !preset_object.is_evicted(0);
10951c0b2f7Stbbdev     REQUIRE_MESSAGE(is_correct, "cache should not evict items in use");
11051c0b2f7Stbbdev }
11151c0b2f7Stbbdev 
11251c0b2f7Stbbdev //! \brief \ref error_guessing
11351c0b2f7Stbbdev TEST_CASE("basic test for eviction of only unused items 2") {
11451c0b2f7Stbbdev     using preset = concurrent_lru_cache_presets::preset_map_instance_count;
11551c0b2f7Stbbdev     preset preset_object{};
11651c0b2f7Stbbdev 
11751c0b2f7Stbbdev     preset::handle_type h = preset_object.cache[0];
11851c0b2f7Stbbdev     {
11951c0b2f7Stbbdev         preset::handle_type h1 = preset_object.cache[0];
12051c0b2f7Stbbdev     }
12151c0b2f7Stbbdev 
12251c0b2f7Stbbdev     //cause eviction
12351c0b2f7Stbbdev     preset_object.fill_up_cache(1,preset_object.number_of_lru_history_items+2);
12451c0b2f7Stbbdev 
12551c0b2f7Stbbdev     bool is_correct = preset_object.is_evicted(1) && !preset_object.is_evicted(0);
12651c0b2f7Stbbdev     REQUIRE_MESSAGE(is_correct, "cache should not evict items in use");
12751c0b2f7Stbbdev }
12851c0b2f7Stbbdev 
129*cb88c51cSIlya Isaev //! \brief \ref error_guessing
130*cb88c51cSIlya Isaev TEST_CASE("basic test for handling case when number_of_lru_history_items is zero") {
__anoncd5487920202(int) 131*cb88c51cSIlya Isaev     auto foo = [] (int) {
132*cb88c51cSIlya Isaev         return utils::LifeTrackableObject{};
133*cb88c51cSIlya Isaev     };
134*cb88c51cSIlya Isaev     using cache_type =  tbb::concurrent_lru_cache<int, utils::LifeTrackableObject, decltype(foo)>;
135*cb88c51cSIlya Isaev     cache_type cache{foo, 0};
136*cb88c51cSIlya Isaev 
137*cb88c51cSIlya Isaev     for(int i = 0; i < 10; ++i) {
138*cb88c51cSIlya Isaev         // Check that no history is stored when my_history_list_capacity is 0.
139*cb88c51cSIlya Isaev         // In this case, when trying to fill the cache, the items will be deleted if reference was not taken.
140*cb88c51cSIlya Isaev         const utils::LifeTrackableObject* obj_addr = &cache[1].value();
141*cb88c51cSIlya Isaev         REQUIRE_MESSAGE(utils::LifeTrackableObject::is_alive(obj_addr) == false, "when number_of_lru_history_items is zero, element must be erased after use");
142*cb88c51cSIlya Isaev     }
143*cb88c51cSIlya Isaev 
144*cb88c51cSIlya Isaev     cache_type::handle h = cache[1];
145*cb88c51cSIlya Isaev     const utils::LifeTrackableObject* obj_addr = &h.value();
146*cb88c51cSIlya Isaev     auto& object_set = utils::LifeTrackableObject::set();
147*cb88c51cSIlya Isaev     for(int i = 0; i < 10; ++i) {
148*cb88c51cSIlya Isaev         // Verify that item will still be alive if there is a handle holding that item.
149*cb88c51cSIlya Isaev         cache[1];
150*cb88c51cSIlya Isaev         REQUIRE_MESSAGE(utils::LifeTrackableObject::is_alive(obj_addr), "the object with the key=1 was destroyed but should not");
151*cb88c51cSIlya Isaev         REQUIRE_MESSAGE(object_set.size() == 1, "no other values should be added");
152*cb88c51cSIlya Isaev     }
153*cb88c51cSIlya Isaev }
154