From c174f008fe74bc8bd483ff7aeab4c15ca38b4a92 Mon Sep 17 00:00:00 2001 From: John Blum Date: Wed, 8 Aug 2018 18:42:40 -0700 Subject: [PATCH] Fix Region AttributesMutator initialization bug. The bug was caused by the mocking logic in Region.getAttributes(), which lazily initializes the RegionAttributes upon first access, and by extension, the Region AttributesMutator only gets intialized after the Region.getAttributes() method is invoked. Therefore, if Region.getAttributesMutator() is called before Region.getAttributes() and any AttributesMutator method is invoked then a NullPointerException is thrown. --- .../tests/mock/GemFireMockObjectsSupport.java | 11 +- .../GemFireMockObjectsSupportUnitTests.java | 111 ++++++++++++++++++ 2 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 spring-data-geode-test/src/test/java/org/springframework/data/gemfire/tests/mock/GemFireMockObjectsSupportUnitTests.java diff --git a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/GemFireMockObjectsSupport.java b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/GemFireMockObjectsSupport.java index 25ccab7..75e001d 100644 --- a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/GemFireMockObjectsSupport.java +++ b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/GemFireMockObjectsSupport.java @@ -2095,9 +2095,11 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { Region mockRegion = mock(Region.class, name); + RegionAttributes mockRegionAttributes = mockRegionAttributes(mockRegion, regionAttributes); + Set> subRegions = new CopyOnWriteArraySet<>(); - when(mockRegion.getAttributes()).thenAnswer(invocation -> mockRegionAttributes(mockRegion, regionAttributes)); + when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); when(mockRegion.getFullPath()).thenReturn(toRegionPath(name)); when(mockRegion.getName()).thenReturn(toRegionName(name)); when(mockRegion.getRegionService()).thenReturn(regionService); @@ -2150,13 +2152,13 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { when(mockAttributesMutator.getEvictionAttributesMutator()).thenReturn(mockEvictionAttributesMutator); when(mockAttributesMutator.getRegion()).thenReturn(mockRegion); + AtomicBoolean cloningEnabled = new AtomicBoolean(baseRegionAttributes.getCloningEnabled()); + AtomicInteger evictionMaximum = new AtomicInteger(Optional.ofNullable(baseRegionAttributes.getEvictionAttributes()) .map(EvictionAttributes::getMaximum) .orElse(EvictionAttributes.DEFAULT_ENTRIES_MAXIMUM)); - AtomicReference cloningEnabled = new AtomicReference<>(null); - AtomicReference> cacheLoader = new AtomicReference<>(baseRegionAttributes.getCacheLoader()); AtomicReference> cacheWriter = new AtomicReference<>(baseRegionAttributes.getCacheWriter()); @@ -2198,8 +2200,7 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { doAnswer(newAdder(gatewaySenderIds, null)). when(mockAttributesMutator).addGatewaySenderId(anyString()); - when(mockAttributesMutator.getCloningEnabled()).thenAnswer(newGetter(() -> - Optional.ofNullable(cloningEnabled.get()).orElseGet(baseRegionAttributes::getCloningEnabled))); + when(mockAttributesMutator.getCloningEnabled()).thenAnswer(newGetter(cloningEnabled::get)); doAnswer(invocation -> { diff --git a/spring-data-geode-test/src/test/java/org/springframework/data/gemfire/tests/mock/GemFireMockObjectsSupportUnitTests.java b/spring-data-geode-test/src/test/java/org/springframework/data/gemfire/tests/mock/GemFireMockObjectsSupportUnitTests.java new file mode 100644 index 0000000..958e765 --- /dev/null +++ b/spring-data-geode-test/src/test/java/org/springframework/data/gemfire/tests/mock/GemFireMockObjectsSupportUnitTests.java @@ -0,0 +1,111 @@ +/* + * Copyright 2018 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.tests.mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.apache.geode.cache.AttributesMutator; +import org.apache.geode.cache.Region; +import org.apache.geode.cache.RegionAttributes; +import org.apache.geode.cache.RegionService; +import org.junit.After; +import org.junit.Test; + +/** + * Unit tests for {@link GemFireMockObjectsSupport}. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mockito + * @see org.springframework.data.gemfire.tests.mock.GemFireMockObjectsSupport + * @since 1.0.0 + */ +public class GemFireMockObjectsSupportUnitTests { + + @After + public void tearDown() { + GemFireMockObjectsSupport.destroy(); + } + + @Test + public void regionCloningEnableReturnsFalseByDefault() { + + RegionService mockRegionService = mock(RegionService.class); + + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); + + Region mockRegion = + GemFireMockObjectsSupport.mockRegion(mockRegionService, "MockRegion", mockRegionAttributes); + + assertThat(mockRegion).isNotNull(); + assertThat(mockRegion.getName()).isEqualTo("MockRegion"); + assertThat(mockRegion.getAttributes()).isNotNull(); + assertThat(mockRegion.getAttributes()).isNotSameAs(mockRegionAttributes); + assertThat(mockRegion.getAttributes().getCloningEnabled()).isFalse(); + } + + @Test + public void regionAttributesMutatorGetRegionReturnsRegion() { + + RegionService mockRegionService = mock(RegionService.class); + + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); + + Region mockRegion = + GemFireMockObjectsSupport.mockRegion(mockRegionService, "MockRegion", mockRegionAttributes); + + assertThat(mockRegion).isNotNull(); + assertThat(mockRegion.getName()).isEqualTo("MockRegion"); + assertThat(mockRegion.getRegionService()).isSameAs(mockRegionService); + + AttributesMutator mockAttributesMutator = mockRegion.getAttributesMutator(); + + assertThat(mockAttributesMutator).isNotNull(); + assertThat(mockAttributesMutator.getRegion()).isSameAs(mockRegion); + } + + @Test + public void regionAttributesMutatorIsInitialized() { + + RegionService mockRegionService = mock(RegionService.class); + + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); + + Region mockRegion = + GemFireMockObjectsSupport.mockRegion(mockRegionService, "MockRegion", mockRegionAttributes); + + assertThat(mockRegion).isNotNull(); + assertThat(mockRegion.getName()).isEqualTo("MockRegion"); + assertThat(mockRegion.getRegionService()).isSameAs(mockRegionService); + + AttributesMutator mockAttributesMutator = mockRegion.getAttributesMutator(); + + assertThat(mockAttributesMutator).isNotNull(); + + mockAttributesMutator.setCloningEnabled(true); + + assertThat(mockRegion.getAttributes()).isNotNull(); + assertThat(mockRegion.getAttributes()).isNotSameAs(mockRegionAttributes); + assertThat(mockRegion.getAttributes().getCloningEnabled()).isTrue(); + + verify(mockAttributesMutator, times(1)).setCloningEnabled(eq(true)); + } +}