package ca.fragag.testhelpers;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeThat;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

import org.junit.Test;

/**
 * A helper class that implements test cases that test the contract of the {@link Object#hashCode()} and
 * {@link Object#equals(Object)} methods.
 *
 * @author Francis Gagné
 */
public abstract class ObjectHashCodeEqualsContract {

    /** The main object to run tests on. */
    @Nonnull
    protected final Object mainObject;

    /** An object that is equal (by {@link Object#equals(Object)} to the main object. */
    @Nonnull
    protected final Object otherEqualObject;

    /** Another object that is equal (by {@link Object#equals(Object)} to the main object and to the other equal object. */
    @Nonnull
    protected final Object anotherEqualObject;

    /** An object that is not equal (by {@link Object#equals(Object)} to the other objects. */
    @CheckForNull
    protected final Object[] differentObjects;

    /**
     * Initializes a new ObjectHashCodeEqualsContract.
     *
     * @param mainObject
     *            the main object to run tests on
     * @param otherEqualObject
     *            an object that is equal (by {@link Object#equals(Object)} to the main object
     * @param anotherEqualObject
     *            another object that is equal (by {@link Object#equals(Object)} to the main object and to the other equal object
     * @param differentObjects
     *            objects that are not equal (by {@link Object#equals(Object)} to the other objects
     */
    protected ObjectHashCodeEqualsContract(@Nonnull Object mainObject, @Nonnull Object otherEqualObject,
            @Nonnull Object anotherEqualObject, @CheckForNull Object... differentObjects) {
        this.mainObject = mainObject;
        this.otherEqualObject = otherEqualObject;
        this.anotherEqualObject = anotherEqualObject;
        this.differentObjects = differentObjects;
    }

    /**
     * Asserts that the {@link Object#equals(Object)} method on different objects returns <code>false</code>.
     */
    @Test
    public void differentObjectsAreNotEqual() {
        if (this.differentObjects != null) {
            for (Object differentObject : this.differentObjects) {
                assertThat(this.mainObject.equals(differentObject), is(false));
            }
        }
    }

    /**
     * Asserts that the {@link Object#equals(Object)} method on equal objects returns <code>true</code>.
     */
    @Test
    public void equalObjectsAreEqual() {
        assertThat(this.mainObject.equals(this.otherEqualObject), is(true));
    }

    /**
     * Asserts that the {@link Object#equals(Object)} method on equal objects returns <code>true</code> when the subject and the
     * argument are swapped.
     */
    @Test
    public void equalsIsCommutative() {
        assertThat(this.otherEqualObject.equals(this.mainObject), is(true));
    }

    /**
     * Asserts that the {@link Object#equals(Object)} method called with the same object for the subject (<code>this</code>) and for
     * the argument returns <code>true</code>.
     */
    @Test
    public void equalsIsReflexive() {
        assertThat(this.mainObject.equals(this.mainObject), is(true));
    }

    /**
     * Asserts that the {@link Object#equals(Object)} method returns <code>true</code> when called as <code>a.equals(c)</code>,
     * assuming that <code>a.equals(b)</code> and <code>b.equals(c)</code> both return <code>true</code>.
     */
    @Test
    public void equalsIsTransitive() {
        assumeThat(this.mainObject.equals(this.otherEqualObject), is(true));
        assumeThat(this.otherEqualObject.equals(this.anotherEqualObject), is(true));
        assertThat(this.mainObject.equals(this.anotherEqualObject), is(true));
    }

    /**
     * Asserts that the {@link Object#equals(Object)} method returns <code>false</code> when the argument is <code>null</code>.
     */
    @Test
    public void equalsNullIsFalse() {
        assertThat(this.mainObject.equals(null), is(false));
    }

    /**
     * Asserts that the {@link Object#hashCode()} method on equal objects returns the same hash code.
     */
    @Test
    public void hashCodeOnEqualObjectsIsEqual() {
        assertThat(this.mainObject.hashCode() == this.otherEqualObject.hashCode(), is(true));
    }

}

@@ -930,30 +930,41 @@
  /** An 
+array of 
 object
+s
  that 
-is
+are
  not equ
@@ -1853,32 +1853,695 @@
 erentObjects) %7B%0A
+        if (mainObject == null) %7B%0A            throw new NullPointerException(%22mainObject%22);%0A        %7D%0A%0A        if (otherEqualObject == null) %7B%0A            throw new NullPointerException(%22otherEqualObject%22);%0A        %7D%0A%0A        if (anotherEqualObject == null) %7B%0A            throw new NullPointerException(%22anotherEqualObject%22);%0A        %7D%0A%0A        if (differentObjects == null) %7B%0A            throw new NullPointerException(%22differentObjects%22);%0A        %7D%0A%0A        for (int i = 0; i %3C differentObjects.length; i++) %7B%0A            if (differentObjects%5Bi%5D == null) %7B%0A                throw new NullPointerException(%22differentObjects%5B%22 + i + %22%5D%22);%0A            %7D%0A        %7D%0A%0A
         this.mai

Add null checks in ObjectHashCodeEqualsContract's constructor