/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.emultest.java.util;

import com.google.gwt.testing.TestUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NavigableSet;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

/**
 * Tests <code>TreeSet</code>.
 *
 * <p>TODO(jat): this whole structure needs work. Ideally we would port a new Apache collections
 * test to GWT, but that is not an insignificant amount of work.
 *
 * @param <E> The key type for the underlying TreeSet
 */
abstract class TreeSetTest<E extends Comparable<E>> extends TestSet {

  /**
   * Verify a Set is explicitly and implicitly empty.
   *
   * @param set
   */
  private static <E> void _assertEmpty(Set<E> set) {
    assertNotNull(set);
    assertTrue(set.isEmpty());
    assertEquals(0, set.size());
  }

  /**
   * Verify that two Collections are deeply equivalent. Some of the Sets that need to be verified do
   * not implement a sensible equals method (TreeSet.values for example).
   *
   * @param expected
   * @param actual
   */
  private static <T> void _assertEquals(Collection<T> expected, Collection<T> actual) {
    // verify equivalence using collection interface
    assertEquals(expected.isEmpty(), actual.isEmpty());
    assertEquals(expected.size(), actual.size());
    assertTrue(expected.containsAll(actual));
    assertTrue(actual.containsAll(expected));
    for (T expectedValue : expected) {
      assertTrue(actual.contains(expectedValue));
    }
    for (T actualValue : actual) {
      assertTrue(expected.contains(actualValue));
    }
  }

  /**
   * Verify that two SortedMaps are deeply equivalent.
   *
   * @param expected
   * @param actual
   */
  private static <E> void _assertEquals(SortedSet<E> expected, SortedSet<E> actual) {
    _assertEquals(expected, (Set<E>) actual);

    // verify the order of the associated collections
    assertEquals(expected.toArray(), actual.toArray());
  }

  private static <E> Collection<E> asCollection(Iterator<E> it) {
    List<E> list = new ArrayList<E>();
    while (it.hasNext()) {
      list.add(it.next());
    }
    return list;
  }

  private static <E> Collection<E> reverseCollection(Collection<E> c) {
    List<E> reversedCollection = new ArrayList<E>(c);
    Collections.reverse(reversedCollection);
    return reversedCollection;
  }

  /** comparator used when creating the SortedSet. */
  private Comparator<E> comparator = null;

  private final boolean isAddSupported = true;
  private final boolean isClearSupported = true;
  private final boolean isNullKeySupported = true;
  private final boolean isPutAllSupported = true;
  private final boolean isRemoveSupported = true;

  public TreeSetTest() {
    super("TreeSetTest");
  }

  @Override
  public String getModuleName() {
    return "com.google.gwt.emultest.EmulSuite";
  }

