You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by rm...@apache.org on 2021/10/09 15:56:27 UTC

[lucene] branch main updated: LUCENE-10155: Refactor TestMultiMMap into a BaseChunkedDirectoryTestCase (#360)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 6c6a3bd  LUCENE-10155: Refactor TestMultiMMap into a BaseChunkedDirectoryTestCase (#360)
6c6a3bd is described below

commit 6c6a3bd5bd58579ecf31acee1178141c9138eac6
Author: Robert Muir <rm...@apache.org>
AuthorDate: Sat Oct 9 11:55:41 2021 -0400

    LUCENE-10155: Refactor TestMultiMMap into a BaseChunkedDirectoryTestCase (#360)
    
    BaseChunkedDirectoryTestCase is an extension of BaseDirectoryTestCase
    where the concrete test class instantiates with a specified chunk size.
    It then tries to test boundary conditions around all the chunking.
---
 .../apache/lucene/store/ByteBuffersDataOutput.java |  48 +++-
 .../lucene/store/BaseDataOutputTestCase.java       |   8 +-
 .../lucene/store/TestByteBuffersDataOutput.java    |  78 +++++-
 .../store/TestMultiByteBuffersDirectory.java       |  49 ++++
 .../org/apache/lucene/store/TestMultiMMap.java     | 311 +--------------------
 .../store/BaseChunkedDirectoryTestCase.java}       | 232 ++++-----------
 6 files changed, 228 insertions(+), 498 deletions(-)

diff --git a/lucene/core/src/java/org/apache/lucene/store/ByteBuffersDataOutput.java b/lucene/core/src/java/org/apache/lucene/store/ByteBuffersDataOutput.java
index c76c848..8639ac5 100644
--- a/lucene/core/src/java/org/apache/lucene/store/ByteBuffersDataOutput.java
+++ b/lucene/core/src/java/org/apache/lucene/store/ByteBuffersDataOutput.java
@@ -80,9 +80,16 @@ public final class ByteBuffersDataOutput extends DataOutput implements Accountab
     }
   }
 
+  /** Default {@code minBitsPerBlock} */
   public static final int DEFAULT_MIN_BITS_PER_BLOCK = 10; // 1024 B
+  /** Default {@code maxBitsPerBlock} */
   public static final int DEFAULT_MAX_BITS_PER_BLOCK = 26; //   64 MB
 
+  /** Smallest {@code minBitsPerBlock} allowed */
+  public static final int LIMIT_MIN_BITS_PER_BLOCK = 1;
+  /** Largest {@code maxBitsPerBlock} allowed */
+  public static final int LIMIT_MAX_BITS_PER_BLOCK = 31;
+
   /**
    * Maximum number of blocks at the current {@link #blockBits} block size before we increase the
    * block size (and thus decrease the number of blocks).
@@ -110,6 +117,14 @@ public final class ByteBuffersDataOutput extends DataOutput implements Accountab
   /** The current-or-next write block. */
   private ByteBuffer currentBlock = EMPTY;
 
+  /**
+   * Create a new output, suitable for writing a file of around {@code expectedSize} bytes.
+   *
+   * <p>Memory allocation will be optimized based on the {@code expectedSize} hint, so that there is
+   * less overhead for larger files.
+   *
+   * @param expectedSize estimated size of the output file
+   */
   public ByteBuffersDataOutput(long expectedSize) {
     this(
         computeBlockSizeBitsFor(expectedSize),
@@ -118,18 +133,47 @@ public final class ByteBuffersDataOutput extends DataOutput implements Accountab
         NO_REUSE);
   }
 
+  /** Creates a new output with all defaults. */
   public ByteBuffersDataOutput() {
     this(DEFAULT_MIN_BITS_PER_BLOCK, DEFAULT_MAX_BITS_PER_BLOCK, ALLOCATE_BB_ON_HEAP, NO_REUSE);
   }
 
