Skip to content

Commit ed48d4e

Browse files
glasstigerclaude
andcommitted
Assert the facade length injection drove the mapping
The routed header-fault test asserted only that recovery returned no ring. But SegmentRing.openExisting rebuilds the segment path as sfDir + "/" + name and swallows the per-file MmapSegmentException, so the test could no longer see why the skip happened. If the facade's targetPath ever stopped matching that rebuilt path, openExisting would read the real length 0, throw "file shorter than header" before the mmap-fault guard, still skip, and still return null -- the test would pass even with the guard reverted, silently gutting the only portable fail-on-revert guard. MapPastEofFacade now records whether it served the inflated length for its target path, and the test asserts it did. That proves the beyond-EOF fault, not a short-file throw, drove the skip, restoring the structural guarantee the pre-routing reference-identity form had. A mutation that breaks the path match now fails loudly at the assertion instead of passing green (verified: it fails at the new assertTrue with the coupling broken, and passes on JDK 11 and JDK 17 intact). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 8c53638 commit ed48d4e

1 file changed

Lines changed: 19 additions & 2 deletions

File tree

core/src/test/java/io/questdb/client/test/cutlass/qwp/client/sf/cursor/MmapSegmentRecoveryFaultTest.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,11 +317,20 @@ public void testHeaderFaultOnMapPastEofIsSkippableAnyFilesystem() throws Excepti
317317
// facade reports a full page, so openExisting maps that beyond-EOF
318318
// page and the header read faults on it on any filesystem.
319319
truncateTo(path, 0L);
320-
FilesFacade ff = new MapPastEofFacade(path, page);
320+
MapPastEofFacade ff = new MapPastEofFacade(path, page);
321321
// SegmentRing opens the sole segment through the facade, catches the
322322
// converted MmapSegmentException, skips the file, and returns no ring.
323323
assertNull("a beyond-EOF header page must be skipped, not recovered or fatal",
324324
SegmentRing.openExisting(ff, tmpDir, SEGMENT_BYTES));
325+
// Guard against a silent pass: the skip must be the beyond-EOF mmap
326+
// fault, not a "file shorter than header" throw. That holds only if
327+
// the facade's inflated length actually reached the recovery mapping
328+
// -- i.e. the path SegmentRing rebuilds (sfDir + "/" + name) matched
329+
// targetPath. If that coupling ever broke, openExisting would see the
330+
// real length 0 and skip via a throw that survives reverting the
331+
// mmap-fault guard, gutting this fail-on-revert guard unnoticed.
332+
assertTrue("the injected beyond-EOF length must have driven the recovery mapping",
333+
ff.isInflatedLengthServed);
325334
});
326335
}
327336

@@ -394,6 +403,10 @@ private static void truncateTo(String path, long keepBytes) {
394403
* {@link FilesFacade#INSTANCE}.
395404
*/
396405
private static final class MapPastEofFacade implements FilesFacade {
406+
// Set once length() has served the inflated length for targetPath, i.e.
407+
// the injection actually reached the recovery mapping. A test asserts
408+
// this so a broken path match cannot let the guard pass vacuously.
409+
private boolean isInflatedLengthServed;
397410
private final long reportedLength;
398411
private final String targetPath;
399412

@@ -469,7 +482,11 @@ public long length(long pathPtr) {
469482

470483
@Override
471484
public long length(String path) {
472-
return targetPath.equals(path) ? reportedLength : INSTANCE.length(path);
485+
if (targetPath.equals(path)) {
486+
isInflatedLengthServed = true;
487+
return reportedLength;
488+
}
489+
return INSTANCE.length(path);
473490
}
474491

475492
@Override

0 commit comments

Comments
 (0)