  /**
   * Test method for 'java.util.Set.add(Object)'.
   *
   * @see java.util.Set#put(Object)
   */
  public void testAdd() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isAddSupported) {
      Set<E> set = createSet();
      assertTrue(set.add(getKeys()[0]));
      assertFalse(set.isEmpty());
      assertEquals(1, set.size());
    }
  }

  /**
   * Test method for 'java.util.Set.add(Object)'.
   *
   * @see java.util.Set#add(Object)
   */
  public void testAdd_entries3() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isAddSupported) {
      // populate the set
      Set<E> set = createSet();
      set.add(getKeys()[0]);
      set.add(getKeys()[1]);
      set.add(getKeys()[2]);

      // test contents
      assertFalse(set.isEmpty());
      assertEquals(3, set.size());
      Collection<E> keys = set;
      // test contains all keys
      assertTrue(keys.contains(getKeys()[0]));
      assertTrue(keys.contains(getKeys()[1]));
      assertTrue(keys.contains(getKeys()[2]));
    }
  }

  /**
   * Test method for 'java.util.Set.add(Object)'.
   *
   * @see java.util.Set#add(Object)
   */
  public void testAdd_replace() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isAddSupported) {
      Set<E> set = createSet();
      assertTrue(set.add(getKeys()[0]));
      assertFalse(set.isEmpty());
      assertEquals(1, set.size());

      assertFalse(set.add(getKeys()[0]));
      assertEquals(1, set.size());
    }
  }

  /**
   * Test method for 'java.util.Set.add(Object)'.
   *
   * @see java.util.Set#add(Object)
   */
  @SuppressWarnings("unchecked")
  public void testAdd_throwsClassCastException_key() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isAddSupported) {
      Set<E> set = createSet();
      set.add(getKeys()[0]);
      try {
        Set untypedSet = set;
        untypedSet.add(getConflictingKey());
        assertTrue("CCE expected in Development Mode", !TestUtils.isJvm());
      } catch (ClassCastException e) {
        // expected outcome
      }
    }
  }

  /**
   * Test method for 'java.util.Set.add(Object)'.
   *
   * @see java.util.Set#add(Object)
   */
  @SuppressWarnings("unchecked")
  public void testAdd_throwsClassCastException_value() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isAddSupported) {
      Set<E> set = createSet();
      set.add(getKeys()[0]);

      Set untypedSet = set;
      untypedSet.add(getKeys()[1]);
      // You might think this should throw an exception here but, no. Makes
      // sense since the class cast is attributed to comparability of the
      // keys... generics really have nothing to do with it .
    }
  }

  /**
   * Test method for 'java.util.Set.add(Object)'.
   *
   * @see java.util.Set#add(Object)
   */
  public void testAdd_throwsUnsupportedOperationException() {
    if (!isAddSupported) {
      Set<E> set = createSet();
      try {
        set.add(getKeys()[0]);
        fail("expected exception");
      } catch (UnsupportedOperationException e) {
        // expected outcome
      }
    }
  }

  /**
   * Test method for 'java.util.Set.addAll(Map)'.
   *
   * @see java.util.Set#addAll(Map)
   */
  public void testAddAll() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isPutAllSupported) {
      Set<E> sourceSet = createSet();
      sourceSet.add(getKeys()[0]);
      sourceSet.add(getKeys()[1]);
      sourceSet.add(getKeys()[2]);

      Set<E> destSet = createSet();
      destSet.addAll(sourceSet);
      // Make sure that the data is copied correctly
      _assertEquals(sourceSet, destSet);
    }
  }

  /**
   * Test method for 'java.util.Set.addAll(Map)'.
   *
   * @see java.util.Set#addAll(Map)
   */
  public void testAddAll_addEntries() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isPutAllSupported) {
      Set<E> sourceMap = createSet();
      sourceMap.add(getKeys()[0]);

      Set<E> destSet = createSet();
      destSet.addAll(sourceMap);
      // Verify that entries get added.
      sourceMap.add(getKeys()[1]);
      destSet.addAll(sourceMap);
      _assertEquals(sourceMap, destSet);
    }
  }

  /**
   * Test method for 'java.util.Set.addAll(Map)'.
   *
   * @see java.util.Set#addAll(Map)
   */
  public void testAddAll_emptyMap() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isPutAllSupported) {
      Set<E> sourceSet = createSet();
      sourceSet.add(getKeys()[0]);

      Set<E> destSet = createSet();
      destSet.addAll(sourceSet);
      // Verify that putting an empty set does not clear.
      destSet.addAll(createSet());
      _assertEquals(sourceSet, destSet);
    }
  }

  /**
   * Test method for 'java.util.Set.addAll(Map)'.
   *
   * @see java.util.Set#addAll(Map)
   */
  public void testAddAll_overwrite() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isPutAllSupported) {
      Set<E> sourceSet = createSet();
      sourceSet.add(getKeys()[0]);

      Set<E> destSet = createSet();
      destSet.addAll(sourceSet);
      // Verify that entries get replaced.
      sourceSet.add(getKeys()[0]);
      destSet.addAll(sourceSet);
      _assertEquals(sourceSet, destSet);
    }
  }

  /**
   * Test method for 'java.util.Set.addAll(Map)'.
   *
   * @see java.util.Set#addAll(Map)
   */
  @SuppressWarnings("ModifyingCollectionWithItself")
  public void testAddAll_self() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isPutAllSupported) {
      Set<E> sourceSet = createSet();
      sourceSet.add(getKeys()[0]);
      sourceSet.addAll(sourceSet);
      // verify putAll with self succeeds and has no effect.
      assertEquals(1, sourceSet.size());
      assertEquals(getKeys()[0], sourceSet.iterator().next());
    }
  }

  /**
   * Test method for 'java.util.Set.addAll(Map)'.
   *
   * @see java.util.Set#addAll(Map)
   */
  @SuppressWarnings("unchecked")
  public void testAddAll_throwsClassCastException() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isPutAllSupported) {
      Set sourceSet = new HashSet();
      sourceSet.add(getConflictingKey());

      Set<E> destSet = createSet();
      destSet.add(getKeys()[0]);
      try {
        // This throws in dev mode because we're putting a second entry in
        // the set and TreeSet calls the compare method to order them.
        destSet.addAll(sourceSet);
        assertTrue("CCE expected in Development Mode", !TestUtils.isJvm());
      } catch (ClassCastException e) {
        // expected outcome
      }
    }
  }

  /**
   * Test method for 'java.util.Set.addAll(Map)'.
   *
   * @see java.util.Set#addAll(Map)
   */
  public void testAddAll_throwsNullPointerException() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isPutAllSupported) {
      Set<E> set = createSet();
      try {
        set.addAll(null);
        fail("expected exception");
      } catch (NullPointerException e) {
        // expected outcome
      }
    }
  }

  /**
   * Test method for 'java.util.Set.addAll(Map)'.
   *
   * @see java.util.Set#addAll(Map)
   */
  public void testAddAll_throwsUnsupportedOperationException() {
    Set<E> set = createSet();
    if (!isPutAllSupported) {
      try {
        set.addAll(createSet());
        fail("expected exception");
      } catch (UnsupportedOperationException e) {
        // expected outcome
      }
    }
  }

  public void testCeiling() {
    NavigableSet<E> set = createNavigableSet();

    set.add(getKeys()[0]);
    assertEquals(getKeys()[0], set.ceiling(getKeys()[0]));
    assertEquals(getKeys()[0], set.ceiling(getLessThanMinimumKey()));
    assertEquals(set.toArray()[0], set.ceiling(getLessThanMinimumKey()));

    set.add(getKeys()[1]);
    assertEquals(getKeys()[0], set.ceiling(getLessThanMinimumKey()));
    assertEquals(getKeys()[0], set.ceiling(getKeys()[0]));
    assertEquals(getKeys()[1], set.ceiling(getKeys()[1]));
    assertEquals(null, set.ceiling(getGreaterThanMaximumKey()));
  }

  /**
   * Test method for 'java.util.Set.clear()'.
   *
   * @see java.util.Set#clear()
   */
  public void testClear() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isClearSupported) {
      // Execute this test only if supported.
      Set<E> set = createSet();
      set.add(getKeys()[0]);
      assertFalse(set.isEmpty());
      set.clear();
      _assertEmpty(set);
    }
  }

  /**
   * Test method for 'java.util.Set.clear()'.
   *
   * @see java.util.Set#clear()
   */
  public void testClear_throwsUnsupportedOperationException() {
    Set<E> set = createSet();
    if (!isClearSupported) {
      try {
        set.clear();
        fail("expected exception");
      } catch (UnsupportedOperationException e) {
        // expected outcome
      }
    }
  }

  /**
   * Test method for 'java.util.SortedSet.comparator()'.
   *
   * @see java.util.SortedSet#comparator()
   */
  public void testComparator() {
    SortedSet<E> sortedSet = createNavigableSet();
    if (isNaturalOrder()) {
      assertEquals(null, sortedSet.comparator());
    } else {
      assertEquals(getComparator(), sortedSet.comparator());
    }
  }

  /**
   * Test method for default constructor.
   *
   * @see java.util.TreeSet#TreeSet()
   */
  public void testConstructor() {
    TreeSet<E> treeSet = new TreeSet<E>();
    _assertEmpty(treeSet);
  }

  /**
   * Test method for 'java.util.TreeSet.TreeSet(Comparator)'.
   *
   * @see java.util.TreeSet#TreeSet(Comparator)
   */
  public void testConstructor_comparator() {
    TreeSet<E> treeSet = new TreeSet<E>(getComparator());
    _assertEmpty(treeSet);
    if (isNaturalOrder()) {
      assertNull(treeSet.comparator());
    } else {
      assertSame(getComparator(), treeSet.comparator());
    }
  }

  /**
   * Test method for 'java.util.TreeSet.TreeSet(Set)'.
   *
   * @see java.util.TreeSet#TreeSet(Set)
   */
  public void testConstructor_Set() {
    // The source set should be just a Map. Not a sorted set.
    Set<E> sourceMap = new HashSet<E>();

    // populate the source set
    sourceMap.add(getKeys()[0]);
    sourceMap.add(getKeys()[1]);
    sourceMap.add(getKeys()[2]);

    TreeSet<E> copyConstructed = new TreeSet<E>(sourceMap);
    _assertEquals(sourceMap, copyConstructed);
  }

  /**
   * Test method for 'java.util.TreeSet.TreeSet(Set)'.
   *
   * @see java.util.TreeSet#TreeSet(Set)
   */
  @SuppressWarnings("unchecked")
  public void testConstructor_Set_rawType() {
    Set sourceSet = new HashSet();
    sourceSet.add(getConflictingKey());
    // Raw types can be used to defeat the type system, and this will work
    // so long as the key is Comparable and there's only one entry (so compare()
    // won't be called.)
    new TreeSet<E>(sourceSet);
  }

  /**
   * Test method for 'java.util.TreeSet.TreeSet(Set)'.
   *
   * @see java.util.TreeSet#TreeSet(Set)
   */
  public void testConstructor_Set_throwsNullPointerException() {
    try {
      new TreeSet<E>((Set<E>) null);
      fail("expected exception");
    } catch (NullPointerException e) {
      // expected outcome
    }
  }

  /**
   * Test method for 'java.util.TreeSet.TreeSet(SortedSet).
   *
   * @see java.util.TreeSet#TreeSet(SortedSet)
   */
  public void testConstructor_SortedMap_throwsNullPointerException() {
    try {
      new TreeSet<E>((SortedSet<E>) null);
      fail("expected exception");
    } catch (NullPointerException e) {
      // expected outcome
    }
  }

  /**
   * Test method for 'java.util.TreeSet.TreeSet(SortedSet)'.
   *
   * @see java.util.TreeSet#TreeSet(SortedSet)
   */
  public void testConstructor_SortedSet() {
    SortedSet<E> sourceMap = new TreeSet<E>();
    _assertEmpty(sourceMap);

    // populate the source set
    sourceMap.add(getKeys()[0]);
    sourceMap.add(getKeys()[1]);
    sourceMap.add(getKeys()[2]);

    TreeSet<E> copyConstructed = new TreeSet<E>(sourceMap);
    _assertEquals(sourceMap, copyConstructed);
  }

  /**
   * Test method for 'java.util.Set.contains(Object)'. *
   *
   * @see java.util.Set#contains(Object)
   */
  public void testContains() {
    Set<E> set = createSet();
    assertFalse(set.contains(getKeys()[0]));
    assertTrue(set.add(getKeys()[0]));
    assertEquals(1, set.size());
    assertTrue(set.contains(getKeys()[0]));
    assertFalse(set.contains(getKeys()[1]));
  }

  /**
   * Test method for 'java.util.Set.contains(Object)'.
   *
   * @see java.util.Set#contains(Object)
   */
  @SuppressWarnings("ReturnValueIgnored")
  public void testContains_throwsClassCastException() {
    Set<E> set = createSet();
    set.add(getKeys()[0]);
    try {
      set.contains(getConflictingKey());
      assertTrue("CCE expected in Development Mode", !TestUtils.isJvm());
    } catch (ClassCastException e) {
      // expected outcome
    }
  }

  /**
   * Test method for 'java.util.Set.contains(Object)'.
   *
   * @see java.util.Set#contains(Object)
   */
  public void testContains_throwsNullPointerException() {
    Set<E> set = createSet();
    if (isNaturalOrder() && !isNullKeySupported) {
      try {
        set.contains(null);
        fail("expected exception");
      } catch (NullPointerException e) {
        // expected outcome
      }
    }
  }

  public void testDescendingIterator() {
    NavigableSet<E> set = createNavigableSet();
    set.add(getKeys()[0]);

    Iterator<E> descendingIterator = set.descendingIterator();
    _assertEquals(set, reverseCollection(asCollection(descendingIterator)));

    set.add(getKeys()[1]);
    set.add(getKeys()[2]);
    descendingIterator = set.descendingIterator();
    _assertEquals(set, reverseCollection(asCollection(descendingIterator)));

    descendingIterator = set.descendingIterator();
    while (descendingIterator.hasNext()) {
      descendingIterator.next();
      descendingIterator.remove();
    }
    assertEquals(0, set.size());
  }

  public void testDescendingSet() {
    NavigableSet<E> set = createNavigableSet();
    set.add(getKeys()[0]);

    NavigableSet<E> descendingSet = set.descendingSet();
    _assertEquals(descendingSet, descendingSet);
    _assertEquals(set.descendingSet(), descendingSet);

    set.add(getKeys()[1]);
    set.add(getKeys()[2]);
    _assertEquals(reverseCollection(set), descendingSet);
    _assertEquals(set, descendingSet.descendingSet());

    set.remove(getKeys()[1]);
    _assertEquals(reverseCollection(set), descendingSet);

    descendingSet.add(getKeys()[0]);
    _assertEquals(reverseCollection(set), descendingSet);

    descendingSet.remove(getKeys()[1]);
    _assertEquals(reverseCollection(set), descendingSet);

    descendingSet.clear();
    assertEquals(0, descendingSet.size());
    _assertEquals(set, descendingSet);
  }

  /**
   * Test method for 'java.util.Object.equals(Object)'.
   *
   * @see java.util.Set#equals(Object)
   */
  public void testEquals() {
    Set<E> set0 = createSet();
    Set<E> set1 = createSet();
    assertTrue(set0.equals(set1));
    set0.add(getKeys()[0]);
    set1.add(getKeys()[0]);
    assertTrue(set0.equals(set0));
    assertTrue(set0.equals(set1));
    set0.add(getKeys()[1]);
    assertFalse(set0.equals(set1));
  }

  public void testSetClone() {
    TreeSet<E> set = createTreeSet();
    TreeSet<E> clone = (TreeSet<E>) set.clone();
    assertEquals(set, clone);

    set.add(getKeys()[0]);
    assertFalse(set.equals(clone));

    clone = (TreeSet<E>) set.clone();
    assertEquals(set, clone);
  }

  /**
   * Test method for 'java.util.SortedSet.first()'.
   *
   * @see java.util.SortedSet#first()
   */
  public void testFirst() {
    SortedSet<E> sortedSet = createNavigableSet();
    // test with a single entry set
    sortedSet.add(getKeys()[0]);
    assertEquals(getKeys()[0], sortedSet.first());
    // is it consistent with other methods
    assertEquals(sortedSet.toArray()[0], sortedSet.first());
    assertEquals(getKeys()[0], sortedSet.last());
    assertEquals(sortedSet.last(), sortedSet.first());

    // test with two entry set
    sortedSet.add(getKeys()[1]);
    assertEquals(getKeys()[0], sortedSet.first());
    assertFalse(getKeys()[1].equals(sortedSet.first()));
    // is it consistent with other methods
    assertEquals(sortedSet.toArray()[0], sortedSet.first());
    assertFalse(getKeys()[0].equals(sortedSet.last()));
    assertFalse(sortedSet.last().equals(sortedSet.first()));
  }

  /**
   * Test method for 'java.util.SortedSet.first()'.
   *
   * @see java.util.SortedSet#first()
   */
  public void testFirstKey_throwsNoSuchElementException() {
    SortedSet<E> sortedSet = createNavigableSet();
    // test with no entries
    try {
      sortedSet.first();
      fail("expected exception");
    } catch (NoSuchElementException e) {
      // expected outcome
    }
  }

  public void testFloor() {
    NavigableSet<E> set = createNavigableSet();

    set.add(getKeys()[0]);
    assertEquals(null, set.floor(getLessThanMinimumKey()));
    assertEquals(getKeys()[0], set.floor(getKeys()[0]));
    assertEquals(getKeys()[0], set.floor(getKeys()[1]));
    assertEquals(getKeys()[0], set.floor(getGreaterThanMaximumKey()));
    assertEquals(set.toArray()[0], set.floor(getKeys()[1]));

    set.add(getKeys()[1]);
    assertEquals(null, set.floor(getLessThanMinimumKey()));
    assertEquals(getKeys()[0], set.floor(getKeys()[0]));
    assertEquals(getKeys()[1], set.floor(getKeys()[1]));
    assertEquals(getKeys()[1], set.floor(getGreaterThanMaximumKey()));
  }

  /**
   * Test method for 'java.lang.Object.hashCode()'.
   *
   * @see java.util.Set#hashCode()
   */
  public void testHashCode() {
    Set<E> set0 = createSet();
    Set<E> set1 = createSet();

    int hashCode0 = set0.hashCode();
    int hashCode1 = set1.hashCode();
    assertTrue("empty maps have different hash codes", hashCode0 == hashCode1);

    // Check that hashCode changes
    set0.add(getKeys()[0]);
    hashCode0 = set0.hashCode();
    assertTrue("hash code didn't change", hashCode0 != hashCode1);

    Set<String> set2 = new TreeSet<String>();
    set2.add("");
    Set<Integer> set3 = new TreeSet<Integer>();
    set3.add(0);
    int hashCode2 = set2.hashCode();
    int hashCode3 = set3.hashCode();
    assertEquals("empty string/0 hash codes not the same", hashCode2, hashCode3);
  }

  /**
   * Test method for 'java.util.SortedSet.headSet(Object, Object)'.
   *
   * @see java.util.SortedSet#headSet(Object)
   */
  @SuppressWarnings("unchecked")
  public void testHeadMap_throwsClassCastException() {
    SortedSet sortedSet = createNavigableSet();
    sortedSet.add(getKeys()[0]);
    if (isNaturalOrder()) {
      // TODO Why does this succeed with natural ordering when subSet doesn't?
      sortedSet.headSet(getConflictingKey());
    } else {
      try {
        sortedSet.headSet(getConflictingKey());
        assertTrue("CCE expected in Development Mode", !TestUtils.isJvm());
      } catch (ClassCastException e) {
        // expected outcome
      }
    }
  }

  /**
   * Test method for 'java.util.SortedSet.headSet(Object)' and
   * 'java.util.NavigableSet.headSet(Object, boolean)'.
   *
   * @see java.util.SortedSet#headSet(Object)
   * @see java.util.NavigableSet#headSet(Object, boolean)
   */
  public void testHeadSet() {
    // test with no entries
    NavigableSet<E> set = createNavigableSet();
    assertNotNull(set.headSet(getKeys()[0]));
    assertNotNull(set.headSet(getKeys()[0], false));
    assertNotNull(set.headSet(getKeys()[0], true));
  }

  /**
   * Test method for 'java.util.SortedSet.headSet(Object)' and
   * 'java.util.NavigableSet.headSet(Object, boolean)'.
   *
   * @see java.util.SortedSet#headSet(Object)
   * @see java.util.NavigableSet#headSet(Object, boolean)
   */
  public void testHeadSet_entries0_size() {
    // test with no entries
    NavigableSet<E> set = createNavigableSet();
    assertEquals(0, set.headSet(getKeys()[0]).size());
    assertEquals(0, set.headSet(getKeys()[0], false).size());
    assertEquals(0, set.headSet(getKeys()[0], true).size());
  }

  /**
   * Test method for 'java.util.SortedSet.headSet(Object)' and
   * 'java.util.NavigableSet.headSet(Object, boolean)'.
   *
   * @see java.util.SortedSet#headSet(Object)
   * @see java.util.NavigableSet#headSet(Object, boolean)
   */
  public void testHeadSet_entries1() {
    NavigableSet<E> set = createNavigableSet();
    // test with a single entry set
    set.add(getKeys()[0]);

    assertEquals(0, set.headSet(getKeys()[0]).size());
    assertEquals(0, set.headSet(getKeys()[0], false).size());
    assertEquals(1, set.headSet(getKeys()[0], true).size());
  }

  /**
   * Test method for 'java.util.SortedSet.headSet(Object)' and
   * 'java.util.NavigableSet.headSet(Object, boolean)'.
   *
   * @see java.util.SortedSet#headSet(Object)
   * @see java.util.NavigableSet#headSet(Object, boolean)
   */
  public void testHeadSet_entries2() {
    NavigableSet<E> set = createNavigableSet();
    // test with two entry set
    set.add(getKeys()[0]);
    set.add(getKeys()[1]);

    assertEquals(0, set.headSet(getKeys()[0]).size());
    assertEquals(1, set.headSet(getKeys()[1]).size());
    assertEquals(getKeys()[0], set.tailSet(getKeys()[0]).toArray()[0]);

    assertEquals(0, set.headSet(getKeys()[0], false).size());
    assertEquals(1, set.headSet(getKeys()[1], false).size());
    assertEquals(getKeys()[0], set.headSet(getKeys()[0], true).toArray()[0]);

    assertEquals(1, set.headSet(getKeys()[0], true).size());
    assertEquals(2, set.headSet(getKeys()[1], true).size());
    assertEquals(getKeys()[0], set.headSet(getKeys()[1], false).toArray()[0]);
    assertEquals(getKeys()[1], set.headSet(getKeys()[1], true).toArray()[1]);
    assertEquals(0, set.headSet(getKeys()[0], false).size());
    assertEquals(getKeys()[1], set.headSet(getKeys()[1], true).toArray()[1]);
  }

  public void testHigher() {
    NavigableSet<E> set = createNavigableSet();

    set.add(getKeys()[0]);
    assertEquals(null, set.higher(getKeys()[0]));
    assertEquals(getKeys()[0], set.higher(getLessThanMinimumKey()));
    assertEquals(set.toArray()[0], set.higher(getLessThanMinimumKey()));

    set.add(getKeys()[1]);
    assertEquals(getKeys()[0], set.higher(getLessThanMinimumKey()));
    assertEquals(getKeys()[1], set.higher(getKeys()[0]));
    assertEquals(null, set.higher(getKeys()[1]));
    assertEquals(null, set.higher(getGreaterThanMaximumKey()));
  }

  /**
   * Test method for 'java.util.Set.isEmpty()'. *
   *
   * @see java.util.Set#isEmpty()
   */
  public void testIsEmpty() {
    Set<E> sourceSet = createSet();
    Set<E> destSet = createSet();

    destSet.addAll(sourceSet);
    assertTrue(destSet.isEmpty());

    destSet.add(getKeys()[0]);
    assertFalse(destSet.isEmpty());

    destSet.remove(getKeys()[0]);
    assertTrue(destSet.isEmpty());
    assertEquals(0, destSet.size());
  }

  /**
   * Test method for 'java.util.SortedSet.last()'.
   *
   * @see java.util.SortedSet#last()
   */
  public void testLastKey() {
    SortedSet<E> sortedSet = createNavigableSet();

    // test with a single entry set
    sortedSet.add(getKeys()[0]);
    assertEquals(getKeys()[0], sortedSet.last());
    // is it consistent with other methods
    assertEquals(sortedSet.toArray()[0], sortedSet.last());
    assertEquals(getKeys()[0], sortedSet.first());
    assertEquals(sortedSet.first(), sortedSet.last());

    // test with two entry set
    sortedSet.add(getKeys()[1]);
    assertEquals(getKeys()[1], sortedSet.last());
    assertFalse(getKeys()[0].equals(sortedSet.last()));
    // is it consistent with other methods
    assertEquals(sortedSet.toArray()[1], sortedSet.last());
    assertEquals(getKeys()[0], sortedSet.first());
    assertFalse(sortedSet.first().equals(sortedSet.last()));
  }

  /**
   * Test method for 'java.util.SortedSet.last()'.
   *
   * @see java.util.SortedSet#last()
   */
  public void testLastKey_throwsNoSuchElementException() {
    SortedSet<E> sortedSet = createNavigableSet();
    // test with no entries
    try {
      sortedSet.last();
      fail("expected exception");
    } catch (NoSuchElementException e) {
      // expected outcome
    }
  }

  public void testLower() {
    NavigableSet<E> set = createNavigableSet();

    set.add(getKeys()[0]);
    assertEquals(null, set.lower(getLessThanMinimumKey()));
    assertEquals(null, set.lower(getKeys()[0]));
    assertEquals(getKeys()[0], set.lower(getKeys()[1]));
    assertEquals(getKeys()[0], set.lower(getGreaterThanMaximumKey()));
    assertEquals(set.toArray()[0], set.lower(getKeys()[1]));

    set.add(getKeys()[1]);
    assertEquals(null, set.lower(getLessThanMinimumKey()));
    assertEquals(null, set.lower(getKeys()[0]));
    assertEquals(getKeys()[0], set.lower(getKeys()[1]));
    assertEquals(getKeys()[1], set.lower(getGreaterThanMaximumKey()));
  }

  public void testPollFirst() {
    NavigableSet<E> set = createNavigableSet();

    assertNull(set.pollFirst());
    assertEquals(0, set.size());

    set.add(getKeys()[0]);
    assertEquals(getKeys()[0], set.pollFirst());
    assertEquals(0, set.size());

    set.add(getKeys()[0]);
    set.add(getKeys()[1]);
    assertEquals(getKeys()[0], set.pollFirst());
    assertEquals(1, set.size());
    assertEquals(getKeys()[1], set.pollFirst());
    assertEquals(0, set.size());
    assertNull(set.pollFirst());
  }

  public void testPollLast() {
    NavigableSet<E> set = createNavigableSet();

    assertNull(set.pollLast());
    assertEquals(0, set.size());

    set.add(getKeys()[0]);
    assertEquals(getKeys()[0], set.pollLast());
    assertEquals(0, set.size());

    set.add(getKeys()[0]);
    set.add(getKeys()[1]);
    assertEquals(getKeys()[1], set.pollLast());
    assertEquals(1, set.size());
    assertEquals(getKeys()[0], set.pollLast());
    assertEquals(0, set.size());
    assertNull(set.pollLast());
  }

  /**
   * Test method for 'java.util.Set.remove(Object)'.
   *
   * @see java.util.Set#remove(Object)
   */
  public void testRemove() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isRemoveSupported) {
      Set<E> set = createSet();

      assertFalse(set.remove(getKeys()[0]));
      assertTrue(set.add(getKeys()[0]));
      assertTrue(set.remove(getKeys()[0]));
      assertFalse(set.remove(getKeys()[0]));
    }
  }

  /**
   * Test method for 'java.util.Set.remove(Object)'.
   *
   * @see java.util.Set#remove(Object)
   */
  public void testRemove_throwsClassCastException() {
    // The _throwsUnsupportedOperationException version of this test will
    // verify that the method is not supported.
    if (isRemoveSupported) {
      Set<E> set = createSet();
      set.add(getKeys()[0]);
      try {
        set.remove(getConflictingKey());
        assertTrue("CCE expected in Development Mode", !TestUtils.isJvm());
      } catch (ClassCastException e) {
        // expected outcome
      }
    }
  }

  /**
   * Test method for 'java.util.Set.remove(Object)'.
   *
   * @see java.util.Set#remove(Object)
   */
  public void testRemove_throwsUnsupportedOperationException() {
    Set<E> set = createSet();
    if (!isRemoveSupported) {
      try {
        set.remove(getKeys()[0]);
        fail("expected exception");
      } catch (UnsupportedOperationException e) {
        // expected outcome
      }
    }
  }

  /**
   * Test method for 'java.util.Set.size()'.
   *
   * @see java.util.Set#size()
   */
  public void testSize() {
    Set<E> set = createSet();

    // Test size behavior on add
    set.add(getKeys()[0]);
    assertEquals(1, set.size());
    set.add(getKeys()[1]);
    assertEquals(2, set.size());
    set.add(getKeys()[2]);
    assertEquals(3, set.size());

    // Test size behavior on remove
    set.remove(getKeys()[0]);
    assertEquals(2, set.size());
    set.remove(getKeys()[1]);
    assertEquals(1, set.size());
    set.remove(getKeys()[2]);
    assertEquals(0, set.size());

    // Test size behavior on putAll
    set.add(getKeys()[0]);
    set.add(getKeys()[1]);
    set.add(getKeys()[2]);
    assertEquals(3, set.size());

    // Test size behavior on clear
    set.clear();
    _assertEmpty(set);
  }

  /**
   * Test method for 'java.util.SortedSet.subSet(Object, Object)'.
   *
   * @see java.util.SortedSet#subSet(Object, Object)
   */
  @SuppressWarnings("unchecked")
  public void testSubMap_throwsClassCastException() {
    SortedSet sortedSet = createNavigableSet();
    sortedSet.add(getKeys()[0]);
    try {
      sortedSet.subSet(getConflictingKey(), getKeys()[0]);
      assertTrue("CCE expected in Development Mode", !TestUtils.isJvm());
    } catch (IllegalArgumentException e) {
      // since we can't ensure CCEs in Production Mode, we may get IAE
      assertTrue("IllegalArgumentException in Development Mode", !TestUtils.isJvm());
    } catch (ClassCastException e) {
      // expected outcome
    }
    try {
      sortedSet.subSet(getKeys()[0], getConflictingKey());
      assertTrue("CCE expected in Development Mode", !TestUtils.isJvm());
    } catch (IllegalArgumentException e) {
      // since we can't ensure CCEs in Production Mode, we may get IAE
      assertTrue("IllegalArgumentException in Development Mode", !TestUtils.isJvm());
    } catch (ClassCastException e) {
      // expected outcome
    }
  }

  /**
   * Test method for 'java.util.SortedSet.subSet(Object, Object)'.
   *
   * @see java.util.SortedSet#subSet(Object, Object)
   */
  public void testSubMap_throwsIllegalArgumentException() {
    SortedSet<E> sortedSet = createNavigableSet();
    try {
      sortedSet.subSet(getGreaterThanMaximumKey(), getLessThanMinimumKey());
      fail("expected exception");
    } catch (IllegalArgumentException e) {
      // from key is greater than the to key
      // expected outcome
    }
  }

  /**
   * Test method for 'java.util.SortedSet.subSet(Object, Object)' and
   * 'java.util.NavigableSet.subSet(Object, boolean, Object, boolean)'.
   *
   * @see java.util.SortedSet#subSet(Object, Object)
   * @see java.util.NavigableSet#subSet(Object, boolean, Object, boolean)
   */
  public void testSubSet() {
    NavigableSet<E> sortedSet = createNavigableSet();
    // test with no entries
    assertEquals(0, sortedSet.subSet(getKeys()[0], getKeys()[0]).size());
    assertEquals(0, sortedSet.subSet(getKeys()[0], false, getKeys()[0], false).size());
    assertEquals(0, sortedSet.subSet(getKeys()[0], true, getKeys()[0], false).size());
    assertEquals(0, sortedSet.subSet(getKeys()[0], false, getKeys()[0], true).size());
    assertEquals(0, sortedSet.subSet(getKeys()[0], true, getKeys()[0], true).size());

    // test with a single entry set
    sortedSet.add(getKeys()[0]);
    assertEquals(0, sortedSet.subSet(getKeys()[0], getKeys()[0]).size());
    // bounded by a "wide" range
    assertEquals(1, sortedSet.subSet(getLessThanMinimumKey(), getGreaterThanMaximumKey()).size());
    assertEquals(
        1,
        sortedSet.subSet(getLessThanMinimumKey(), false, getGreaterThanMaximumKey(), false).size());
    assertEquals(
        1,
        sortedSet.subSet(getLessThanMinimumKey(), true, getGreaterThanMaximumKey(), false).size());
    assertEquals(
        1,
        sortedSet.subSet(getLessThanMinimumKey(), false, getGreaterThanMaximumKey(), true).size());
    assertEquals(
        1,
        sortedSet.subSet(getLessThanMinimumKey(), true, getGreaterThanMaximumKey(), true).size());

    // test with two entry set
    sortedSet.add(getKeys()[1]);

    assertEquals(1, sortedSet.subSet(getKeys()[0], getKeys()[1]).size());
    assertEquals(getKeys()[0], sortedSet.subSet(getKeys()[0], getKeys()[1]).toArray()[0]);

    assertEquals(0, sortedSet.subSet(getKeys()[0], false, getKeys()[1], false).size());

    assertEquals(1, sortedSet.subSet(getKeys()[0], false, getKeys()[1], true).size());
    assertEquals(
        getKeys()[1], sortedSet.subSet(getKeys()[0], false, getKeys()[1], true).toArray()[0]);

    assertEquals(1, sortedSet.subSet(getKeys()[0], true, getKeys()[1], false).size());
    assertEquals(
        getKeys()[0], sortedSet.subSet(getKeys()[0], true, getKeys()[1], false).toArray()[0]);

    assertEquals(2, sortedSet.subSet(getKeys()[0], true, getKeys()[1], true).size());
    assertEquals(
        getKeys()[0], sortedSet.subSet(getKeys()[0], true, getKeys()[1], true).toArray()[0]);
    assertEquals(
        getKeys()[1], sortedSet.subSet(getKeys()[0], true, getKeys()[1], true).toArray()[1]);

    // bounded by a "wide" range
    SortedSet<E> subSet = sortedSet.subSet(getLessThanMinimumKey(), getGreaterThanMaximumKey());

    assertEquals(2, subSet.size());

    assertEquals(
        2,
        sortedSet.subSet(getLessThanMinimumKey(), false, getGreaterThanMaximumKey(), false).size());
    assertEquals(
        1, sortedSet.subSet(getKeys()[0], false, getGreaterThanMaximumKey(), false).size());
    assertEquals(0, sortedSet.subSet(getKeys()[0], false, getKeys()[1], false).size());
    assertEquals(2, sortedSet.subSet(getKeys()[0], true, getGreaterThanMaximumKey(), false).size());
    assertEquals(1, sortedSet.subSet(getKeys()[0], true, getKeys()[1], false).size());
    assertEquals(2, sortedSet.subSet(getKeys()[0], true, getGreaterThanMaximumKey(), true).size());
    assertEquals(2, sortedSet.subSet(getKeys()[0], true, getKeys()[1], true).size());
  }

  /**
   * Test method for 'java.util.SortedSet.tailSet(Object)' and '@see
   * java.util.NavigableSet.tailSet(Object, boolean)'.
   *
   * @see java.util.SortedSet#tailSet(Object)
   * @see java.util.NavigableSet#tailSet(Object, boolean)
   */
  public void testTailSet_entries0() {
    // test with no entries
    NavigableSet<E> navigableSet = createNavigableSet();

    assertNotNull(navigableSet.tailSet(getKeys()[0]));
    assertNotNull(navigableSet.tailSet(getKeys()[0], false));
    assertNotNull(navigableSet.tailSet(getKeys()[0], true));
  }

  /**
   * Test method for 'java.util.SortedSet.tailSet(Object)' and '@see
   * java.util.NavigableSet.tailSet(Object, boolean)'.
   *
   * @see java.util.SortedSet#tailSet(Object)
   * @see java.util.NavigableSet#tailSet(Object, boolean)
   */
  public void testTailSet_entries0_size() {
    // test with no entries
    NavigableSet<E> navigableSet = createNavigableSet();

    Set<E> tailSet = navigableSet.tailSet(getKeys()[0]);
    assertNotNull(tailSet);
    assertEquals(0, tailSet.size());

    Set<E> exclusiveTailSet = navigableSet.tailSet(getKeys()[0], false);
    assertNotNull(exclusiveTailSet);
    assertEquals(0, exclusiveTailSet.size());

    Set<E> inclusiveTailSet = navigableSet.tailSet(getKeys()[0], true);
    assertNotNull(inclusiveTailSet);
    assertEquals(0, inclusiveTailSet.size());
  }

  /**
   * Test method for 'java.util.SortedSet.tailSet(Object)' and '@see
   * java.util.NavigableSet.tailSet(Object, boolean)'.
   *
   * @see java.util.SortedSet#tailSet(Object)
   * @see java.util.NavigableSet#tailSet(Object, boolean)
   */
  public void testTailSet_entries1_size_keyValue() {
    NavigableSet<E> sortedSet = createNavigableSet();
    // test with a single entry set
    sortedSet.add(getKeys()[0]);

    Set<E> tailSet = sortedSet.tailSet(getKeys()[0]);
    assertEquals(1, tailSet.size());
    assertEquals(getKeys()[0], tailSet.toArray()[0]);

    Set<E> exclusiveTailSet = sortedSet.tailSet(getKeys()[0], false);
    assertEquals(0, exclusiveTailSet.size());
    assertEquals(0, exclusiveTailSet.size());

    Set<E> inclusiveTailSet = sortedSet.tailSet(getKeys()[0], true);
    assertEquals(1, inclusiveTailSet.size());
    assertEquals(getKeys()[0], inclusiveTailSet.toArray()[0]);
  }

  /**
   * Test method for 'java.util.SortedSet.tailSet(Object)' and '@see
   * java.util.NavigableSet.tailSet(Object, boolean)'.
   *
   * @see java.util.SortedSet#tailSet(Object)
   * @see java.util.NavigableSet#tailSet(Object, boolean)
   */
  public void testTailSet_entries2_size_keyValue() {
    NavigableSet<E> sortedSet = createNavigableSet();
    // test with two entry set
    sortedSet.add(getKeys()[0]);

    Set<E> tailSet = sortedSet.tailSet(getKeys()[0]);
    assertEquals(1, tailSet.size());
    Set<E> exclusiveTailMap = sortedSet.tailSet(getKeys()[0], false);
    assertEquals(0, exclusiveTailMap.size());
    Set<E> inclusiveTailMap = sortedSet.tailSet(getKeys()[0], true);
    assertEquals(1, inclusiveTailMap.size());

    sortedSet.add(getKeys()[1]);

    tailSet = sortedSet.tailSet(getKeys()[1]);
    assertEquals(1, tailSet.size());

    exclusiveTailMap = sortedSet.tailSet(getKeys()[1], false);
    assertEquals(0, exclusiveTailMap.size());

    inclusiveTailMap = sortedSet.tailSet(getKeys()[1], true);
    assertEquals(1, inclusiveTailMap.size());

    tailSet = sortedSet.tailSet(getKeys()[0]);
    assertEquals(2, tailSet.size());
    assertEquals(getKeys()[0], tailSet.toArray()[0]);
    assertEquals(getKeys()[1], tailSet.toArray()[1]);

    exclusiveTailMap = sortedSet.tailSet(getKeys()[0], false);
    assertEquals(1, exclusiveTailMap.size());
    assertEquals(getKeys()[1], exclusiveTailMap.toArray()[0]);

    inclusiveTailMap = sortedSet.tailSet(getKeys()[0], true);
    assertEquals(2, inclusiveTailMap.size());
    assertEquals(getKeys()[0], inclusiveTailMap.toArray()[0]);
    assertEquals(getKeys()[1], inclusiveTailMap.toArray()[1]);
  }

  /**
   * Test method for 'java.util.SortedSet.tailSet(Object, Object)'.
   *
   * @see java.util.SortedSet#tailSet(Object)
   */
  @SuppressWarnings("unchecked")
  public void testTailSet_throwsClassCastException() {
    SortedSet sortedSet = createNavigableSet();
    sortedSet.add(getKeys()[0]);
    if (isNaturalOrder()) {
      // TODO Why does this succeed with natural ordering when subSet doesn't?
      sortedSet.tailSet(getConflictingKey());
    } else {
      try {
        sortedSet.tailSet(getConflictingKey());
        assertTrue("CCE expected in Development Mode", !TestUtils.isJvm());
      } catch (ClassCastException e) {
        // expected outcome
      }
    }
  }

  /**
   * Test method for 'java.util.SortedSet.tailSet(Object, Object)'.
   *
   * @see java.util.SortedSet#tailSet(Object)
   */
  public void testTailSet_throwsIllegalArgumentException() {
    // TODO I don't know of any case where this could happen.
  }

  protected Comparator<E> getComparator() {
    return comparator;
  }

  protected abstract Object getConflictingKey();

  protected abstract Object getConflictingValue();

  @Override
  protected Object[] getFullElements() {
    return getKeys();
  }

  @Override
  protected Object[] getOtherElements() {
    return getKeys2();
  }

  @Override
  protected void gwtSetUp() throws Exception {
    setComparator(null);
  }

  protected boolean isNaturalOrder() {
    return comparator == null;
  }

  @Override
  protected Set makeEmptySet() {
    return createTreeSet();
  }

  protected void setComparator(Comparator<E> comparator) {
    this.comparator = comparator;
  }

  NavigableSet<E> createNavigableSet() {
    return createTreeSet();
  }

  Set<E> createSet() {
    return createNavigableSet();
  }

  TreeSet<E> createTreeSet() {
    if (isNaturalOrder()) {
      return new TreeSet<E>();
    } else {
      return new TreeSet<E>(getComparator());
    }
  }

  abstract E getGreaterThanMaximumKey();

  abstract E[] getKeys();

  abstract E[] getKeys2();

  abstract E getLessThanMinimumKey();
}
