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

taklwu pushed a commit to branch HBASE-30018
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/HBASE-30018 by this push:
     new ccc5ca13a4e HBASE-30206 Add test factory helpers for 
CacheAccessService-backed cache instances (#8359)
ccc5ca13a4e is described below

commit ccc5ca13a4ecfe76d90d51c094f4c7d38dd78784
Author: Vladimir Rodionov <[email protected]>
AuthorDate: Tue Jun 16 11:22:51 2026 -0700

    HBASE-30206 Add test factory helpers for CacheAccessService-backed cache 
instances (#8359)
    
    Signed-off-by: Tak Lon (Stephen) Wu <[email protected]>
---
 .../hbase/io/hfile/cache/CacheAccessServices.java  |  31 +
 .../hfile/cache/CacheAccessServiceTestFactory.java | 643 +++++++++++++++++++++
 .../cache/CacheAccessServiceTestInstance.java      |  64 ++
 .../cache/TestCacheAccessServiceTestFactory.java   | 215 +++++++
 4 files changed, 953 insertions(+)

diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheAccessServices.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheAccessServices.java
index c98dd2275d8..a4de6035728 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheAccessServices.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheAccessServices.java
@@ -18,7 +18,9 @@
 package org.apache.hadoop.hbase.io.hfile.cache;
 
 import java.util.Objects;
+import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.io.hfile.BlockCache;
+import org.apache.hadoop.hbase.io.hfile.BlockCacheFactory;
 import org.apache.yetus.audience.InterfaceAudience;
 
 /**
@@ -56,6 +58,35 @@ public final class CacheAccessServices {
       Objects.requireNonNull(blockCache, "blockCache must not be null"));
   }
 
+  /**
+   * Creates a {@link CacheAccessService} from the block cache configuration.
+   * <p>
+   * This method is a compatibility factory for tests and transitional code 
paths that want to
+   * obtain a {@link CacheAccessService} directly from {@link Configuration}, 
while still using the
+   * existing {@link BlockCacheFactory} and legacy {@link BlockCache} 
implementations underneath.
+   * </p>
+   * <p>
+   * The method delegates block-cache construction to
+   * {@link BlockCacheFactory#createBlockCache(Configuration)}. If the legacy 
factory creates a
+   * {@link BlockCache}, the returned service is backed by that cache through
+   * {@link BlockCacheBackedCacheAccessService}. If the legacy factory does 
not create a cache, this
+   * method returns the disabled/no-op cache access service.
+   * </p>
+   * <p>
+   * This method does not introduce new cache-engine or topology-based runtime 
wiring. It is
+   * intended only as a bridge while existing HBase tests and integration 
paths migrate from direct
+   * {@link BlockCache} usage to {@link CacheAccessService}.
+   * </p>
+   * @param conf configuration used by {@link BlockCacheFactory}
+   * @return cache access service created from the configured legacy block 
cache, or disabled when
+   *         no block cache is configured
+   * @throws NullPointerException if {@code conf} is {@code null}
+   */
+  public static CacheAccessService fromConfiguration(Configuration conf) {
+    Objects.requireNonNull(conf, "conf must not be null");
+    return fromBlockCache(BlockCacheFactory.createBlockCache(conf));
+  }
+
   /**
    * Creates a {@link CacheAccessService} backed by a {@link CacheTopology} 
and placement/admission
    * policy.
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/cache/CacheAccessServiceTestFactory.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/cache/CacheAccessServiceTestFactory.java
new file mode 100644
index 00000000000..069fc6fb6bc
--- /dev/null
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/cache/CacheAccessServiceTestFactory.java
@@ -0,0 +1,643 @@
+/*
+ * 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.hadoop.hbase.io.hfile.cache;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.io.hfile.BlockCache;
+import org.apache.hadoop.hbase.io.hfile.LruAdaptiveBlockCache;
+import org.apache.hadoop.hbase.io.hfile.LruBlockCache;
+import org.apache.hadoop.hbase.io.hfile.TinyLfuBlockCache;
+import org.apache.hadoop.hbase.io.hfile.bucket.BucketCache;
+import org.apache.hadoop.hbase.regionserver.HRegion;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Test-only factory helpers for creating {@link CacheAccessService} instances 
backed by legacy
+ * {@link BlockCache} implementations.
+ * <p>
+ * These helpers are intended to keep integration-style tests concise while 
the block cache
+ * architecture migrates toward {@link CacheAccessService}. They deliberately 
do not replace
+ * implementation-specific tests for {@link LruBlockCache}, {@link 
LruAdaptiveBlockCache},
+ * {@link TinyLfuBlockCache}, {@link BucketCache}, or other concrete cache 
implementations.
+ * </p>
+ * <p>
+ * Each helper mirrors a public constructor on one of the legacy cache 
implementations and returns
+ * either:
+ * </p>
+ * <ul>
+ * <li>a {@link CacheAccessService} facade backed by the constructed cache, 
or</li>
+ * <li>a {@link CacheAccessServiceTestInstance} containing both the concrete 
cache and the
+ * facade.</li>
+ * </ul>
+ * <p>
+ * The factory currently creates legacy {@link BlockCache} implementations and 
wraps them using
+ * {@link CacheAccessServices#fromBlockCache(BlockCache)}. Later, when cache 
engines and topologies
+ * are wired directly, the internals of this factory can change without 
forcing integration-style
+ * tests to be rewritten again.
+ * </p>
+ */
[email protected]
+public final class CacheAccessServiceTestFactory {
+
+  private CacheAccessServiceTestFactory() {
+  }
+
+  /**
+   * Wraps an existing {@link BlockCache} in a {@link CacheAccessService}.
+   * @param blockCache block cache to wrap
+   * @return cache access service backed by the supplied block cache
+   * @throws NullPointerException if {@code blockCache} is {@code null}
+   */
+  public static CacheAccessService fromBlockCache(BlockCache blockCache) {
+    return CacheAccessServices.fromBlockCache(blockCache);
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} from the block cache configuration.
+   * <p>
+   * This method delegates to {@link 
CacheAccessServices#fromConfiguration(Configuration)} and is
+   * useful for tests that want the normal legacy
+   * {@link org.apache.hadoop.hbase.io.hfile.BlockCacheFactory} construction 
path while receiving a
+   * {@link CacheAccessService} facade.
+   * </p>
+   * @param conf configuration used to create the legacy block cache
+   * @return cache access service created from configuration, or disabled when 
no cache is
+   *         configured
+   * @throws NullPointerException if {@code conf} is {@code null}
+   */
+  public static CacheAccessService fromConfiguration(Configuration conf) {
+    return CacheAccessServices.fromConfiguration(conf);
+  }
+
+  /**
+   * Returns a disabled/no-op cache access service.
+   * @return disabled cache access service
+   */
+  public static CacheAccessService disabled() {
+    return CacheAccessServices.disabled();
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by {@link LruBlockCache}.
+   * @param maxSize   maximum size of cache, in bytes
+   * @param blockSize approximate size of each block, in bytes
+   * @return cache access service backed by {@link LruBlockCache}
+   */
+  public static CacheAccessService lru(long maxSize, long blockSize) {
+    return lruInstance(maxSize, blockSize).service();
+  }
+
+  /**
+   * Creates a test instance containing {@link LruBlockCache} and its {@link 
CacheAccessService}
+   * facade.
+   * @param maxSize   maximum size of cache, in bytes
+   * @param blockSize approximate size of each block, in bytes
+   * @return test instance
+   */
+  public static CacheAccessServiceTestInstance<LruBlockCache> lruInstance(long 
maxSize,
+    long blockSize) {
+    return new CacheAccessServiceTestInstance<>(new LruBlockCache(maxSize, 
blockSize));
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by {@link LruBlockCache}.
+   * @param maxSize        maximum size of cache, in bytes
+   * @param blockSize      approximate size of each block, in bytes
+   * @param evictionThread whether to start the eviction thread
+   * @return cache access service backed by {@link LruBlockCache}
+   */
+  public static CacheAccessService lru(long maxSize, long blockSize, boolean 
evictionThread) {
+    return lruInstance(maxSize, blockSize, evictionThread).service();
+  }
+
+  /**
+   * Creates a test instance containing {@link LruBlockCache} and its {@link 
CacheAccessService}
+   * facade.
+   * @param maxSize        maximum size of cache, in bytes
+   * @param blockSize      approximate size of each block, in bytes
+   * @param evictionThread whether to start the eviction thread
+   * @return test instance
+   */
+  public static CacheAccessServiceTestInstance<LruBlockCache> lruInstance(long 
maxSize,
+    long blockSize, boolean evictionThread) {
+    return new CacheAccessServiceTestInstance<>(
+      new LruBlockCache(maxSize, blockSize, evictionThread));
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by {@link LruBlockCache}.
+   * @param maxSize        maximum size of cache, in bytes
+   * @param blockSize      approximate size of each block, in bytes
+   * @param evictionThread whether to start the eviction thread
+   * @param conf           configuration used by the cache constructor
+   * @return cache access service backed by {@link LruBlockCache}
+   * @throws NullPointerException if {@code conf} is {@code null}
+   */
+  public static CacheAccessService lru(long maxSize, long blockSize, boolean 
evictionThread,
+    Configuration conf) {
+    return lruInstance(maxSize, blockSize, evictionThread, conf).service();
+  }
+
+  /**
+   * Creates a test instance containing {@link LruBlockCache} and its {@link 
CacheAccessService}
+   * facade.
+   * @param maxSize        maximum size of cache, in bytes
+   * @param blockSize      approximate size of each block, in bytes
+   * @param evictionThread whether to start the eviction thread
+   * @param conf           configuration used by the cache constructor
+   * @return test instance
+   * @throws NullPointerException if {@code conf} is {@code null}
+   */
+  public static CacheAccessServiceTestInstance<LruBlockCache> lruInstance(long 
maxSize,
+    long blockSize, boolean evictionThread, Configuration conf) {
+    Objects.requireNonNull(conf, "conf must not be null");
+    return new CacheAccessServiceTestInstance<>(
+      new LruBlockCache(maxSize, blockSize, evictionThread, conf));
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by {@link LruBlockCache}.
+   * @param maxSize   maximum size of cache, in bytes
+   * @param blockSize approximate size of each block, in bytes
+   * @param conf      configuration used by the cache constructor
+   * @return cache access service backed by {@link LruBlockCache}
+   * @throws NullPointerException if {@code conf} is {@code null}
+   */
+  public static CacheAccessService lru(long maxSize, long blockSize, 
Configuration conf) {
+    return lruInstance(maxSize, blockSize, conf).service();
+  }
+
+  /**
+   * Creates a test instance containing {@link LruBlockCache} and its {@link 
CacheAccessService}
+   * facade.
+   * @param maxSize   maximum size of cache, in bytes
+   * @param blockSize approximate size of each block, in bytes
+   * @param conf      configuration used by the cache constructor
+   * @return test instance
+   * @throws NullPointerException if {@code conf} is {@code null}
+   */
+  public static CacheAccessServiceTestInstance<LruBlockCache> lruInstance(long 
maxSize,
+    long blockSize, Configuration conf) {
+    Objects.requireNonNull(conf, "conf must not be null");
+    return new CacheAccessServiceTestInstance<>(new LruBlockCache(maxSize, 
blockSize, conf));
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by a fully configured {@link 
LruBlockCache}.
+   * @param maxSize             maximum size of this cache, in bytes
+   * @param blockSize           expected average size of blocks, in bytes
+   * @param evictionThread      whether to run evictions in a background thread
+   * @param mapInitialSize      initial size of the backing concurrent map
+   * @param mapLoadFactor       initial load factor of the backing concurrent 
map
+   * @param mapConcurrencyLevel initial concurrency level of the backing 
concurrent map
+   * @param minFactor           percentage of total size to evict down to
+   * @param acceptableFactor    percentage of total size that triggers eviction
+   * @param singleFactor        percentage of total size for single-access 
blocks
+   * @param multiFactor         percentage of total size for multi-access 
blocks
+   * @param memoryFactor        percentage of total size for in-memory blocks
+   * @param hardLimitFactor     hard capacity limit factor
+   * @param forceInMemory       whether in-memory HFile data blocks have 
higher priority
+   * @param maxBlockSize        maximum block size accepted by the cache
+   * @return cache access service backed by {@link LruBlockCache}
+   */
+  public static CacheAccessService lru(long maxSize, long blockSize, boolean 
evictionThread,
+    int mapInitialSize, float mapLoadFactor, int mapConcurrencyLevel, float 
minFactor,
+    float acceptableFactor, float singleFactor, float multiFactor, float 
memoryFactor,
+    float hardLimitFactor, boolean forceInMemory, long maxBlockSize) {
+    return lruInstance(maxSize, blockSize, evictionThread, mapInitialSize, 
mapLoadFactor,
+      mapConcurrencyLevel, minFactor, acceptableFactor, singleFactor, 
multiFactor, memoryFactor,
+      hardLimitFactor, forceInMemory, maxBlockSize).service();
+  }
+
+  /**
+   * Creates a test instance containing a fully configured {@link 
LruBlockCache} and its
+   * {@link CacheAccessService} facade.
+   * @param maxSize             maximum size of this cache, in bytes
+   * @param blockSize           expected average size of blocks, in bytes
+   * @param evictionThread      whether to run evictions in a background thread
+   * @param mapInitialSize      initial size of the backing concurrent map
+   * @param mapLoadFactor       initial load factor of the backing concurrent 
map
+   * @param mapConcurrencyLevel initial concurrency level of the backing 
concurrent map
+   * @param minFactor           percentage of total size to evict down to
+   * @param acceptableFactor    percentage of total size that triggers eviction
+   * @param singleFactor        percentage of total size for single-access 
blocks
+   * @param multiFactor         percentage of total size for multi-access 
blocks
+   * @param memoryFactor        percentage of total size for in-memory blocks
+   * @param hardLimitFactor     hard capacity limit factor
+   * @param forceInMemory       whether in-memory HFile data blocks have 
higher priority
+   * @param maxBlockSize        maximum block size accepted by the cache
+   * @return test instance
+   */
+  public static CacheAccessServiceTestInstance<LruBlockCache> lruInstance(long 
maxSize,
+    long blockSize, boolean evictionThread, int mapInitialSize, float 
mapLoadFactor,
+    int mapConcurrencyLevel, float minFactor, float acceptableFactor, float 
singleFactor,
+    float multiFactor, float memoryFactor, float hardLimitFactor, boolean 
forceInMemory,
+    long maxBlockSize) {
+    return new CacheAccessServiceTestInstance<>(
+      new LruBlockCache(maxSize, blockSize, evictionThread, mapInitialSize, 
mapLoadFactor,
+        mapConcurrencyLevel, minFactor, acceptableFactor, singleFactor, 
multiFactor, memoryFactor,
+        hardLimitFactor, forceInMemory, maxBlockSize));
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by {@link 
LruAdaptiveBlockCache}.
+   * @param maxSize   maximum size of cache, in bytes
+   * @param blockSize approximate size of each block, in bytes
+   * @return cache access service backed by {@link LruAdaptiveBlockCache}
+   */
+  public static CacheAccessService lruAdaptive(long maxSize, long blockSize) {
+    return lruAdaptiveInstance(maxSize, blockSize).service();
+  }
+
+  /**
+   * Creates a test instance containing {@link LruAdaptiveBlockCache} and its
+   * {@link CacheAccessService} facade.
+   * @param maxSize   maximum size of cache, in bytes
+   * @param blockSize approximate size of each block, in bytes
+   * @return test instance
+   */
+  public static CacheAccessServiceTestInstance<LruAdaptiveBlockCache>
+    lruAdaptiveInstance(long maxSize, long blockSize) {
+    return new CacheAccessServiceTestInstance<>(new 
LruAdaptiveBlockCache(maxSize, blockSize));
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by {@link 
LruAdaptiveBlockCache}.
+   * @param maxSize        maximum size of cache, in bytes
+   * @param blockSize      approximate size of each block, in bytes
+   * @param evictionThread whether to start the eviction thread
+   * @return cache access service backed by {@link LruAdaptiveBlockCache}
+   */
+  public static CacheAccessService lruAdaptive(long maxSize, long blockSize,
+    boolean evictionThread) {
+    return lruAdaptiveInstance(maxSize, blockSize, evictionThread).service();
+  }
+
+  /**
+   * Creates a test instance containing {@link LruAdaptiveBlockCache} and its
+   * {@link CacheAccessService} facade.
+   * @param maxSize        maximum size of cache, in bytes
+   * @param blockSize      approximate size of each block, in bytes
+   * @param evictionThread whether to start the eviction thread
+   * @return test instance
+   */
+  public static CacheAccessServiceTestInstance<LruAdaptiveBlockCache>
+    lruAdaptiveInstance(long maxSize, long blockSize, boolean evictionThread) {
+    return new CacheAccessServiceTestInstance<>(
+      new LruAdaptiveBlockCache(maxSize, blockSize, evictionThread));
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by {@link 
LruAdaptiveBlockCache}.
+   * @param maxSize        maximum size of cache, in bytes
+   * @param blockSize      approximate size of each block, in bytes
+   * @param evictionThread whether to start the eviction thread
+   * @param conf           configuration used by the cache constructor
+   * @return cache access service backed by {@link LruAdaptiveBlockCache}
+   * @throws NullPointerException if {@code conf} is {@code null}
+   */
+  public static CacheAccessService lruAdaptive(long maxSize, long blockSize, 
boolean evictionThread,
+    Configuration conf) {
+    return lruAdaptiveInstance(maxSize, blockSize, evictionThread, 
conf).service();
+  }
+
+  /**
+   * Creates a test instance containing {@link LruAdaptiveBlockCache} and its
+   * {@link CacheAccessService} facade.
+   * @param maxSize        maximum size of cache, in bytes
+   * @param blockSize      approximate size of each block, in bytes
+   * @param evictionThread whether to start the eviction thread
+   * @param conf           configuration used by the cache constructor
+   * @return test instance
+   * @throws NullPointerException if {@code conf} is {@code null}
+   */
+  public static CacheAccessServiceTestInstance<LruAdaptiveBlockCache>
+    lruAdaptiveInstance(long maxSize, long blockSize, boolean evictionThread, 
Configuration conf) {
+    Objects.requireNonNull(conf, "conf must not be null");
+    return new CacheAccessServiceTestInstance<>(
+      new LruAdaptiveBlockCache(maxSize, blockSize, evictionThread, conf));
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by {@link 
LruAdaptiveBlockCache}.
+   * @param maxSize   maximum size of cache, in bytes
+   * @param blockSize approximate size of each block, in bytes
+   * @param conf      configuration used by the cache constructor
+   * @return cache access service backed by {@link LruAdaptiveBlockCache}
+   * @throws NullPointerException if {@code conf} is {@code null}
+   */
+  public static CacheAccessService lruAdaptive(long maxSize, long blockSize, 
Configuration conf) {
+    return lruAdaptiveInstance(maxSize, blockSize, conf).service();
+  }
+
+  /**
+   * Creates a test instance containing {@link LruAdaptiveBlockCache} and its
+   * {@link CacheAccessService} facade.
+   * @param maxSize   maximum size of cache, in bytes
+   * @param blockSize approximate size of each block, in bytes
+   * @param conf      configuration used by the cache constructor
+   * @return test instance
+   * @throws NullPointerException if {@code conf} is {@code null}
+   */
+  public static CacheAccessServiceTestInstance<LruAdaptiveBlockCache>
+    lruAdaptiveInstance(long maxSize, long blockSize, Configuration conf) {
+    Objects.requireNonNull(conf, "conf must not be null");
+    return new CacheAccessServiceTestInstance<>(
+      new LruAdaptiveBlockCache(maxSize, blockSize, conf));
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by a fully configured
+   * {@link LruAdaptiveBlockCache}.
+   * @param maxSize                          maximum size of this cache, in 
bytes
+   * @param blockSize                        expected average size of blocks, 
in bytes
+   * @param evictionThread                   whether to run evictions in a 
background thread
+   * @param mapInitialSize                   initial size of the backing 
concurrent map
+   * @param mapLoadFactor                    initial load factor of the 
backing concurrent map
+   * @param mapConcurrencyLevel              initial concurrency level of the 
backing concurrent map
+   * @param minFactor                        percentage of total size to evict 
down to
+   * @param acceptableFactor                 percentage of total size that 
triggers eviction
+   * @param singleFactor                     percentage of total size for 
single-access blocks
+   * @param multiFactor                      percentage of total size for 
multi-access blocks
+   * @param memoryFactor                     percentage of total size for 
in-memory blocks
+   * @param hardLimitFactor                  hard capacity limit factor
+   * @param forceInMemory                    whether in-memory HFile data 
blocks have higher
+   *                                         priority
+   * @param maxBlockSize                     maximum block size accepted by 
the cache
+   * @param heavyEvictionCountLimit          threshold count for 
heavy-eviction adaptive behavior
+   * @param heavyEvictionMbSizeLimit         threshold size for heavy-eviction 
adaptive behavior
+   * @param heavyEvictionOverheadCoefficient coefficient controlling adaptive 
reduction
+   *                                         aggressiveness
+   * @return cache access service backed by {@link LruAdaptiveBlockCache}
+   */
+  public static CacheAccessService lruAdaptive(long maxSize, long blockSize, 
boolean evictionThread,
+    int mapInitialSize, float mapLoadFactor, int mapConcurrencyLevel, float 
minFactor,
+    float acceptableFactor, float singleFactor, float multiFactor, float 
memoryFactor,
+    float hardLimitFactor, boolean forceInMemory, long maxBlockSize, int 
heavyEvictionCountLimit,
+    long heavyEvictionMbSizeLimit, float heavyEvictionOverheadCoefficient) {
+    return lruAdaptiveInstance(maxSize, blockSize, evictionThread, 
mapInitialSize, mapLoadFactor,
+      mapConcurrencyLevel, minFactor, acceptableFactor, singleFactor, 
multiFactor, memoryFactor,
+      hardLimitFactor, forceInMemory, maxBlockSize, heavyEvictionCountLimit,
+      heavyEvictionMbSizeLimit, heavyEvictionOverheadCoefficient).service();
+  }
+
+  /**
+   * Creates a test instance containing a fully configured {@link 
LruAdaptiveBlockCache} and its
+   * {@link CacheAccessService} facade.
+   * @param maxSize                          maximum size of this cache, in 
bytes
+   * @param blockSize                        expected average size of blocks, 
in bytes
+   * @param evictionThread                   whether to run evictions in a 
background thread
+   * @param mapInitialSize                   initial size of the backing 
concurrent map
+   * @param mapLoadFactor                    initial load factor of the 
backing concurrent map
+   * @param mapConcurrencyLevel              initial concurrency level of the 
backing concurrent map
+   * @param minFactor                        percentage of total size to evict 
down to
+   * @param acceptableFactor                 percentage of total size that 
triggers eviction
+   * @param singleFactor                     percentage of total size for 
single-access blocks
+   * @param multiFactor                      percentage of total size for 
multi-access blocks
+   * @param memoryFactor                     percentage of total size for 
in-memory blocks
+   * @param hardLimitFactor                  hard capacity limit factor
+   * @param forceInMemory                    whether in-memory HFile data 
blocks have higher
+   *                                         priority
+   * @param maxBlockSize                     maximum block size accepted by 
the cache
+   * @param heavyEvictionCountLimit          threshold count for 
heavy-eviction adaptive behavior
+   * @param heavyEvictionMbSizeLimit         threshold size for heavy-eviction 
adaptive behavior
+   * @param heavyEvictionOverheadCoefficient coefficient controlling adaptive 
reduction
+   *                                         aggressiveness
+   * @return test instance
+   */
+  public static CacheAccessServiceTestInstance<LruAdaptiveBlockCache> 
lruAdaptiveInstance(
+    long maxSize, long blockSize, boolean evictionThread, int mapInitialSize, 
float mapLoadFactor,
+    int mapConcurrencyLevel, float minFactor, float acceptableFactor, float 
singleFactor,
+    float multiFactor, float memoryFactor, float hardLimitFactor, boolean 
forceInMemory,
+    long maxBlockSize, int heavyEvictionCountLimit, long 
heavyEvictionMbSizeLimit,
+    float heavyEvictionOverheadCoefficient) {
+    return new CacheAccessServiceTestInstance<>(
+      new LruAdaptiveBlockCache(maxSize, blockSize, evictionThread, 
mapInitialSize, mapLoadFactor,
+        mapConcurrencyLevel, minFactor, acceptableFactor, singleFactor, 
multiFactor, memoryFactor,
+        hardLimitFactor, forceInMemory, maxBlockSize, heavyEvictionCountLimit,
+        heavyEvictionMbSizeLimit, heavyEvictionOverheadCoefficient));
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by {@link TinyLfuBlockCache}.
+   * @param maximumSizeInBytes maximum size of this cache, in bytes
+   * @param avgBlockSize       expected average size of blocks, in bytes
+   * @param executor           executor used by the underlying Caffeine cache
+   * @param conf               configuration used by the cache constructor
+   * @return cache access service backed by {@link TinyLfuBlockCache}
+   * @throws NullPointerException if {@code executor} or {@code conf} is 
{@code null}
+   */
+  public static CacheAccessService tinyLfu(long maximumSizeInBytes, long 
avgBlockSize,
+    Executor executor, Configuration conf) {
+    return tinyLfuInstance(maximumSizeInBytes, avgBlockSize, executor, 
conf).service();
+  }
+
+  /**
+   * Creates a test instance containing {@link TinyLfuBlockCache} and its 
{@link CacheAccessService}
+   * facade.
+   * @param maximumSizeInBytes maximum size of this cache, in bytes
+   * @param avgBlockSize       expected average size of blocks, in bytes
+   * @param executor           executor used by the underlying Caffeine cache
+   * @param conf               configuration used by the cache constructor
+   * @return test instance
+   * @throws NullPointerException if {@code executor} or {@code conf} is 
{@code null}
+   */
+  public static CacheAccessServiceTestInstance<TinyLfuBlockCache> 
tinyLfuInstance(
+    long maximumSizeInBytes, long avgBlockSize, Executor executor, 
Configuration conf) {
+    Objects.requireNonNull(executor, "executor must not be null");
+    Objects.requireNonNull(conf, "conf must not be null");
+    return new CacheAccessServiceTestInstance<>(
+      new TinyLfuBlockCache(maximumSizeInBytes, avgBlockSize, executor, conf));
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by {@link TinyLfuBlockCache}.
+   * @param maximumSizeInBytes maximum size of this cache, in bytes
+   * @param avgBlockSize       expected average size of blocks, in bytes
+   * @param maxBlockSize       maximum size of a block, in bytes
+   * @param executor           executor used by the underlying Caffeine cache
+   * @return cache access service backed by {@link TinyLfuBlockCache}
+   * @throws NullPointerException if {@code executor} is {@code null}
+   */
+  public static CacheAccessService tinyLfu(long maximumSizeInBytes, long 
avgBlockSize,
+    long maxBlockSize, Executor executor) {
+    return tinyLfuInstance(maximumSizeInBytes, avgBlockSize, maxBlockSize, 
executor).service();
+  }
+
+  /**
+   * Creates a test instance containing {@link TinyLfuBlockCache} and its 
{@link CacheAccessService}
+   * facade.
+   * @param maximumSizeInBytes maximum size of this cache, in bytes
+   * @param avgBlockSize       expected average size of blocks, in bytes
+   * @param maxBlockSize       maximum size of a block, in bytes
+   * @param executor           executor used by the underlying Caffeine cache
+   * @return test instance
+   * @throws NullPointerException if {@code executor} is {@code null}
+   */
+  public static CacheAccessServiceTestInstance<TinyLfuBlockCache> 
tinyLfuInstance(
+    long maximumSizeInBytes, long avgBlockSize, long maxBlockSize, Executor 
executor) {
+    Objects.requireNonNull(executor, "executor must not be null");
+    return new CacheAccessServiceTestInstance<>(
+      new TinyLfuBlockCache(maximumSizeInBytes, avgBlockSize, maxBlockSize, 
executor));
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by {@link BucketCache}.
+   * @param ioEngineName    IO engine name
+   * @param capacity        cache capacity, in bytes
+   * @param blockSize       expected block size, in bytes
+   * @param bucketSizes     configured bucket sizes, or {@code null} to use 
defaults
+   * @param writerThreadNum number of writer threads
+   * @param writerQLen      writer queue length
+   * @param persistencePath persistence path, or {@code null} for 
non-persistent cache
+   * @return cache access service backed by {@link BucketCache}
+   * @throws IOException          if the bucket cache cannot be created
+   * @throws NullPointerException if {@code ioEngineName} is {@code null}
+   */
+  public static CacheAccessService bucket(String ioEngineName, long capacity, 
int blockSize,
+    int[] bucketSizes, int writerThreadNum, int writerQLen, String 
persistencePath)
+    throws IOException {
+    return bucketInstance(ioEngineName, capacity, blockSize, bucketSizes, 
writerThreadNum,
+      writerQLen, persistencePath).service();
+  }
+
+  /**
+   * Creates a test instance containing {@link BucketCache} and its {@link 
CacheAccessService}
+   * facade.
+   * @param ioEngineName    IO engine name
+   * @param capacity        cache capacity, in bytes
+   * @param blockSize       expected block size, in bytes
+   * @param bucketSizes     configured bucket sizes, or {@code null} to use 
defaults
+   * @param writerThreadNum number of writer threads
+   * @param writerQLen      writer queue length
+   * @param persistencePath persistence path, or {@code null} for 
non-persistent cache
+   * @return test instance
+   * @throws IOException          if the bucket cache cannot be created
+   * @throws NullPointerException if {@code ioEngineName} is {@code null}
+   */
+  public static CacheAccessServiceTestInstance<BucketCache> 
bucketInstance(String ioEngineName,
+    long capacity, int blockSize, int[] bucketSizes, int writerThreadNum, int 
writerQLen,
+    String persistencePath) throws IOException {
+    Objects.requireNonNull(ioEngineName, "ioEngineName must not be null");
+    return new CacheAccessServiceTestInstance<>(new BucketCache(ioEngineName, 
capacity, blockSize,
+      bucketSizes, writerThreadNum, writerQLen, persistencePath));
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by {@link BucketCache}.
+   * @param ioEngineName               IO engine name
+   * @param capacity                   cache capacity, in bytes
+   * @param blockSize                  expected block size, in bytes
+   * @param bucketSizes                configured bucket sizes, or {@code 
null} to use defaults
+   * @param writerThreadNum            number of writer threads
+   * @param writerQLen                 writer queue length
+   * @param persistencePath            persistence path, or {@code null} for 
non-persistent cache
+   * @param ioErrorsTolerationDuration duration for tolerating IO engine errors
+   * @param conf                       configuration used by the bucket cache
+   * @return cache access service backed by {@link BucketCache}
+   * @throws IOException          if the bucket cache cannot be created
+   * @throws NullPointerException if {@code ioEngineName} or {@code conf} is 
{@code null}
+   */
+  public static CacheAccessService bucket(String ioEngineName, long capacity, 
int blockSize,
+    int[] bucketSizes, int writerThreadNum, int writerQLen, String 
persistencePath,
+    int ioErrorsTolerationDuration, Configuration conf) throws IOException {
+    return bucketInstance(ioEngineName, capacity, blockSize, bucketSizes, 
writerThreadNum,
+      writerQLen, persistencePath, ioErrorsTolerationDuration, conf).service();
+  }
+
+  /**
+   * Creates a test instance containing {@link BucketCache} and its {@link 
CacheAccessService}
+   * facade.
+   * @param ioEngineName               IO engine name
+   * @param capacity                   cache capacity, in bytes
+   * @param blockSize                  expected block size, in bytes
+   * @param bucketSizes                configured bucket sizes, or {@code 
null} to use defaults
+   * @param writerThreadNum            number of writer threads
+   * @param writerQLen                 writer queue length
+   * @param persistencePath            persistence path, or {@code null} for 
non-persistent cache
+   * @param ioErrorsTolerationDuration duration for tolerating IO engine errors
+   * @param conf                       configuration used by the bucket cache
+   * @return test instance
+   * @throws IOException          if the bucket cache cannot be created
+   * @throws NullPointerException if {@code ioEngineName} or {@code conf} is 
{@code null}
+   */
+  public static CacheAccessServiceTestInstance<BucketCache> 
bucketInstance(String ioEngineName,
+    long capacity, int blockSize, int[] bucketSizes, int writerThreadNum, int 
writerQLen,
+    String persistencePath, int ioErrorsTolerationDuration, Configuration 
conf) throws IOException {
+    Objects.requireNonNull(ioEngineName, "ioEngineName must not be null");
+    Objects.requireNonNull(conf, "conf must not be null");
+    return new CacheAccessServiceTestInstance<>(new BucketCache(ioEngineName, 
capacity, blockSize,
+      bucketSizes, writerThreadNum, writerQLen, persistencePath, 
ioErrorsTolerationDuration, conf));
+  }
+
+  /**
+   * Creates a {@link CacheAccessService} backed by {@link BucketCache}.
+   * @param ioEngineName               IO engine name
+   * @param capacity                   cache capacity, in bytes
+   * @param blockSize                  expected block size, in bytes
+   * @param bucketSizes                configured bucket sizes, or {@code 
null} to use defaults
+   * @param writerThreadNum            number of writer threads
+   * @param writerQLen                 writer queue length
+   * @param persistencePath            persistence path, or {@code null} for 
non-persistent cache
+   * @param ioErrorsTolerationDuration duration for tolerating IO engine errors
+   * @param conf                       configuration used by the bucket cache
+   * @param onlineRegions              online regions map used by the bucket 
cache, or {@code null}
+   * @return cache access service backed by {@link BucketCache}
+   * @throws IOException          if the bucket cache cannot be created
+   * @throws NullPointerException if {@code ioEngineName} or {@code conf} is 
{@code null}
+   */
+  public static CacheAccessService bucket(String ioEngineName, long capacity, 
int blockSize,
+    int[] bucketSizes, int writerThreadNum, int writerQLen, String 
persistencePath,
+    int ioErrorsTolerationDuration, Configuration conf, Map<String, HRegion> 
onlineRegions)
+    throws IOException {
+    return bucketInstance(ioEngineName, capacity, blockSize, bucketSizes, 
writerThreadNum,
+      writerQLen, persistencePath, ioErrorsTolerationDuration, conf, 
onlineRegions).service();
+  }
+
+  /**
+   * Creates a test instance containing {@link BucketCache} and its {@link 
CacheAccessService}
+   * facade.
+   * @param ioEngineName               IO engine name
+   * @param capacity                   cache capacity, in bytes
+   * @param blockSize                  expected block size, in bytes
+   * @param bucketSizes                configured bucket sizes, or {@code 
null} to use defaults
+   * @param writerThreadNum            number of writer threads
+   * @param writerQLen                 writer queue length
+   * @param persistencePath            persistence path, or {@code null} for 
non-persistent cache
+   * @param ioErrorsTolerationDuration duration for tolerating IO engine errors
+   * @param conf                       configuration used by the bucket cache
+   * @param onlineRegions              online regions map used by the bucket 
cache, or {@code null}
+   * @return test instance
+   * @throws IOException          if the bucket cache cannot be created
+   * @throws NullPointerException if {@code ioEngineName} or {@code conf} is 
{@code null}
+   */
+  public static CacheAccessServiceTestInstance<BucketCache> 
bucketInstance(String ioEngineName,
+    long capacity, int blockSize, int[] bucketSizes, int writerThreadNum, int 
writerQLen,
+    String persistencePath, int ioErrorsTolerationDuration, Configuration conf,
+    Map<String, HRegion> onlineRegions) throws IOException {
+    Objects.requireNonNull(ioEngineName, "ioEngineName must not be null");
+    Objects.requireNonNull(conf, "conf must not be null");
+    return new CacheAccessServiceTestInstance<>(
+      new BucketCache(ioEngineName, capacity, blockSize, bucketSizes, 
writerThreadNum, writerQLen,
+        persistencePath, ioErrorsTolerationDuration, conf, onlineRegions));
+  }
+}
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/cache/CacheAccessServiceTestInstance.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/cache/CacheAccessServiceTestInstance.java
new file mode 100644
index 00000000000..b9d8789f3c1
--- /dev/null
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/cache/CacheAccessServiceTestInstance.java
@@ -0,0 +1,64 @@
+/*
+ * 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.hadoop.hbase.io.hfile.cache;
+
+import java.util.Objects;
+import org.apache.hadoop.hbase.io.hfile.BlockCache;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Test helper that keeps a concrete {@link BlockCache} together with the 
{@link CacheAccessService}
+ * facade backed by that cache.
+ * <p>
+ * This is useful for tests that need to exercise production code through
+ * {@link CacheAccessService}, while still inspecting implementation-specific 
state through the
+ * concrete cache instance.
+ * </p>
+ * @param <T> concrete block cache type
+ */
[email protected]
+public final class CacheAccessServiceTestInstance<T extends BlockCache> {
+
+  private final T blockCache;
+  private final CacheAccessService cacheAccessService;
+
+  CacheAccessServiceTestInstance(T blockCache) {
+    this.blockCache = Objects.requireNonNull(blockCache, "blockCache must not 
be null");
+    this.cacheAccessService = CacheAccessServices.fromBlockCache(blockCache);
+  }
+
+  /**
+   * Returns the concrete block cache instance.
+   * <p>
+   * Tests should use this only when they need implementation-specific 
inspection or assertions.
+   * Normal cache access should go through {@link #service()}.
+   * </p>
+   * @return concrete block cache
+   */
+  public T blockCache() {
+    return blockCache;
+  }
+
+  /**
+   * Returns the {@link CacheAccessService} facade backed by the concrete 
block cache.
+   * @return cache access service
+   */
+  public CacheAccessService service() {
+    return cacheAccessService;
+  }
+}
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/cache/TestCacheAccessServiceTestFactory.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/cache/TestCacheAccessServiceTestFactory.java
new file mode 100644
index 00000000000..ac0b09bee21
--- /dev/null
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/cache/TestCacheAccessServiceTestFactory.java
@@ -0,0 +1,215 @@
+/*
+ * 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.hadoop.hbase.io.hfile.cache;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.util.concurrent.Executor;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.io.hfile.LruAdaptiveBlockCache;
+import org.apache.hadoop.hbase.io.hfile.LruBlockCache;
+import org.apache.hadoop.hbase.io.hfile.TinyLfuBlockCache;
+import org.apache.hadoop.hbase.io.hfile.bucket.BucketCache;
+import org.apache.hadoop.hbase.testclassification.IOTests;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link CacheAccessServiceTestFactory}.
+ */
+@Tag(IOTests.TAG)
+@Tag(SmallTests.TAG)
+public class TestCacheAccessServiceTestFactory {
+
+  private static final long MAX_SIZE = 1024L * 1024L;
+  private static final long BUCKET_CACHE_SIZE = 64L * 1024L * 1024L;
+  private static final long BLOCK_SIZE = 4096L;
+  private static final Executor DIRECT_EXECUTOR = Runnable::run;
+
+  @Test
+  void testDisabled() {
+    CacheAccessService service = CacheAccessServiceTestFactory.disabled();
+
+    assertNotNull(service);
+    assertFalse(service.isCacheEnabled());
+  }
+
+  @Test
+  void testFromBlockCacheRejectsNull() {
+    assertThrows(NullPointerException.class,
+      () -> CacheAccessServiceTestFactory.fromBlockCache(null));
+  }
+
+  @Test
+  void testFromConfigurationRejectsNull() {
+    assertThrows(NullPointerException.class,
+      () -> CacheAccessServiceTestFactory.fromConfiguration(null));
+  }
+
+  @Test
+  void testLruServiceOnlyFactory() {
+    Configuration conf = HBaseConfiguration.create();
+    CacheAccessService service =
+      CacheAccessServiceTestFactory.lru(MAX_SIZE, BLOCK_SIZE, true, conf);
+
+    try {
+      assertNotNull(service);
+      assertTrue(service.isCacheEnabled());
+    } finally {
+      service.shutdown();
+    }
+  }
+
+  @Test
+  void testLruInstanceFactory() {
+    Configuration conf = HBaseConfiguration.create();
+    CacheAccessServiceTestInstance<LruBlockCache> instance =
+      CacheAccessServiceTestFactory.lruInstance(MAX_SIZE, BLOCK_SIZE, true, 
conf);
+
+    try {
+      assertNotNull(instance);
+      assertNotNull(instance.blockCache());
+      assertNotNull(instance.service());
+      assertInstanceOf(LruBlockCache.class, instance.blockCache());
+      assertTrue(instance.service().isCacheEnabled());
+    } finally {
+      instance.service().shutdown();
+    }
+  }
+
+  @Test
+  void testLruAdaptiveServiceOnlyFactory() {
+    Configuration conf = HBaseConfiguration.create();
+    CacheAccessService service =
+      CacheAccessServiceTestFactory.lruAdaptive(MAX_SIZE, BLOCK_SIZE, true, 
conf);
+
+    try {
+      assertNotNull(service);
+      assertTrue(service.isCacheEnabled());
+    } finally {
+      service.shutdown();
+    }
+  }
+
+  @Test
+  void testLruAdaptiveInstanceFactory() {
+    Configuration conf = HBaseConfiguration.create();
+    CacheAccessServiceTestInstance<LruAdaptiveBlockCache> instance =
+      CacheAccessServiceTestFactory.lruAdaptiveInstance(MAX_SIZE, BLOCK_SIZE, 
true, conf);
+
+    try {
+      assertNotNull(instance);
+      assertNotNull(instance.blockCache());
+      assertNotNull(instance.service());
+      assertInstanceOf(LruAdaptiveBlockCache.class, instance.blockCache());
+      assertTrue(instance.service().isCacheEnabled());
+    } finally {
+      instance.service().shutdown();
+    }
+  }
+
+  @Test
+  void testTinyLfuServiceOnlyFactory() {
+    Configuration conf = HBaseConfiguration.create();
+    CacheAccessService service =
+      CacheAccessServiceTestFactory.tinyLfu(MAX_SIZE, BLOCK_SIZE, 
DIRECT_EXECUTOR, conf);
+
+    try {
+      assertNotNull(service);
+      assertTrue(service.isCacheEnabled());
+    } finally {
+      service.shutdown();
+    }
+  }
+
+  @Test
+  void testTinyLfuInstanceFactory() {
+    Configuration conf = HBaseConfiguration.create();
+    CacheAccessServiceTestInstance<TinyLfuBlockCache> instance =
+      CacheAccessServiceTestFactory.tinyLfuInstance(MAX_SIZE, BLOCK_SIZE, 
DIRECT_EXECUTOR, conf);
+
+    try {
+      assertNotNull(instance);
+      assertNotNull(instance.blockCache());
+      assertNotNull(instance.service());
+      assertInstanceOf(TinyLfuBlockCache.class, instance.blockCache());
+      assertTrue(instance.service().isCacheEnabled());
+    } finally {
+      instance.service().shutdown();
+    }
+  }
+
+  @Test
+  void testTinyLfuInstanceFactoryRejectsNullExecutor() {
+    assertThrows(NullPointerException.class, () -> 
CacheAccessServiceTestFactory
+      .tinyLfuInstance(MAX_SIZE, BLOCK_SIZE, null, 
HBaseConfiguration.create()));
+  }
+
+  @Test
+  void testTinyLfuInstanceFactoryRejectsNullConfiguration() {
+    assertThrows(NullPointerException.class, () -> 
CacheAccessServiceTestFactory
+      .tinyLfuInstance(MAX_SIZE, BLOCK_SIZE, DIRECT_EXECUTOR, null));
+  }
+
+  @Test
+  void testBucketServiceOnlyFactory() throws IOException {
+    CacheAccessService service = 
CacheAccessServiceTestFactory.bucket("offheap", BUCKET_CACHE_SIZE,
+      (int) BLOCK_SIZE, null, 1, 1, null);
+
+    try {
+      assertNotNull(service);
+      assertTrue(service.isCacheEnabled());
+    } finally {
+      service.shutdown();
+    }
+  }
+
+  @Test
+  void testBucketInstanceFactory() throws IOException {
+    CacheAccessServiceTestInstance<BucketCache> instance = 
CacheAccessServiceTestFactory
+      .bucketInstance("offheap", BUCKET_CACHE_SIZE, (int) BLOCK_SIZE, null, 1, 
1, null);
+
+    try {
+      assertNotNull(instance);
+      assertNotNull(instance.blockCache());
+      assertNotNull(instance.service());
+      assertInstanceOf(BucketCache.class, instance.blockCache());
+      assertTrue(instance.service().isCacheEnabled());
+    } finally {
+      instance.service().shutdown();
+    }
+  }
+
+  @Test
+  void testBucketInstanceFactoryRejectsNullIoEngineName() {
+    assertThrows(NullPointerException.class, () -> 
CacheAccessServiceTestFactory
+      .bucketInstance(null, MAX_SIZE, (int) BLOCK_SIZE, null, 1, 1, null));
+  }
+
+  @Test
+  void testInstanceRejectsNullBlockCache() {
+    assertThrows(NullPointerException.class, () -> new 
CacheAccessServiceTestInstance<>(null));
+  }
+}

Reply via email to