This is an automated email from the ASF dual-hosted git repository.

afs pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git

commit c856c0c5ea3bd93da523d24f101795164dab9842
Author: Andy Seaborne <[email protected]>
AuthorDate: Thu Apr 30 11:23:22 2026 +0100

    GH-3877: Add truncation tests to o.a.j.dboe.base.file.TS_File
---
 .../org/apache/jena/dboe/base/file/TS_File.java    |   2 +
 .../base/file/TestBinaryDataFileRandomAccess.java  | 186 +++++++++++++++++++++
 .../TestBinaryDataFileRandomAccessTruncate.java    |  13 +-
 3 files changed, 195 insertions(+), 6 deletions(-)

diff --git 
a/jena-db/jena-dboe-base/src/test/java/org/apache/jena/dboe/base/file/TS_File.java
 
b/jena-db/jena-dboe-base/src/test/java/org/apache/jena/dboe/base/file/TS_File.java
index f5aa2a7f2e..f5f29b06d0 100644
--- 
a/jena-db/jena-dboe-base/src/test/java/org/apache/jena/dboe/base/file/TS_File.java
+++ 
b/jena-db/jena-dboe-base/src/test/java/org/apache/jena/dboe/base/file/TS_File.java
@@ -41,6 +41,8 @@ import org.junit.platform.suite.api.Suite;
     , TestBinaryDataRAFInitial.class
     , TestBinaryDataRAF.class
     , TestBinaryDataFileWriteBufferedFile.class
+    , TestBinaryDataFileRandomAccessTruncate.class
+    , TestBinaryDataFileRandomAccess.class
 
     , TestProcessFileLock.class
 })
diff --git 
a/jena-db/jena-dboe-base/src/test/java/org/apache/jena/dboe/base/file/TestBinaryDataFileRandomAccess.java
 
