@Test public void equalsAndHashCode() {

  for (Supplier<MutableSpan> constructor : PERMUTATIONS) {
    // same instance are equivalent
    MutableSpan span = constructor.get();
    assertEqualWithSameHashCode(span, span);
    assertThat(span).usingRecursiveComparison().isEqualTo(span); // double check
    // same fields are equivalent
    MutableSpan sameFields = constructor.get();
    assertEqualWithSameHashCode(sameFields, span);
    assertThat(sameFields).usingRecursiveComparison().isEqualTo(span); // double check our impl
    // copy constructor are equivalent
    MutableSpan copy = new MutableSpan(span);
    assertEqualWithSameHashCode(copy, span);
    assertThat(copy).usingRecursiveComparison().isEqualTo(span); // double check our impl
    // This seems redundant, and mostly is, but the order of equals matters
    List<Supplier<MutableSpan>> exceptMe = new ArrayList<>(PERMUTATIONS);
    exceptMe.remove(constructor);
    for (Supplier<MutableSpan> otherConstructor : exceptMe) {
      MutableSpan other = otherConstructor.get();
      assertThat(span).isNotSameAs(other); // sanity
      assertNeitherEqualNorShareHashCode(span, other);
      assertThat(Objects.deepEquals(span, other)).isFalse(); // double check our impl
    }
  }
}