Add Java JNI round-trip test
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6111495..979e43c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml
@@ -65,6 +65,17 @@ steps: - uses: actions/checkout@v4 + - name: Rust cache + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + - name: Set up JDK 8 uses: actions/setup-java@v4 with: @@ -72,8 +83,13 @@ java-version: '8' cache: maven + - name: Build JNI library + run: cargo build --release -p paimon-ftindex-jni + - name: Test Java API run: mvn -q -f java/pom.xml test + env: + PAIMON_FTINDEX_JNI_LIB_PATH: ${{ github.workspace }}/target/release/libpaimon_ftindex_jni.so python: runs-on: ubuntu-latest
diff --git a/README.md b/README.md index 6c0f2a8..c265aad 100644 --- a/README.md +++ b/README.md
@@ -20,7 +20,7 @@ - C FFI writer/reader/search JSON. - Java API and JNI bridge. - Python ctypes package. -- Cross-boundary round-trip tests for Rust core, FFI, and Python. +- Cross-boundary round-trip tests for Rust core, FFI, Java/JNI, and Python. Supported tokenizers in this first implementation: @@ -44,7 +44,7 @@ cargo test -p paimon-ftindex-core cargo test -p paimon-ftindex-ffi cargo build -p paimon-ftindex-ffi -cargo check -p paimon-ftindex-jni +cargo build -p paimon-ftindex-jni mvn -q -f java/pom.xml test PYTHONPATH=python python3 -m pytest -q python/tests ```
diff --git a/java/pom.xml b/java/pom.xml index 7bf9eca..0354201 100644 --- a/java/pom.xml +++ b/java/pom.xml
@@ -14,4 +14,13 @@ <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.13.2</version> + <scope>test</scope> + </dependency> + </dependencies> </project>
diff --git a/java/src/test/java/org/apache/paimon/index/fulltext/FullTextNativeRoundTripTest.java b/java/src/test/java/org/apache/paimon/index/fulltext/FullTextNativeRoundTripTest.java new file mode 100644 index 0000000..c5c657a --- /dev/null +++ b/java/src/test/java/org/apache/paimon/index/fulltext/FullTextNativeRoundTripTest.java
@@ -0,0 +1,64 @@ +package org.apache.paimon.index.fulltext; + +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; + +public class FullTextNativeRoundTripTest { + + @Test + public void testJavaNativeRoundTrip() throws Exception { + assumeTrue( + "PAIMON_FTINDEX_JNI_LIB_PATH must point to the built JNI library", + nativeLibraryConfigured()); + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + try (FullTextIndexWriter writer = FullTextIndexWriter.create(Collections.emptyMap())) { + writer.addDocument(10L, "Apache Paimon full text search"); + writer.addDocument(11L, "Tantivy only"); + writer.writeIndex(output::write); + } + + byte[] indexBytes = output.toByteArray(); + FullTextIndexInput input = + (position, buffer, offset, length) -> + readAt(indexBytes, position, buffer, offset, length); + + try (FullTextIndexReader reader = new FullTextIndexReader(input)) { + FullTextSearchResult result = reader.search(FullTextQuery.match("paimon", "text"), 10); + + assertEquals(1, result.size()); + assertEquals(10L, result.rowIds()[0]); + assertTrue(result.scores()[0] > 0.0f); + + try { + reader.search(FullTextQuery.match("paimon", "text"), 0); + fail("Expected non-positive search limit to fail"); + } catch (IllegalArgumentException expected) { + assertEquals("search limit must be positive", expected.getMessage()); + } + } + } + + private static boolean nativeLibraryConfigured() { + String path = System.getenv("PAIMON_FTINDEX_JNI_LIB_PATH"); + return path != null && !path.isEmpty() && new File(path).isFile(); + } + + private static void readAt( + byte[] source, long position, byte[] buffer, int offset, int length) throws IOException { + long end = position + length; + if (position < 0 || end > source.length || end < position) { + throw new IOException("read past end of index bytes"); + } + System.arraycopy(source, (int) position, buffer, offset, length); + } +}