package nl.jqno.equalsverifier.integration.extended_contract;

import java.util.Map;
import java.util.Objects;
import nl.jqno.equalsverifier.EqualsVerifier;
import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException;
import org.junit.jupiter.api.Test;

class MapEntrySubclassTest {

    @Test
    void fails_whenMapEntryHashCodeContractIsNotHonored() {
        ExpectedException
            .when(() -> EqualsVerifier.forClass(HashCodeContractNotHonored.class).verify())
            .assertFailure()
            .assertMessageContains("hashCode: value does not follow Map.Entry specification");
    }

    @Test
    void succeeds_whenMapEntryHashCodeContractIsHonored() {
        EqualsVerifier.forClass(HashCodeContractHonored.class).verify();
    }

    static final class HashCodeContractNotHonored<K, V> implements Map.Entry<K, V> {

        private final K key;
        private final V value;

        HashCodeContractNotHonored(K key, V value) {
            this.key = key;
            this.value = value;
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Map.Entry)) {
                return false;
            }
            Map.Entry<?, ?> other = (Map.Entry<?, ?>) obj;
            return Objects.equals(key, other.getKey()) && Objects.equals(value, other.getValue());
        }

        @Override
        public int hashCode() {
            return Objects.hash(key, value);
        }

        @Override
        public K getKey() {
            return key;
        }

        @Override
        public V getValue() {
            return value;
        }

        @Override
        public V setValue(V value) {
            throw new UnsupportedOperationException();
        }
    }

    static final class HashCodeContractHonored<K, V> implements Map.Entry<K, V> {

        private final K key;
        private final V value;

        HashCodeContractHonored(K key, V value) {
            this.key = key;
            this.value = value;
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Map.Entry)) {
                return false;
            }
            Map.Entry<?, ?> other = (Map.Entry<?, ?>) obj;
            return Objects.equals(key, other.getKey()) && Objects.equals(value, other.getValue());
        }

        @Override
        public int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        @Override
        public K getKey() {
            return key;
        }

        @Override
        public V getValue() {
            return value;
        }

        @Override
        public V setValue(V value) {
            throw new UnsupportedOperationException();
        }
    }
}

@@ -1612,32 +1612,71 @@
 lue;%0A        %7D%0A%0A
+        // CHECKSTYLE OFF: HiddenField%0A
         @Overrid
@@ -1775,24 +1775,62 @@
 ;%0A        %7D%0A
+        // CHECKSTYLE ON: HiddenField%0A
     %7D%0A%0A    s
@@ -2698,32 +2698,71 @@
 lue;%0A        %7D%0A%0A
+        // CHECKSTYLE OFF: HiddenField%0A
         @Overrid
@@ -2861,20 +2861,58 @@
 ;%0A        %7D%0A
+        // CHECKSTYLE ON: HiddenField%0A
     %7D%0A%7D%0A

Make checkstyle happy