/*
 * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
 * (the "License"). You may not use this work except in compliance with the License, which is
 * available at www.apache.org/licenses/LICENSE-2.0
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied, as more fully set forth in the License.
 *
 * See the NOTICE file distributed with this work for information regarding copyright ownership.
 */

package alluxio;

import alluxio.underfs.UnderFileSystem.SpaceType;

import junit.framework.AssertionFailedError;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.util.Objects;

/**
 * Unit tests for {@link CommonTestUtils}.
 */
@RunWith(PowerMockRunner.class)
@PrepareForTest(CommonTestUtilsTest.HardToInstantiateClass.class)
public final class CommonTestUtilsTest {
  public static final class HardToInstantiateClass {
    private HardToInstantiateClass(Object o) {}
  }

  private static class Basic {
    private String mField;

    @Override
    public boolean equals(Object o) {
      if (this == o) {
        return true;
      }
      if (!(o instanceof Basic)) {
        return false;
      }
      Basic that = (Basic) o;
      return Objects.equals(mField, that.mField);
    }

    @Override
    public int hashCode() {
      return Objects.hash(mField);
    }
  }

  private static class BadHashCode {
    private String mField;

    @Override
    public boolean equals(Object o) {
      if (!(o instanceof Basic)) {
        return false;
      }
      Basic that = (Basic) o;
      return Objects.equals(mField, that.mField);
    }

    @Override
    public int hashCode() {
      return Objects.hash(10); // Should be mField.
    }
  }

  private static class ManyFields {
    private String mField1;
    private boolean mField2;
    // Use HardToInstantiateClass as an example of a final class without a no-arg constructor.
    private HardToInstantiateClass mField3;
    // Example of an enum.
    private SpaceType mField4;

    @Override
    public boolean equals(Object o) {
      if (!(o instanceof ManyFields)) {
        return false;
      }
      ManyFields that = (ManyFields) o;
      return Objects.equals(mField1, that.mField1)
          && Objects.equals(mField2, that.mField2)
          && Objects.equals(mField3, that.mField3)
          && Objects.equals(mField4, that.mField4);
    }

    @Override
    public int hashCode() {
      return Objects.hash(mField1, mField2, mField3, mField4);
    }
  }

  private static class MissingField {
    private String mField1;
    private boolean mField2;
    private CommonTestUtils mField3;

    @Override
    public boolean equals(Object o) {
      if (!(o instanceof MissingField)) {
        return false;
      }
      MissingField that = (MissingField) o;
      // Missing test for mField3.
      return Objects.equals(mField1, that.mField1)
          && Objects.equals(mField2, that.mField2);
    }

    @Override
    public int hashCode() {
      // Missing mField3.
      return Objects.hash(mField1, mField2);
    }
  }

  private static class EqualsMissingInstanceCheck {
    private String mField1;
    private boolean mField2;
    private CommonTestUtils mField3;

    @Override
    public boolean equals(Object o) {
      // Missing test for o instanceof EqualsMissingField.
      MissingField that = (MissingField) o;

      return Objects.equals(mField1, that.mField1)
          && Objects.equals(mField2, that.mField2)
          && Objects.equals(mField3, that.mField3);
    }

    @Override
    public int hashCode() {
      return Objects.hash(mField1, mField2, mField3);
    }
  }

  @Test
  public void testEqualsBasic() throws Exception {
    CommonTestUtils.testEquals(Basic.class);
  }

  @Test
  public void testEqualsManyFields() {
    CommonTestUtils.testEquals(ManyFields.class);
  }

  @Test
  public void testEqualsBadHashCodeCheck() {
    testFail(BadHashCode.class);
  }

  @Test
  public void testEqualsMissingFieldCheck() {
    testFail(MissingField.class);
  }

  @Test
  public void testEqualsMissingInstanceCheck() {
    testFail(EqualsMissingInstanceCheck.class);
  }

  private void testFail(Class<?> clazz) {
    try {
      CommonTestUtils.testEquals(clazz);
      Assert.fail("testEquals didn't throw an error for class " + clazz.getName());
    } catch (AssertionFailedError | NullPointerException e) {
      // expected
    }
  }
}

@@ -1141,24 +1141,92 @@
  o) %7B%7D%0A  %7D%0A%0A
+  /**%0A   * A simple class that has a correct hashcode method.%0A   */%0A
   private st
@@ -1244,16 +1244,16 @@
 Basic %7B%0A
-
     priv
@@ -1608,32 +1608,96 @@
 ld);%0A    %7D%0A  %7D%0A%0A
+  /**%0A   * A simple class that has a bad hashcode method.%0A   */%0A
   private static
@@ -2046,32 +2046,98 @@
 eld.%0A    %7D%0A  %7D%0A%0A
+  /**%0A   * A simple class that has a correct equals method.%0A   */%0A
   private static
@@ -2893,32 +2893,115 @@
 d4);%0A    %7D%0A  %7D%0A%0A
+  /**%0A   * A simple class that has a bad equals method due to missing field.%0A   */%0A
   private static
@@ -3550,24 +3550,116 @@
 %0A    %7D%0A  %7D%0A%0A
+  /**%0A   * A simple class that has a bad equals method due to missing instance check.%0A   */%0A
   private st

add javadoc for CommonTestUtilsTest