mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-12-07 09:39:01 +08:00
test(core):添加Sieve缓存测试
This commit is contained in:
parent
2db66af020
commit
d2d4f8499b
155
hutool-core/src/test/java/cn/hutool/v7/core/cache/SieveCacheTest.java
vendored
Normal file
155
hutool-core/src/test/java/cn/hutool/v7/core/cache/SieveCacheTest.java
vendored
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2025 Hutool Team and hutool.cn
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cn.hutool.v7.core.cache;
|
||||||
|
|
||||||
|
import cn.hutool.v7.core.cache.impl.SieveCache;
|
||||||
|
import cn.hutool.v7.core.thread.ThreadUtil;
|
||||||
|
import cn.hutool.v7.core.util.RandomUtil;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SIEVE 缓存算法单元测试
|
||||||
|
*/
|
||||||
|
public class SieveCacheTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void evictionLogicTest() {
|
||||||
|
SieveCache<String, String> cache = new SieveCache<>(3);
|
||||||
|
|
||||||
|
cache.put("A", "A");
|
||||||
|
cache.put("B", "B");
|
||||||
|
cache.put("C", "C");
|
||||||
|
|
||||||
|
cache.get("A");
|
||||||
|
|
||||||
|
cache.put("D", "D");
|
||||||
|
|
||||||
|
Assertions.assertEquals("A", cache.get("A"));
|
||||||
|
Assertions.assertEquals("C", cache.get("C"));
|
||||||
|
Assertions.assertEquals("D", cache.get("D"));
|
||||||
|
|
||||||
|
Assertions.assertNull(cache.get("B"), "B 应该被淘汰,因为它是未访问过的节点");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void expiryTest() {
|
||||||
|
SieveCache<String, String> cache = new SieveCache<>(3);
|
||||||
|
cache.put("k1", "v1", 100);
|
||||||
|
cache.put("k2", "v2", 10000);
|
||||||
|
|
||||||
|
ThreadUtil.sleep(200);
|
||||||
|
|
||||||
|
Assertions.assertNull(cache.get("k1"), "k1 应该过期");
|
||||||
|
Assertions.assertEquals("v2", cache.get("k2"), "k2 应该存在");
|
||||||
|
Assertions.assertEquals(1, cache.size(), "size 应该为 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void listenerTest() {
|
||||||
|
final AtomicInteger removeCount = new AtomicInteger();
|
||||||
|
SieveCache<Integer, Integer> cache = new SieveCache<>(2);
|
||||||
|
|
||||||
|
cache.setListener((key, value) -> {
|
||||||
|
removeCount.incrementAndGet();
|
||||||
|
});
|
||||||
|
|
||||||
|
cache.put(1, 1);
|
||||||
|
cache.put(2, 2);
|
||||||
|
cache.put(3, 3);
|
||||||
|
|
||||||
|
Assertions.assertEquals(1, removeCount.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void concurrencyPressureTest() throws InterruptedException {
|
||||||
|
int threadCount = 20;
|
||||||
|
int loopCount = 2000;
|
||||||
|
int capacity = 100;
|
||||||
|
|
||||||
|
final SieveCache<String, String> cache = new SieveCache<>(capacity);
|
||||||
|
final CountDownLatch latch = new CountDownLatch(threadCount);
|
||||||
|
final AtomicInteger errorCount = new AtomicInteger(0);
|
||||||
|
|
||||||
|
for (int i = 0; i < threadCount; i++) {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
for (int j = 0; j < loopCount; j++) {
|
||||||
|
String key = String.valueOf(RandomUtil.randomInt(0, 1000));
|
||||||
|
if (RandomUtil.randomBoolean()) {
|
||||||
|
cache.put(key, "val-" + key);
|
||||||
|
} else {
|
||||||
|
cache.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
errorCount.incrementAndGet();
|
||||||
|
} finally {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
latch.await();
|
||||||
|
|
||||||
|
Assertions.assertEquals(0, errorCount.get(), "并发执行不应出现异常");
|
||||||
|
Assertions.assertTrue(cache.size() <= capacity, "缓存大小不应超过容量");
|
||||||
|
|
||||||
|
int iteratorCount = 0;
|
||||||
|
for (String ignored : cache) {
|
||||||
|
iteratorCount++;
|
||||||
|
}
|
||||||
|
Assertions.assertEquals(cache.size(), iteratorCount, "迭代器数量与 size() 应一致");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抗扫描能力测试
|
||||||
|
* 如果扫描数据量过大(如 50% 容量)且热点数据无访问,热点数据的保护位会被耗尽,因此这里仅模拟少量数据的扫描攻击。
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void scanResistanceTest() {
|
||||||
|
int capacity = 10;
|
||||||
|
SieveCache<Integer, Integer> cache = new SieveCache<>(capacity);
|
||||||
|
|
||||||
|
// 填满热点数据
|
||||||
|
for (int i = 0; i < capacity; i++) {
|
||||||
|
cache.put(i, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟热点访问
|
||||||
|
for (int i = 0; i < capacity; i++) {
|
||||||
|
cache.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插入 1 个冷数据
|
||||||
|
cache.put(10, 10);
|
||||||
|
|
||||||
|
int retainedHotItems = 0;
|
||||||
|
for (int i = 0; i < capacity; i++) {
|
||||||
|
if (cache.get(i) != null) {
|
||||||
|
retainedHotItems++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assertions.assertNull(cache.get(10), "冷数据 (10) 应该被淘汰");
|
||||||
|
Assertions.assertEquals(capacity, retainedHotItems, "所有热点数据 (0-9) 应该被保留");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user