From 96632a05028ec278e43d22f2436bfe6f2c1ba33d Mon Sep 17 00:00:00 2001 From: John Blum Date: Sat, 15 Dec 2018 10:40:43 -0800 Subject: [PATCH] Add an IsDirtyPredicate strategy interface for determining when users application domain objects are dirty in a DataSerialization, Delta capable Session configuration. Resolves gh-17. --- .../support/DeltaAwareDirtyPredicate.java | 59 +++++++ .../gemfire/support/EqualsDirtyPredicate.java | 54 ++++++ .../support/IdentityEqualsDirtyPredicate.java | 50 ++++++ .../gemfire/support/IsDirtyPredicate.java | 89 ++++++++++ .../DeltaAwareDirtyPredicateUnitTests.java | 77 +++++++++ .../EqualsDirtyPredicateUnitTests.java | 88 ++++++++++ ...IdentityEqualsDirtyPredicateUnitTests.java | 50 ++++++ .../support/IsDirtyPredicateUnitTests.java | 156 ++++++++++++++++++ 8 files changed, 623 insertions(+) create mode 100644 spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/DeltaAwareDirtyPredicate.java create mode 100644 spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/EqualsDirtyPredicate.java create mode 100644 spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/IdentityEqualsDirtyPredicate.java create mode 100644 spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/IsDirtyPredicate.java create mode 100644 spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/DeltaAwareDirtyPredicateUnitTests.java create mode 100644 spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/EqualsDirtyPredicateUnitTests.java create mode 100644 spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/IdentityEqualsDirtyPredicateUnitTests.java create mode 100644 spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/IsDirtyPredicateUnitTests.java diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/DeltaAwareDirtyPredicate.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/DeltaAwareDirtyPredicate.java new file mode 100644 index 0000000..485529c --- /dev/null +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/DeltaAwareDirtyPredicate.java @@ -0,0 +1,59 @@ +/* + * 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.session.data.gemfire.support; + +import org.apache.geode.Delta; + +import org.springframework.lang.Nullable; + +/** + * {@link DeltaAwareDirtyPredicate} is an {@link IsDirtyPredicate} strategy interface implementation that evaluates + * the {@link Object new value} as instance of {@link Delta} and uses the {@link Delta#hasDelta()} method + * to determine if the {@link Object new value} is dirty. + * + * @author John Blum + * @see org.apache.geode.Delta + * @see org.springframework.session.data.gemfire.support.IsDirtyPredicate + * @since 2.1.2 + */ +@SuppressWarnings("unused") +public class DeltaAwareDirtyPredicate implements IsDirtyPredicate { + + public static final DeltaAwareDirtyPredicate INSTANCE = new DeltaAwareDirtyPredicate(); + + /** + * Determines whether the {@link Object new value} is dirty by evaluating the {@link Object new value} + * as an instance of {@link Delta} and invoking its {@link Delta#hasDelta()} method. + * + * The {@link Object new value} is considered dirty immediately and automatically if the {@link Object new value} + * is not an instance of {@link Delta}. + * + * This method is {@literal null-safe}. + * + * @param oldValue {@link Object} referring to the previous value. + * @param newValue {@link Object} referring to the new value. + * @return a boolean value indicating whether the {@link Object new value} is dirty by evaluating + * the {@link Object new value} as an instance of {@link Delta} and invoking its {@link Delta#hasDelta()} method. + * Returns {@literal true} immediately if the {@link Object new value} is not an instance of {@link Delta}. + * @see org.apache.geode.Delta#hasDelta() + * @see org.apache.geode.Delta + */ + @Override + public boolean isDirty(@Nullable Object oldValue, @Nullable Object newValue) { + return !(newValue instanceof Delta) || ((Delta) newValue).hasDelta(); + } +} diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/EqualsDirtyPredicate.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/EqualsDirtyPredicate.java new file mode 100644 index 0000000..145cb3b --- /dev/null +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/EqualsDirtyPredicate.java @@ -0,0 +1,54 @@ +/* + * 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.session.data.gemfire.support; + +import org.springframework.lang.Nullable; + +/** + * {@link EqualsDirtyPredicate} is an {@link IsDirtyPredicate} strategy interface implementation that determines + * whether the {@link Object new value} is dirty by comparing the {@link Object new value} + * with the {@link Object old value} using the {@link Object#equals(Object)} method. + * + * @author John Blum + * @see org.springframework.session.data.gemfire.support.IsDirtyPredicate + * @since 2.1.2 + */ +@SuppressWarnings("unused") +public class EqualsDirtyPredicate implements IsDirtyPredicate { + + public static final EqualsDirtyPredicate INSTANCE = new EqualsDirtyPredicate(); + + /** + * Determines whether the {@link Object new value} is dirty by comparing the {@link Object new value} + * with the {@link Object old value} using the {@link Object#equals(Object)} method. + * + * This method is {@literal null-safe}. + * + * @param oldValue {@link Object} referring to the previous value. + * @param newValue {@link Object} referring to the new value. + * @return a boolean value indicating whether the {@link Object new value} is dirty by comparing + * the {@link Object new value} with the {@link Object old value} using the {@link Object#equals(Object)} method. + * @see java.lang.Object#equals(Object) + */ + @Override + public boolean isDirty(@Nullable Object oldValue, @Nullable Object newValue) { + + return newValue != null + ? !newValue.equals(oldValue) + : oldValue != null; + } +} diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/IdentityEqualsDirtyPredicate.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/IdentityEqualsDirtyPredicate.java new file mode 100644 index 0000000..d4c9bf4 --- /dev/null +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/IdentityEqualsDirtyPredicate.java @@ -0,0 +1,50 @@ +/* + * 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.session.data.gemfire.support; + +import org.springframework.lang.Nullable; + +/** + * {@link IdentityEqualsDirtyPredicate} is an {@link IsDirtyPredicate} strategy interface implementation + * that determines whether the {@link Object new value} is dirty by comparing the {@link Object new value} + * with the {@link Object old value} using the identity operator ({@literal ==}). + * + * @author John Blum + * @see org.springframework.session.data.gemfire.support.IsDirtyPredicate + * @since 2.1.2 + */ +@SuppressWarnings("unused") +public class IdentityEqualsDirtyPredicate implements IsDirtyPredicate { + + public static final IdentityEqualsDirtyPredicate INSTANCE = new IdentityEqualsDirtyPredicate(); + + /** + * Determines whether the {@link Object new value} is dirty by comparing the {@link Object new value} + * with the {@link Object old value} using the identity operator ({@literal ==}). + * + * This method is {@literal null-safe}. + * + * @param oldValue {@link Object} referring to the previous value. + * @param newValue {@link Object} referring to the new value. + * @return a boolean value indicating whether the {@link Object new value} is dirty by comparing + * the {@link Object new value} with the {@link Object old value} using the identity operator ({@literal ==}). + */ + @Override + public boolean isDirty(@Nullable Object oldValue, @Nullable Object newValue) { + return newValue != oldValue; + } +} diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/IsDirtyPredicate.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/IsDirtyPredicate.java new file mode 100644 index 0000000..697201b --- /dev/null +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/IsDirtyPredicate.java @@ -0,0 +1,89 @@ +/* + * 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.session.data.gemfire.support; + +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; + +/** + * {@link IsDirtyPredicate} is a strategy interface used to configure Spring Session on how to evaluate application + * domain objects to determine whether they are dirty or not. + * + * @author John Blum + * @since 2.1.2 + */ +@FunctionalInterface +@SuppressWarnings("unused") +public interface IsDirtyPredicate { + + IsDirtyPredicate ALWAYS_DIRTY = (oldValue, newValue) -> false; + IsDirtyPredicate NEVER_DIRTY = (oldValue, newValue) -> true; + + /** + * Determines whether the {@link Object newValue} is dirty relative to the {@link Object oldValue}. + * + * @param oldValue {@link Object} referring to the previous value. + * @param newValue {@link Object} referring to the new value. + * @return a boolean value indicating whether the {@link Object newValue} is dirty + * relative to the {@link Object oldValue}. + */ + boolean isDirty(Object oldValue, Object newValue); + + /** + * Composes 2 {@link IsDirtyPredicate} objects using the logical AND operator. + * + * The {@code other} {@link IsDirtyPredicate} is applied after {@literal this} {@link IsDirtyPredicate} + * in the {@link #isDirty(Object, Object)} comparison evaluation. + * + * This composition is {@literal null-safe} and returns {@literal this} {@link IsDirtyPredicate} + * if {@link IsDirtyPredicate other} is {@literal null}. + * + * @param other {@link IsDirtyPredicate} composed with {@literal this} {@link IsDirtyPredicate}. + * @return an {@link IsDirtyPredicate} composition consisting of {@literal this} {@link IsDirtyPredicate} + * composed with the {@code other} {@link IsDirtyPredicate} using the logical AND operator. + * Returns {@literal this} {@link IsDirtyPredicate} if {@link IsDirtyPredicate other} is {@literal null}. + * @see #orThen(IsDirtyPredicate) + */ + default @NonNull IsDirtyPredicate andThen(@Nullable IsDirtyPredicate other) { + + return other != null + ? (oldValue, newValue) -> this.isDirty(oldValue, newValue) && other.isDirty(oldValue, newValue) + : this; + } + + /** + * Composes 2 {@link IsDirtyPredicate} objects using the logical OR operator. + * + * The {@code other} {@link IsDirtyPredicate} is applied after {@literal this} {@link IsDirtyPredicate} + * in the {@link #isDirty(Object, Object)} comparison evaluation. + * + * This composition is {@literal null-safe} and returns {@literal this} {@link IsDirtyPredicate} + * if {@link IsDirtyPredicate other} is {@literal null}. + * + * @param other {@link IsDirtyPredicate} composed with {@literal this} {@link IsDirtyPredicate}. + * @return an {@link IsDirtyPredicate} composition consisting of {@literal this} {@link IsDirtyPredicate} + * composed with the {@code other} {@link IsDirtyPredicate} using the logical OR operator. + * Returns {@literal this} {@link IsDirtyPredicate} if {@link IsDirtyPredicate other} is {@literal null}. + * @see #andThen(IsDirtyPredicate) + */ + default @NonNull IsDirtyPredicate orThen(@Nullable IsDirtyPredicate other) { + + return other != null + ? (oldValue, newValue) -> this.isDirty(oldValue, newValue) || other.isDirty(oldValue, newValue) + : this; + } +} diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/DeltaAwareDirtyPredicateUnitTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/DeltaAwareDirtyPredicateUnitTests.java new file mode 100644 index 0000000..5d408fd --- /dev/null +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/DeltaAwareDirtyPredicateUnitTests.java @@ -0,0 +1,77 @@ +/* + * 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.session.data.gemfire.support; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; + +import org.apache.geode.Delta; + +/** + * Unit Tests for {@link DeltaAwareDirtyPredicate}. + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.session.data.gemfire.support.DeltaAwareDirtyPredicate + * @since 2.1.2 + */ +public class DeltaAwareDirtyPredicateUnitTests { + + @Test + public void isDirtyIsNullSafe() { + + assertThat(DeltaAwareDirtyPredicate.INSTANCE.isDirty(null, null)).isTrue(); + assertThat(DeltaAwareDirtyPredicate.INSTANCE.isDirty("one", null)).isTrue(); + assertThat(DeltaAwareDirtyPredicate.INSTANCE.isDirty(null, "one")).isTrue(); + } + + @Test + public void isDirtyWithNonDeltaObjectReturnsTrue() { + + assertThat(DeltaAwareDirtyPredicate.INSTANCE.isDirty("one", "one")).isTrue(); + assertThat(DeltaAwareDirtyPredicate.INSTANCE.isDirty("one", "two")).isTrue(); + } + + @Test + public void isDirtyWithDeltaObjectReturnsTrue() { + + Delta mockDelta = mock(Delta.class); + + when(mockDelta.hasDelta()).thenReturn(true); + + assertThat(DeltaAwareDirtyPredicate.INSTANCE.isDirty(null, mockDelta)).isTrue(); + + verify(mockDelta, times(1)).hasDelta(); + } + + @Test + public void isDirtyWithDeltaObjectReturnsFalse() { + + Delta mockDelta = mock(Delta.class); + + when(mockDelta.hasDelta()).thenReturn(false); + + assertThat(DeltaAwareDirtyPredicate.INSTANCE.isDirty("one", mockDelta)).isFalse(); + + verify(mockDelta, times(1)).hasDelta(); + } +} diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/EqualsDirtyPredicateUnitTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/EqualsDirtyPredicateUnitTests.java new file mode 100644 index 0000000..e0dc6da --- /dev/null +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/EqualsDirtyPredicateUnitTests.java @@ -0,0 +1,88 @@ +/* + * 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.session.data.gemfire.support; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +/** + * Unit Tests for {@link EqualsDirtyPredicate}. + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.session.data.gemfire.support.EqualsDirtyPredicate + * @since 2.1.2 + */ +public class EqualsDirtyPredicateUnitTests { + + @Test + public void isDirtyIsIsNullSafe() { + + assertThat(EqualsDirtyPredicate.INSTANCE.isDirty(null, null)).isFalse(); + assertThat(EqualsDirtyPredicate.INSTANCE.isDirty("one", null)).isTrue(); + assertThat(EqualsDirtyPredicate.INSTANCE.isDirty(null, "one")).isTrue(); + } + + @Test + public void isDirtyWithSameObjectReturnsFalse() { + + Entry keyValue = Entry.newEntry(1, "one"); + + assertThat(EqualsDirtyPredicate.INSTANCE.isDirty(keyValue, keyValue)); + } + + @Test + public void isDirtyWithEqualObjectsReturnsFalse() { + + Entry entryOne = Entry.newEntry(1, "one"); + Entry entryTwo = Entry.newEntry(1, "one"); + + assertThat(EqualsDirtyPredicate.INSTANCE.isDirty(entryOne, entryTwo)); + } + + @Test + public void isDirtyWithDifferentObjectsReturnsFalse() { + + Entry entryOne = Entry.newEntry(1, "one"); + Entry entryTwo = Entry.newEntry(2, "two"); + + assertThat(EqualsDirtyPredicate.INSTANCE.isDirty(entryOne, entryTwo)); + } + + @Data + @EqualsAndHashCode + @RequiredArgsConstructor(staticName = "newEntry") + static class Entry { + + @NonNull + private final Object key; + + @NonNull + private final Object value; + + @Override + public String toString() { + return String.format("%1$s = %2$s", getKey(), getValue()); + } + } +} diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/IdentityEqualsDirtyPredicateUnitTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/IdentityEqualsDirtyPredicateUnitTests.java new file mode 100644 index 0000000..9f94353 --- /dev/null +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/IdentityEqualsDirtyPredicateUnitTests.java @@ -0,0 +1,50 @@ +/* + * 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.session.data.gemfire.support; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; + +/** + * Unit Tests for {@link IdentityEqualsDirtyPredicate}. + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.session.data.gemfire.support.IdentityEqualsDirtyPredicate + * @since 2.1.2 + */ +public class IdentityEqualsDirtyPredicateUnitTests { + + @Test + public void isDirtyIsNullSafe() { + + assertThat(IdentityEqualsDirtyPredicate.INSTANCE.isDirty(null, null)).isFalse(); + assertThat(IdentityEqualsDirtyPredicate.INSTANCE.isDirty("one", null)).isTrue(); + assertThat(IdentityEqualsDirtyPredicate.INSTANCE.isDirty(null, "one")).isTrue(); + } + + @Test + public void isDirtyWithDifferentObjectsReturnsTrue() { + assertThat(IdentityEqualsDirtyPredicate.INSTANCE.isDirty("one", "two")).isTrue(); + } + + @Test + public void isDirtyWithSameObjectsReturnsFalse() { + assertThat(IdentityEqualsDirtyPredicate.INSTANCE.isDirty("one", "one")).isFalse(); + } +} diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/IsDirtyPredicateUnitTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/IsDirtyPredicateUnitTests.java new file mode 100644 index 0000000..075bfc8 --- /dev/null +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/IsDirtyPredicateUnitTests.java @@ -0,0 +1,156 @@ +/* + * 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.session.data.gemfire.support; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Test; + +/** + * Unit Tests for {@link IsDirtyPredicate}. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mockito + * @see org.springframework.session.data.gemfire.support.IsDirtyPredicate + * @since 2.1.2 + */ +public class IsDirtyPredicateUnitTests { + + @Test + public void alwaysDirtyPredicateIsAlwaysTrue() { + + assertThat(IsDirtyPredicate.ALWAYS_DIRTY.isDirty("one", "one")).isFalse(); + assertThat(IsDirtyPredicate.ALWAYS_DIRTY.isDirty("one", "two")).isFalse(); + } + + @Test + public void alwaysDirtyPredicateIsNullSafe() { + + assertThat(IsDirtyPredicate.ALWAYS_DIRTY.isDirty("one", null)).isFalse(); + assertThat(IsDirtyPredicate.ALWAYS_DIRTY.isDirty(null, "one")).isFalse(); + assertThat(IsDirtyPredicate.ALWAYS_DIRTY.isDirty(null, null)).isFalse(); + } + + @Test + public void neverDirtyPredicateIsAlwaysTrue() { + + assertThat(IsDirtyPredicate.NEVER_DIRTY.isDirty("one", "one")).isTrue(); + assertThat(IsDirtyPredicate.NEVER_DIRTY.isDirty("one", "two")).isTrue(); + } + + @Test + public void neverDirtyPredicateIsNullSafe() { + + assertThat(IsDirtyPredicate.NEVER_DIRTY.isDirty("one", null)).isTrue(); + assertThat(IsDirtyPredicate.NEVER_DIRTY.isDirty(null, "one")).isTrue(); + assertThat(IsDirtyPredicate.NEVER_DIRTY.isDirty(null, null)).isTrue(); + } + + @Test + public void andThenComposesThisWithNull() { + + IsDirtyPredicate dirtyPredicate = mock(IsDirtyPredicate.class); + + when(dirtyPredicate.andThen(any())).thenCallRealMethod(); + + IsDirtyPredicate composedDirtyPredicate = dirtyPredicate.andThen(null); + + assertThat(composedDirtyPredicate).isSameAs(dirtyPredicate); + } + + @Test + public void andThenComposesTwoIsDirtyPredicates() { + + IsDirtyPredicate dirtyPredicateOne = mock(IsDirtyPredicate.class); + IsDirtyPredicate dirtyPredicateTwo = mock(IsDirtyPredicate.class); + + when(dirtyPredicateOne.andThen(any())).thenCallRealMethod(); + + IsDirtyPredicate composedDirtyPredicate = dirtyPredicateOne.andThen(dirtyPredicateTwo); + + assertThat(composedDirtyPredicate).isNotNull(); + assertThat(composedDirtyPredicate).isNotSameAs(dirtyPredicateOne); + assertThat(composedDirtyPredicate).isNotSameAs(dirtyPredicateTwo); + } + + @Test + public void andThenIsDirtyReturnsTrue() { + + assertThat(IsDirtyPredicate.NEVER_DIRTY.andThen(IsDirtyPredicate.NEVER_DIRTY) + .isDirty("one", "one")).isTrue(); + } + + @Test + public void andThenIsDirtyReturnsFalse() { + + assertThat(IsDirtyPredicate.NEVER_DIRTY.andThen(IsDirtyPredicate.ALWAYS_DIRTY) + .isDirty("one", "two")).isFalse(); + assertThat(IsDirtyPredicate.ALWAYS_DIRTY.andThen(IsDirtyPredicate.NEVER_DIRTY) + .isDirty("one", "two")).isFalse(); + assertThat(IsDirtyPredicate.ALWAYS_DIRTY.andThen(IsDirtyPredicate.ALWAYS_DIRTY) + .isDirty("one", "two")).isFalse(); + } + + @Test + public void orThenComposesThisWithNull() { + + IsDirtyPredicate dirtyPredicate = mock(IsDirtyPredicate.class); + + when(dirtyPredicate.orThen(any())).thenCallRealMethod(); + + IsDirtyPredicate composedDirtyPredicate = dirtyPredicate.orThen(null); + + assertThat(composedDirtyPredicate).isSameAs(dirtyPredicate); + } + + @Test + public void orThenComposesTwoIsDirtyPredicates() { + + IsDirtyPredicate dirtyPredicateOne = mock(IsDirtyPredicate.class); + IsDirtyPredicate dirtyPredicateTwo = mock(IsDirtyPredicate.class); + + when(dirtyPredicateOne.orThen(any())).thenCallRealMethod(); + + IsDirtyPredicate composedDirtyPredicate = dirtyPredicateOne.orThen(dirtyPredicateTwo); + + assertThat(composedDirtyPredicate).isNotNull(); + assertThat(composedDirtyPredicate).isNotSameAs(dirtyPredicateOne); + assertThat(composedDirtyPredicate).isNotSameAs(dirtyPredicateTwo); + } + + @Test + public void orThenIsDirtyReturnsTrue() { + + assertThat(IsDirtyPredicate.NEVER_DIRTY.orThen(IsDirtyPredicate.NEVER_DIRTY) + .isDirty("one", "one")).isTrue(); + assertThat(IsDirtyPredicate.NEVER_DIRTY.orThen(IsDirtyPredicate.ALWAYS_DIRTY) + .isDirty("one", "one")).isTrue(); + assertThat(IsDirtyPredicate.ALWAYS_DIRTY.orThen(IsDirtyPredicate.NEVER_DIRTY) + .isDirty("one", "one")).isTrue(); + } + + @Test + public void orThenIsDirtyReturnsFalse() { + + assertThat(IsDirtyPredicate.ALWAYS_DIRTY.andThen(IsDirtyPredicate.ALWAYS_DIRTY) + .isDirty("one", "two")).isFalse(); + } +}