package it.cavallium.dbengine; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import io.netty.buffer.ByteBuf; import it.cavallium.dbengine.DbTestUtils.TempDb; import it.cavallium.dbengine.database.LLDictionary; import it.cavallium.dbengine.database.LLDictionaryResultType; import it.cavallium.dbengine.database.LLKeyValueDatabase; import it.cavallium.dbengine.database.LLUtils; import it.cavallium.dbengine.database.UpdateMode; import it.cavallium.dbengine.database.UpdateReturnMode; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Objects; import java.util.concurrent.Flow.Publisher; import java.util.stream.Stream; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; 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.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; public class TestLLDictionaryLeaks { private TempDb tempDb; private LLKeyValueDatabase db; @BeforeEach public void beforeEach() { tempDb = Objects.requireNonNull(DbTestUtils.openTempDb().block(), "TempDB"); db = tempDb.db(); } public static Stream provideArguments() { return Arrays.stream(UpdateMode.values()).map(Arguments::of); } public static Stream 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 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).block(); 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")); 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; } private ByteBuf fromString(String s) { var sb = s.getBytes(StandardCharsets.UTF_8); var b = db.getAllocator().buffer(sb.length); b.writeBytes(b); return b; } private void run(Flux publisher) { publisher.subscribeOn(Schedulers.immediate()).blockLast(); } private void runVoid(Mono publisher) { publisher.then().subscribeOn(Schedulers.immediate()).block(); } private T run(Mono publisher) { return publisher.subscribeOn(Schedulers.immediate()).block(); } private T run(boolean shouldFail, Mono publisher) { return publisher.subscribeOn(Schedulers.immediate()).transform(mono -> { if (shouldFail) { return mono.onErrorResume(ex -> Mono.empty()); } else { return mono; } }).block(); } private void runVoid(boolean shouldFail, Mono publisher) { publisher.then().subscribeOn(Schedulers.immediate()).transform(mono -> { if (shouldFail) { return mono.onErrorResume(ex -> Mono.empty()); } else { return mono; } }).block(); } @Test public void testNoOp() { } @ParameterizedTest @MethodSource("provideArguments") public void testGetDict(UpdateMode updateMode) { var dict = getDict(updateMode); } @ParameterizedTest @MethodSource("provideArguments") public void testGetColumnName(UpdateMode updateMode) { var dict = getDict(updateMode); dict.getColumnName(); } @ParameterizedTest @MethodSource("provideArguments") public void testGetAllocator(UpdateMode updateMode) { var dict = getDict(updateMode); dict.getAllocator(); } @ParameterizedTest @MethodSource("provideArguments") public void testGet(UpdateMode updateMode) { var dict = getDict(updateMode); var key = Mono.fromCallable(() -> fromString("test")); runVoid(dict.get(null, key).then().transform(LLUtils::handleDiscard)); runVoid(dict.get(null, key, true).then().transform(LLUtils::handleDiscard)); runVoid(dict.get(null, key, false).then().transform(LLUtils::handleDiscard)); } @ParameterizedTest @MethodSource("providePutArguments") public void testPut(UpdateMode updateMode, LLDictionaryResultType resultType) { var dict = getDict(updateMode); var key = Mono.fromCallable(() -> fromString("test-key")); var value = Mono.fromCallable(() -> fromString("test-value")); runVoid(dict.put(key, value, resultType).then()); } @ParameterizedTest @MethodSource("provideArguments") public void testGetUpdateMode(UpdateMode updateMode) { var dict = getDict(updateMode); assertEquals(updateMode, run(dict.getUpdateMode())); } @ParameterizedTest @MethodSource("provideUpdateArguments") public void testUpdate(UpdateMode updateMode, UpdateReturnMode updateReturnMode) { var dict = getDict(updateMode); var key = Mono.fromCallable(() -> fromString("test-key")); runVoid(updateMode == UpdateMode.DISALLOW, dict.update(key, old -> old, updateReturnMode, true).then().transform(LLUtils::handleDiscard) ); runVoid(updateMode == UpdateMode.DISALLOW, dict.update(key, old -> old, updateReturnMode, false).then().transform(LLUtils::handleDiscard) ); runVoid(updateMode == UpdateMode.DISALLOW, dict.update(key, old -> old, updateReturnMode).then().transform(LLUtils::handleDiscard) ); } @ParameterizedTest @MethodSource("provideArguments") public void testUpdateAndGetDelta(UpdateMode updateMode) { var dict = getDict(updateMode); var key = Mono.fromCallable(() -> fromString("test-key")); runVoid(updateMode == UpdateMode.DISALLOW, dict.updateAndGetDelta(key, old -> old, true).then().transform(LLUtils::handleDiscard) ); runVoid(updateMode == UpdateMode.DISALLOW, dict.updateAndGetDelta(key, old -> old, false).then().transform(LLUtils::handleDiscard) ); runVoid(updateMode == UpdateMode.DISALLOW, dict.updateAndGetDelta(key, old -> old).then().transform(LLUtils::handleDiscard) ); } @ParameterizedTest @MethodSource("provideArguments") public void testClear(UpdateMode updateMode) { var dict = getDict(updateMode); runVoid(dict.clear()); } @ParameterizedTest @MethodSource("providePutArguments") public void testRemove(UpdateMode updateMode, LLDictionaryResultType resultType) { var dict = getDict(updateMode); var key = Mono.fromCallable(() -> fromString("test-key")); runVoid(dict.remove(key, resultType).then()); } @AfterEach public void afterEach() { DbTestUtils.closeTempDb(tempDb).block(); } }