From a6f07e88b0ce80c3571d8f3508b753fbbec5d7fa Mon Sep 17 00:00:00 2001 From: John Blum Date: Tue, 12 May 2020 21:29:32 -0700 Subject: [PATCH] Add Unit Tests for PdxInstanceWrapperAspect. Resolve gh-67. --- .../support/PdxInstanceWrapperAspect.java | 27 +- .../PdxInstanceWrapperAspectUnitTests.java | 346 ++++++++++++++++++ 2 files changed, 362 insertions(+), 11 deletions(-) create mode 100644 spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/pdx/PdxInstanceWrapperAspectUnitTests.java diff --git a/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/support/PdxInstanceWrapperAspect.java b/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/support/PdxInstanceWrapperAspect.java index bce886c7..99c5db94 100644 --- a/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/support/PdxInstanceWrapperAspect.java +++ b/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/support/PdxInstanceWrapperAspect.java @@ -23,9 +23,9 @@ import java.util.stream.Collectors; import org.apache.geode.cache.CacheStatistics; import org.apache.geode.cache.Region; -import org.apache.shiro.util.Assert; - import org.springframework.geode.pdx.PdxInstanceWrapper; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; @@ -33,20 +33,25 @@ import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; /** - * The PdxInstanceWrapperAspect class... + * A Spring, AspectJ {@link Aspect} advising GemFire/Geode cache {@link Region} data access operations. * * @author John Blum - * @since 1.0.0 + * @see org.apache.geode.cache.Region + * @see org.aspectj.lang.annotation.Around + * @see org.aspectj.lang.annotation.Aspect + * @see org.aspectj.lang.annotation.Pointcut + * @see org.springframework.geode.pdx.PdxInstanceWrapper + * @since 1.3.0 */ @Aspect @SuppressWarnings("unused") public class PdxInstanceWrapperAspect { - private Collection asCollection(Object value) { + private static Collection asCollection(Object value) { return value instanceof Collection ? (Collection) value : Collections.emptyList(); } - private Map asMap(Object value) { + private static Map asMap(Object value) { return value instanceof Map ? (Map) value : Collections.emptyMap(); } @@ -98,26 +103,26 @@ public class PdxInstanceWrapperAspect { .collect(Collectors.toList()); } - protected static class RegionEntryWrapper implements Region.Entry { + public static class RegionEntryWrapper implements Region.Entry { @SuppressWarnings("unchecked") - protected static T from(T value) { + public static T from(T value) { return value instanceof Region.Entry - ? (T) new RegionEntryWrapper((Region.Entry) value) + ? (T) new RegionEntryWrapper<>((Region.Entry) value) : value; } private final Region.Entry delegate; - protected RegionEntryWrapper(Region.Entry regionEntry) { + protected RegionEntryWrapper(@NonNull Region.Entry regionEntry) { Assert.notNull(regionEntry, "Region.Entry must not be null"); this.delegate = regionEntry; } - protected Region.Entry getDelegate() { + protected @NonNull Region.Entry getDelegate() { return this.delegate; } diff --git a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/pdx/PdxInstanceWrapperAspectUnitTests.java b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/pdx/PdxInstanceWrapperAspectUnitTests.java new file mode 100644 index 00000000..5f016d7e --- /dev/null +++ b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/pdx/PdxInstanceWrapperAspectUnitTests.java @@ -0,0 +1,346 @@ +/* + * Copyright 2020 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 + * + * https://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.geode.boot.autoconfigure.pdx; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.Test; + +import org.apache.geode.cache.CacheStatistics; +import org.apache.geode.cache.Region; +import org.apache.geode.pdx.PdxInstance; + +import org.springframework.geode.boot.autoconfigure.support.PdxInstanceWrapperAspect; +import org.springframework.geode.boot.autoconfigure.support.PdxInstanceWrapperAspect.RegionEntryWrapper; +import org.springframework.geode.pdx.PdxInstanceWrapper; + +import org.aspectj.lang.ProceedingJoinPoint; + +/** + * Unit Tests for {@link PdxInstanceWrapperAspect} + * + * @author John Blum + * @see java.util.Map + * @see org.junit.Test + * @see org.mockito.Mockito + * @see org.apache.geode.cache.Region + * @see org.apache.geode.pdx.PdxInstance + * @see org.springframework.geode.boot.autoconfigure.support.PdxInstanceWrapperAspect + * @see org.springframework.geode.boot.autoconfigure.support.PdxInstanceWrapperAspect.RegionEntryWrapper + * @see org.springframework.geode.pdx.PdxInstanceWrapper + * @since 1.3.0 + */ +public class PdxInstanceWrapperAspectUnitTests { + + private PdxInstanceWrapperAspect aspect = new PdxInstanceWrapperAspect(); + + @Test + public void regionGetAdviceWrapsPdx() throws Throwable { + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + ProceedingJoinPoint mockJointPoint = mock(ProceedingJoinPoint.class); + + doReturn(mockPdxInstance).when(mockJointPoint).proceed(); + + Object pdx = this.aspect.regionGetAdvice(mockJointPoint); + + assertThat(pdx).isInstanceOf(PdxInstanceWrapper.class); + assertThat(((PdxInstanceWrapper) pdx).getDelegate()).isEqualTo(mockPdxInstance); + + verify(mockJointPoint, times(1)).proceed(); + verifyNoInteractions(mockPdxInstance); + } + + @Test + public void regionGetAdviceReturnsNull() throws Throwable { + + ProceedingJoinPoint mockJointPoint = mock(ProceedingJoinPoint.class); + + doReturn(null).when(mockJointPoint).proceed(); + + assertThat(this.aspect.regionGetAdvice(mockJointPoint)).isNull(); + + verify(mockJointPoint, times(1)).proceed(); + } + + @Test + @SuppressWarnings("unchecked") + public void regionGetAllAdviceWrapsPdx() throws Throwable { + + ProceedingJoinPoint mockJointPoint = mock(ProceedingJoinPoint.class); + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + Map map = new HashMap<>(); + + map.put(1, "TEST"); + map.put(2, mockPdxInstance); + + doReturn(map).when(mockJointPoint).proceed(); + + Object result = this.aspect.regionGetAllAdvice(mockJointPoint); + + assertThat(result).isInstanceOf(Map.class); + + Map mapResult = (Map) result; + + assertThat(mapResult).hasSize(2); + assertThat(mapResult.get(1)).isEqualTo("TEST"); + + Object value = mapResult.get(2); + + assertThat(value).isInstanceOf(PdxInstanceWrapper.class); + assertThat(((PdxInstanceWrapper) value).getDelegate()).isEqualTo(mockPdxInstance); + + verify(mockJointPoint, times(1)).proceed(); + verifyNoInteractions(mockPdxInstance); + } + + @Test + @SuppressWarnings("unchecked") + public void regionGetEntryAdviceWrapsPdx() throws Throwable { + + Region.Entry mockRegionEntry = mock(Region.Entry.class); + + ProceedingJoinPoint mockJoinPoint = mock(ProceedingJoinPoint.class); + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + doReturn(mockPdxInstance).when(mockRegionEntry).getValue(); + doReturn(mockRegionEntry).when(mockJoinPoint).proceed(); + + Object result = this.aspect.regionGetEntryAdvice(mockJoinPoint); + + assertThat(result).isInstanceOf(Region.Entry.class); + assertThat(result).isNotSameAs(mockRegionEntry); + + Object regionEntryValue = ((Region.Entry) result).getValue(); + + assertThat(regionEntryValue).isInstanceOf(PdxInstanceWrapper.class); + assertThat(((PdxInstanceWrapper) regionEntryValue).getDelegate()).isEqualTo(mockPdxInstance); + + verify(mockJoinPoint, times(1)).proceed(); + verify(mockRegionEntry, times(1)).getValue(); + verifyNoInteractions(mockPdxInstance); + } + + @Test + public void regionSelectValueAdviceWrapsPdx() throws Throwable { + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + ProceedingJoinPoint mockJointPoint = mock(ProceedingJoinPoint.class); + + doReturn(mockPdxInstance).when(mockJointPoint).proceed(); + + Object pdx = this.aspect.regionSelectValueAdvice(mockJointPoint); + + assertThat(pdx).isInstanceOf(PdxInstanceWrapper.class); + assertThat(((PdxInstanceWrapper) pdx).getDelegate()).isEqualTo(mockPdxInstance); + + verify(mockJointPoint, times(1)).proceed(); + verifyNoInteractions(mockPdxInstance); + } + + @Test + public void regionSelectValueAdviceReturnsNull() throws Throwable { + + ProceedingJoinPoint mockJointPoint = mock(ProceedingJoinPoint.class); + + doReturn(null).when(mockJointPoint).proceed(); + + assertThat(this.aspect.regionSelectValueAdvice(mockJointPoint)).isNull(); + + verify(mockJointPoint, times(1)).proceed(); + } + + @Test + @SuppressWarnings("unchecked") + public void regionValuesAdviceWrapsPdx() throws Throwable { + + ProceedingJoinPoint mockJointPoint = mock(ProceedingJoinPoint.class); + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + Collection regionValues = Arrays.asList("TEST", mockPdxInstance); + + doReturn(regionValues).when(mockJointPoint).proceed(); + + Object result = this.aspect.regionValuesAdvice(mockJointPoint); + + assertThat(result).isInstanceOf(Collection.class); + assertThat(result).isNotSameAs(regionValues); + + Collection collectionResult = (Collection) result; + + assertThat(collectionResult).hasSize(2); + + for (Object element : collectionResult) { + if (element instanceof PdxInstance) { + assertThat(element).isInstanceOf(PdxInstanceWrapper.class); + assertThat(((PdxInstanceWrapper) element).getDelegate()).isEqualTo(mockPdxInstance); + } + else { + assertThat(element).isEqualTo("TEST"); + } + } + + verify(mockJointPoint, times(1)).proceed(); + verifyNoInteractions(mockPdxInstance); + } + + @Test + @SuppressWarnings("unchecked") + public void constructRegionEntryWrapperWithRegionEntry() { + + Region.Entry mockRegionEntry = mock(Region.Entry.class); + + TestRegionEntryWrapper wrapper = new TestRegionEntryWrapper<>(mockRegionEntry); + + assertThat(wrapper).isNotNull(); + assertThat(wrapper.getDelegate()).isEqualTo(mockRegionEntry); + + verifyNoInteractions(mockRegionEntry); + } + + @Test(expected = IllegalArgumentException.class) + public void constructMockRegionEntryWrapperWithNull() { + + try { + new TestRegionEntryWrapper<>(null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("Region.Entry must not be null"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + @SuppressWarnings("unchecked") + public void fromRegionEntryReturnsNonNullRegionEntryWrapper() { + + Region.Entry mockRegionEntry = mock(Region.Entry.class); + + Object wrapper = RegionEntryWrapper.from(mockRegionEntry); + + assertThat(wrapper).isInstanceOf(RegionEntryWrapper.class); + + verifyNoInteractions(mockRegionEntry); + } + + @Test + public void fromObjectReturnsObject() { + assertThat(RegionEntryWrapper.from("TEST")).isEqualTo("TEST"); + } + + @Test + public void fromNullReturnsNull() { + assertThat(RegionEntryWrapper.from(null)).isNull(); + } + + @Test + @SuppressWarnings("unchecked") + public void regionEntryWrapperDelegatesAllRegionEntryOperations() { + + AtomicReference userAttribute = new AtomicReference<>(null); + AtomicReference value = new AtomicReference<>(null); + + CacheStatistics mockCacheStatistics = mock(CacheStatistics.class); + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + Region mockRegion = mock(Region.class); + + Region.Entry mockRegionEntry = mock(Region.Entry.class); + + doReturn(false).when(mockRegionEntry).isDestroyed(); + doReturn(true).when(mockRegionEntry).isLocal(); + doReturn(2).when(mockRegionEntry).getKey(); + doReturn(mockRegion).when(mockRegionEntry).getRegion(); + doReturn(mockCacheStatistics).when(mockRegionEntry).getStatistics(); + + doAnswer(invocation -> userAttribute.getAndUpdate(it -> invocation.getArgument(0))) + .when(mockRegionEntry).setUserAttribute(any()); + + doAnswer(invocation -> userAttribute.get()).when(mockRegionEntry).getUserAttribute(); + + doAnswer(invocation -> value.getAndUpdate(it -> invocation.getArgument(0))) + .when(mockRegionEntry).setValue(any()); + + doAnswer(invocation -> value.get()).when(mockRegionEntry).getValue(); + + Region.Entry regionEntryWrapper = RegionEntryWrapper.from(mockRegionEntry); + + assertThat(regionEntryWrapper).isInstanceOf(RegionEntryWrapper.class); + assertThat(regionEntryWrapper.isDestroyed()).isFalse(); + assertThat(regionEntryWrapper.isLocal()).isTrue(); + assertThat(regionEntryWrapper.getKey()).isEqualTo(2); + assertThat(regionEntryWrapper.getRegion()).isEqualTo(mockRegion); + assertThat(regionEntryWrapper.getStatistics()).isEqualTo(mockCacheStatistics); + assertThat(regionEntryWrapper.setUserAttribute("TEST")).isNull(); + assertThat(regionEntryWrapper.getUserAttribute()).isEqualTo("TEST"); + assertThat(regionEntryWrapper.setValue(42)).isNull(); + assertThat(regionEntryWrapper.getValue()).isEqualTo(42); + assertThat(regionEntryWrapper.setValue(mockPdxInstance)).isEqualTo(42); + + Object regionValue = regionEntryWrapper.getValue(); + + assertThat(regionValue).isInstanceOf(PdxInstanceWrapper.class); + assertThat(((PdxInstanceWrapper) regionValue).getDelegate()).isEqualTo(mockPdxInstance); + + verify(mockRegionEntry, times(1)).isDestroyed(); + verify(mockRegionEntry, times(1)).isLocal(); + verify(mockRegionEntry, times(1)).getKey(); + verify(mockRegionEntry, times(1)).getRegion(); + verify(mockRegionEntry, times(1)).getStatistics(); + verify(mockRegionEntry, times(1)).setUserAttribute(eq("TEST")); + verify(mockRegionEntry, times(1)).getUserAttribute(); + verify(mockRegionEntry, times(1)).setValue(eq(42)); + verify(mockRegionEntry, times(1)).setValue(eq(mockPdxInstance)); + verify(mockRegionEntry, times(2)).getValue(); + verifyNoInteractions(mockCacheStatistics, mockRegion); + } + + static final class TestRegionEntryWrapper extends RegionEntryWrapper { + + TestRegionEntryWrapper(Region.Entry regionEntry) { + super(regionEntry); + } + + @Override + protected Region.Entry getDelegate() { + return super.getDelegate(); + } + } +}