+  /**
+   * Expert: Creates a new output with custom parameters.
+   *
+   * @param minBitsPerBlock minimum bits per block
+   * @param maxBitsPerBlock maximum bits per block
+   * @param blockAllocate block allocator
+   * @param blockReuse block recycler
+   */
   public ByteBuffersDataOutput(
       int minBitsPerBlock,
       int maxBitsPerBlock,
       IntFunction<ByteBuffer> blockAllocate,
       Consumer<ByteBuffer> blockReuse) {
-    if (minBitsPerBlock < 10 || minBitsPerBlock > maxBitsPerBlock || maxBitsPerBlock > 31) {
+    if (minBitsPerBlock < LIMIT_MIN_BITS_PER_BLOCK) {
+      throw new IllegalArgumentException(
+          String.format(
+              Locale.ROOT,
+              "minBitsPerBlock (%s) too small, must be at least %s",
+              minBitsPerBlock,
+              LIMIT_MIN_BITS_PER_BLOCK));
+    }
+    if (maxBitsPerBlock > LIMIT_MAX_BITS_PER_BLOCK) {
+      throw new IllegalArgumentException(
+          String.format(
+              Locale.ROOT,
+              "maxBitsPerBlock (%s) too large, must not exceed %s",
+              maxBitsPerBlock,
+              LIMIT_MAX_BITS_PER_BLOCK));
+    }
+    if (minBitsPerBlock > maxBitsPerBlock) {
       throw new IllegalArgumentException(
-          String.format(Locale.ROOT, "Invalid arguments: %s %s", minBitsPerBlock, maxBitsPerBlock));
+          String.format(
+              Locale.ROOT,
+              "minBitsPerBlock (%s) cannot exceed maxBitsPerBlock (%s)",
+              minBitsPerBlock,
+              maxBitsPerBlock));
     }
     this.maxBitsPerBlock = maxBitsPerBlock;
     this.blockBits = minBitsPerBlock;
diff --git a/lucene/core/src/test/org/apache/lucene/store/BaseDataOutputTestCase.java b/lucene/core/src/test/org/apache/lucene/store/BaseDataOutputTestCase.java
index 1be9ffe..8d5e24d 100644
--- a/lucene/core/src/test/org/apache/lucene/store/BaseDataOutputTestCase.java
+++ b/lucene/core/src/test/org/apache/lucene/store/BaseDataOutputTestCase.java
@@ -16,9 +16,6 @@
  */
 package org.apache.lucene.store;
 
-import static org.junit.Assert.*;
-
-import com.carrotsearch.randomizedtesting.RandomizedTest;
 import com.carrotsearch.randomizedtesting.Xoroshiro128PlusRandom;
 import com.carrotsearch.randomizedtesting.generators.RandomBytes;
 import com.carrotsearch.randomizedtesting.generators.RandomNumbers;
@@ -32,9 +29,10 @@ import java.util.List;
 import java.util.Random;
 import org.apache.lucene.util.ArrayUtil;
 import org.apache.lucene.util.IOUtils.IOConsumer;
+import org.apache.lucene.util.LuceneTestCase;
 import org.junit.Test;
 
-public abstract class BaseDataOutputTestCase<T extends DataOutput> extends RandomizedTest {
+public abstract class BaseDataOutputTestCase<T extends DataOutput> extends LuceneTestCase {
   protected abstract T newInstance();
 
   protected abstract byte[] toBytes(T instance);
@@ -50,7 +48,7 @@ public abstract class BaseDataOutputTestCase<T extends DataOutput> extends Rando
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     DataOutput ref = new OutputStreamDataOutput(baos);
 
-    long seed = randomLong();
+    long seed = random().nextLong();
     int max = 50_000;
     addRandomData(dst, new Xoroshiro128PlusRandom(seed), max);
     addRandomData(ref, new Xoroshiro128PlusRandom(seed), max);
diff --git a/lucene/core/src/test/org/apache/lucene/store/TestByteBuffersDataOutput.java b/lucene/core/src/test/org/apache/lucene/store/TestByteBuffersDataOutput.java
index 477646d..4594420 100644
--- a/lucene/core/src/test/org/apache/lucene/store/TestByteBuffersDataOutput.java
+++ b/lucene/core/src/test/org/apache/lucene/store/TestByteBuffersDataOutput.java
@@ -16,8 +16,6 @@
  */
 package org.apache.lucene.store;
 
-import static org.junit.Assert.*;
-
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.List;
@@ -26,6 +24,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.lucene.util.ArrayUtil;
 import org.apache.lucene.util.LuceneTestCase;
 import org.apache.lucene.util.RamUsageEstimator;
+import org.apache.lucene.util.TestUtil;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -58,8 +57,8 @@ public final class TestByteBuffersDataOutput extends BaseDataOutputTestCase<Byte
             reuser::reuse);
 
     // Add some random data first.
-    long genSeed = randomLong();
-    int addCount = randomIntBetween(1000, 5000);
+    long genSeed = random().nextLong();
+    int addCount = TestUtil.nextInt(random(), 1000, 5000);
     addRandomData(o, new Random(genSeed), addCount);
     byte[] data = o.toArrayCopy();
 
@@ -84,7 +83,7 @@ public final class TestByteBuffersDataOutput extends BaseDataOutputTestCase<Byte
 
     {
       long MB = 1024 * 1024;
-      long expectedSize = randomLongBetween(MB, MB * 1024);
+      long expectedSize = TestUtil.nextLong(random(), MB, MB * 1024);
       ByteBuffersDataOutput o = new ByteBuffersDataOutput(expectedSize);
       o.writeByte((byte) 0);
       int cap = o.toBufferList().get(0).capacity();
@@ -96,6 +95,63 @@ public final class TestByteBuffersDataOutput extends BaseDataOutputTestCase<Byte
     }
   }
 
+  public void testIllegalMinBitsPerBlock() {
+    expectThrows(
+        IllegalArgumentException.class,
+        () -> {
+          new ByteBuffersDataOutput(
+              ByteBuffersDataOutput.LIMIT_MIN_BITS_PER_BLOCK - 1,
+              ByteBuffersDataOutput.DEFAULT_MAX_BITS_PER_BLOCK,
+              ByteBuffersDataOutput.ALLOCATE_BB_ON_HEAP,
+              ByteBuffersDataOutput.NO_REUSE);
+        });
+  }
+
+  public void testIllegalMaxBitsPerBlock() {
+    expectThrows(
+        IllegalArgumentException.class,
+        () -> {
+          new ByteBuffersDataOutput(
+              ByteBuffersDataOutput.DEFAULT_MIN_BITS_PER_BLOCK,
+              ByteBuffersDataOutput.LIMIT_MAX_BITS_PER_BLOCK + 1,
+              ByteBuffersDataOutput.ALLOCATE_BB_ON_HEAP,
+              ByteBuffersDataOutput.NO_REUSE);
+        });
+  }
+
+  public void testIllegalBitsPerBlockRange() {
+    expectThrows(
+        IllegalArgumentException.class,
+        () -> {
+          new ByteBuffersDataOutput(
+              20, 19, ByteBuffersDataOutput.ALLOCATE_BB_ON_HEAP, ByteBuffersDataOutput.NO_REUSE);
+        });
+  }
+
+  public void testNullAllocator() {
+    expectThrows(
+        NullPointerException.class,
+        () -> {
+          new ByteBuffersDataOutput(
+              ByteBuffersDataOutput.DEFAULT_MIN_BITS_PER_BLOCK,
+              ByteBuffersDataOutput.DEFAULT_MAX_BITS_PER_BLOCK,
+              null,
+              ByteBuffersDataOutput.NO_REUSE);
+        });
+  }
+
+  public void testNullRecycler() {
+    expectThrows(
+        NullPointerException.class,
+        () -> {
+          new ByteBuffersDataOutput(
+              ByteBuffersDataOutput.DEFAULT_MIN_BITS_PER_BLOCK,
+              ByteBuffersDataOutput.DEFAULT_MAX_BITS_PER_BLOCK,
+              ByteBuffersDataOutput.ALLOCATE_BB_ON_HEAP,
+              null);
+        });
+  }
+
   @Test
   public void testSanity() {
     ByteBuffersDataOutput o = newInstance();
@@ -116,9 +172,10 @@ public final class TestByteBuffersDataOutput extends BaseDataOutputTestCase<Byte
   @Test
   public void testWriteByteBuffer() {
     ByteBuffersDataOutput o = new ByteBuffersDataOutput();
-    byte[] bytes = randomBytesOfLength(1024 * 8 + 10);
+    byte[] bytes = new byte[1024 * 8 + 10];
+    random().nextBytes(bytes);
     ByteBuffer src = ByteBuffer.wrap(bytes);
-    int offset = randomIntBetween(0, 100);
+    int offset = TestUtil.nextInt(random(), 0, 100);
     int len = bytes.length - offset;
     src.position(offset);
     src.limit(offset + len);
@@ -134,11 +191,12 @@ public final class TestByteBuffersDataOutput extends BaseDataOutputTestCase<Byte
     int MB = 1024 * 1024;
     final byte[] bytes;
     if (LuceneTestCase.TEST_NIGHTLY) {
-      bytes = randomBytesOfLength(5 * MB, 15 * MB);
+      bytes = new byte[TestUtil.nextInt(random(), 5 * MB, 15 * MB)];
     } else {
-      bytes = randomBytesOfLength(MB / 2, MB);
+      bytes = new byte[TestUtil.nextInt(random(), MB / 2, MB)];
     }
-    int offset = randomIntBetween(0, 100);
+    random().nextBytes(bytes);
+    int offset = TestUtil.nextInt(random(), 0, 100);
     int len = bytes.length - offset;
     o.writeBytes(bytes, offset, len);
     assertEquals(len, o.size());
diff --git a/lucene/core/src/test/org/apache/lucene/store/TestMultiByteBuffersDirectory.java b/lucene/core/src/test/org/apache/lucene/store/TestMultiByteBuffersDirectory.java
new file mode 100644
index 0000000..643f7c8
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/store/TestMultiByteBuffersDirectory.java
@@ -0,0 +1,49 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+package org.apache.lucene.store;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.function.Supplier;
+import org.apache.lucene.util.BitUtil;
+
+/** Tests ByteBuffersDirectory's chunking */
+public class TestMultiByteBuffersDirectory extends BaseChunkedDirectoryTestCase {
+
+  @Override
+  protected Directory getDirectory(Path path, int maxChunkSize) throws IOException {
+    // round down huge values (above 20) to keep RAM usage low in tests (especially in nightly)
+    int bitsPerBlock =
+        Math.min(
+            20,
+            Math.max(
+                ByteBuffersDataOutput.LIMIT_MIN_BITS_PER_BLOCK,
+                Integer.numberOfTrailingZeros(BitUtil.nextHighestPowerOfTwo(maxChunkSize))));
+    Supplier<ByteBuffersDataOutput> outputSupplier =
+        () -> {
+          return new ByteBuffersDataOutput(
+              bitsPerBlock,
+              bitsPerBlock,
+              ByteBuffersDataOutput.ALLOCATE_BB_ON_HEAP,
+              ByteBuffersDataOutput.NO_REUSE);
+        };
+    return new ByteBuffersDirectory(
+        new SingleInstanceLockFactory(),
+        outputSupplier,
+        ByteBuffersDirectory.OUTPUT_AS_MANY_BUFFERS);
+  }
+}
diff --git a/lucene/core/src/test/org/apache/lucene/store/TestMultiMMap.java b/lucene/core/src/test/org/apache/lucene/store/TestMultiMMap.java
index 59d2ec7..b5d22e8 100644
--- a/lucene/core/src/test/org/apache/lucene/store/TestMultiMMap.java
+++ b/lucene/core/src/test/org/apache/lucene/store/TestMultiMMap.java
@@ -18,14 +18,7 @@ package org.apache.lucene.store;
 
 import java.io.IOException;
 import java.nio.file.Path;
-import java.util.Random;
-import org.apache.lucene.analysis.MockAnalyzer;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.TestUtil;
 
 /**
  * Tests MMapDirectory's MultiMMapIndexInput
@@ -33,11 +26,11 @@ import org.apache.lucene.util.TestUtil;
  * <p>Because Java's ByteBuffer uses an int to address the values, it's necessary to access a file
  * &gt; Integer.MAX_VALUE in size using multiple byte buffers.
  */
-public class TestMultiMMap extends BaseDirectoryTestCase {
+public class TestMultiMMap extends BaseChunkedDirectoryTestCase {
 
   @Override
-  protected Directory getDirectory(Path path) throws IOException {
-    return new MMapDirectory(path, 1 << TestUtil.nextInt(random(), 10, 28));
+  protected Directory getDirectory(Path path, int maxChunkSize) throws IOException {
+    return new MMapDirectory(path, maxChunkSize);
   }
 
   @Override
@@ -46,8 +39,11 @@ public class TestMultiMMap extends BaseDirectoryTestCase {
     assumeTrue(MMapDirectory.UNMAP_NOT_SUPPORTED_REASON, MMapDirectory.UNMAP_SUPPORTED);
   }
 
+  // TODO: can we improve ByteBuffersDirectory (without overhead) and move these clone safety tests
+  // to the base test case?
+
   public void testCloneSafety() throws Exception {
-    MMapDirectory mmapDir = new MMapDirectory(createTempDir("testCloneSafety"));
+    Directory mmapDir = getDirectory(createTempDir("testCloneSafety"));
     IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
     io.writeVInt(5);
     io.close();
@@ -78,29 +74,8 @@ public class TestMultiMMap extends BaseDirectoryTestCase {
     mmapDir.close();
   }
 
-  public void testCloneClose() throws Exception {
-    MMapDirectory mmapDir = new MMapDirectory(createTempDir("testCloneClose"));
-    IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
-    io.writeVInt(5);
-    io.close();
-    IndexInput one = mmapDir.openInput("bytes", IOContext.DEFAULT);
-    IndexInput two = one.clone();
-    IndexInput three = two.clone(); // clone of clone
-    two.close();
-    assertEquals(5, one.readVInt());
-    expectThrows(
-        AlreadyClosedException.class,
-        () -> {
-          two.readVInt();
-        });
-    assertEquals(5, three.readVInt());
-    one.close();
-    three.close();
-    mmapDir.close();
-  }
-
   public void testCloneSliceSafety() throws Exception {
-    MMapDirectory mmapDir = new MMapDirectory(createTempDir("testCloneSliceSafety"));
+    Directory mmapDir = getDirectory(createTempDir("testCloneSliceSafety"));
     IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
     io.writeInt(1);
     io.writeInt(2);
@@ -141,238 +116,11 @@ public class TestMultiMMap extends BaseDirectoryTestCase {
     mmapDir.close();
   }
 
-  public void testCloneSliceClose() throws Exception {
-    MMapDirectory mmapDir = new MMapDirectory(createTempDir("testCloneSliceClose"));
-    IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
-    io.writeInt(1);
-    io.writeInt(2);
-    io.close();
-    IndexInput slicer = mmapDir.openInput("bytes", newIOContext(random()));
-    IndexInput one = slicer.slice("first int", 0, 4);
-    IndexInput two = slicer.slice("second int", 4, 4);
-    one.close();
-    expectThrows(
-        AlreadyClosedException.class,
-        () -> {
-          one.readInt();
-        });
-    assertEquals(2, two.readInt());
-    // reopen a new slice "another":
-    IndexInput another = slicer.slice("first int", 0, 4);
-    assertEquals(1, another.readInt());
-    another.close();
-    two.close();
-    slicer.close();
-    mmapDir.close();
-  }
-
-  public void testSeekZero() throws Exception {
-    int upto = TEST_NIGHTLY ? 31 : 3;
-    for (int i = 0; i < upto; i++) {
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testSeekZero"), 1 << i);
-      IndexOutput io = mmapDir.createOutput("zeroBytes", newIOContext(random()));
-      io.close();
-      IndexInput ii = mmapDir.openInput("zeroBytes", newIOContext(random()));
-      ii.seek(0L);
-      ii.close();
-      mmapDir.close();
-    }
-  }
-
-  public void testSeekSliceZero() throws Exception {
-    int upto = TEST_NIGHTLY ? 31 : 3;
-    for (int i = 0; i < upto; i++) {
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testSeekSliceZero"), 1 << i);
-      IndexOutput io = mmapDir.createOutput("zeroBytes", newIOContext(random()));
-      io.close();
-      IndexInput slicer = mmapDir.openInput("zeroBytes", newIOContext(random()));
-      IndexInput ii = slicer.slice("zero-length slice", 0, 0);
-      ii.seek(0L);
-      ii.close();
-      slicer.close();
-      mmapDir.close();
-    }
-  }
-
-  public void testSeekEnd() throws Exception {
-    for (int i = 0; i < 17; i++) {
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testSeekEnd"), 1 << i);
-      IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
-      byte[] bytes = new byte[1 << i];
-      random().nextBytes(bytes);
-      io.writeBytes(bytes, bytes.length);
-      io.close();
-      IndexInput ii = mmapDir.openInput("bytes", newIOContext(random()));
-      byte[] actual = new byte[1 << i];
-      ii.readBytes(actual, 0, actual.length);
-      assertEquals(new BytesRef(bytes), new BytesRef(actual));
-      ii.seek(1 << i);
-      ii.close();
-      mmapDir.close();
-    }
-  }
-
-  public void testSeekSliceEnd() throws Exception {
-    for (int i = 0; i < 17; i++) {
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testSeekSliceEnd"), 1 << i);
-      IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
-      byte[] bytes = new byte[1 << i];
-      random().nextBytes(bytes);
-      io.writeBytes(bytes, bytes.length);
-      io.close();
-      IndexInput slicer = mmapDir.openInput("bytes", newIOContext(random()));
-      IndexInput ii = slicer.slice("full slice", 0, bytes.length);
-      byte[] actual = new byte[1 << i];
-      ii.readBytes(actual, 0, actual.length);
-      assertEquals(new BytesRef(bytes), new BytesRef(actual));
-      ii.seek(1 << i);
-      ii.close();
-      slicer.close();
-      mmapDir.close();
-    }
-  }
-
-  public void testSeeking() throws Exception {
-    int numIters = TEST_NIGHTLY ? 10 : 1;
-    for (int i = 0; i < numIters; i++) {
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testSeeking"), 1 << i);
-      IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
-      byte[] bytes = new byte[1 << (i + 1)]; // make sure we switch buffers
-      random().nextBytes(bytes);
-      io.writeBytes(bytes, bytes.length);
-      io.close();
-      IndexInput ii = mmapDir.openInput("bytes", newIOContext(random()));
-      byte[] actual = new byte[1 << (i + 1)]; // first read all bytes
-      ii.readBytes(actual, 0, actual.length);
-      assertEquals(new BytesRef(bytes), new BytesRef(actual));
-      for (int sliceStart = 0; sliceStart < bytes.length; sliceStart++) {
-        for (int sliceLength = 0; sliceLength < bytes.length - sliceStart; sliceLength++) {
-          byte[] slice = new byte[sliceLength];
-          ii.seek(sliceStart);
-          ii.readBytes(slice, 0, slice.length);
-          assertEquals(new BytesRef(bytes, sliceStart, sliceLength), new BytesRef(slice));
-        }
-      }
-      ii.close();
-      mmapDir.close();
-    }
-  }
-
-  // note instead of seeking to offset and reading length, this opens slices at the
-  // the various offset+length and just does readBytes.
-  public void testSlicedSeeking() throws Exception {
-    int numIters = TEST_NIGHTLY ? 10 : 1;
-    for (int i = 0; i < numIters; i++) {
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testSlicedSeeking"), 1 << i);
-      IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
-      byte[] bytes = new byte[1 << (i + 1)]; // make sure we switch buffers
-      random().nextBytes(bytes);
-      io.writeBytes(bytes, bytes.length);
-      io.close();
-      IndexInput ii = mmapDir.openInput("bytes", newIOContext(random()));
-      byte[] actual = new byte[1 << (i + 1)]; // first read all bytes
-      ii.readBytes(actual, 0, actual.length);
-      ii.close();
-      assertEquals(new BytesRef(bytes), new BytesRef(actual));
-      IndexInput slicer = mmapDir.openInput("bytes", newIOContext(random()));
-      for (int sliceStart = 0; sliceStart < bytes.length; sliceStart++) {
-        for (int sliceLength = 0; sliceLength < bytes.length - sliceStart; sliceLength++) {
-          assertSlice(bytes, slicer, 0, sliceStart, sliceLength);
-        }
-      }
-      slicer.close();
-      mmapDir.close();
-    }
-  }
-
-  @Override
-  public void testSliceOfSlice() throws Exception {
-    int upto = TEST_NIGHTLY ? 10 : 8;
-    for (int i = 0; i < upto; i++) {
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testSliceOfSlice"), 1 << i);
-      IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
-      byte[] bytes = new byte[1 << (i + 1)]; // make sure we switch buffers
-      random().nextBytes(bytes);
-      io.writeBytes(bytes, bytes.length);
-      io.close();
-      IndexInput ii = mmapDir.openInput("bytes", newIOContext(random()));
-      byte[] actual = new byte[1 << (i + 1)]; // first read all bytes
-      ii.readBytes(actual, 0, actual.length);
-      ii.close();
-      assertEquals(new BytesRef(bytes), new BytesRef(actual));
-      IndexInput outerSlicer = mmapDir.openInput("bytes", newIOContext(random()));
-      final int outerSliceStart = random().nextInt(bytes.length / 2);
-      final int outerSliceLength = random().nextInt(bytes.length - outerSliceStart);
-      IndexInput innerSlicer =
-          outerSlicer.slice("parentBytesSlice", outerSliceStart, outerSliceLength);
-      for (int sliceStart = 0; sliceStart < outerSliceLength; sliceStart++) {
-        for (int sliceLength = 0; sliceLength < outerSliceLength - sliceStart; sliceLength++) {
-          assertSlice(bytes, innerSlicer, outerSliceStart, sliceStart, sliceLength);
-        }
-      }
-      innerSlicer.close();
-      outerSlicer.close();
-      mmapDir.close();
-    }
-  }
-
-  private void assertSlice(
-      byte[] bytes, IndexInput slicer, int outerSliceStart, int sliceStart, int sliceLength)
-      throws IOException {
-    byte[] slice = new byte[sliceLength];
-    IndexInput input = slicer.slice("bytesSlice", sliceStart, slice.length);
-    input.readBytes(slice, 0, slice.length);
-    input.close();
-    assertEquals(
-        new BytesRef(bytes, outerSliceStart + sliceStart, sliceLength), new BytesRef(slice));
-  }
-
-  public void testRandomChunkSizes() throws Exception {
-    int num = TEST_NIGHTLY ? atLeast(10) : 3;
-    for (int i = 0; i < num; i++) {
-      assertChunking(random(), TestUtil.nextInt(random(), 20, 100));
-    }
-  }
-
-  private void assertChunking(Random random, int chunkSize) throws Exception {
-    Path path = createTempDir("mmap" + chunkSize);
-    MMapDirectory mmapDir = new MMapDirectory(path, chunkSize);
-    // we will map a lot, try to turn on the unmap hack
-    if (MMapDirectory.UNMAP_SUPPORTED) mmapDir.setUseUnmap(true);
-    MockDirectoryWrapper dir = new MockDirectoryWrapper(random, mmapDir);
-    RandomIndexWriter writer =
-        new RandomIndexWriter(
-            random,
-            dir,
-            newIndexWriterConfig(new MockAnalyzer(random)).setMergePolicy(newLogMergePolicy()));
-    Document doc = new Document();
-    Field docid = newStringField("docid", "0", Field.Store.YES);
-    Field junk = newStringField("junk", "", Field.Store.YES);
-    doc.add(docid);
-    doc.add(junk);
-
-    int numDocs = 100;
-    for (int i = 0; i < numDocs; i++) {
-      docid.setStringValue("" + i);
-      junk.setStringValue(TestUtil.randomUnicodeString(random));
-      writer.addDocument(doc);
-    }
-    IndexReader reader = writer.getReader();
-    writer.close();
-
-    int numAsserts = atLeast(100);
-    for (int i = 0; i < numAsserts; i++) {
-      int docID = random.nextInt(numDocs);
-      assertEquals("" + docID, reader.document(docID).get("docid"));
-    }
-    reader.close();
-    dir.close();
-  }
-
+  // test has asserts specific to mmap impl...
   public void testImplementations() throws Exception {
     for (int i = 2; i < 12; i++) {
       final int chunkSize = 1 << i;
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testImplementations"), chunkSize);
+      Directory mmapDir = getDirectory(createTempDir("testImplementations"), chunkSize);
       IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
       int size = random().nextInt(chunkSize * 2) + 3; // add some buffer of 3 for slice tests
       byte[] bytes = new byte[size];
@@ -420,43 +168,4 @@ public class TestMultiMMap extends BaseDirectoryTestCase {
       mmapDir.close();
     }
   }
-
-  public void testLittleEndianLongsCrossBoundary() throws Exception {
-    try (Directory dir =
-        new MMapDirectory(createTempDir("testLittleEndianLongsCrossBoundary"), 16)) {
-      try (IndexOutput out = dir.createOutput("littleEndianLongs", newIOContext(random()))) {
-        out.writeByte((byte) 2);
-        out.writeLong(3L);
-        out.writeLong(Long.MAX_VALUE);
-        out.writeLong(-3L);
-      }
-      try (IndexInput input = dir.openInput("littleEndianLongs", newIOContext(random()))) {
-        assertEquals(25, input.length());
-        assertEquals(2, input.readByte());
-        long[] l = new long[4];
-        input.readLongs(l, 1, 3);
-        assertArrayEquals(new long[] {0L, 3L, Long.MAX_VALUE, -3L}, l);
-        assertEquals(25, input.getFilePointer());
-      }
-    }
-  }
-
-  public void testLittleEndianFloatsCrossBoundary() throws Exception {
-    try (Directory dir = new MMapDirectory(createTempDir("testFloatsCrossBoundary"), 8)) {
-      try (IndexOutput out = dir.createOutput("Floats", newIOContext(random()))) {
-        out.writeByte((byte) 2);
-        out.writeInt(Float.floatToIntBits(3f));
-        out.writeInt(Float.floatToIntBits(Float.MAX_VALUE));
-        out.writeInt(Float.floatToIntBits(-3f));
-      }
-      try (IndexInput input = dir.openInput("Floats", newIOContext(random()))) {
-        assertEquals(13, input.length());
-        assertEquals(2, input.readByte());
-        float[] ff = new float[4];
-        input.readFloats(ff, 1, 3);
-        assertArrayEquals(new float[] {0, 3f, Float.MAX_VALUE, -3f}, ff, 0);
-        assertEquals(13, input.getFilePointer());
-      }
-    }
-  }
 }
diff --git a/lucene/core/src/test/org/apache/lucene/store/TestMultiMMap.java b/lucene/test-framework/src/java/org/apache/lucene/store/BaseChunkedDirectoryTestCase.java
similarity index 57%
copy from lucene/core/src/test/org/apache/lucene/store/TestMultiMMap.java
copy to lucene/test-framework/src/java/org/apache/lucene/store/BaseChunkedDirectoryTestCase.java
index 59d2ec7..db5be49 100644
--- a/lucene/core/src/test/org/apache/lucene/store/TestMultiMMap.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/store/BaseChunkedDirectoryTestCase.java
@@ -28,62 +28,27 @@ import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.TestUtil;
 
 /**
- * Tests MMapDirectory's MultiMMapIndexInput
+ * Base class for Directories that "chunk" the input into blocks.
  *
- * <p>Because Java's ByteBuffer uses an int to address the values, it's necessary to access a file
- * &gt; Integer.MAX_VALUE in size using multiple byte buffers.
+ * <p>It tries to explicitly chunk with different sizes and test boundary conditions around the
+ * chunks.
  */
-public class TestMultiMMap extends BaseDirectoryTestCase {
+public abstract class BaseChunkedDirectoryTestCase extends BaseDirectoryTestCase {
 
   @Override
   protected Directory getDirectory(Path path) throws IOException {
-    return new MMapDirectory(path, 1 << TestUtil.nextInt(random(), 10, 28));
+    return getDirectory(path, 1 << TestUtil.nextInt(random(), 10, 20));
   }
 
-  @Override
-  public void setUp() throws Exception {
-    super.setUp();
-    assumeTrue(MMapDirectory.UNMAP_NOT_SUPPORTED_REASON, MMapDirectory.UNMAP_SUPPORTED);
-  }
-
-  public void testCloneSafety() throws Exception {
-    MMapDirectory mmapDir = new MMapDirectory(createTempDir("testCloneSafety"));
-    IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
-    io.writeVInt(5);
-    io.close();
-    IndexInput one = mmapDir.openInput("bytes", IOContext.DEFAULT);
-    IndexInput two = one.clone();
-    IndexInput three = two.clone(); // clone of clone
-    one.close();
-    expectThrows(
-        AlreadyClosedException.class,
-        () -> {
-          one.readVInt();
-        });
-    expectThrows(
-        AlreadyClosedException.class,
-        () -> {
-          two.readVInt();
-        });
-    expectThrows(
-        AlreadyClosedException.class,
-        () -> {
-          three.readVInt();
-        });
-
-    two.close();
-    three.close();
-    // test double close of master:
-    one.close();
-    mmapDir.close();
-  }
+  /** Creates a new directory with the specified max chunk size */
+  protected abstract Directory getDirectory(Path path, int maxChunkSize) throws IOException;
 
   public void testCloneClose() throws Exception {
-    MMapDirectory mmapDir = new MMapDirectory(createTempDir("testCloneClose"));
-    IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
+    Directory dir = getDirectory(createTempDir("testCloneClose"));
+    IndexOutput io = dir.createOutput("bytes", newIOContext(random()));
     io.writeVInt(5);
     io.close();
-    IndexInput one = mmapDir.openInput("bytes", IOContext.DEFAULT);
+    IndexInput one = dir.openInput("bytes", IOContext.DEFAULT);
     IndexInput two = one.clone();
     IndexInput three = two.clone(); // clone of clone
     two.close();
@@ -96,58 +61,16 @@ public class TestMultiMMap extends BaseDirectoryTestCase {
     assertEquals(5, three.readVInt());
     one.close();
     three.close();
-    mmapDir.close();
-  }
-
-  public void testCloneSliceSafety() throws Exception {
-    MMapDirectory mmapDir = new MMapDirectory(createTempDir("testCloneSliceSafety"));
-    IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
-    io.writeInt(1);
-    io.writeInt(2);
-    io.close();
-    IndexInput slicer = mmapDir.openInput("bytes", newIOContext(random()));
-    IndexInput one = slicer.slice("first int", 0, 4);
-    IndexInput two = slicer.slice("second int", 4, 4);
-    IndexInput three = one.clone(); // clone of clone
-    IndexInput four = two.clone(); // clone of clone
-    slicer.close();
-    expectThrows(
-        AlreadyClosedException.class,
-        () -> {
-          one.readInt();
-        });
-    expectThrows(
-        AlreadyClosedException.class,
-        () -> {
-          two.readInt();
-        });
-    expectThrows(
-        AlreadyClosedException.class,
-        () -> {
-          three.readInt();
-        });
-    expectThrows(
-        AlreadyClosedException.class,
-        () -> {
-          four.readInt();
-        });
-
-    one.close();
-    two.close();
-    three.close();
-    four.close();
-    // test double-close of slicer:
-    slicer.close();
-    mmapDir.close();
+    dir.close();
   }
 
   public void testCloneSliceClose() throws Exception {
-    MMapDirectory mmapDir = new MMapDirectory(createTempDir("testCloneSliceClose"));
-    IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
+    Directory dir = getDirectory(createTempDir("testCloneSliceClose"));
+    IndexOutput io = dir.createOutput("bytes", newIOContext(random()));
     io.writeInt(1);
     io.writeInt(2);
     io.close();
-    IndexInput slicer = mmapDir.openInput("bytes", newIOContext(random()));
+    IndexInput slicer = dir.openInput("bytes", newIOContext(random()));
     IndexInput one = slicer.slice("first int", 0, 4);
     IndexInput two = slicer.slice("second int", 4, 4);
     one.close();
@@ -163,64 +86,64 @@ public class TestMultiMMap extends BaseDirectoryTestCase {
     another.close();
     two.close();
     slicer.close();
-    mmapDir.close();
+    dir.close();
   }
 
   public void testSeekZero() throws Exception {
     int upto = TEST_NIGHTLY ? 31 : 3;
     for (int i = 0; i < upto; i++) {
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testSeekZero"), 1 << i);
-      IndexOutput io = mmapDir.createOutput("zeroBytes", newIOContext(random()));
+      Directory dir = getDirectory(createTempDir("testSeekZero"), 1 << i);
+      IndexOutput io = dir.createOutput("zeroBytes", newIOContext(random()));
       io.close();
-      IndexInput ii = mmapDir.openInput("zeroBytes", newIOContext(random()));
+      IndexInput ii = dir.openInput("zeroBytes", newIOContext(random()));
       ii.seek(0L);
       ii.close();
-      mmapDir.close();
+      dir.close();
     }
   }
 
   public void testSeekSliceZero() throws Exception {
     int upto = TEST_NIGHTLY ? 31 : 3;
     for (int i = 0; i < upto; i++) {
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testSeekSliceZero"), 1 << i);
-      IndexOutput io = mmapDir.createOutput("zeroBytes", newIOContext(random()));
+      Directory dir = getDirectory(createTempDir("testSeekSliceZero"), 1 << i);
+      IndexOutput io = dir.createOutput("zeroBytes", newIOContext(random()));
       io.close();
-      IndexInput slicer = mmapDir.openInput("zeroBytes", newIOContext(random()));
+      IndexInput slicer = dir.openInput("zeroBytes", newIOContext(random()));
       IndexInput ii = slicer.slice("zero-length slice", 0, 0);
       ii.seek(0L);
       ii.close();
       slicer.close();
-      mmapDir.close();
+      dir.close();
     }
   }
 
   public void testSeekEnd() throws Exception {
     for (int i = 0; i < 17; i++) {
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testSeekEnd"), 1 << i);
-      IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
+      Directory dir = getDirectory(createTempDir("testSeekEnd"), 1 << i);
+      IndexOutput io = dir.createOutput("bytes", newIOContext(random()));
       byte[] bytes = new byte[1 << i];
       random().nextBytes(bytes);
       io.writeBytes(bytes, bytes.length);
       io.close();
-      IndexInput ii = mmapDir.openInput("bytes", newIOContext(random()));
+      IndexInput ii = dir.openInput("bytes", newIOContext(random()));
       byte[] actual = new byte[1 << i];
       ii.readBytes(actual, 0, actual.length);
       assertEquals(new BytesRef(bytes), new BytesRef(actual));
       ii.seek(1 << i);
       ii.close();
-      mmapDir.close();
+      dir.close();
     }
   }
 
   public void testSeekSliceEnd() throws Exception {
     for (int i = 0; i < 17; i++) {
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testSeekSliceEnd"), 1 << i);
-      IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
+      Directory dir = getDirectory(createTempDir("testSeekSliceEnd"), 1 << i);
+      IndexOutput io = dir.createOutput("bytes", newIOContext(random()));
       byte[] bytes = new byte[1 << i];
       random().nextBytes(bytes);
       io.writeBytes(bytes, bytes.length);
       io.close();
-      IndexInput slicer = mmapDir.openInput("bytes", newIOContext(random()));
+      IndexInput slicer = dir.openInput("bytes", newIOContext(random()));
       IndexInput ii = slicer.slice("full slice", 0, bytes.length);
       byte[] actual = new byte[1 << i];
       ii.readBytes(actual, 0, actual.length);
@@ -228,20 +151,20 @@ public class TestMultiMMap extends BaseDirectoryTestCase {
       ii.seek(1 << i);
       ii.close();
       slicer.close();
-      mmapDir.close();
+      dir.close();
     }
   }
 
   public void testSeeking() throws Exception {
     int numIters = TEST_NIGHTLY ? 10 : 1;
     for (int i = 0; i < numIters; i++) {
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testSeeking"), 1 << i);
-      IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
+      Directory dir = getDirectory(createTempDir("testSeeking"), 1 << i);
+      IndexOutput io = dir.createOutput("bytes", newIOContext(random()));
       byte[] bytes = new byte[1 << (i + 1)]; // make sure we switch buffers
       random().nextBytes(bytes);
       io.writeBytes(bytes, bytes.length);
       io.close();
-      IndexInput ii = mmapDir.openInput("bytes", newIOContext(random()));
+      IndexInput ii = dir.openInput("bytes", newIOContext(random()));
       byte[] actual = new byte[1 << (i + 1)]; // first read all bytes
       ii.readBytes(actual, 0, actual.length);
       assertEquals(new BytesRef(bytes), new BytesRef(actual));
@@ -254,7 +177,7 @@ public class TestMultiMMap extends BaseDirectoryTestCase {
         }
       }
       ii.close();
-      mmapDir.close();
+      dir.close();
     }
   }
 
@@ -263,25 +186,25 @@ public class TestMultiMMap extends BaseDirectoryTestCase {
   public void testSlicedSeeking() throws Exception {
     int numIters = TEST_NIGHTLY ? 10 : 1;
     for (int i = 0; i < numIters; i++) {
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testSlicedSeeking"), 1 << i);
-      IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
+      Directory dir = getDirectory(createTempDir("testSlicedSeeking"), 1 << i);
+      IndexOutput io = dir.createOutput("bytes", newIOContext(random()));
       byte[] bytes = new byte[1 << (i + 1)]; // make sure we switch buffers
       random().nextBytes(bytes);
       io.writeBytes(bytes, bytes.length);
       io.close();
-      IndexInput ii = mmapDir.openInput("bytes", newIOContext(random()));
+      IndexInput ii = dir.openInput("bytes", newIOContext(random()));
       byte[] actual = new byte[1 << (i + 1)]; // first read all bytes
       ii.readBytes(actual, 0, actual.length);
       ii.close();
       assertEquals(new BytesRef(bytes), new BytesRef(actual));
-      IndexInput slicer = mmapDir.openInput("bytes", newIOContext(random()));
+      IndexInput slicer = dir.openInput("bytes", newIOContext(random()));
       for (int sliceStart = 0; sliceStart < bytes.length; sliceStart++) {
         for (int sliceLength = 0; sliceLength < bytes.length - sliceStart; sliceLength++) {
           assertSlice(bytes, slicer, 0, sliceStart, sliceLength);
         }
       }
       slicer.close();
-      mmapDir.close();
+      dir.close();
     }
   }
 
@@ -289,18 +212,18 @@ public class TestMultiMMap extends BaseDirectoryTestCase {
   public void testSliceOfSlice() throws Exception {
     int upto = TEST_NIGHTLY ? 10 : 8;
     for (int i = 0; i < upto; i++) {
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testSliceOfSlice"), 1 << i);
-      IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
+      Directory dir = getDirectory(createTempDir("testSliceOfSlice"), 1 << i);
+      IndexOutput io = dir.createOutput("bytes", newIOContext(random()));
       byte[] bytes = new byte[1 << (i + 1)]; // make sure we switch buffers
       random().nextBytes(bytes);
       io.writeBytes(bytes, bytes.length);
       io.close();
-      IndexInput ii = mmapDir.openInput("bytes", newIOContext(random()));
+      IndexInput ii = dir.openInput("bytes", newIOContext(random()));
       byte[] actual = new byte[1 << (i + 1)]; // first read all bytes
       ii.readBytes(actual, 0, actual.length);
       ii.close();
       assertEquals(new BytesRef(bytes), new BytesRef(actual));
-      IndexInput outerSlicer = mmapDir.openInput("bytes", newIOContext(random()));
+      IndexInput outerSlicer = dir.openInput("bytes", newIOContext(random()));
       final int outerSliceStart = random().nextInt(bytes.length / 2);
       final int outerSliceLength = random().nextInt(bytes.length - outerSliceStart);
       IndexInput innerSlicer =
@@ -312,7 +235,7 @@ public class TestMultiMMap extends BaseDirectoryTestCase {
       }
       innerSlicer.close();
       outerSlicer.close();
-      mmapDir.close();
+      dir.close();
     }
   }
 
@@ -336,10 +259,12 @@ public class TestMultiMMap extends BaseDirectoryTestCase {
 
   private void assertChunking(Random random, int chunkSize) throws Exception {
     Path path = createTempDir("mmap" + chunkSize);
-    MMapDirectory mmapDir = new MMapDirectory(path, chunkSize);
+    Directory chunkedDir = getDirectory(path, chunkSize);
     // we will map a lot, try to turn on the unmap hack
-    if (MMapDirectory.UNMAP_SUPPORTED) mmapDir.setUseUnmap(true);
-    MockDirectoryWrapper dir = new MockDirectoryWrapper(random, mmapDir);
+    if (chunkedDir instanceof MMapDirectory && MMapDirectory.UNMAP_SUPPORTED) {
+      ((MMapDirectory) chunkedDir).setUseUnmap(true);
+    }
+    MockDirectoryWrapper dir = new MockDirectoryWrapper(random, chunkedDir);
     RandomIndexWriter writer =
         new RandomIndexWriter(
             random,
@@ -369,61 +294,8 @@ public class TestMultiMMap extends BaseDirectoryTestCase {
     dir.close();
   }
 
-  public void testImplementations() throws Exception {
-    for (int i = 2; i < 12; i++) {
-      final int chunkSize = 1 << i;
-      MMapDirectory mmapDir = new MMapDirectory(createTempDir("testImplementations"), chunkSize);
-      IndexOutput io = mmapDir.createOutput("bytes", newIOContext(random()));
-      int size = random().nextInt(chunkSize * 2) + 3; // add some buffer of 3 for slice tests
-      byte[] bytes = new byte[size];
-      random().nextBytes(bytes);
-      io.writeBytes(bytes, bytes.length);
-      io.close();
-      IndexInput ii = mmapDir.openInput("bytes", newIOContext(random()));
-      byte[] actual = new byte[size]; // first read all bytes
-      ii.readBytes(actual, 0, actual.length);
-      assertEquals(new BytesRef(bytes), new BytesRef(actual));
-      // reinit:
-      ii.seek(0L);
-
-      // check impl (we must check size < chunksize: currently, if size==chunkSize, we get 2
-      // buffers, the second one empty:
-      assertTrue(
-          (size < chunkSize)
-              ? (ii instanceof ByteBufferIndexInput.SingleBufferImpl)
-              : (ii instanceof ByteBufferIndexInput.MultiBufferImpl));
-
-      // clone tests:
-      assertSame(ii.getClass(), ii.clone().getClass());
-
-      // slice test (offset 0)
-      int sliceSize = random().nextInt(size);
-      IndexInput slice = ii.slice("slice", 0, sliceSize);
-      assertTrue(
-          (sliceSize < chunkSize)
-              ? (slice instanceof ByteBufferIndexInput.SingleBufferImpl)
-              : (slice instanceof ByteBufferIndexInput.MultiBufferImpl));
-
-      // slice test (offset > 0 )
-      int offset = random().nextInt(size - 1) + 1;
-      sliceSize = random().nextInt(size - offset + 1);
-      slice = ii.slice("slice", offset, sliceSize);
-      // System.out.println(offset + "/" + sliceSize + " chunkSize=" + chunkSize + " " +
-      // slice.getClass());
-      if (offset % chunkSize + sliceSize < chunkSize) {
-        assertTrue(slice instanceof ByteBufferIndexInput.SingleBufferImpl);
-      } else {
-        assertTrue(slice instanceof ByteBufferIndexInput.MultiBufferImpl);
-      }
-
-      ii.close();
-      mmapDir.close();
-    }
-  }
-
   public void testLittleEndianLongsCrossBoundary() throws Exception {
-    try (Directory dir =
-        new MMapDirectory(createTempDir("testLittleEndianLongsCrossBoundary"), 16)) {
+    try (Directory dir = getDirectory(createTempDir("testLittleEndianLongsCrossBoundary"), 16)) {
       try (IndexOutput out = dir.createOutput("littleEndianLongs", newIOContext(random()))) {
         out.writeByte((byte) 2);
         out.writeLong(3L);
@@ -442,7 +314,7 @@ public class TestMultiMMap extends BaseDirectoryTestCase {
   }
 
   public void testLittleEndianFloatsCrossBoundary() throws Exception {
-    try (Directory dir = new MMapDirectory(createTempDir("testFloatsCrossBoundary"), 8)) {
+    try (Directory dir = getDirectory(createTempDir("testFloatsCrossBoundary"), 8)) {
       try (IndexOutput out = dir.createOutput("Floats", newIOContext(random()))) {
         out.writeByte((byte) 2);
         out.writeInt(Float.floatToIntBits(3f));