2021-09-23 02:15:58 +02:00
|
|
|
package it.cavallium.dbengine;
|
|
|
|
|
|
|
|
import static it.cavallium.dbengine.DbTestUtils.destroyAllocator;
|
|
|
|
import static it.cavallium.dbengine.DbTestUtils.ensureNoLeaks;
|
|
|
|
import static it.cavallium.dbengine.DbTestUtils.newAllocator;
|
2022-01-26 19:03:51 +01:00
|
|
|
import static it.cavallium.dbengine.SyncUtils.run;
|
|
|
|
import static it.cavallium.dbengine.SyncUtils.runVoid;
|
2021-09-23 02:15:58 +02:00
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
|
|
2022-03-16 13:47:56 +01:00
|
|
|
import io.netty5.buffer.api.Buffer;
|
2022-05-20 10:20:00 +02:00
|
|
|
import io.netty5.buffer.api.Resource;
|
2022-03-16 13:47:56 +01:00
|
|
|
import io.netty5.buffer.api.Send;
|
2021-09-23 02:15:58 +02:00
|
|
|
import it.cavallium.dbengine.DbTestUtils.TempDb;
|
|
|
|
import it.cavallium.dbengine.DbTestUtils.TestAllocator;
|
|
|
|
import it.cavallium.dbengine.database.LLDictionary;
|
|
|
|
import it.cavallium.dbengine.database.LLDictionaryResultType;
|
|
|
|
import it.cavallium.dbengine.database.LLKeyValueDatabase;
|
|
|
|
import it.cavallium.dbengine.database.LLRange;
|
|
|
|
import it.cavallium.dbengine.database.LLUtils;
|
|
|
|
import it.cavallium.dbengine.database.UpdateMode;
|
|
|
|
import it.cavallium.dbengine.database.UpdateReturnMode;
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.Objects;
|
|
|
|
import java.util.stream.Stream;
|
2022-01-04 12:55:33 +01:00
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
|
|
import org.apache.logging.log4j.Logger;
|
2021-09-23 02:15:58 +02:00
|
|
|
import org.junit.jupiter.api.AfterEach;
|
|
|
|
import org.junit.jupiter.api.Assertions;
|
|
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
import org.junit.jupiter.params.ParameterizedTest;
|
|
|
|
import org.junit.jupiter.params.provider.Arguments;
|
|
|
|
import org.junit.jupiter.params.provider.MethodSource;
|
|
|
|
import reactor.core.publisher.Mono;
|
|
|
|
|
|
|
|
public abstract class TestLLDictionary {
|
|
|
|
|
2022-01-04 12:55:33 +01:00
|
|
|
private final Logger log = LogManager.getLogger(this.getClass());
|
2022-05-20 10:20:00 +02:00
|
|
|
private static final Mono<LLRange> RANGE_ALL = Mono.fromCallable(LLRange::all);
|
2021-09-23 02:15:58 +02:00
|
|
|
private TestAllocator allocator;
|
|
|
|
private TempDb tempDb;
|
|
|
|
private LLKeyValueDatabase db;
|
|
|
|
|
|
|
|
protected abstract TemporaryDbGenerator getTempDbGenerator();
|
|
|
|
|
|
|
|
@BeforeEach
|
|
|
|
public void beforeEach() {
|
|
|
|
this.allocator = newAllocator();
|
|
|
|
ensureNoLeaks(allocator.allocator(), false, false);
|
|
|
|
tempDb = Objects.requireNonNull(getTempDbGenerator().openTempDb(allocator).block(), "TempDB");
|
|
|
|
db = tempDb.db();
|
|
|
|
}
|
|
|
|
|
|
|
|
@AfterEach
|
|
|
|
public void afterEach() {
|
|
|
|
getTempDbGenerator().closeTempDb(tempDb).block();
|
|
|
|
ensureNoLeaks(allocator.allocator(), true, false);
|
|
|
|
destroyAllocator(allocator);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Stream<Arguments> provideArguments() {
|
|
|
|
return Arrays.stream(UpdateMode.values()).map(Arguments::of);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Stream<Arguments> providePutArguments() {
|
|
|
|
var updateModes = Arrays.stream(UpdateMode.values());
|
|
|
|
return updateModes.flatMap(updateMode -> {
|
|
|
|
var resultTypes = Arrays.stream(LLDictionaryResultType.values());
|
|
|
|
return resultTypes.map(resultType -> Arguments.of(updateMode, resultType));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Stream<Arguments> provideUpdateArguments() {
|
|
|
|
var updateModes = Arrays.stream(UpdateMode.values());
|
|
|
|
return updateModes.flatMap(updateMode -> {
|
|
|
|
var resultTypes = Arrays.stream(UpdateReturnMode.values());
|
|
|
|
return resultTypes.map(resultType -> Arguments.of(updateMode, resultType));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private LLDictionary getDict(UpdateMode updateMode) {
|
|
|
|
var dict = DbTestUtils.tempDictionary(db, updateMode).blockOptional().orElseThrow();
|
2022-05-20 10:20:00 +02:00
|
|
|
var key1 = Mono.fromCallable(() -> fromString("test-key-1"));
|
|
|
|
var key2 = Mono.fromCallable(() -> fromString("test-key-2"));
|
|
|
|
var key3 = Mono.fromCallable(() -> fromString("test-key-3"));
|
|
|
|
var key4 = Mono.fromCallable(() -> fromString("test-key-4"));
|
|
|
|
var value = Mono.fromCallable(() -> fromString("test-value"));
|
2021-09-23 02:15:58 +02:00
|
|
|
dict.put(key1, value, LLDictionaryResultType.VOID).block();
|
|
|
|
dict.put(key2, value, LLDictionaryResultType.VOID).block();
|
|
|
|
dict.put(key3, value, LLDictionaryResultType.VOID).block();
|
|
|
|
dict.put(key4, value, LLDictionaryResultType.VOID).block();
|
|
|
|
return dict;
|
|
|
|
}
|
|
|
|
|
2021-11-08 16:33:41 +01:00
|
|
|
private Buffer fromString(String s) {
|
2021-09-23 02:15:58 +02:00
|
|
|
var sb = s.getBytes(StandardCharsets.UTF_8);
|
|
|
|
try (var b = db.getAllocator().allocate(sb.length + 3 + 13)) {
|
|
|
|
assert b.writerOffset() == 0;
|
|
|
|
assert b.readerOffset() == 0;
|
|
|
|
b.writerOffset(3).writeBytes(sb);
|
|
|
|
b.readerOffset(3);
|
|
|
|
assert b.readableBytes() == sb.length;
|
|
|
|
|
|
|
|
var part1 = b.split();
|
|
|
|
|
2021-11-08 16:33:41 +01:00
|
|
|
return LLUtils.compositeBuffer(db.getAllocator(), part1.send(), b.send());
|
2021-09-23 02:15:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-20 10:20:00 +02:00
|
|
|
private String toString(Buffer bb) {
|
|
|
|
try (bb) {
|
2021-09-23 02:15:58 +02:00
|
|
|
byte[] data = new byte[bb.readableBytes()];
|
|
|
|
bb.copyInto(bb.readerOffset(), data, 0, data.length);
|
|
|
|
return new String(data, StandardCharsets.UTF_8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testNoOp() {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testNoOpAllocation() {
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
|
var a = allocator.allocator().allocate(i * 512);
|
|
|
|
a.send().receive().close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
@MethodSource("provideArguments")
|
|
|
|
public void testGetDict(UpdateMode updateMode) {
|
|
|
|
var dict = getDict(updateMode);
|
|
|
|
Assertions.assertNotNull(dict);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
@MethodSource("provideArguments")
|
|
|
|
public void testGetColumnName(UpdateMode updateMode) {
|
|
|
|
var dict = getDict(updateMode);
|
|
|
|
Assertions.assertEquals("hash_map_testmap", dict.getColumnName());
|
|
|
|
}
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
@MethodSource("provideArguments")
|
|
|
|
public void testGetAllocator(UpdateMode updateMode) {
|
|
|
|
var dict = getDict(updateMode);
|
|
|
|
var alloc = dict.getAllocator();
|
|
|
|
Assertions.assertEquals(alloc, alloc);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
@MethodSource("provideArguments")
|
|
|
|
public void testGet(UpdateMode updateMode) {
|
|
|
|
var dict = getDict(updateMode);
|
2022-05-20 10:20:00 +02:00
|
|
|
var keyEx = Mono.fromCallable(() -> fromString("test-key-1"));
|
|
|
|
var keyNonEx = Mono.fromCallable(() -> fromString("test-nonexistent"));
|
2022-01-26 14:22:54 +01:00
|
|
|
Assertions.assertEquals("test-value", run(dict.get(null, keyEx).map(this::toString)));
|
2022-03-24 21:14:17 +01:00
|
|
|
Assertions.assertEquals("test-value", run(dict.get(null, keyEx).map(this::toString)));
|
|
|
|
Assertions.assertEquals("test-value", run(dict.get(null, keyEx).map(this::toString)));
|
|
|
|
Assertions.assertEquals((String) null, run(dict.get(null, keyNonEx).map(this::toString)));
|
|
|
|
Assertions.assertEquals((String) null, run(dict.get(null, keyNonEx).map(this::toString)));
|
2022-01-26 14:22:54 +01:00
|
|
|
Assertions.assertEquals((String) null, run(dict.get(null, keyNonEx).map(this::toString)));
|
2021-09-23 02:15:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
@MethodSource("providePutArguments")
|
|
|
|
public void testPutExisting(UpdateMode updateMode, LLDictionaryResultType resultType) {
|
|
|
|
var dict = getDict(updateMode);
|
2022-05-20 10:20:00 +02:00
|
|
|
var keyEx = Mono.fromCallable(() -> fromString("test-key-1"));
|
|
|
|
var value = Mono.fromCallable(() -> fromString("test-value"));
|
2021-09-23 02:15:58 +02:00
|
|
|
|
|
|
|
var beforeSize = run(dict.sizeRange(null, RANGE_ALL, false));
|
|
|
|
|
2022-05-20 10:20:00 +02:00
|
|
|
runVoid(dict.put(keyEx, value, resultType).then().doOnDiscard(Resource.class, Resource::close));
|
2021-09-23 02:15:58 +02:00
|
|
|
|
|
|
|
var afterSize = run(dict.sizeRange(null, RANGE_ALL, false));
|
|
|
|
Assertions.assertEquals(0, afterSize - beforeSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
@MethodSource("providePutArguments")
|
|
|
|
public void testPutNew(UpdateMode updateMode, LLDictionaryResultType resultType) {
|
|
|
|
var dict = getDict(updateMode);
|
2022-05-20 10:20:00 +02:00
|
|
|
var keyNonEx = Mono.fromCallable(() -> fromString("test-nonexistent"));
|
|
|
|
var value = Mono.fromCallable(() -> fromString("test-value"));
|
2021-09-23 02:15:58 +02:00
|
|
|
|
|
|
|
var beforeSize = run(dict.sizeRange(null, RANGE_ALL, false));
|
|
|
|
|
2022-05-20 10:20:00 +02:00
|
|
|
runVoid(dict.put(keyNonEx, value, resultType).then().doOnDiscard(Resource.class, Resource::close));
|
2021-09-23 02:15:58 +02:00
|
|
|
|
2022-05-20 10:20:00 +02:00
|
|
|
var afterSize = run(dict.sizeRange(null, Mono.fromCallable(LLRange::all), false));
|
2021-09-23 02:15:58 +02:00
|
|
|
Assertions.assertEquals(1, afterSize - beforeSize);
|
|
|
|
|
2022-03-24 23:56:23 +01:00
|
|
|
Assertions.assertTrue(run(dict.getRangeKeys(null, RANGE_ALL, false, false).map(this::toString).collectList()).contains("test-nonexistent"));
|
|
|
|
Assertions.assertTrue(run(dict.getRangeKeys(null, RANGE_ALL, true, false).map(this::toString).collectList()).contains("test-nonexistent"));
|
2021-09-23 02:15:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
@MethodSource("provideArguments")
|
|
|
|
public void testGetUpdateMode(UpdateMode updateMode) {
|
|
|
|
var dict = getDict(updateMode);
|
|
|
|
assertEquals(updateMode, run(dict.getUpdateMode()));
|
|
|
|
}
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
@MethodSource("provideUpdateArguments")
|
|
|
|
public void testUpdateExisting(UpdateMode updateMode, UpdateReturnMode updateReturnMode) {
|
|
|
|
var dict = getDict(updateMode);
|
2022-05-20 10:20:00 +02:00
|
|
|
var keyEx = Mono.fromCallable(() -> fromString("test-key-1"));
|
2021-09-23 02:15:58 +02:00
|
|
|
var beforeSize = run(dict.sizeRange(null, RANGE_ALL, false));
|
|
|
|
long afterSize;
|
|
|
|
runVoid(updateMode == UpdateMode.DISALLOW,
|
2022-05-20 10:20:00 +02:00
|
|
|
dict.update(keyEx, old -> fromString("test-value"), updateReturnMode).doOnNext(Resource::close).then()
|
2021-09-23 02:15:58 +02:00
|
|
|
);
|
|
|
|
afterSize = run(dict.sizeRange(null, RANGE_ALL, false));
|
|
|
|
assertEquals(0, afterSize - beforeSize);
|
|
|
|
runVoid(updateMode == UpdateMode.DISALLOW,
|
2022-05-20 10:20:00 +02:00
|
|
|
dict.update(keyEx, old -> fromString("test-value"), updateReturnMode).doOnNext(Resource::close).then()
|
2021-09-23 02:15:58 +02:00
|
|
|
);
|
|
|
|
afterSize = run(dict.sizeRange(null, RANGE_ALL, false));
|
|
|
|
assertEquals(0, afterSize - beforeSize);
|
|
|
|
runVoid(updateMode == UpdateMode.DISALLOW,
|
2022-05-20 10:20:00 +02:00
|
|
|
dict.update(keyEx, old -> fromString("test-value"), updateReturnMode).doOnNext(Resource::close).then()
|
2021-09-23 02:15:58 +02:00
|
|
|
);
|
|
|
|
afterSize = run(dict.sizeRange(null, RANGE_ALL, false));
|
|
|
|
assertEquals(0, afterSize - beforeSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
@MethodSource("provideUpdateArguments")
|
|
|
|
public void testUpdateNew(UpdateMode updateMode, UpdateReturnMode updateReturnMode) {
|
|
|
|
int expected = updateMode == UpdateMode.DISALLOW ? 0 : 1;
|
|
|
|
var dict = getDict(updateMode);
|
2022-05-20 10:20:00 +02:00
|
|
|
var keyNonEx = Mono.fromCallable(() -> fromString("test-nonexistent"));
|
2021-09-23 02:15:58 +02:00
|
|
|
var beforeSize = run(dict.sizeRange(null, RANGE_ALL, false));
|
|
|
|
long afterSize;
|
|
|
|
runVoid(updateMode == UpdateMode.DISALLOW,
|
2022-05-20 10:20:00 +02:00
|
|
|
dict.update(keyNonEx, old -> fromString("test-value"), updateReturnMode).doOnNext(Resource::close).then()
|
2021-09-23 02:15:58 +02:00
|
|
|
);
|
|
|
|
afterSize = run(dict.sizeRange(null, RANGE_ALL, false));
|
|
|
|
assertEquals(expected, afterSize - beforeSize);
|
|
|
|
runVoid(updateMode == UpdateMode.DISALLOW,
|
2022-05-20 10:20:00 +02:00
|
|
|
dict.update(keyNonEx, old -> fromString("test-value"), updateReturnMode).doOnNext(Resource::close).then()
|
2021-09-23 02:15:58 +02:00
|
|
|
);
|
|
|
|
afterSize = run(dict.sizeRange(null, RANGE_ALL, false));
|
|
|
|
assertEquals(expected, afterSize - beforeSize);
|
|
|
|
runVoid(updateMode == UpdateMode.DISALLOW,
|
2022-05-20 10:20:00 +02:00
|
|
|
dict.update(keyNonEx, old -> fromString("test-value"), updateReturnMode).doOnNext(Resource::close).then()
|
2021-09-23 02:15:58 +02:00
|
|
|
);
|
|
|
|
afterSize = run(dict.sizeRange(null, RANGE_ALL, false));
|
|
|
|
assertEquals(expected, afterSize - beforeSize);
|
|
|
|
|
|
|
|
if (updateMode != UpdateMode.DISALLOW) {
|
2022-03-24 23:56:23 +01:00
|
|
|
Assertions.assertTrue(run(dict.getRangeKeys(null, RANGE_ALL, false, false).map(this::toString).collectList()).contains(
|
2022-03-24 21:14:17 +01:00
|
|
|
"test-nonexistent"));
|
2022-03-24 23:56:23 +01:00
|
|
|
Assertions.assertTrue(run(dict.getRangeKeys(null, RANGE_ALL, true, false).map(this::toString).collectList()).contains(
|
2021-09-23 02:15:58 +02:00
|
|
|
"test-nonexistent"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
@MethodSource("provideArguments")
|
|
|
|
public void testUpdateAndGetDelta(UpdateMode updateMode) {
|
|
|
|
log.warn("Test not implemented");
|
|
|
|
//todo: implement
|
|
|
|
}
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
@MethodSource("provideArguments")
|
|
|
|
public void testClear(UpdateMode updateMode) {
|
|
|
|
log.warn("Test not implemented");
|
|
|
|
//todo: implement
|
|
|
|
}
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
@MethodSource("providePutArguments")
|
|
|
|
public void testRemove(UpdateMode updateMode, LLDictionaryResultType resultType) {
|
|
|
|
log.warn("Test not implemented");
|
|
|
|
//todo: implement
|
|
|
|
}
|
|
|
|
}
|