b/jena-db/jena-dboe-base/src/test/java/org/apache/jena/dboe/base/file/TestBinaryDataFileRandomAccess.java
new file mode 100644
index 0000000000..fdacd38984
--- /dev/null
+++ 
b/jena-db/jena-dboe-base/src/test/java/org/apache/jena/dboe/base/file/TestBinaryDataFileRandomAccess.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
+ *
+ *   https://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.
+ *
+ *   SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.dboe.base.file;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.nio.file.Path;
+import java.util.Arrays;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import org.apache.jena.atlas.RuntimeIOException;
+
+public class TestBinaryDataFileRandomAccess {
+
+    @TempDir Path tempDir;
+
+    @Test public void bad_truncate() {
+        Path file = tempDir.resolve("truncate1");
+        BinaryDataFileRandomAccess subject = new 
BinaryDataFileRandomAccess(file.toString());
+        subject.open();
+        try {
+            // Put some thing in the file.
+            byte[] block = filled(1024, (byte) 0xAB);
+            subject.write(block);
+            // Attempt to truncate outside the file.
+            assertThrows(RuntimeIOException.class, ()->subject.truncate(2048));
+        } finally { subject.close(); }
+    }
+
+    @Test public void bad_seek_read() {
+        Path file = tempDir.resolve("truncate2");
+        BinaryDataFileRandomAccess subject = new 
BinaryDataFileRandomAccess(file.toString());
+        subject.open();
+        try {
+            byte[] block = filled(1024, (byte) 0xAB);
+            subject.write(block);
+
+            byte[] block2 = new byte[10];
+            // OK
+            subject.read(512, block2);
+            // Attempt to read at end of file.
+            // OK
+            subject.read(1024, block2);
+
+            // Attempt to read starting beyond end of file.
+            assertThrows(RuntimeIOException.class, ()->subject.read(2048, 
block2));
+
+            subject.truncate(512);
+            // Now not OK.
+            assertThrows(RuntimeIOException.class, ()->subject.read(1024, 
block2));
+        } finally { subject.close(); }
+    }
+
+
+    private static byte[] filled(int size, byte v) {
+        byte[] b = new byte[size];
+        Arrays.fill(b, v);
+        return b;
+    }
+
+
+}
+//
+//    private static final String FILE = "target/test-bdfra-truncate-writepos";
+//
+//    private BinaryDataFile file;
+//
+//    @BeforeEach public void before() {
+//        FileOps.delete(FILE);
+//        file = new BinaryDataFileRandomAccess(FILE);
+//        file.open();
+//    }
+//
+//    @AfterEach public void after() {
+//        file.close();
+//        FileOps.delete(FILE);
+//    }
+//
+//    /**
+//     * truncate() then write() (no intervening read) — file must contain only
+//     * the written bytes up to the new length.
+//     */
+//    @Test public void truncate_then_write_no_gap() {
+//        byte[] block = filled(300 * 1024, (byte) 0xAB);
+//        file.write(block);
+//        assertEquals(300 * 1024L, file.length());
+//
+//        file.truncate(100 * 1024);
+//        assertEquals(100 * 1024L, file.length());
+//
+//        byte[] tail = filled(100, (byte) 0xCD);
+//        long writtenAt = file.write(tail);
+//        assertEquals(100 * 1024L, writtenAt,
+//                "write() after truncate() must start at the truncated 
length, not at a stale writePosition");
+//        assertEquals(100 * 1024L + 100, file.length());
+//
+//        assertNoZeroRun(FILE);
+//    }
+//
+//    /**
+//     * truncate() then read() then write() — the read flips the internal
+//     * readMode flag, so the subsequent write goes through switchToWriteMode
+//     * and seeks to the stored writePosition. Without the fix, that position
+//     * is stale and writes past the truncated EOF, zero-filling the gap.
+//     */
+//    @Test public void truncate_then_read_then_write_no_gap() {
+//        byte[] block = filled(300 * 1024, (byte) 0xAB);
+//        file.write(block);
+//
+//        file.truncate(100 * 1024);
+//        assertEquals(100 * 1024L, file.length());
+//
+//        byte[] sample = new byte[16];
+//        file.read(0, sample);
+//
+//        byte[] tail = filled(100, (byte) 0xCD);
+//        long writtenAt = file.write(tail);
+//        assertEquals(100 * 1024L, writtenAt,
+//                "write() after truncate()+read() must start at the truncated 
length, not at a stale writePosition");
+//        assertEquals(100 * 1024L + 100, file.length());
+//
+//        assertNoZeroRun(FILE);
+//    }
+//
+//    private static byte[] filled(int size, byte v) {
+//        byte[] b = new byte[size];
+//        Arrays.fill(b, v);
+//        return b;
+//    }
+//
+//    /**
+//     * Fail the enclosing test if the on-disk file contains a contiguous run
+//     * of zero bytes of length >= 16. The source blocks are all 0xAB/0xCD so
+//     * any zero byte is evidence of filesystem zero-fill past a stale seek.
+//     */
+//    private static void assertNoZeroRun(String path) {
+//        try (RandomAccessFile raf = new RandomAccessFile(path, "r")) {
+//            long total = raf.length();
+//            byte[] buf = new byte[(int) total];
+//            raf.readFully(buf);
+//            int runStart = -1;
+//            int runLen = 0;
+//            for (int i = 0; i < buf.length; i++) {
+//                if (buf[i] == 0) {
+//                    if (runStart < 0) { runStart = i; runLen = 1; }
+//                    else runLen++;
+//                } else {
+//                    if (runLen >= 16) {
+//                        assertTrue(false,
+//                                "Zero-byte run of " + runLen + " bytes 
starting at offset " + runStart
+//                                        + " indicates a filesystem zero-fill 
(stale writePosition after truncate).");
+//                    }
+//                    runStart = -1;
+//                    runLen = 0;
+//                }
+//            }
+//            if (runLen >= 16) {
+//                assertTrue(false,
+//                        "Zero-byte run of " + runLen + " bytes at end of 
file starting at offset " + runStart);
+//            }
+//        } catch (IOException ex) {
+//            throw new RuntimeException(ex);
+//        }
+//    }
+//}
diff --git 
a/jena-db/jena-dboe-base/src/test/java/org/apache/jena/dboe/base/file/TestBinaryDataFileRandomAccessTruncate.java
 
b/jena-db/jena-dboe-base/src/test/java/org/apache/jena/dboe/base/file/TestBinaryDataFileRandomAccessTruncate.java
index 233325ef1a..3aadc2d182 100644
--- 
a/jena-db/jena-dboe-base/src/test/java/org/apache/jena/dboe/base/file/TestBinaryDataFileRandomAccessTruncate.java
+++ 
b/jena-db/jena-dboe-base/src/test/java/org/apache/jena/dboe/base/file/TestBinaryDataFileRandomAccessTruncate.java
@@ -26,12 +26,13 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
 import java.io.RandomAccessFile;
+import java.nio.file.Path;
 import java.util.Arrays;
 
-import org.apache.jena.atlas.lib.FileOps;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
 
 /**
  * Regression test: {@link BinaryDataFileRandomAccess#truncate(long)} must 
reset
@@ -51,19 +52,19 @@ import org.junit.jupiter.api.Test;
  */
 public class TestBinaryDataFileRandomAccessTruncate {
 
-    private static final String FILE = "target/test-bdfra-truncate-writepos";
-
-    private BinaryDataFile file;
+    @TempDir Path tempDir;
+    private BinaryDataFile file = null;
+    private String FILE = null;
+    private static int counter = 0 ;
 
     @BeforeEach public void before() {
-        FileOps.delete(FILE);
+        FILE = tempDir.resolve("truncate-"+(++counter)).toString();
         file = new BinaryDataFileRandomAccess(FILE);
         file.open();
     }
 
     @AfterEach public void after() {
         file.close();
-        FileOps.delete(FILE);
     }
 
     /**

Reply via email to