✅ Add equals for AbstractState
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sat, 29 Apr 2023 19:29:08 +0000 (21:29 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sat, 29 Apr 2023 19:29:08 +0000 (21:29 +0200)
src/main/java/net/pterodactylus/rhynodge/states/AbstractState.java
src/test/kotlin/net/pterodactylus/rhynodge/states/AbstractStateTest.kt [new file with mode: 0644]

index 081fbcc..8828fe4 100644 (file)
@@ -17,6 +17,8 @@
 
 package net.pterodactylus.rhynodge.states;
 
+import java.time.Clock;
+import java.util.Objects;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
@@ -69,11 +71,26 @@ public abstract class AbstractState implements State {
         *            otherwise
         */
        protected AbstractState(boolean success) {
-               this(success, true, null);
+               this(Clock.systemUTC(), success);
+       }
+
+       /**
+        * Creates a new state.
+        *
+        * @param clock The clock for generating {@link #time}
+        * @param success {@code true} if the state is successful, {@code false}
+        *              otherwise
+        */
+       protected AbstractState(Clock clock, boolean success) {
+               this(clock, success, true, null);
        }
 
        protected AbstractState(boolean success, boolean empty) {
-               this(success, empty, null);
+               this(Clock.systemUTC(), success, empty);
+       }
+
+       protected AbstractState(Clock clock, boolean success, boolean empty) {
+               this(clock, success, empty, null);
        }
 
        /**
@@ -83,7 +100,17 @@ public abstract class AbstractState implements State {
         *            The exception that occured while retrieving the state
         */
        protected AbstractState(Throwable exception) {
-               this(false, true, exception);
+               this(Clock.systemUTC(), exception);
+       }
+
+       /**
+        * Creates a new non-successful state with the given exception.
+        *
+        * @param clock The clock for generating {@link #time}
+        * @param exception The exception that occured while retrieving the state
+        */
+       protected AbstractState(Clock clock, Throwable exception) {
+               this(clock, false, true, exception);
        }
 
        /**
@@ -96,7 +123,19 @@ public abstract class AbstractState implements State {
         *            The exception that occured while retrieving the state
         */
        protected AbstractState(boolean success, boolean empty, Throwable exception) {
-               this.time = System.currentTimeMillis();
+               this(Clock.systemUTC(), success, empty, exception);
+       }
+
+       /**
+        * Creates a new state.
+        *
+        * @param clock The clock for generating {@link #time}
+        * @param success {@code true} if the state is successful, {@code false}
+        *              otherwise
+        * @param exception The exception that occured while retrieving the state
+        */
+       protected AbstractState(Clock clock, boolean success, boolean empty, Throwable exception) {
+               this.time = clock.millis();
                this.success = success;
                this.empty = empty;
                this.exception = exception;
@@ -176,4 +215,18 @@ public abstract class AbstractState implements State {
        @SuppressWarnings("UnstableApiUsage")
        private static final Escaper htmlEscaper = HtmlEscapers.htmlEscaper();
 
+       @Override
+       public int hashCode() {
+               return Objects.hash(success, empty, time, failCount, exception);
+       }
+
+       @Override
+       public boolean equals(Object object) {
+               if (!(object instanceof AbstractState)) {
+                       return false;
+               }
+               AbstractState abstractState = (AbstractState) object;
+               return (success == abstractState.success) && (empty == abstractState.empty) && (time == abstractState.time) && (failCount == abstractState.failCount) && Objects.equals(exception, abstractState.exception);
+       }
+
 }
diff --git a/src/test/kotlin/net/pterodactylus/rhynodge/states/AbstractStateTest.kt b/src/test/kotlin/net/pterodactylus/rhynodge/states/AbstractStateTest.kt
new file mode 100644 (file)
index 0000000..29d7386
--- /dev/null
@@ -0,0 +1,83 @@
+package net.pterodactylus.rhynodge.states
+
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.equalTo
+import org.junit.Test
+import java.time.Clock
+import java.time.Instant.now
+import java.time.ZoneOffset.UTC
+
+/**
+ * Unit test for [AbstractState].
+ */
+class AbstractStateTest {
+
+       @Test
+       fun `equals returns true when everything is the same`() {
+               val first = object : AbstractState(testClock, false) {
+                       override fun plainText() = "foo"
+               }
+               val second = object : AbstractState(testClock, false) {
+                       override fun plainText() = "foo"
+               }
+               assertThat(first.equals(second), equalTo(true))
+       }
+
+       @Test
+       fun `equals returns false for when success is different`() {
+               val first = object : AbstractState(testClock, false) {
+                       override fun plainText() = "foo"
+               }
+               val second = object : AbstractState(testClock, true) {
+                       override fun plainText() = "foo"
+               }
+               assertThat(first.equals(second), equalTo(false))
+       }
+
+       @Test
+       fun `equals return false when empty is different`() {
+               val first = object : AbstractState(testClock, false, false) {
+                       override fun plainText() = "foo"
+               }
+               val second = object : AbstractState(testClock, false, true) {
+                       override fun plainText() = "foo"
+               }
+               assertThat(first.equals(second), equalTo(false))
+       }
+
+       @Test
+       fun `equals returns false when time is different`() {
+               val first = object : AbstractState(testClock, false) {
+                       override fun plainText() = "foo"
+               }
+               val second = object : AbstractState(Clock.fixed(now().plusSeconds(1), UTC), false) {
+                       override fun plainText() = "foo"
+               }
+               assertThat(first.equals(second), equalTo(false))
+       }
+
+       @Test
+       fun `equals returns false when failCount is different`() {
+               val first = object : AbstractState(testClock, false) {
+                       override fun plainText() = "foo"
+               }.apply { setFailCount(12) }
+               val second = object : AbstractState(testClock, false) {
+                       override fun plainText() = "foo"
+               }.apply { setFailCount(23) }
+               assertThat(first.equals(second), equalTo(false))
+       }
+
+       @Test
+       fun `equals returns false when exception is different`() {
+               val first = object : AbstractState(testClock, RuntimeException()) {
+                       override fun plainText() = "foo"
+               }
+               val second = object : AbstractState(testClock, IllegalArgumentException()) {
+                       override fun plainText() = "foo"
+               }
+               assertThat(first.equals(second), equalTo(false))
+       }
+
+}
+
+private val testClock = Clock.fixed(now(), UTC)