diff --git a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/test/context/DestroyGemFireMockObjectsTestExecutionListener.java b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/test/context/DestroyGemFireMockObjectsTestExecutionListener.java new file mode 100644 index 0000000..fbd274e --- /dev/null +++ b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/test/context/DestroyGemFireMockObjectsTestExecutionListener.java @@ -0,0 +1,201 @@ +/* + * Copyright 2019 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.data.gemfire.tests.mock.test.context; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import org.springframework.data.gemfire.tests.mock.GemFireMockObjectsSupport; +import org.springframework.data.gemfire.tests.mock.test.context.event.TestContextEventType; +import org.springframework.lang.Nullable; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.TestExecutionListener; +import org.springframework.test.context.event.TestContextEvent; + +/** + * A Spring {@link TestExecutionListener} implementation listening for and handling different {@link TestContextEvent} + * in order to destroy all GemFire/Geode {@link Object Mock Objects} at the appropriate test lifecycle event. + * + * By default, event handling for {@link TestContextEventType#AFTER_TEST_CLASS} is enabled and GemFire/Geode + * {@link Object Mock Objects} will be destroyed when this event occurs. + * + * @author John Blum + * @see org.springframework.data.gemfire.tests.mock.GemFireMockObjectsSupport + * @see org.springframework.data.gemfire.tests.mock.test.context.event.TestContextEventType + * @see org.springframework.test.context.TestContext + * @see org.springframework.test.context.TestExecutionListener + * @see org.springframework.test.context.event.TestContextEvent + * @since 0.0.16 + */ +public class DestroyGemFireMockObjectsTestExecutionListener implements TestExecutionListener { + + private static final AtomicReference instance = + new AtomicReference<>(null); + + /** + * Returns an {@link Optional} reference to the constructed {@link DestroyGemFireMockObjectsTestExecutionListener} + * created by the Spring {@link TestContext} test framework on test execution. + * + * @return an {@link Optional} reference to a {@link DestroyGemFireMockObjectsTestExecutionListener} instance. + * @see java.util.Optional + */ + public static Optional getInstance() { + return Optional.ofNullable(instance.get()); + } + + private final Set destroyOnEventTypes = + Collections.synchronizedSet(new HashSet<>(TestContextEventType.values().length)); + + /** + * Constructs a new instance of the {@link DestroyGemFireMockObjectsTestExecutionListener}. + */ + public DestroyGemFireMockObjectsTestExecutionListener() { + instance.set(this); + enableDestroyOnEventType(TestContextEventType.AFTER_TEST_CLASS); + } + + /** + * Disables event handling and destruction of GemFire/Geode {@link Object Mock Objects} for + * the given {@link TestContextEventType}. + * + * @param eventType {@link TestContextEventType} to disable event handling for. + * @return a boolean value indicating whether event handling for the given {@link TestContextEventType} + * was successfully disabled. + * @see org.springframework.data.gemfire.tests.mock.test.context.event.TestContextEventType + * @see #enableDestroyOnEventType(TestContextEventType) + */ + public boolean disableDestroyOnEventType(@Nullable TestContextEventType eventType) { + + return eventType != null + && (this.destroyOnEventTypes.remove(eventType) || !this.destroyOnEventTypes.contains(eventType)); + } + + /** + * Enables event handling and destruction of GemFire/Geode {@link Object Mock Objects} for + * the given {@link TestContextEventType}. + * + * @param eventType {@link TestContextEventType} to enable event handling for. + * @return a boolean value indicating whether event handling for the given {@link TestContextEventType} + * was successfully enabled. + * @see org.springframework.data.gemfire.tests.mock.test.context.event.TestContextEventType + * @see #disableDestroyOnEventType(TestContextEventType) + */ + public boolean enableDestroyOnEventType(@Nullable TestContextEventType eventType) { + + return eventType != null + && (this.destroyOnEventTypes.add(eventType) || this.destroyOnEventTypes.contains(eventType)); + } + + /** + * Determines whether event handling for the given {@link TestContextEventType} is enabled. + * + * @param eventType {{@link TestContextEventType} to evaluate. + * @return a boolean value indicating whether event handling for the given {@link TestContextEventType} is enabled. + * @see org.springframework.data.gemfire.tests.mock.test.context.event.TestContextEventType + */ + protected boolean isDestroyOnEventTypeEnabled(@Nullable TestContextEventType eventType) { + return this.destroyOnEventTypes.contains(eventType); + } + + /** + * @inheritDoc + */ + @Override + public void beforeTestClass(TestContext testContext) { + + if (isDestroyOnEventTypeEnabled(TestContextEventType.BEFORE_TEST_CLASS)) { + destroyGemFireMockObjects(); + } + } + + /** + * @inheritDoc + */ + @Override + public void prepareTestInstance(TestContext testContext) { + + if (isDestroyOnEventTypeEnabled(TestContextEventType.PREPARE_TEST_INSTANCE)) { + destroyGemFireMockObjects(); + } + } + + /** + * @inheritDoc + */ + @Override + public void beforeTestMethod(TestContext testContext) { + + if (isDestroyOnEventTypeEnabled(TestContextEventType.BEFORE_TEST_METHOD)) { + destroyGemFireMockObjects(); + } + } + + /** + * @inheritDoc + */ + @Override + public void beforeTestExecution(TestContext testContext) { + + if (isDestroyOnEventTypeEnabled(TestContextEventType.BEFORE_TEST_EXECUTION)) { + destroyGemFireMockObjects(); + } + } + + /** + * @inheritDoc + */ + @Override + public void afterTestExecution(TestContext testContext) { + + if (isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_EXECUTION)) { + destroyGemFireMockObjects(); + } + } + + /** + * @inheritDoc + */ + @Override + public void afterTestMethod(TestContext testContext) { + + if (isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_METHOD)) { + destroyGemFireMockObjects(); + } + } + + /** + * @inheritDoc + */ + @Override + public void afterTestClass(TestContext testContext) { + + if (isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_CLASS)) { + destroyGemFireMockObjects(); + } + } + + /** + * Destroys all GemFire/Geode {@link Object Mock Objects}. + * + * @see org.springframework.data.gemfire.tests.mock.GemFireMockObjectsSupport#destroy() + */ + protected void destroyGemFireMockObjects() { + GemFireMockObjectsSupport.destroy(); + } +} diff --git a/spring-data-geode-test/src/test/java/org/springframework/data/gemfire/tests/mock/test/context/DestroyGemFireMockObjectsTestExecutionListenerUnitTests.java b/spring-data-geode-test/src/test/java/org/springframework/data/gemfire/tests/mock/test/context/DestroyGemFireMockObjectsTestExecutionListenerUnitTests.java new file mode 100644 index 0000000..334f55e --- /dev/null +++ b/spring-data-geode-test/src/test/java/org/springframework/data/gemfire/tests/mock/test/context/DestroyGemFireMockObjectsTestExecutionListenerUnitTests.java @@ -0,0 +1,318 @@ +/* + * Copyright 2019 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.data.gemfire.tests.mock.test.context; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.data.gemfire.tests.mock.test.context.event.TestContextEventType; +import org.springframework.test.context.TestContext; + +/** + * Unit Tests for {@link DestroyGemFireMockObjectsTestExecutionListener}. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mockito + * @see org.mockito.junit.MockitoJUnitRunner + * @see org.springframework.data.gemfire.tests.mock.test.context.DestroyGemFireMockObjectsTestExecutionListener + * @see org.springframework.data.gemfire.tests.mock.test.context.event.TestContextEventType + * @since 0.0.16 + */ +@RunWith(MockitoJUnitRunner.class) +public class DestroyGemFireMockObjectsTestExecutionListenerUnitTests { + + @Mock + private TestContext mockTestContext; + + @Test + public void enableDestroyOnValidTestContextEventTypesReturnsTrue() { + + DestroyGemFireMockObjectsTestExecutionListener listener = new DestroyGemFireMockObjectsTestExecutionListener(); + + assertThat(listener).isNotNull(); + assertThat(DestroyGemFireMockObjectsTestExecutionListener.getInstance().orElse(null)).isSameAs(listener); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_CLASS)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_EXECUTION)).isFalse(); + assertThat(listener.enableDestroyOnEventType(TestContextEventType.AFTER_TEST_EXECUTION)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_EXECUTION)).isTrue(); + assertThat(listener.enableDestroyOnEventType(TestContextEventType.AFTER_TEST_EXECUTION)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_EXECUTION)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.BEFORE_TEST_METHOD)).isFalse(); + assertThat(listener.enableDestroyOnEventType(TestContextEventType.BEFORE_TEST_METHOD)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.BEFORE_TEST_METHOD)).isTrue(); + } + + @Test + public void enableDestroyWithNullIsNullSafeReturnsFalse() { + + DestroyGemFireMockObjectsTestExecutionListener listener = new DestroyGemFireMockObjectsTestExecutionListener(); + + assertThat(listener).isNotNull(); + assertThat(DestroyGemFireMockObjectsTestExecutionListener.getInstance().orElse(null)).isSameAs(listener); + assertThat(listener.enableDestroyOnEventType(null)).isFalse(); + } + + @Test + public void disableDestroyOnValidTestContextEventTypesReturnsTrue() { + + DestroyGemFireMockObjectsTestExecutionListener listener = new DestroyGemFireMockObjectsTestExecutionListener(); + + assertThat(listener).isNotNull(); + assertThat(DestroyGemFireMockObjectsTestExecutionListener.getInstance().orElse(null)).isSameAs(listener); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_CLASS)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_EXECUTION)).isFalse(); + assertThat(listener.disableDestroyOnEventType(TestContextEventType.AFTER_TEST_EXECUTION)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_EXECUTION)).isFalse(); + assertThat(listener.enableDestroyOnEventType(TestContextEventType.PREPARE_TEST_INSTANCE)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.PREPARE_TEST_INSTANCE)).isTrue(); + assertThat(listener.disableDestroyOnEventType(TestContextEventType.PREPARE_TEST_INSTANCE)).isTrue(); + assertThat(listener.disableDestroyOnEventType(TestContextEventType.PREPARE_TEST_INSTANCE)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.PREPARE_TEST_INSTANCE)).isFalse(); + } + + @Test + public void disableDestroyWithNullIsNullSafeReturnsFalse() { + + DestroyGemFireMockObjectsTestExecutionListener listener = new DestroyGemFireMockObjectsTestExecutionListener(); + + assertThat(listener).isNotNull(); + assertThat(DestroyGemFireMockObjectsTestExecutionListener.getInstance().orElse(null)).isSameAs(listener); + assertThat(listener.disableDestroyOnEventType(null)).isFalse(); + } + + @Test + public void isDestroyOnEventTypeEnableIsNullSafe() { + + DestroyGemFireMockObjectsTestExecutionListener listener = new DestroyGemFireMockObjectsTestExecutionListener(); + + assertThat(listener).isNotNull(); + assertThat(DestroyGemFireMockObjectsTestExecutionListener.getInstance().orElse(null)).isSameAs(listener); + assertThat(listener.isDestroyOnEventTypeEnabled(null)).isFalse(); + } + + @Test + public void beforeTestClassIsDisabledByDefault() { + + DestroyGemFireMockObjectsTestExecutionListener listener = + spy(new DestroyGemFireMockObjectsTestExecutionListener()); + + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.BEFORE_TEST_CLASS)).isFalse(); + + listener.beforeTestClass(this.mockTestContext); + + verify(listener, never()).destroyGemFireMockObjects(); + } + + @Test + public void beforeTestClassDestroysGemFireMockObjectsWhenEnabled() { + + DestroyGemFireMockObjectsTestExecutionListener listener = + spy(new DestroyGemFireMockObjectsTestExecutionListener()); + + doNothing().when(listener).destroyGemFireMockObjects(); + + assertThat(listener.enableDestroyOnEventType(TestContextEventType.BEFORE_TEST_CLASS)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.BEFORE_TEST_CLASS)).isTrue(); + + listener.beforeTestClass(this.mockTestContext); + + verify(listener, times(1)).destroyGemFireMockObjects(); + } + + @Test + public void prepareTestInstanceIsDisabledByDefault() { + + DestroyGemFireMockObjectsTestExecutionListener listener = + spy(new DestroyGemFireMockObjectsTestExecutionListener()); + + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.PREPARE_TEST_INSTANCE)).isFalse(); + + listener.prepareTestInstance(this.mockTestContext); + + verify(listener, never()).destroyGemFireMockObjects(); + } + + @Test + public void prepareTestInstanceDestroysGemFireMockObjectsWhenEnabled() { + + DestroyGemFireMockObjectsTestExecutionListener listener = + spy(new DestroyGemFireMockObjectsTestExecutionListener()); + + doNothing().when(listener).destroyGemFireMockObjects(); + + assertThat(listener.enableDestroyOnEventType(TestContextEventType.PREPARE_TEST_INSTANCE)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.PREPARE_TEST_INSTANCE)).isTrue(); + + listener.prepareTestInstance(this.mockTestContext); + + verify(listener, times(1)).destroyGemFireMockObjects(); + } + + @Test + public void beforeTestMethodIsDisabledByDefault() { + + DestroyGemFireMockObjectsTestExecutionListener listener = + spy(new DestroyGemFireMockObjectsTestExecutionListener()); + + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.BEFORE_TEST_METHOD)).isFalse(); + + listener.beforeTestMethod(this.mockTestContext); + + verify(listener, never()).destroyGemFireMockObjects(); + } + + @Test + public void beforeTestMethodDestroysGemFireMockObjectsWhenEnabled() { + + DestroyGemFireMockObjectsTestExecutionListener listener = + spy(new DestroyGemFireMockObjectsTestExecutionListener()); + + doNothing().when(listener).destroyGemFireMockObjects(); + + assertThat(listener.enableDestroyOnEventType(TestContextEventType.BEFORE_TEST_METHOD)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.BEFORE_TEST_METHOD)).isTrue(); + + listener.beforeTestMethod(this.mockTestContext); + + verify(listener, times(1)).destroyGemFireMockObjects(); + } + + @Test + public void beforeTestExecutionIsDisabledByDefault() { + + DestroyGemFireMockObjectsTestExecutionListener listener = + spy(new DestroyGemFireMockObjectsTestExecutionListener()); + + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.BEFORE_TEST_EXECUTION)).isFalse(); + + listener.beforeTestExecution(this.mockTestContext); + + verify(listener, never()).destroyGemFireMockObjects(); + } + + @Test + public void beforeTestExecutionDestroysGemFireMockObjectsWhenEnabled() { + + DestroyGemFireMockObjectsTestExecutionListener listener = + spy(new DestroyGemFireMockObjectsTestExecutionListener()); + + doNothing().when(listener).destroyGemFireMockObjects(); + + assertThat(listener.enableDestroyOnEventType(TestContextEventType.BEFORE_TEST_EXECUTION)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.BEFORE_TEST_EXECUTION)).isTrue(); + + listener.beforeTestExecution(this.mockTestContext); + + verify(listener, times(1)).destroyGemFireMockObjects(); + } + + @Test + public void afterTestExecutionIsDisabledByDefault() { + + DestroyGemFireMockObjectsTestExecutionListener listener = + spy(new DestroyGemFireMockObjectsTestExecutionListener()); + + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_EXECUTION)).isFalse(); + + listener.afterTestExecution(this.mockTestContext); + + verify(listener, never()).destroyGemFireMockObjects(); + } + + @Test + public void afterTestExecutionDestroysGemFireMockObjectsWhenEnabled() { + + DestroyGemFireMockObjectsTestExecutionListener listener = + spy(new DestroyGemFireMockObjectsTestExecutionListener()); + + doNothing().when(listener).destroyGemFireMockObjects(); + + assertThat(listener.enableDestroyOnEventType(TestContextEventType.AFTER_TEST_EXECUTION)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_EXECUTION)).isTrue(); + + listener.afterTestExecution(this.mockTestContext); + + verify(listener, times(1)).destroyGemFireMockObjects(); + } + + @Test + public void afterTestMethodIsDisabledByDefault() { + + DestroyGemFireMockObjectsTestExecutionListener listener = + spy(new DestroyGemFireMockObjectsTestExecutionListener()); + + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_METHOD)).isFalse(); + + listener.afterTestMethod(this.mockTestContext); + + verify(listener, never()).destroyGemFireMockObjects(); + } + + @Test + public void afterTestMethodDestroysGemFireMockObjectsWhenEnabled() { + + DestroyGemFireMockObjectsTestExecutionListener listener = + spy(new DestroyGemFireMockObjectsTestExecutionListener()); + + doNothing().when(listener).destroyGemFireMockObjects(); + + assertThat(listener.enableDestroyOnEventType(TestContextEventType.AFTER_TEST_METHOD)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_METHOD)).isTrue(); + + listener.afterTestMethod(this.mockTestContext); + + verify(listener, times(1)).destroyGemFireMockObjects(); + } + + @Test + public void afterTestClassIsEnabledByDefault() { + + DestroyGemFireMockObjectsTestExecutionListener listener = + spy(new DestroyGemFireMockObjectsTestExecutionListener()); + + doNothing().when(listener).destroyGemFireMockObjects(); + + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_CLASS)).isTrue(); + + listener.afterTestClass(this.mockTestContext); + + verify(listener, times(1)).destroyGemFireMockObjects(); + } + + @Test + public void afterTestClassWillNotDestroyGemFireMockObjectsWhenDisabled() { + + DestroyGemFireMockObjectsTestExecutionListener listener = + spy(new DestroyGemFireMockObjectsTestExecutionListener()); + + assertThat(listener.disableDestroyOnEventType(TestContextEventType.AFTER_TEST_CLASS)).isTrue(); + assertThat(listener.isDestroyOnEventTypeEnabled(TestContextEventType.AFTER_TEST_CLASS)).isFalse(); + + listener.afterTestClass(this.mockTestContext); + + verify(listener, never()).destroyGemFireMockObjects(); + } +}