Repository: jmrozanec/cron-utils (java)

Issue:
Implement equals and hashCode
It would be nice if the `equals` and `hashCode` would be implemented especially for the  `CronDefinition`.

Example use case: We need to figure out if a given cron definition is one of the pre-defined ones (QUARTZ, for example). In the following code `eq == false`, because the mentioned methods are not implemented at all.

```
final var a = instanceDefinitionFor(CronType.QUARTZ);
final var b = instanceDefinitionFor(CronType.QUARTZ);
final var eq = a.equals(b); // false
```

Or is there any other way to find whether a given cron definition matches another one?

Pull request description:
implement equals and hash code
implement equals and hash code for CronDefinition with corresponding tests

resolve #507

Patch:
diff --git a/pom.xml b/pom.xml
index ab6b117..4517ad8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -102,6 +102,12 @@
             <version>${mockito.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>pl.pragmatists</groupId>
+            <artifactId>JUnitParams</artifactId>
+            <version>1.1.1</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.powermock</groupId>
             <artifactId>powermock-module-junit4</artifactId>
diff --git a/src/main/java/com/cronutils/model/definition/CronConstraint.java b/src/main/java/com/cronutils/model/definition/CronConstraint.java
index 8cf66f1..80e61cc 100644
--- a/src/main/java/com/cronutils/model/definition/CronConstraint.java
+++ b/src/main/java/com/cronutils/model/definition/CronConstraint.java
@@ -16,6 +16,7 @@ package com.cronutils.model.definition;
 import com.cronutils.model.Cron;
 
 import java.io.Serializable;
+import java.util.Objects;
 
 public abstract class CronConstraint implements Serializable {
 
@@ -31,4 +32,21 @@ public abstract class CronConstraint implements Serializable {
     public String getDescription() {
         return description;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        CronConstraint that = (CronConstraint) o;
+        return Objects.equals(description, that.description);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(description);
+    }
 }
diff --git a/src/main/java/com/cronutils/model/definition/CronDefinition.java b/src/main/java/com/cronutils/model/definition/CronDefinition.java
index 95533e6..523de53 100755
--- a/src/main/java/com/cronutils/model/definition/CronDefinition.java
+++ b/src/main/java/com/cronutils/model/definition/CronDefinition.java
@@ -107,5 +107,26 @@ public class CronDefinition implements Serializable {
     public Set<CronNicknames> getCronNicknames() {
         return cronNicknames;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        CronDefinition that = (CronDefinition) o;
+        return matchDayOfWeekAndDayOfMonth == that.matchDayOfWeekAndDayOfMonth
+            && fieldDefinitions.equals(that.fieldDefinitions)
+            && cronConstraints.equals(that.cronConstraints)
+            && cronNicknames.equals(that.cronNicknames);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+            fieldDefinitions, cronConstraints, cronNicknames, matchDayOfWeekAndDayOfMonth);
+    }
 }
 


Test patch:
diff --git a/src/test/java/com/cronutils/model/definition/CronDefinitionTest.java b/src/test/java/com/cronutils/model/definition/CronDefinitionTest.java
index 354eeeb..5ba1942 100755
--- a/src/test/java/com/cronutils/model/definition/CronDefinitionTest.java
+++ b/src/test/java/com/cronutils/model/definition/CronDefinitionTest.java
@@ -15,8 +15,11 @@ package com.cronutils.model.definition;
 
 import com.cronutils.model.field.CronFieldName;
 import com.cronutils.model.field.definition.FieldDefinition;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -28,8 +31,9 @@ import java.util.Set;
 import static org.junit.Assert.*;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnitParamsRunner.class)
 public class CronDefinitionTest {
-    private boolean enforceStrictRange;
+
     private boolean matchDayOfWeekAndDayOfMonth;
     @Mock
     private FieldDefinition mockFieldDefinition1;
@@ -74,10 +78,7 @@ public class CronDefinitionTest {
 
     @Test
     public void testLastFieldOptionalTrueWhenSet() {
-        final List<FieldDefinition> fields = new ArrayList<>();
-        fields.add(mockFieldDefinition1);
-        fields.add(mockFieldDefinition2);
-        fields.add(mockFieldDefinition3optional);
+        final List<FieldDefinition> fields = provideFieldDefinitionListWithOptionalDefinition();
         final Set<FieldDefinition> fieldDefinitions = new CronDefinition(fields, new HashSet<>(), new HashSet<>(), matchDayOfWeekAndDayOfMonth)
                 .getFieldDefinitions();
         final List<FieldDefinition> sortedFieldDefinitions = new ArrayList<>(fieldDefinitions);
@@ -85,6 +86,19 @@ public class CronDefinitionTest {
         assertTrue(sortedFieldDefinitions.get(fields.size() - 1).isOptional());
     }
 
+    private List<FieldDefinition> provideFieldDefinitionListWithOptionalDefinition() {
+        List<FieldDefinition> definitions = provideFieldDefinitionListWithSingleDefinition();
+        definitions.add(mockFieldDefinition2);
+        definitions.add(mockFieldDefinition3optional);
+        return definitions;
+    }
+
+    private List<FieldDefinition> provideFieldDefinitionListWithSingleDefinition() {
+        List<FieldDefinition> definitions = new ArrayList<>();
+        definitions.add(mockFieldDefinition1);
+        return definitions;
+    }
+
     @Test(expected = IllegalArgumentException.class)
     public void testLastFieldOptionalNotAllowedOnSingleFieldDefinition() {
         final List<FieldDefinition> fields = new ArrayList<>();
@@ -94,11 +108,118 @@ public class CronDefinitionTest {
 
     @Test
     public void testGetFieldDefinitions() {
-        final List<FieldDefinition> fields = new ArrayList<>();
-        fields.add(mockFieldDefinition1);
+        final List<FieldDefinition> fields = provideFieldDefinitionListWithSingleDefinition();
         final CronDefinition cronDefinition = new CronDefinition(fields, new HashSet<>(), new HashSet<>(), matchDayOfWeekAndDayOfMonth);
         assertNotNull(cronDefinition.getFieldDefinitions());
         assertEquals(1, cronDefinition.getFieldDefinitions().size());
         assertTrue(cronDefinition.getFieldDefinitions().contains(mockFieldDefinition1));
     }
+
+    @Test
+    @Parameters(method = "parametersToTestIfEqual")
+    public void testIfDefinitionsAreEqual(
+        Set<CronConstraint> cronConstraintsOne,
+        Set<CronConstraint> cronConstraintsTwo,
+        Set<CronNicknames> cronNicknamesOne,
+        Set<CronNicknames> cronNicknamesTwo,
+        boolean shouldMatchDayOfWeekAndDayOfMonthOne,
+        boolean shouldMatchDayOfWeekAndDayOfMonthTwo,
+        boolean shouldBeEqual) {
+        // given
+        CronDefinition definitionOne = new CronDefinition(
+            provideFieldDefinitionListWithSingleDefinition(),
+            cronConstraintsOne,
+            cronNicknamesOne,
+            shouldMatchDayOfWeekAndDayOfMonthOne);
+        CronDefinition definitionTwo = new CronDefinition(
+            provideFieldDefinitionListWithSingleDefinition(),
+            cronConstraintsTwo,
+            cronNicknamesTwo,
+            shouldMatchDayOfWeekAndDayOfMonthTwo);
+
+        // when
+        boolean areEqual = definitionOne.equals(definitionTwo);
+
+        // then
+        if (shouldBeEqual) {
+            assertTrue(areEqual);
+        } else {
+            assertFalse(areEqual);
+        }
+    }
+
+    @SuppressWarnings("unused")
+    private Object[] parametersToTestIfEqual() {
+        return new Object[] {
+            new Object[] {
+                provideCronConstraintSetWithTwoConstraints(),
+                provideCronConstraintSetWithTwoConstraints(),
+                provideCronNicknameSetWithTwoNicknames(),
+                provideCronNicknameSetWithTwoNicknames(),
+                true,
+                true,
+                true
+            },
+            new Object[] {
+                provideCronConstraintSetWithOneConstraint(),
+                provideCronConstraintSetWithTwoConstraints(),
+                provideCronNicknameSetWithTwoNicknames(),
+                provideCronNicknameSetWithTwoNicknames(),
+                true,
+                true,
+                false
+            },
+            new Object[] {
+                provideCronConstraintSetWithTwoConstraints(),
+                provideCronConstraintSetWithTwoConstraints(),
+                provideCronNicknameSetWithOneNickname(),
+                provideCronNicknameSetWithTwoNicknames(),
+                true,
+                true,
+                false
+            },
+            new Object[] {
+                provideCronConstraintSetWithTwoConstraints(),
+                provideCronConstraintSetWithTwoConstraints(),
+                provideCronNicknameSetWithTwoNicknames(),
+                provideCronNicknameSetWithTwoNicknames(),
+                false,
+                true,
+                false
+            },
+            new Object[] {
+                provideCronConstraintSetWithTwoConstraints(),
+                provideCronConstraintSetWithOneConstraint(),
+                provideCronNicknameSetWithTwoNicknames(),
+                provideCronNicknameSetWithOneNickname(),
+                false,
+                true,
+                false
+            }
+        };
+    }
+
+    private Set<CronNicknames> provideCronNicknameSetWithTwoNicknames() {
+        Set<CronNicknames> constraints = provideCronNicknameSetWithOneNickname();
+        constraints.add(CronNicknames.DAILY);
+        return constraints;
+    }
+
+    private Set<CronNicknames> provideCronNicknameSetWithOneNickname() {
+        Set<CronNicknames> constraints = new HashSet<>();
+        constraints.add(CronNicknames.ANNUALLY);
+        return constraints;
+    }
+
+    private Set<CronConstraint> provideCronConstraintSetWithTwoConstraints() {
+        Set<CronConstraint> constraints = provideCronConstraintSetWithOneConstraint();
+        constraints.add(CronConstraintsFactory.ensureEitherDayOfYearOrMonth());
+        return constraints;
+    }
+
+    private Set<CronConstraint> provideCronConstraintSetWithOneConstraint() {
+        Set<CronConstraint> constraints = new HashSet<>();
+        constraints.add(CronConstraintsFactory.ensureEitherDayOfWeekOrDayOfMonth());
+        return constraints;
+    }
 }