IDFileSyncBehaviorTest.java
/*******************************************************************************
* Copyright (c) 2025 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.sail.nativerdf.datastore;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.File;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import org.eclipse.rdf4j.common.io.NioFile;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
/**
* Verifies that IDFile.sync(boolean) passes the caller's boolean flag through to FileChannel#force(boolean), ensuring
* metadata can be flushed when requested.
*/
public class IDFileSyncBehaviorTest {
@TempDir
File tempDir;
@Test
public void syncTrueHonorsMetadataFlag() throws Exception {
File file = new File(tempDir, "values.id");
try (IDFile id = new IDFile(file, /* forceSync= */ false)) {
TrackingFileChannel tracker = injectTrackingChannel(id);
id.sync(true);
assertThat(tracker.forceTrueCount)
.as("IDFile.sync(true) should call force(true) on the underlying channel")
.isGreaterThan(0);
}
}
private static TrackingFileChannel injectTrackingChannel(IDFile id) throws Exception {
Field nioFileField = IDFile.class.getDeclaredField("nioFile");
nioFileField.setAccessible(true);
NioFile nio = (NioFile) nioFileField.get(id);
Field fcField = NioFile.class.getDeclaredField("fc");
fcField.setAccessible(true);
FileChannel delegate = (FileChannel) fcField.get(nio);
TrackingFileChannel tracking = new TrackingFileChannel(delegate);
fcField.set(nio, tracking);
return tracking;
}
static class TrackingFileChannel extends FileChannel {
final FileChannel delegate;
volatile int forceTrueCount = 0;
volatile int forceFalseCount = 0;
TrackingFileChannel(FileChannel delegate) {
this.delegate = delegate;
}
@Override
public void force(boolean metaData) throws java.io.IOException {
if (metaData) {
forceTrueCount++;
} else {
forceFalseCount++;
}
delegate.force(metaData);
}
@Override
public int read(ByteBuffer dst) throws java.io.IOException {
return delegate.read(dst);
}
@Override
public long read(ByteBuffer[] dsts, int offset, int length) throws java.io.IOException {
return delegate.read(dsts, offset, length);
}
@Override
public int write(ByteBuffer src) throws java.io.IOException {
return delegate.write(src);
}
@Override
public long write(ByteBuffer[] srcs, int offset, int length) throws java.io.IOException {
return delegate.write(srcs, offset, length);
}
@Override
public long position() throws java.io.IOException {
return delegate.position();
}
@Override
public FileChannel position(long newPosition) throws java.io.IOException {
delegate.position(newPosition);
return this;
}
@Override
public long size() throws java.io.IOException {
return delegate.size();
}
@Override
public FileChannel truncate(long size) throws java.io.IOException {
delegate.truncate(size);
return this;
}
@Override
protected void implCloseChannel() throws java.io.IOException {
delegate.close();
}
@Override
public int read(ByteBuffer dst, long position) throws java.io.IOException {
return delegate.read(dst, position);
}
@Override
public int write(ByteBuffer src, long position) throws java.io.IOException {
return delegate.write(src, position);
}
@Override
public long transferTo(long position, long count, java.nio.channels.WritableByteChannel target)
throws java.io.IOException {
return delegate.transferTo(position, count, target);
}
@Override
public long transferFrom(java.nio.channels.ReadableByteChannel src, long position, long count)
throws java.io.IOException {
return delegate.transferFrom(src, position, count);
}
@Override
public MappedByteBuffer map(MapMode mode, long position, long size) throws java.io.IOException {
return delegate.map(mode, position, size);
}
@Override
public FileLock lock(long position, long size, boolean shared) throws java.io.IOException {
return delegate.lock(position, size, shared);
}
@Override
public FileLock tryLock(long position, long size, boolean shared) throws java.io.IOException {
return delegate.tryLock(position, size, shared);
}
}
}