From 5ce738c2b00379edde9e70be829b31fff392aa62 Mon Sep 17 00:00:00 2001 From: John Blum Date: Mon, 17 Jul 2017 20:02:04 -0700 Subject: [PATCH] DATAGEODE-18 - Introduce new Apache Geode Mock Objects infrastructure for Unit Testing. --- .../data/gemfire/CacheFactoryBean.java | 67 +- .../client/ClientCacheFactoryBean.java | 3 +- .../test/mock/MockGemFireObjectsSupport.java | 959 ++++++++++++++++++ .../mock/annotation/EnableGemFireMocking.java | 47 + .../GemFireMockingConfiguration.java | 41 + .../test/mock/annotation/GemFireUnitTest.java | 58 ++ .../MockGemFireObjectsBeanPostProcessor.java | 77 ++ ...eObjectsApplicationContextInitializer.java | 41 + 8 files changed, 1291 insertions(+), 2 deletions(-) create mode 100644 src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java create mode 100644 src/test/java/org/springframework/data/gemfire/test/mock/annotation/EnableGemFireMocking.java create mode 100644 src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireMockingConfiguration.java create mode 100644 src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireUnitTest.java create mode 100644 src/test/java/org/springframework/data/gemfire/test/mock/config/MockGemFireObjectsBeanPostProcessor.java create mode 100644 src/test/java/org/springframework/data/gemfire/test/mock/context/MockGemFireObjectsApplicationContextInitializer.java diff --git a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java index 64c3c1bd..dec12b04 100644 --- a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java @@ -46,6 +46,7 @@ import org.apache.geode.cache.GemFireCache; import org.apache.geode.cache.RegionService; import org.apache.geode.cache.TransactionListener; import org.apache.geode.cache.TransactionWriter; +import org.apache.geode.cache.client.ClientCacheFactory; import org.apache.geode.cache.util.GatewayConflictResolver; import org.apache.geode.distributed.DistributedSystem; import org.apache.geode.internal.datasource.ConfigProperty; @@ -63,6 +64,7 @@ import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer; import org.springframework.data.gemfire.support.AbstractFactoryBeanSupport; import org.springframework.data.gemfire.support.GemfireBeanFactoryLocator; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -119,6 +121,8 @@ public class CacheFactoryBean extends AbstractFactoryBeanSupport private Cache cache; + private CacheFactoryInitializer cacheFactoryInitializer; + private DynamicRegionSupport dynamicRegionSupport; private Float criticalHeapPercentage; @@ -257,6 +261,7 @@ public class CacheFactoryBean extends AbstractFactoryBeanSupport setCache(postProcess(resolveCache())); Optional.ofNullable(this.getCache()).ifPresent(cache -> { + Optional.ofNullable(cache.getDistributedSystem()).map(DistributedSystem::getDistributedMember) .ifPresent(member -> logInfo(() -> String.format("Connected to Distributed System [%1$s] as Member [%2$s]" @@ -266,6 +271,7 @@ public class CacheFactoryBean extends AbstractFactoryBeanSupport logInfo(() -> String.format("%1$s %2$s version [%3$s] Cache [%4$s]", this.cacheResolutionMessagePrefix, apacheGeodeProductName(), apacheGeodeVersion(), cache.getName())); + }); @@ -300,7 +306,7 @@ public class CacheFactoryBean extends AbstractFactoryBeanSupport catch (CacheClosedException ex) { this.cacheResolutionMessagePrefix = "Created new"; initDynamicRegionFactory(); - return (Cache) createCache(prepareFactory(createFactory(resolveProperties()))); + return (Cache) createCache(prepareFactory(initializeFactory(createFactory(resolveProperties())))); } } @@ -355,6 +361,23 @@ public class CacheFactoryBean extends AbstractFactoryBeanSupport return new CacheFactory(gemfireProperties); } + /** + * Initializes the given cache factory with the configured {@link CacheFactoryInitializer}. + * + * @param factory cache factory to initialize; may be {@literal null}. + * @return the given cache factory. + * @see org.springframework.data.gemfire.CacheFactoryBean.CacheFactoryInitializer#initialize(Object) + * @see #getCacheFactoryInitializer() + */ + @Nullable + @SuppressWarnings("unchecked") + protected Object initializeFactory(Object factory) { + + return Optional.ofNullable(getCacheFactoryInitializer()) + .map(cacheFactoryInitializer -> cacheFactoryInitializer.initialize(factory)) + .orElse(factory); + } + /** * Prepares and initializes the {@link CacheFactory} used to create the {@link Cache}. * @@ -751,6 +774,28 @@ public class CacheFactoryBean extends AbstractFactoryBeanSupport return this.properties; } + /** + * Set the {@link CacheFactoryInitializer} that will be called to initialize the cache factory used to create + * the cache constructed by this {@link CacheFactoryBean}. + * + * @param cacheFactoryInitializer {@link CacheFactoryInitializer} configured to initialize the cache factory. + * @see org.springframework.data.gemfire.CacheFactoryBean.CacheFactoryInitializer + */ + public void setCacheFactoryInitializer(CacheFactoryInitializer cacheFactoryInitializer) { + this.cacheFactoryInitializer = cacheFactoryInitializer; + } + + /** + * Return the {@link CacheFactoryInitializer} that will be called to initialize the cache factory used to create + * the cache constructed by this {@link CacheFactoryBean}. + * + * @return the {@link CacheFactoryInitializer} configured to initialize the cache factory. + * @see org.springframework.data.gemfire.CacheFactoryBean.CacheFactoryInitializer + */ + public CacheFactoryInitializer getCacheFactoryInitializer() { + return this.cacheFactoryInitializer; + } + /** * Sets a value to indicate whether the cache will be closed on shutdown of the Spring container. * @@ -1150,6 +1195,26 @@ public class CacheFactoryBean extends AbstractFactoryBeanSupport return this.useClusterConfiguration; } + /** + * Callback interface for initializing either a {@link CacheFactory} or a {@link ClientCacheFactory} instance, + * which is used to create an instance of {@link GemFireCache}. + * + * @see org.apache.geode.cache.CacheFactory + * @see org.apache.geode.cache.client.ClientCacheFactory + */ + public interface CacheFactoryInitializer { + + /** + * Initialize the given cache factory. + * + * @param cacheFactory cache factory to initialize. + * @return the given cache factory. + * @see org.apache.geode.cache.CacheFactory + * @see org.apache.geode.cache.client.ClientCacheFactory + */ + T initialize(T cacheFactory); + } + /* (non-Javadoc) */ public static class DynamicRegionSupport { diff --git a/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java index 806d5b7c..37fd06dc 100644 --- a/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java @@ -136,7 +136,7 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat /* (non-Javadoc) */ private void applyClientCacheConfigurers() { - applyClientCacheConfigurers(this.compositeClientCacheConfigurer); + applyClientCacheConfigurers(getCompositeClientCacheConfigurer()); } /** @@ -193,6 +193,7 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat protected Properties resolveProperties() { Properties gemfireProperties = super.resolveProperties(); + DistributedSystem distributedSystem = getDistributedSystem(); if (GemfireUtils.isConnected(distributedSystem)) { diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java b/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java new file mode 100644 index 00000000..c6bc3d06 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java @@ -0,0 +1,959 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed 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.springframework.data.gemfire.test.mock; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray; +import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.NOT_SUPPORTED; +import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException; +import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newUnsupportedOperationException; + +import java.io.File; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.geode.cache.Cache; +import org.apache.geode.cache.CacheFactory; +import org.apache.geode.cache.DiskStore; +import org.apache.geode.cache.DiskStoreFactory; +import org.apache.geode.cache.GemFireCache; +import org.apache.geode.cache.Region; +import org.apache.geode.cache.RegionAttributes; +import org.apache.geode.cache.RegionService; +import org.apache.geode.cache.client.ClientCache; +import org.apache.geode.cache.client.ClientCacheFactory; +import org.apache.geode.cache.client.Pool; +import org.apache.geode.cache.client.PoolFactory; +import org.apache.geode.cache.control.ResourceManager; +import org.apache.geode.cache.server.CacheServer; +import org.apache.geode.cache.server.ClientSubscriptionConfig; +import org.apache.geode.distributed.DistributedSystem; +import org.apache.geode.pdx.PdxSerializer; +import org.springframework.data.gemfire.server.SubscriptionEvictionPolicy; +import org.springframework.data.gemfire.test.support.FileSystemUtils; + +/** + * The {@link MockGemFireObjectsSupport} class is an abstract base class encapsulating factory methods for creating + * Mock GemFire Objects (e.g. {@link Cache}, {@link ClientCache}, {@link Region}, etc). + * + * @author John Blum + * @see org.mockito.Mockito + * @see org.apache.geode.cache.Cache + * @see org.apache.geode.cache.CacheFactory + * @see org.apache.geode.cache.DiskStore + * @see org.apache.geode.cache.GemFireCache + * @see org.apache.geode.cache.Region + * @see org.apache.geode.cache.client.ClientCache + * @see org.apache.geode.cache.client.ClientCacheFactory + * @see org.apache.geode.cache.control.ResourceManager + * @see org.apache.geode.cache.server.CacheServer + * @see org.apache.geode.cache.server.ClientSubscriptionConfig + * @see org.apache.geode.distributed.DistributedSystem + * @since 2.0.0 + */ +@SuppressWarnings("unused") +public abstract class MockGemFireObjectsSupport { + + private static final Map diskStores = new ConcurrentHashMap<>(); + + private static final Map> regions = new ConcurrentHashMap<>(); + + private static final Map> regionAttributes = new ConcurrentHashMap<>(); + + /* (non-Javadoc) */ + private static boolean isRootRegion(Region region) { + return isRootRegion(region.getFullPath()); + } + + /* (non-Javadoc) */ + private static boolean isRootRegion(String regionPath) { + return (regionPath.lastIndexOf(Region.SEPARATOR) <= 0); + } + + /* (non-Javadoc) */ + @SuppressWarnings("unchecked") + private static T mockCacheApi(T mockGemFireCache) { + + AtomicBoolean copyOnRead = new AtomicBoolean(false); + + DistributedSystem mockDistributedSystem = mockDistributedSystem(); + + ResourceManager mockResourceManager = mockResourceManager(); + + doAnswer(invocation -> { + copyOnRead.set(invocation.getArgument(0)); + return null; + }).when(mockGemFireCache).setCopyOnRead(anyBoolean()); + + doAnswer(invocation -> { + regionAttributes.put(invocation.getArgument(0), invocation.getArgument(1)); + return null; + }).when(mockGemFireCache).setRegionAttributes(anyString(), any(RegionAttributes.class)); + + when(mockGemFireCache.getCopyOnRead()).thenAnswer(invocation -> copyOnRead.get()); + + when(mockGemFireCache.getDistributedSystem()).thenReturn(mockDistributedSystem); + + when(mockGemFireCache.getRegionAttributes(anyString())) + .thenAnswer(invocation -> regionAttributes.get(invocation.getArgument(0))); + + when(mockGemFireCache.getResourceManager()).thenReturn(mockResourceManager); + + when(mockGemFireCache.createDiskStoreFactory()).thenAnswer(invocation -> mockDiskStoreFactory()); + + when(mockGemFireCache.findDiskStore(anyString())) + .thenAnswer(invocation -> diskStores.get(invocation.getArgument(0))); + + when(mockGemFireCache.listRegionAttributes()).thenReturn(Collections.unmodifiableMap(regionAttributes)); + + doThrow(newUnsupportedOperationException(NOT_SUPPORTED)).when(mockGemFireCache) + .loadCacheXml(any(InputStream.class)); + + return mockRegionServiceApi(mockGemFireCache); + } + + /* (non-Javadoc) */ + private static T mockRegionServiceApi(T mockRegionService) { + + AtomicBoolean close = new AtomicBoolean(false); + + doAnswer(invocation -> { + close.set(true); + return null; + }).when(mockRegionService).close(); + + when(mockRegionService.isClosed()).thenAnswer(invocation -> close.get()); + + when(mockRegionService.getCancelCriterion()).thenThrow(newUnsupportedOperationException(NOT_SUPPORTED)); + + when(mockRegionService.getRegion(anyString())).thenAnswer(invocation -> { + + String regionPath = invocation.getArgument(0); + + Optional.ofNullable(regionPath).map(String::trim).filter(it -> !it.isEmpty()) + .map(it -> it.startsWith(Region.SEPARATOR) ? it : String.format("%1$s%2$s", Region.SEPARATOR, it)) + .orElseThrow(() -> newIllegalArgumentException("Region path [%s] is not valid", regionPath)); + + return regions.get(regionPath); + }); + + when(mockRegionService.createPdxEnum(anyString(), anyString(), anyInt())) + .thenThrow(newUnsupportedOperationException(NOT_SUPPORTED)); + + when(mockRegionService.createPdxInstanceFactory(anyString())) + .thenThrow(newUnsupportedOperationException(NOT_SUPPORTED)); + + when(mockRegionService.rootRegions()).thenAnswer(invocation -> { + + Set> rootRegions = new HashSet<>(); + + for (Region region : regions.values()) { + if (isRootRegion(region)) { + rootRegions.add(region); + } + } + + return rootRegions; + }); + + return mockRegionService; + } + + public static ClientCache mockClientCache() { + + ClientCache mockClientCache = mock(ClientCache.class); + + return mockCacheApi(mockClientCache); + } + + public static GemFireCache mockGemFireCache() { + + GemFireCache mockGemFireCache = mock(GemFireCache.class); + + return mockCacheApi(mockGemFireCache); + } + + public static Cache mockPeerCache() { + + Cache mockCache = mock(Cache.class); + + AtomicInteger lockLease = new AtomicInteger(); + AtomicInteger lockTimeout = new AtomicInteger(); + AtomicInteger messageSyncInterval = new AtomicInteger(); + AtomicInteger searchTimeout = new AtomicInteger(); + + List cacheServers = new ArrayList<>(); + + when(mockCache.addCacheServer()).thenAnswer(invocation -> { + + CacheServer mockCacheServer = mockCacheServer(); + + cacheServers.add(mockCacheServer); + + return mockCacheServer; + }); + + doAnswer(invocation -> { + lockLease.set(invocation.getArgument(0)); + return null; + }).when(mockCache).setLockLease(anyInt()); + + doAnswer(invocation -> { + lockTimeout.set(invocation.getArgument(0)); + return null; + }).when(mockCache).setLockTimeout(anyInt()); + + doAnswer(invocation -> { + messageSyncInterval.set(invocation.getArgument(0)); + return null; + }).when(mockCache).setMessageSyncInterval(anyInt()); + + doAnswer(invocation -> { + searchTimeout.set(invocation.getArgument(0)); + return null; + }).when(mockCache).setSearchTimeout(anyInt()); + + when(mockCache.isServer()).thenReturn(true); + when(mockCache.getCacheServers()).thenAnswer(invocation -> Collections.unmodifiableList(cacheServers)); + when(mockCache.getLockLease()).thenAnswer(invocation -> lockLease.get()); + when(mockCache.getLockTimeout()).thenAnswer(invocation -> lockTimeout.get()); + when(mockCache.getMessageSyncInterval()).thenAnswer(invocation -> messageSyncInterval.get()); + when(mockCache.getReconnectedCache()).thenAnswer(invocation -> mockPeerCache()); + when(mockCache.getSearchTimeout()).thenAnswer(invocation -> searchTimeout.get()); + + return mockCacheApi(mockCache); + } + + public static CacheServer mockCacheServer() { + + CacheServer mockCacheServer = mock(CacheServer.class); + + AtomicBoolean tcpNoDelay = new AtomicBoolean(CacheServer.DEFAULT_TCP_NO_DELAY); + + AtomicInteger maxConnections = new AtomicInteger(CacheServer.DEFAULT_MAX_CONNECTIONS); + AtomicInteger maxMessageCount = new AtomicInteger(CacheServer.DEFAULT_MAXIMUM_MESSAGE_COUNT); + AtomicInteger maxThreads = new AtomicInteger(CacheServer.DEFAULT_MAX_THREADS); + AtomicInteger maxTimeBetweenPings = new AtomicInteger(CacheServer.DEFAULT_MAXIMUM_TIME_BETWEEN_PINGS); + AtomicInteger messageTimeToLive = new AtomicInteger(CacheServer.DEFAULT_MESSAGE_TIME_TO_LIVE); + AtomicInteger port = new AtomicInteger(CacheServer.DEFAULT_PORT); + AtomicInteger socketBufferSize = new AtomicInteger(CacheServer.DEFAULT_SOCKET_BUFFER_SIZE); + + AtomicLong loadPollInterval = new AtomicLong(CacheServer.DEFAULT_LOAD_POLL_INTERVAL); + + AtomicReference bindAddress = new AtomicReference<>(CacheServer.DEFAULT_BIND_ADDRESS); + AtomicReference hostnameForClients = new AtomicReference<>(CacheServer.DEFAULT_HOSTNAME_FOR_CLIENTS); + + doAnswer(invocation -> { + bindAddress.set(invocation.getArgument(0)); + return null; + }).when(mockCacheServer).setBindAddress(anyString()); + + doAnswer(invocation -> { + hostnameForClients.set(invocation.getArgument(0)); + return null; + }).when(mockCacheServer).setHostnameForClients(anyString()); + + doAnswer(invocation -> { + loadPollInterval.set(invocation.getArgument(0)); + return null; + }).when(mockCacheServer).setLoadPollInterval(anyLong()); + + doAnswer(invocation -> { + maxConnections.set(invocation.getArgument(0)); + return null; + }).when(mockCacheServer).setMaxConnections(anyInt()); + + doAnswer(invocation -> { + maxMessageCount.set(invocation.getArgument(0)); + return null; + }).when(mockCacheServer).setMaximumMessageCount(anyInt()); + + doAnswer(invocation -> { + maxThreads.set(invocation.getArgument(0)); + return null; + }).when(mockCacheServer).setMaxThreads(anyInt()); + + doAnswer(invocation -> { + maxTimeBetweenPings.set(invocation.getArgument(0)); + return null; + }).when(mockCacheServer).setMaximumTimeBetweenPings(anyInt()); + + doAnswer(invocation -> { + messageTimeToLive.set(invocation.getArgument(0)); + return null; + }).when(mockCacheServer).setMessageTimeToLive(anyInt()); + + doAnswer(invocation -> { + port.set(invocation.getArgument(0)); + return null; + }).when(mockCacheServer).setPort(anyInt()); + + doAnswer(invocation -> { + socketBufferSize.set(invocation.getArgument(0)); + return null; + }).when(mockCacheServer).setSocketBufferSize(anyInt()); + + doAnswer(invocation -> { + tcpNoDelay.set(invocation.getArgument(0)); + return null; + }).when(mockCacheServer).setTcpNoDelay(anyBoolean()); + + when(mockCacheServer.getBindAddress()).thenAnswer(invocation -> bindAddress.get()); + when(mockCacheServer.getHostnameForClients()).thenAnswer(invocation -> hostnameForClients.get()); + when(mockCacheServer.getLoadPollInterval()).thenAnswer(invocation -> loadPollInterval.get()); + when(mockCacheServer.getMaxConnections()).thenAnswer(invocation -> maxConnections.get()); + when(mockCacheServer.getMaximumMessageCount()).thenAnswer(invocation -> maxMessageCount.get()); + when(mockCacheServer.getMaxThreads()).thenAnswer(invocation -> maxThreads.get()); + when(mockCacheServer.getMaximumTimeBetweenPings()).thenAnswer(invocation -> maxTimeBetweenPings.get()); + when(mockCacheServer.getMessageTimeToLive()).thenAnswer(invocation -> messageTimeToLive.get()); + when(mockCacheServer.getPort()).thenAnswer(invocation -> port.get()); + when(mockCacheServer.getSocketBufferSize()).thenAnswer(invocation -> socketBufferSize.get()); + when(mockCacheServer.getTcpNoDelay()).thenAnswer(invocation -> tcpNoDelay.get()); + + ClientSubscriptionConfig mockClientSubsriptionConfig = mockClientSubscriptionConfig(); + + when(mockCacheServer.getClientSubscriptionConfig()).thenReturn(mockClientSubsriptionConfig); + + return mockCacheServer; + } + + public static ClientSubscriptionConfig mockClientSubscriptionConfig() { + + ClientSubscriptionConfig mockClientSubscriptionConfig = mock(ClientSubscriptionConfig.class); + + AtomicInteger subscriptionCapacity = new AtomicInteger(ClientSubscriptionConfig.DEFAULT_CAPACITY); + + AtomicReference subscriptionDiskStoreName = new AtomicReference<>(""); + + AtomicReference subscriptionEvictionPolicy = + new AtomicReference<>(SubscriptionEvictionPolicy.DEFAULT); + + doAnswer(invocation -> { + subscriptionCapacity.set(invocation.getArgument(0)); + return null; + }).when(mockClientSubscriptionConfig).setCapacity(anyInt()); + + doAnswer(invocation -> { + subscriptionDiskStoreName.set(invocation.getArgument(0)); + return null; + }).when(mockClientSubscriptionConfig).setDiskStoreName(anyString()); + + doAnswer(invocation -> { + subscriptionEvictionPolicy.set(SubscriptionEvictionPolicy.valueOfIgnoreCase(invocation.getArgument(0))); + return null; + }).when(mockClientSubscriptionConfig).setEvictionPolicy(anyString()); + + when(mockClientSubscriptionConfig.getCapacity()).thenAnswer(invocation -> subscriptionCapacity.get()); + when(mockClientSubscriptionConfig.getDiskStoreName()).thenAnswer(invocation -> subscriptionDiskStoreName.get()); + when(mockClientSubscriptionConfig.getEvictionPolicy()).thenAnswer(invocation -> + Optional.ofNullable(subscriptionEvictionPolicy.get()).map(Object::toString).map(String::toLowerCase) + .orElse(null)); + + return mockClientSubscriptionConfig; + } + + public static DiskStoreFactory mockDiskStoreFactory() { + + DiskStoreFactory mockDiskStoreFactory = mock(DiskStoreFactory.class); + + AtomicBoolean allowForceCompaction = new AtomicBoolean(DiskStoreFactory.DEFAULT_ALLOW_FORCE_COMPACTION); + AtomicBoolean autoCompact = new AtomicBoolean(DiskStoreFactory.DEFAULT_AUTO_COMPACT); + + AtomicInteger compactionThreshold = new AtomicInteger(DiskStoreFactory.DEFAULT_COMPACTION_THRESHOLD); + AtomicInteger queueSize = new AtomicInteger(DiskStoreFactory.DEFAULT_QUEUE_SIZE); + AtomicInteger writeBufferSize = new AtomicInteger(DiskStoreFactory.DEFAULT_WRITE_BUFFER_SIZE); + + AtomicLong maxOplogSize = new AtomicLong(DiskStoreFactory.DEFAULT_MAX_OPLOG_SIZE); + AtomicLong timeInterval = new AtomicLong(DiskStoreFactory.DEFAULT_TIME_INTERVAL); + + AtomicReference diskDirectories = + new AtomicReference<>(new File[] { FileSystemUtils.WORKING_DIRECTORY }); + + AtomicReference diskDiretorySizes = new AtomicReference<>(new int[0]); + + AtomicReference diskUsageCriticalPercentage = + new AtomicReference<>(DiskStoreFactory.DEFAULT_DISK_USAGE_CRITICAL_PERCENTAGE); + + AtomicReference diskUsageWarningPercentage = + new AtomicReference<>(DiskStoreFactory.DEFAULT_DISK_USAGE_WARNING_PERCENTAGE); + + when(mockDiskStoreFactory.setAllowForceCompaction(anyBoolean())).thenAnswer(invocation -> { + allowForceCompaction.set(invocation.getArgument(0)); + return mockDiskStoreFactory; + }); + + when(mockDiskStoreFactory.setAutoCompact(anyBoolean())).thenAnswer(invocation -> { + autoCompact.set(invocation.getArgument(0)); + return mockDiskStoreFactory; + }); + + when(mockDiskStoreFactory.setCompactionThreshold(anyInt())).thenAnswer(invocation -> { + compactionThreshold.set(invocation.getArgument(0)); + return mockDiskStoreFactory; + }); + + when(mockDiskStoreFactory.setDiskDirs(any(File[].class))).thenAnswer(invocation -> { + + File[] resolveDiskDirectories = nullSafeArray(invocation.getArgument(0), File.class); + + int[] resolvedDiskDirectorySizes = new int[resolveDiskDirectories.length]; + + Arrays.fill(resolvedDiskDirectorySizes, DiskStoreFactory.DEFAULT_DISK_DIR_SIZE); + + diskDirectories.set(resolveDiskDirectories); + diskDiretorySizes.set(resolvedDiskDirectorySizes); + + return mockDiskStoreFactory; + }); + + when(mockDiskStoreFactory.setDiskDirsAndSizes(any(File[].class), any(int[].class))).thenAnswer(invocation -> { + + diskDirectories.set(invocation.getArgument(0)); + diskDiretorySizes.set(invocation.getArgument(1)); + + return mockDiskStoreFactory; + }); + + when(mockDiskStoreFactory.setDiskUsageCriticalPercentage(anyFloat())).thenAnswer(invocation -> { + diskUsageCriticalPercentage.set(invocation.getArgument(0)); + return mockDiskStoreFactory; + }); + + when(mockDiskStoreFactory.setDiskUsageWarningPercentage(anyFloat())).thenAnswer(invocation -> { + diskUsageWarningPercentage.set(invocation.getArgument(0)); + return mockDiskStoreFactory; + }); + + when(mockDiskStoreFactory.setMaxOplogSize(anyLong())).thenAnswer(invocation -> { + maxOplogSize.set(invocation.getArgument(0)); + return mockDiskStoreFactory; + }); + + when(mockDiskStoreFactory.setQueueSize(anyInt())).thenAnswer(invocation -> { + queueSize.set(invocation.getArgument(0)); + return mockDiskStoreFactory; + }); + + when(mockDiskStoreFactory.setTimeInterval(anyLong())).thenAnswer(invocation -> { + timeInterval.set(invocation.getArgument(0)); + return mockDiskStoreFactory; + }); + + when(mockDiskStoreFactory.setWriteBufferSize(anyInt())).thenAnswer(invocation -> { + writeBufferSize.set(invocation.getArgument(0)); + return mockDiskStoreFactory; + }); + + when(mockDiskStoreFactory.create(anyString())).thenAnswer(invocation -> { + + String name = invocation.getArgument(0); + + DiskStore mockDiskStore = mock(DiskStore.class, name); + + when(mockDiskStore.getAllowForceCompaction()).thenReturn(allowForceCompaction.get()); + when(mockDiskStore.getAutoCompact()).thenReturn(autoCompact.get()); + when(mockDiskStore.getCompactionThreshold()).thenReturn(compactionThreshold.get()); + when(mockDiskStore.getDiskDirs()).thenReturn(diskDirectories.get()); + when(mockDiskStore.getDiskDirSizes()).thenReturn(diskDiretorySizes.get()); + when(mockDiskStore.getDiskUsageCriticalPercentage()).thenReturn(diskUsageCriticalPercentage.get()); + when(mockDiskStore.getDiskUsageWarningPercentage()).thenReturn(diskUsageWarningPercentage.get()); + when(mockDiskStore.getDiskStoreUUID()).thenReturn(UUID.randomUUID()); + when(mockDiskStore.getMaxOplogSize()).thenReturn(maxOplogSize.get()); + when(mockDiskStore.getName()).thenReturn(name); + when(mockDiskStore.getQueueSize()).thenReturn(queueSize.get()); + when(mockDiskStore.getTimeInterval()).thenReturn(timeInterval.get()); + when(mockDiskStore.getWriteBufferSize()).thenReturn(writeBufferSize.get()); + + diskStores.put(name, mockDiskStore); + + return mockDiskStore; + }); + + return mockDiskStoreFactory; + } + + public static DistributedSystem mockDistributedSystem() { + + DistributedSystem mockDistributedSystem = mock(DistributedSystem.class); + + when(mockDistributedSystem.getProperties()).thenReturn(new Properties()); + when(mockDistributedSystem.getReconnectedSystem()).thenAnswer(invocation -> mockDistributedSystem()); + + return mockDistributedSystem; + } + + public static PoolFactory mockPoolFactory() { + + PoolFactory mockPoolFactory = mock(PoolFactory.class); + + AtomicBoolean multiuserAuthentication = new AtomicBoolean(PoolFactory.DEFAULT_MULTIUSER_AUTHENTICATION); + AtomicBoolean prSingleHopEnabled = new AtomicBoolean(PoolFactory.DEFAULT_PR_SINGLE_HOP_ENABLED); + AtomicBoolean subscriptionEnabled = new AtomicBoolean(PoolFactory.DEFAULT_SUBSCRIPTION_ENABLED); + AtomicBoolean threadLocalConnections = new AtomicBoolean(PoolFactory.DEFAULT_THREAD_LOCAL_CONNECTIONS); + + AtomicInteger freeConnectionTimeout = new AtomicInteger(PoolFactory.DEFAULT_FREE_CONNECTION_TIMEOUT); + AtomicInteger loadConditioningInterval = new AtomicInteger(PoolFactory.DEFAULT_LOAD_CONDITIONING_INTERVAL); + AtomicInteger maxConnections = new AtomicInteger(PoolFactory.DEFAULT_MAX_CONNECTIONS); + AtomicInteger minConnections = new AtomicInteger(PoolFactory.DEFAULT_MIN_CONNECTIONS); + AtomicInteger readTimeout = new AtomicInteger(PoolFactory.DEFAULT_READ_TIMEOUT); + AtomicInteger retryAttempts = new AtomicInteger(PoolFactory.DEFAULT_RETRY_ATTEMPTS); + AtomicInteger socketBufferSize = new AtomicInteger(PoolFactory.DEFAULT_SOCKET_BUFFER_SIZE); + AtomicInteger statisticInterval = new AtomicInteger(PoolFactory.DEFAULT_STATISTIC_INTERVAL); + AtomicInteger subscriptionAckInterval = new AtomicInteger(PoolFactory.DEFAULT_SUBSCRIPTION_ACK_INTERVAL); + AtomicInteger subscriptionMessageTrackingTimeout = new AtomicInteger(PoolFactory.DEFAULT_SUBSCRIPTION_MESSAGE_TRACKING_TIMEOUT); + AtomicInteger subscriptionRedundancy = new AtomicInteger(PoolFactory.DEFAULT_SUBSCRIPTION_REDUNDANCY); + + AtomicLong idleTimeout = new AtomicLong(PoolFactory.DEFAULT_IDLE_TIMEOUT); + AtomicLong pingInterval = new AtomicLong(PoolFactory.DEFAULT_PING_INTERVAL); + + AtomicReference serverGroup = new AtomicReference<>(PoolFactory.DEFAULT_SERVER_GROUP); + + List locators = new ArrayList<>(); + List servers = new ArrayList<>(); + + when(mockPoolFactory.addLocator(anyString(), anyInt())).thenAnswer(invocation -> { + locators.add(new InetSocketAddress(invocation.getArgument(0), invocation.getArgument(1))); + return mockPoolFactory; + }); + + when(mockPoolFactory.addServer(anyString(), anyInt())).thenAnswer(invocation -> { + servers.add(new InetSocketAddress(invocation.getArgument(0), invocation.getArgument(1))); + return mockPoolFactory; + }); + + when(mockPoolFactory.setFreeConnectionTimeout(anyInt())).thenAnswer(invocation -> { + freeConnectionTimeout.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setIdleTimeout(anyLong())).thenAnswer(invocation -> { + idleTimeout.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setLoadConditioningInterval(anyInt())).thenAnswer(invocation -> { + loadConditioningInterval.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setMaxConnections(anyInt())).thenAnswer(invocation -> { + maxConnections.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setMinConnections(anyInt())).thenAnswer(invocation -> { + minConnections.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setMultiuserAuthentication(anyBoolean())).thenAnswer(invocation -> { + multiuserAuthentication.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setPingInterval(anyLong())).thenAnswer(invocation -> { + pingInterval.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setPRSingleHopEnabled(anyBoolean())).thenAnswer(invocation -> { + prSingleHopEnabled.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setReadTimeout(anyInt())).thenAnswer(invocation -> { + readTimeout.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setRetryAttempts(anyInt())).thenAnswer(invocation -> { + retryAttempts.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setServerGroup(anyString())).thenAnswer(invocation -> { + serverGroup.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setSocketBufferSize(anyInt())).thenAnswer(invocation -> { + socketBufferSize.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setStatisticInterval(anyInt())).thenAnswer(invocation -> { + statisticInterval.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setSubscriptionAckInterval(anyInt())).thenAnswer(invocation -> { + subscriptionAckInterval.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setSubscriptionEnabled(anyBoolean())).thenAnswer(invocation -> { + subscriptionEnabled.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setSubscriptionMessageTrackingTimeout(anyInt())).thenAnswer(invocation -> { + subscriptionMessageTrackingTimeout.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setSubscriptionRedundancy(anyInt())).thenAnswer(invocation -> { + subscriptionRedundancy.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.setThreadLocalConnections(anyBoolean())).thenAnswer(invocation -> { + threadLocalConnections.set(invocation.getArgument(0)); + return mockPoolFactory; + }); + + when(mockPoolFactory.create(anyString())).thenAnswer(invocation -> { + + String name = invocation.getArgument(0); + + Pool mockPool = mock(Pool.class, name); + + AtomicBoolean destroyed = new AtomicBoolean(false); + + doAnswer(invocationOnMock -> { + destroyed.set(true); + return null; + }).when(mockPool).destroy(); + + doAnswer(invocationOnMock -> { + destroyed.set(true); + return null; + }).when(mockPool).destroy(anyBoolean()); + + when(mockPool.isDestroyed()).thenAnswer(invocationOnMock -> destroyed.get()); + when(mockPool.getFreeConnectionTimeout()).thenReturn(freeConnectionTimeout.get()); + when(mockPool.getIdleTimeout()).thenReturn(idleTimeout.get()); + when(mockPool.getLoadConditioningInterval()).thenReturn(loadConditioningInterval.get()); + when(mockPool.getLocators()).thenReturn(locators); + when(mockPool.getMaxConnections()).thenReturn(maxConnections.get()); + when(mockPool.getMinConnections()).thenReturn(minConnections.get()); + when(mockPool.getMultiuserAuthentication()).thenReturn(multiuserAuthentication.get()); + when(mockPool.getName()).thenReturn(name); + when(mockPool.getPingInterval()).thenReturn(pingInterval.get()); + when(mockPool.getPRSingleHopEnabled()).thenReturn(prSingleHopEnabled.get()); + when(mockPool.getReadTimeout()).thenReturn(readTimeout.get()); + when(mockPool.getRetryAttempts()).thenReturn(retryAttempts.get()); + when(mockPool.getServerGroup()).thenReturn(serverGroup.get()); + when(mockPool.getServers()).thenReturn(servers); + when(mockPool.getSocketBufferSize()).thenReturn(socketBufferSize.get()); + when(mockPool.getStatisticInterval()).thenReturn(statisticInterval.get()); + when(mockPool.getSubscriptionAckInterval()).thenReturn(subscriptionAckInterval.get()); + when(mockPool.getSubscriptionEnabled()).thenReturn(subscriptionEnabled.get()); + when(mockPool.getSubscriptionMessageTrackingTimeout()).thenReturn(subscriptionMessageTrackingTimeout.get()); + when(mockPool.getSubscriptionRedundancy()).thenReturn(subscriptionRedundancy.get()); + when(mockPool.getThreadLocalConnections()).thenReturn(threadLocalConnections.get()); + + return mockPool; + }); + + return mockPoolFactory; + + } + + public static ResourceManager mockResourceManager() { + + ResourceManager mockResourceManager = mock(ResourceManager.class); + + AtomicReference criticalHeapPercentage = + new AtomicReference<>(ResourceManager.DEFAULT_CRITICAL_PERCENTAGE); + + AtomicReference criticalOffHeapPercentage = + new AtomicReference<>(ResourceManager.DEFAULT_CRITICAL_PERCENTAGE); + + AtomicReference evictionHeapPercentage = + new AtomicReference<>(ResourceManager.DEFAULT_EVICTION_PERCENTAGE); + + AtomicReference evictionOffHeapPercentage = + new AtomicReference<>(ResourceManager.DEFAULT_EVICTION_PERCENTAGE); + + doAnswer(invocation -> { + criticalHeapPercentage.set(invocation.getArgument(0)); + return null; + }).when(mockResourceManager).setCriticalHeapPercentage(anyFloat()); + + doAnswer(invocation -> { + criticalOffHeapPercentage.set(invocation.getArgument(0)); + return null; + }).when(mockResourceManager).setCriticalOffHeapPercentage(anyFloat()); + + doAnswer(invocation -> { + evictionHeapPercentage.set(invocation.getArgument(0)); + return null; + }).when(mockResourceManager).setEvictionHeapPercentage(anyFloat()); + + doAnswer(invocation -> { + evictionOffHeapPercentage.set(invocation.getArgument(0)); + return null; + }).when(mockResourceManager).setEvictionOffHeapPercentage(anyFloat()); + + when(mockResourceManager.getCriticalHeapPercentage()).thenAnswer(invocation -> criticalHeapPercentage.get()); + when(mockResourceManager.getCriticalOffHeapPercentage()).thenAnswer(invocation -> criticalOffHeapPercentage.get()); + when(mockResourceManager.getEvictionHeapPercentage()).thenAnswer(invocation -> evictionHeapPercentage.get()); + when(mockResourceManager.getEvictionOffHeapPercentage()).thenAnswer(invocation -> evictionOffHeapPercentage.get()); + when(mockResourceManager.getRebalanceOperations()).thenReturn(Collections.emptySet()); + + return mockResourceManager; + } + + public static CacheFactory spyOn(CacheFactory cacheFactory) { + + CacheFactory cacheFactorySpy = spy(cacheFactory); + + Cache mockCache = mockPeerCache(); + + AtomicBoolean pdxIgnoreUnreadFields = new AtomicBoolean(false); + AtomicBoolean pdxPersistent = new AtomicBoolean(false); + AtomicBoolean pdxReadSerialized = new AtomicBoolean(false); + + AtomicReference pdxDiskStoreName = new AtomicReference<>(null); + AtomicReference pdxSerializer = new AtomicReference<>(null); + + doAnswer(invocation -> { + pdxDiskStoreName.set(invocation.getArgument(0)); + return cacheFactorySpy; + }).when(cacheFactorySpy.setPdxDiskStore(anyString())); + + doAnswer(invocation -> { + pdxIgnoreUnreadFields.set(invocation.getArgument(0)); + return cacheFactorySpy; + }).when(cacheFactorySpy.setPdxIgnoreUnreadFields(anyBoolean())); + + doAnswer(invocation -> { + pdxPersistent.set(invocation.getArgument(0)); + return cacheFactorySpy; + }).when(cacheFactorySpy.setPdxPersistent(anyBoolean())); + + doAnswer(invocation -> { + pdxReadSerialized.set(invocation.getArgument(0)); + return cacheFactorySpy; + }).when(cacheFactorySpy.setPdxReadSerialized(anyBoolean())); + + doAnswer(invocation -> { + pdxSerializer.set(invocation.getArgument(0)); + return cacheFactorySpy; + }).when(cacheFactorySpy.setPdxSerializer(any(PdxSerializer.class))); + + doReturn(mockCache).when(cacheFactorySpy.create()); + + when(mockCache.getPdxDiskStore()).thenAnswer(invocation -> pdxDiskStoreName.get()); + when(mockCache.getPdxIgnoreUnreadFields()).thenAnswer(invocation -> pdxIgnoreUnreadFields.get()); + when(mockCache.getPdxPersistent()).thenAnswer(invocation -> pdxPersistent.get()); + when(mockCache.getPdxReadSerialized()).thenAnswer(invocation -> pdxReadSerialized.get()); + when(mockCache.getPdxSerializer()).thenAnswer(invocation -> pdxSerializer.get()); + + return cacheFactorySpy; + } + + public static ClientCacheFactory spyOn(ClientCacheFactory clientCacheFactory) { + + ClientCacheFactory clientCacheFactorySpy = spy(clientCacheFactory); + + ClientCache mockClientCache = mockClientCache(); + + PoolFactory mockPoolFactory = mockPoolFactory(); + + AtomicBoolean pdxIgnoreUnreadFields = new AtomicBoolean(false); + AtomicBoolean pdxPersistent = new AtomicBoolean(false); + AtomicBoolean pdxReadSerialized = new AtomicBoolean(false); + + AtomicReference pdxDiskStoreName = new AtomicReference<>(null); + AtomicReference pdxSerializer = new AtomicReference<>(null); + AtomicReference defaultPool = new AtomicReference<>(null); + + doAnswer(invocation -> { + pdxDiskStoreName.set(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPdxDiskStore(anyString())); + + doAnswer(invocation -> { + pdxIgnoreUnreadFields.set(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPdxIgnoreUnreadFields(anyBoolean())); + + doAnswer(invocation -> { + pdxPersistent.set(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPdxPersistent(anyBoolean())); + + doAnswer(invocation -> { + pdxReadSerialized.set(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPdxReadSerialized(anyBoolean())); + + doAnswer(invocation -> { + pdxSerializer.set(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPdxSerializer(any(PdxSerializer.class))); + + doAnswer(invocation -> { + mockPoolFactory.addLocator(invocation.getArgument(0), invocation.getArgument(1)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.addPoolLocator(anyString(), anyInt())); + + doAnswer(invocation -> { + mockPoolFactory.addServer(invocation.getArgument(0), invocation.getArgument(1)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.addPoolServer(anyString(), anyInt())); + + doAnswer(invocation -> { + mockPoolFactory.setFreeConnectionTimeout(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolFreeConnectionTimeout(anyInt())); + + doAnswer(invocation -> { + mockPoolFactory.setIdleTimeout(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolIdleTimeout(anyLong())); + + doAnswer(invocation -> { + mockPoolFactory.setLoadConditioningInterval(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolLoadConditioningInterval(anyInt())); + + doAnswer(invocation -> { + mockPoolFactory.setMaxConnections(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolMaxConnections(anyInt())); + + doAnswer(invocation -> { + mockPoolFactory.setMinConnections(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolMinConnections(anyInt())); + + doAnswer(invocation -> { + mockPoolFactory.setMultiuserAuthentication(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolMultiuserAuthentication(anyBoolean())); + + doAnswer(invocation -> { + mockPoolFactory.setPingInterval(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolPingInterval(anyLong())); + + doAnswer(invocation -> { + mockPoolFactory.setPRSingleHopEnabled(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolPRSingleHopEnabled(anyBoolean())); + + doAnswer(invocation -> { + mockPoolFactory.setReadTimeout(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolReadTimeout(anyInt())); + + doAnswer(invocation -> { + mockPoolFactory.setRetryAttempts(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolRetryAttempts(anyInt())); + + doAnswer(invocation -> { + mockPoolFactory.setServerGroup(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolServerGroup(anyString())); + + doAnswer(invocation -> { + mockPoolFactory.setSocketBufferSize(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolSocketBufferSize(anyInt())); + + doAnswer(invocation -> { + mockPoolFactory.setStatisticInterval(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolStatisticInterval(anyInt())); + + doAnswer(invocation -> { + mockPoolFactory.setSubscriptionAckInterval(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolSubscriptionAckInterval(anyInt())); + + doAnswer(invocation -> { + mockPoolFactory.setSubscriptionEnabled(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolSubscriptionEnabled(anyBoolean())); + + doAnswer(invocation -> { + mockPoolFactory.setSubscriptionMessageTrackingTimeout(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolSubscriptionMessageTrackingTimeout(anyInt())); + + doAnswer(invocation -> { + mockPoolFactory.setSubscriptionRedundancy(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolSubscriptionRedundancy(anyInt())); + + doAnswer(invocation -> { + mockPoolFactory.setThreadLocalConnections(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy.setPoolThreadLocalConnections(anyBoolean())); + + doReturn(mockClientCache).when(clientCacheFactorySpy.create()); + + when(mockClientCache.getCurrentServers()).thenAnswer(invocation -> + Collections.unmodifiableSet(new HashSet<>(defaultPool.get().getServers()))); + + when(mockClientCache.getDefaultPool()).thenAnswer(invocation -> { + + if (defaultPool.get() == null) { + defaultPool.set(mockPoolFactory.create("DEFAULT")); + } + + return defaultPool.get(); + }); + + when(mockClientCache.getPdxDiskStore()).thenAnswer(invocation -> pdxDiskStoreName.get()); + when(mockClientCache.getPdxIgnoreUnreadFields()).thenAnswer(invocation -> pdxIgnoreUnreadFields.get()); + when(mockClientCache.getPdxPersistent()).thenAnswer(invocation -> pdxPersistent.get()); + when(mockClientCache.getPdxReadSerialized()).thenAnswer(invocation -> pdxReadSerialized.get()); + when(mockClientCache.getPdxSerializer()).thenAnswer(invocation -> pdxSerializer.get()); + + return clientCacheFactorySpy; + } +} diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/annotation/EnableGemFireMocking.java b/src/test/java/org/springframework/data/gemfire/test/mock/annotation/EnableGemFireMocking.java new file mode 100644 index 00000000..bdf9499f --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/test/mock/annotation/EnableGemFireMocking.java @@ -0,0 +1,47 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed 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.springframework.data.gemfire.test.mock.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Import; + +/** + * The {@link EnableGemFireMocking} annotation enables mocking of GemFire Objects in Unit Tests. + * + * @author John Blum + * @see java.lang.annotation.Documented + * @see java.lang.annotation.Inherited + * @see java.lang.annotation.Retention + * @see java.lang.annotation.Target + * @see org.springframework.context.annotation.Import + * @since 2.0.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +@Import(GemFireMockingConfiguration.class) +@SuppressWarnings("unused") +public @interface EnableGemFireMocking { + +} diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireMockingConfiguration.java b/src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireMockingConfiguration.java new file mode 100644 index 00000000..34f42dfc --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireMockingConfiguration.java @@ -0,0 +1,41 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed 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.springframework.data.gemfire.test.mock.annotation; + +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.gemfire.test.mock.config.MockGemFireObjectsBeanPostProcessor; + +/** + * The {@link GemFireMockingConfiguration} class is a Spring {@link Configuration @Configuration} class + * containing bean definitions to configure GemFire Object mocking. + * + * @author John Blum + * @see org.springframework.context.annotation.Bean + * @see org.springframework.context.annotation.Configuration + * @since 2.0.0 + */ +@SuppressWarnings("unused") +@Configuration +public class GemFireMockingConfiguration { + + @Bean + public BeanPostProcessor mockGemFireObjectsBeanPostProcessor() { + return MockGemFireObjectsBeanPostProcessor.INSTANCE; + } +} diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireUnitTest.java b/src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireUnitTest.java new file mode 100644 index 00000000..55bad977 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireUnitTest.java @@ -0,0 +1,58 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed 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.springframework.data.gemfire.test.mock.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.runner.RunWith; +import org.springframework.data.gemfire.test.mock.context.MockGemFireObjectsApplicationContextInitializer; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * The {@link GemFireUnitTest} annotation marks a test class as a GemFire Unit Test + * with GemFire Object mocking enabled. + * + * Additionally, this annotation enables Spring's {@link SpringRunner} JUnit Runner implementation + * using JUnit's {@link RunWith} annotation. + * + * @author John Blum + * @see java.lang.annotation.Documented + * @see java.lang.annotation.Inherited + * @see java.lang.annotation.Retention + * @see java.lang.annotation.Target + * @see org.junit.runner.RunWith + * @see org.springframework.data.gemfire.test.mock.context.MockGemFireObjectsApplicationContextInitializer + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringRunner + * @since 2.0.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +@RunWith(SpringRunner.class) +@ContextConfiguration(initializers = MockGemFireObjectsApplicationContextInitializer.class) +@SuppressWarnings("unused") +public @interface GemFireUnitTest { + +} diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/config/MockGemFireObjectsBeanPostProcessor.java b/src/test/java/org/springframework/data/gemfire/test/mock/config/MockGemFireObjectsBeanPostProcessor.java new file mode 100644 index 00000000..cee1d3bc --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/test/mock/config/MockGemFireObjectsBeanPostProcessor.java @@ -0,0 +1,77 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed 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.springframework.data.gemfire.test.mock.config; + +import org.apache.geode.cache.CacheFactory; +import org.apache.geode.cache.client.ClientCacheFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.data.gemfire.CacheFactoryBean; +import org.springframework.data.gemfire.client.ClientCacheFactoryBean; +import org.springframework.data.gemfire.test.mock.MockGemFireObjectsSupport; +import org.springframework.lang.Nullable; + +/** + * The MockGemFireObjectsBeanPostProcessor class... + * + * @author John Blum + * @since 1.0.0 + */ +public class MockGemFireObjectsBeanPostProcessor implements BeanPostProcessor { + + public static final MockGemFireObjectsBeanPostProcessor INSTANCE = new MockGemFireObjectsBeanPostProcessor(); + + @Nullable @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return (bean instanceof CacheFactoryBean ? spyOnCacheFactoryBean((CacheFactoryBean) bean) : bean); + } + + private Object spyOnCacheFactoryBean(CacheFactoryBean bean) { + + return (bean instanceof ClientCacheFactoryBean + ? SpyingClientCacheFactoryInitializer.spyOn((ClientCacheFactoryBean) bean) + : SpyingCacheFactoryInitializer.spyOn(bean)); + } + + protected static class SpyingCacheFactoryInitializer + implements CacheFactoryBean.CacheFactoryInitializer { + + public static CacheFactoryBean spyOn(CacheFactoryBean cacheFactoryBean) { + cacheFactoryBean.setCacheFactoryInitializer(new SpyingCacheFactoryInitializer()); + return cacheFactoryBean; + } + + @Override + public CacheFactory initialize(CacheFactory cacheFactory) { + return MockGemFireObjectsSupport.spyOn(cacheFactory); + } + } + + protected static class SpyingClientCacheFactoryInitializer + implements CacheFactoryBean.CacheFactoryInitializer { + + public static ClientCacheFactoryBean spyOn(ClientCacheFactoryBean clientCacheFactoryBean) { + clientCacheFactoryBean.setCacheFactoryInitializer(new SpyingClientCacheFactoryInitializer()); + return clientCacheFactoryBean; + } + + @Override + public ClientCacheFactory initialize(ClientCacheFactory clientCacheFactory) { + return MockGemFireObjectsSupport.spyOn(clientCacheFactory); + } + } +} diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/context/MockGemFireObjectsApplicationContextInitializer.java b/src/test/java/org/springframework/data/gemfire/test/mock/context/MockGemFireObjectsApplicationContextInitializer.java new file mode 100644 index 00000000..4cefce2f --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/test/mock/context/MockGemFireObjectsApplicationContextInitializer.java @@ -0,0 +1,41 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed 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.springframework.data.gemfire.test.mock.context; + +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.data.gemfire.test.mock.config.MockGemFireObjectsBeanPostProcessor; + +/** + * The {@link MockGemFireObjectsApplicationContextInitializer} class is a Spring {@link ApplicationContextInitializer} + * used to initialize the Spring {@link ConfigurableApplicationContext} with GemFire Object mocking. + * + * @author John Blum + * @see org.springframework.context.ApplicationContextInitializer + * @see org.springframework.context.ConfigurableApplicationContext + * @see org.springframework.data.gemfire.test.mock.config.MockGemFireObjectsBeanPostProcessor + * @since 2.0.0 + */ +public class MockGemFireObjectsApplicationContextInitializer + implements ApplicationContextInitializer { + + @Override + @SuppressWarnings("all") + public void initialize(ConfigurableApplicationContext applicationContext) { + applicationContext.getBeanFactory().addBeanPostProcessor(MockGemFireObjectsBeanPostProcessor.INSTANCE); + } +}