/*
 * Copyright (C) 2012 The Guava Authors
 *
 * 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.common.testing;

import com.google.common.base.CharMatcher;
import com.google.common.base.Equivalence;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.base.Ticker;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedMultiset;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.LinkedHashMultiset;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.collect.Ordering;
import com.google.common.collect.RowSortedTable;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.collect.SortedMultiset;
import com.google.common.collect.Table;
import com.google.common.collect.TreeBasedTable;
import com.google.common.collect.TreeMultiset;
import com.google.common.primitives.UnsignedInteger;
import com.google.common.primitives.UnsignedLong;
import com.google.common.reflect.TypeToken;

import junit.framework.TestCase;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Currency;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;

/**
 * Tests for {@link FreshValueGenerator}.
 *
 * @author Ben Yu
 */
public class FreshValueGeneratorTest extends TestCase {

  public void testFreshInstance() {
    assertFreshInstances(
        String.class, CharSequence.class,
        Appendable.class, StringBuffer.class, StringBuilder.class,
        Pattern.class, MatchResult.class,
        Number.class, int.class, Integer.class,
        long.class, Long.class,
        short.class, Short.class,
        byte.class, Byte.class,
        boolean.class, Boolean.class,
        char.class, Character.class,
        int[].class, Object[].class,
        UnsignedInteger.class, UnsignedLong.class,
        BigInteger.class, BigDecimal.class,
        Throwable.class, Error.class, Exception.class, RuntimeException.class,
        Charset.class, Locale.class, Currency.class,
        List.class, Map.Entry.class,
        Object.class,
        Equivalence.class, Predicate.class, Function.class,
        Comparable.class, Comparator.class, Ordering.class,
        Class.class, Type.class, TypeToken.class,
        TimeUnit.class, Ticker.class,
        Joiner.class, Splitter.class, CharMatcher.class,
        InputStream.class, ByteArrayInputStream.class,
        Reader.class, Readable.class, StringReader.class,
        OutputStream.class, ByteArrayOutputStream.class,
        Writer.class, StringWriter.class, File.class,
        Buffer.class, ByteBuffer.class, CharBuffer.class,
        ShortBuffer.class, IntBuffer.class, LongBuffer.class,
        FloatBuffer.class, DoubleBuffer.class,
        String[].class, Object[].class, int[].class);
  }

  public void testStringArray() {
    FreshValueGenerator generator = new FreshValueGenerator();
    String[] a1 = generator.generate(String[].class);
    String[] a2 = generator.generate(String[].class);
    assertFalse(a1[0].equals(a2[0]));
  }

  public void testPrimitiveArray() {
    FreshValueGenerator generator = new FreshValueGenerator();
    int[] a1 = generator.generate(int[].class);
    int[] a2 = generator.generate(int[].class);
    assertTrue(a1[0] != a2[0]);
  }

  public void testImmutableList() {
    assertFreshInstance(new TypeToken<ImmutableList<String>>() {});
    assertValueAndTypeEquals(ImmutableList.of(new FreshValueGenerator().generate(String.class)),
        new FreshValueGenerator().generate(new TypeToken<ImmutableList<String>>() {}));
    assertValueAndTypeEquals(ImmutableList.of(new FreshValueGenerator().generate(int.class)),
        new FreshValueGenerator().generate(new TypeToken<ImmutableList<Integer>>() {}));
    assertValueAndTypeEquals(ImmutableList.of(new FreshValueGenerator().generate(String.class)),
        new FreshValueGenerator().generate(new TypeToken<ImmutableList<?>>() {}));
    assertValueAndTypeEquals(ImmutableList.of(),
        new FreshValueGenerator().generate(new TypeToken<ImmutableList<EmptyEnum>>() {}));
  }

  public void testImmutableSet() {
    assertFreshInstance(new TypeToken<ImmutableSet<String>>() {});
    assertValueAndTypeEquals(ImmutableSet.of(new FreshValueGenerator().generate(String.class)),
        new FreshValueGenerator().generate(new TypeToken<ImmutableSet<String>>() {}));
    assertValueAndTypeEquals(ImmutableSet.of(new FreshValueGenerator().generate(Number.class)),
        new FreshValueGenerator().generate(new TypeToken<ImmutableSet<Number>>() {}));
    assertValueAndTypeEquals(ImmutableSet.of(new FreshValueGenerator().generate(Number.class)),
        new FreshValueGenerator().generate(new TypeToken<ImmutableSet<? extends Number>>() {}));
    assertValueAndTypeEquals(ImmutableSet.of(),
        new FreshValueGenerator().generate(new TypeToken<ImmutableSet<EmptyEnum>>() {}));
  }

  public void testImmutableSortedSet() {
    assertFreshInstance(new TypeToken<ImmutableSortedSet<String>>() {});
    assertValueAndTypeEquals(
        ImmutableSortedSet.of(new FreshValueGenerator().generate(String.class)),
        new FreshValueGenerator().generate(new TypeToken<ImmutableSortedSet<String>>() {}));
    assertValueAndTypeEquals(ImmutableSortedSet.of(),
        new FreshValueGenerator().generate(new TypeToken<ImmutableSortedSet<EmptyEnum>>() {}));
  }

  public void testImmutableMultiset() {
    assertFreshInstance(new TypeToken<ImmutableSortedSet<String>>() {});
    assertValueAndTypeEquals(ImmutableMultiset.of(new FreshValueGenerator().generate(String.class)),
        new FreshValueGenerator().generate(new TypeToken<ImmutableMultiset<String>>() {}));
    assertValueAndTypeEquals(ImmutableMultiset.of(new FreshValueGenerator().generate(Number.class)),
        new FreshValueGenerator().generate(new TypeToken<ImmutableMultiset<Number>>() {}));
    assertValueAndTypeEquals(ImmutableMultiset.of(),
        new FreshValueGenerator().generate(new TypeToken<ImmutableMultiset<EmptyEnum>>() {}));
  }

  public void testImmutableCollection() {
    assertFreshInstance(new TypeToken<ImmutableCollection<String>>() {});
    assertValueAndTypeEquals(ImmutableList.of(new FreshValueGenerator().generate(String.class)),
        new FreshValueGenerator().generate(new TypeToken<ImmutableCollection<String>>() {}));
    assertValueAndTypeEquals(ImmutableList.of(),
        new FreshValueGenerator().generate(new TypeToken<ImmutableCollection<EmptyEnum>>() {}));
  }

  public void testImmutableMap() {
    assertFreshInstance(new TypeToken<ImmutableMap<String, Integer>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    assertValueAndTypeEquals(
        ImmutableMap.of(generator.generate(String.class), generator.generate(int.class)),
        new FreshValueGenerator().generate(new TypeToken<ImmutableMap<String, Integer>>() {}));
    assertValueAndTypeEquals(ImmutableMap.of(),
        new FreshValueGenerator().generate(new TypeToken<ImmutableMap<EmptyEnum, String>>() {}));
  }

  public void testImmutableSortedMap() {
    assertFreshInstance(new TypeToken<ImmutableSortedMap<String, Integer>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    assertValueAndTypeEquals(
        ImmutableSortedMap.of(generator.generate(String.class), generator.generate(int.class)),
        new FreshValueGenerator().generate(
            new TypeToken<ImmutableSortedMap<String, Integer>>() {}));
    assertValueAndTypeEquals(ImmutableSortedMap.of(),
        new FreshValueGenerator().generate(
            new TypeToken<ImmutableSortedMap<EmptyEnum, String>>() {}));
  }

  public void testImmutableMultimap() {
    assertFreshInstance(new TypeToken<ImmutableMultimap<String, Integer>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    assertValueAndTypeEquals(
        ImmutableMultimap.of(generator.generate(String.class), generator.generate(int.class)),
        new FreshValueGenerator().generate(new TypeToken<ImmutableMultimap<String, Integer>>() {}));
    assertValueAndTypeEquals(ImmutableMultimap.of(),
        new FreshValueGenerator().generate(
            new TypeToken<ImmutableMultimap<EmptyEnum, String>>() {}));
  }

  public void testImmutableListMultimap() {
    assertFreshInstance(new TypeToken<ImmutableListMultimap<String, Integer>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    assertValueAndTypeEquals(
        ImmutableListMultimap.of(generator.generate(String.class), generator.generate(int.class)),
        new FreshValueGenerator().generate(
            new TypeToken<ImmutableListMultimap<String, Integer>>() {}));
    assertValueAndTypeEquals(ImmutableListMultimap.of(),
        new FreshValueGenerator().generate(
            new TypeToken<ImmutableListMultimap<EmptyEnum, String>>() {}));
  }

  public void testImmutableSetMultimap() {
    assertFreshInstance(new TypeToken<ImmutableSetMultimap<String, Integer>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    assertValueAndTypeEquals(
        ImmutableSetMultimap.of(generator.generate(String.class), generator.generate(int.class)),
        new FreshValueGenerator().generate(
            new TypeToken<ImmutableSetMultimap<String, Integer>>() {}));
    assertValueAndTypeEquals(ImmutableSetMultimap.of(),
        new FreshValueGenerator().generate(
            new TypeToken<ImmutableSetMultimap<EmptyEnum, String>>() {}));
  }

  public void testImmutableBiMap() {
    assertFreshInstance(new TypeToken<ImmutableBiMap<String, Integer>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    assertValueAndTypeEquals(
        ImmutableBiMap.of(generator.generate(String.class), generator.generate(int.class)),
        new FreshValueGenerator().generate(
            new TypeToken<ImmutableBiMap<String, Integer>>() {}));
    assertValueAndTypeEquals(ImmutableBiMap.of(),
        new FreshValueGenerator().generate(
            new TypeToken<ImmutableBiMap<EmptyEnum, String>>() {}));
  }

  public void testImmutableTable() {
    assertFreshInstance(new TypeToken<ImmutableTable<String, Integer, ImmutableList<String>>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    assertValueAndTypeEquals(
        ImmutableTable.of(
            generator.generate(String.class), generator.generate(int.class),
            generator.generate(new TypeToken<ImmutableList<String>>() {})),
        new FreshValueGenerator().generate(
            new TypeToken<ImmutableTable<String, Integer, ImmutableList<String>>>() {}));
    assertValueAndTypeEquals(ImmutableTable.of(),
        new FreshValueGenerator().generate(
            new TypeToken<ImmutableTable<EmptyEnum, String, Integer>>() {}));
  }

  public void testList() {
    assertFreshInstance(new TypeToken<List<String>>() {});
    assertValueAndTypeEquals(Lists.newArrayList(new FreshValueGenerator().generate(String.class)),
        new FreshValueGenerator().generate(new TypeToken<List<String>>() {}));
    assertValueAndTypeEquals(Lists.newArrayList(new FreshValueGenerator().generate(int.class)),
        new FreshValueGenerator().generate(new TypeToken<List<Integer>>() {}));
    assertValueAndTypeEquals(Lists.newArrayList(new FreshValueGenerator().generate(String.class)),
        new FreshValueGenerator().generate(new TypeToken<List<?>>() {}));
    assertFreshInstance(new TypeToken<List<EmptyEnum>>() {});
  }

  public void testArrayList() {
    assertFreshInstance(new TypeToken<ArrayList<String>>() {});
    assertValueAndTypeEquals(Lists.newArrayList(new FreshValueGenerator().generate(String.class)),
        new FreshValueGenerator().generate(new TypeToken<ArrayList<String>>() {}));
    assertValueAndTypeEquals(Lists.newArrayList(new FreshValueGenerator().generate(int.class)),
        new FreshValueGenerator().generate(new TypeToken<ArrayList<Integer>>() {}));
    assertValueAndTypeEquals(Lists.newArrayList(new FreshValueGenerator().generate(String.class)),
        new FreshValueGenerator().generate(new TypeToken<ArrayList<?>>() {}));
    assertNotNull(new FreshValueGenerator().generate(new TypeToken<ArrayList<EmptyEnum>>() {}));
  }

  public void testLinkedList() {
    assertFreshInstance(new TypeToken<LinkedList<String>>() {});
    assertValueAndTypeEquals(newLinkedList(new FreshValueGenerator().generate(String.class)),
        new FreshValueGenerator().generate(new TypeToken<LinkedList<String>>() {}));
    assertNotNull(new FreshValueGenerator().generate(new TypeToken<LinkedList<EmptyEnum>>() {}));
  }

  public void testSet() {
    assertFreshInstance(new TypeToken<Set<String>>() {});
    assertValueAndTypeEquals(
        newLinkedHashSet(new FreshValueGenerator().generate(Number.class)),
        new FreshValueGenerator().generate(new TypeToken<Set<? extends Number>>() {}));
    assertFreshInstance(new TypeToken<Set<EmptyEnum>>() {});
  }

  public void testHashSet() {
    assertFreshInstance(new TypeToken<HashSet<String>>() {});
    assertValueAndTypeEquals(
        newLinkedHashSet(new FreshValueGenerator().generate(Number.class)),
        new FreshValueGenerator().generate(new TypeToken<HashSet<? extends Number>>() {}));
  }

  public void testLinkedHashSet() {
    assertFreshInstance(new TypeToken<LinkedHashSet<String>>() {});
    assertValueAndTypeEquals(
        newLinkedHashSet(new FreshValueGenerator().generate(Number.class)),
        new FreshValueGenerator().generate(new TypeToken<LinkedHashSet<? extends Number>>() {}));
    assertNotNull(new FreshValueGenerator().generate(new TypeToken<LinkedHashSet<EmptyEnum>>() {}));
  }

  public void testTreeSet() {
    assertFreshInstance(new TypeToken<TreeSet<String>>() {});
    TreeSet<String> expected = Sets.newTreeSet();
    expected.add(new FreshValueGenerator().generate(String.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<TreeSet<? extends CharSequence>>() {}));
    assertNotNull(new FreshValueGenerator().generate(new TypeToken<TreeSet<EmptyEnum>>() {}));
  }

  public void testSortedSet() {
    assertFreshInstance(new TypeToken<SortedSet<String>>() {});
    TreeSet<String> expected = Sets.newTreeSet();
    expected.add(new FreshValueGenerator().generate(String.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<SortedSet<String>>() {}));
    assertFreshInstance(new TypeToken<SortedSet<EmptyEnum>>() {});
  }

  public void testNavigableSet() {
    assertFreshInstance(new TypeToken<NavigableSet<String>>() {});
    TreeSet<String> expected = Sets.newTreeSet();
    expected.add(new FreshValueGenerator().generate(String.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<NavigableSet<String>>() {}));
    assertFreshInstance(new TypeToken<NavigableSet<EmptyEnum>>() {});
  }

  public void testMultiset() {
    assertFreshInstance(new TypeToken<Multiset<String>>() {});
    Multiset<String> expected = HashMultiset.create();
    expected.add(new FreshValueGenerator().generate(String.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<Multiset<String>>() {}));
    assertFreshInstance(new TypeToken<Multiset<EmptyEnum>>() {});
  }

  public void testSortedMultiset() {
    assertFreshInstance(new TypeToken<SortedMultiset<String>>() {});
    SortedMultiset<String> expected = TreeMultiset.create();
    expected.add(new FreshValueGenerator().generate(String.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<SortedMultiset<String>>() {}));
    assertFreshInstance(new TypeToken<Multiset<EmptyEnum>>() {});
  }

  public void testHashMultiset() {
    assertFreshInstance(new TypeToken<HashMultiset<String>>() {});
    HashMultiset<String> expected = HashMultiset.create();
    expected.add(new FreshValueGenerator().generate(String.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<HashMultiset<String>>() {}));
  }

  public void testLinkedHashMultiset() {
    assertFreshInstance(new TypeToken<LinkedHashMultiset<String>>() {});
    LinkedHashMultiset<String> expected = LinkedHashMultiset.create();
    expected.add(new FreshValueGenerator().generate(String.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<LinkedHashMultiset<String>>() {}));
  }

  public void testTreeMultiset() {
    assertFreshInstance(new TypeToken<TreeMultiset<String>>() {});
    TreeMultiset<String> expected = TreeMultiset.create();
    expected.add(new FreshValueGenerator().generate(String.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<TreeMultiset<String>>() {}));
  }

  public void testImmutableSortedMultiset() {
    assertFreshInstance(new TypeToken<ImmutableSortedMultiset<String>>() {});
    assertValueAndTypeEquals(
        ImmutableSortedMultiset.of(new FreshValueGenerator().generate(String.class)),
        new FreshValueGenerator().generate(new TypeToken<ImmutableSortedMultiset<String>>() {}));
    assertFreshInstance(new TypeToken<Multiset<EmptyEnum>>() {});
  }

  public void testCollection() {
    assertFreshInstance(new TypeToken<Collection<String>>() {});
    assertValueAndTypeEquals(Lists.newArrayList(new FreshValueGenerator().generate(String.class)),
        new FreshValueGenerator().generate(new TypeToken<Collection<String>>() {}));
    assertFreshInstance(new TypeToken<Collection<EmptyEnum>>() {});
  }

  public void testIterable() {
    assertFreshInstance(new TypeToken<Iterable<String>>() {});
    assertValueAndTypeEquals(Lists.newArrayList(new FreshValueGenerator().generate(String.class)),
        new FreshValueGenerator().generate(new TypeToken<Iterable<String>>() {}));
    assertFreshInstance(new TypeToken<Iterable<EmptyEnum>>() {});
  }

  public void testMap() {
    assertFreshInstance(new TypeToken<Map<String, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    Map<String, Integer> expected = Maps.newLinkedHashMap();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<Map<String, Integer>>() {}));
    assertFreshInstance(new TypeToken<Map<EmptyEnum, String>>() {});
  }

  public void testHashMap() {
    assertFreshInstance(new TypeToken<HashMap<String, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    HashMap<String, Integer> expected = Maps.newLinkedHashMap();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<HashMap<String, Integer>>() {}));
    assertNotNull(
        new FreshValueGenerator().generate(new TypeToken<HashMap<EmptyEnum, Integer>>() {}));
  }

  public void testLinkedHashMap() {
    assertFreshInstance(new TypeToken<LinkedHashMap<String, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    LinkedHashMap<String, Integer> expected = Maps.newLinkedHashMap();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<LinkedHashMap<String, Integer>>() {}));
    assertNotNull(
        new FreshValueGenerator().generate(new TypeToken<LinkedHashMap<EmptyEnum, String>>() {}));
  }

  public void testTreeMap() {
    assertFreshInstance(new TypeToken<TreeMap<String, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    TreeMap<String, Integer> expected = Maps.newTreeMap();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<TreeMap<String, Integer>>() {}));
    assertNotNull(new FreshValueGenerator().generate(new TypeToken<LinkedHashSet<EmptyEnum>>() {}));
  }

  public void testSortedMap() {
    assertFreshInstance(new TypeToken<SortedMap<?, String>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    TreeMap<String, Integer> expected = Maps.newTreeMap();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(
            new TypeToken<SortedMap<String, Integer>>() {}));
    assertFreshInstance(new TypeToken<SortedMap<EmptyEnum, String>>() {});
  }

  public void testNavigableMap() {
    assertFreshInstance(new TypeToken<NavigableMap<?, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    TreeMap<String, Integer> expected = Maps.newTreeMap();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(
            new TypeToken<NavigableMap<String, Integer>>() {}));
    assertFreshInstance(new TypeToken<NavigableMap<EmptyEnum, String>>() {});
  }

  public void testConcurrentMap() {
    assertFreshInstance(new TypeToken<ConcurrentMap<String, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    ConcurrentMap<String, Integer> expected = Maps.newConcurrentMap();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<ConcurrentMap<String, Integer>>() {}));
    assertFreshInstance(new TypeToken<ConcurrentMap<EmptyEnum, String>>() {});
  }

  public void testMultimap() {
    assertFreshInstance(new TypeToken<Multimap<String, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    Multimap<String, Integer> expected = ArrayListMultimap.create();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<Multimap<String, Integer>>() {}));
    assertFreshInstance(new TypeToken<Multimap<EmptyEnum, String>>() {});
  }

  public void testHashMultimap() {
    assertFreshInstance(new TypeToken<HashMultimap<String, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    HashMultimap<String, Integer> expected = HashMultimap.create();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(new TypeToken<HashMultimap<String, Integer>>() {}));
  }

  public void testLinkedHashMultimap() {
    assertFreshInstance(new TypeToken<LinkedHashMultimap<String, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    LinkedHashMultimap<String, Integer> expected = LinkedHashMultimap.create();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(
            new TypeToken<LinkedHashMultimap<String, Integer>>() {}));
  }

  public void testListMultimap() {
    assertFreshInstance(new TypeToken<ListMultimap<String, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    ListMultimap<String, Integer> expected = ArrayListMultimap.create();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(
            new TypeToken<ListMultimap<String, Integer>>() {}));
    assertFreshInstance(new TypeToken<ListMultimap<EmptyEnum, String>>() {});
  }

  public void testArrayListMultimap() {
    assertFreshInstance(new TypeToken<ArrayListMultimap<String, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    ArrayListMultimap<String, Integer> expected = ArrayListMultimap.create();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(
            new TypeToken<ArrayListMultimap<String, Integer>>() {}));
  }

  public void testSetMultimap() {
    assertFreshInstance(new TypeToken<SetMultimap<String, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    SetMultimap<String, Integer> expected = LinkedHashMultimap.create();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(
            new TypeToken<SetMultimap<String, Integer>>() {}));
    assertFreshInstance(new TypeToken<SetMultimap<EmptyEnum, String>>() {});
  }

  public void testBiMap() {
    assertFreshInstance(new TypeToken<BiMap<String, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    BiMap<String, Integer> expected = HashBiMap.create();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(
            new TypeToken<BiMap<String, Integer>>() {}));
    assertFreshInstance(new TypeToken<BiMap<EmptyEnum, String>>() {});
  }

  public void testHashBiMap() {
    assertFreshInstance(new TypeToken<HashBiMap<String, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    HashBiMap<String, Integer> expected = HashBiMap.create();
    expected.put(generator.generate(String.class), generator.generate(int.class));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(
            new TypeToken<HashBiMap<String, Integer>>() {}));
  }

  public void testTable() {
    assertFreshInstance(new TypeToken<Table<String, ?, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    Table<String, Integer, List<String>> expected = HashBasedTable.create();
    expected.put(generator.generate(String.class), generator.generate(int.class),
            generator.generate(new TypeToken<List<String>>() {}));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(
            new TypeToken<Table<String, Integer, List<String>>>() {}));
    assertFreshInstance(new TypeToken<Table<EmptyEnum, String, Integer>>() {});
  }

  public void testHashBasedTable() {
    assertFreshInstance(new TypeToken<HashBasedTable<String, ?, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    HashBasedTable<String, Integer, List<String>> expected = HashBasedTable.create();
    expected.put(generator.generate(String.class), generator.generate(int.class),
            generator.generate(new TypeToken<List<String>>() {}));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(
            new TypeToken<HashBasedTable<String, Integer, List<String>>>() {}));
  }

  public void testRowSortedTable() {
    assertFreshInstance(new TypeToken<RowSortedTable<String, ?, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    RowSortedTable<String, Integer, List<String>> expected = TreeBasedTable.create();
    expected.put(generator.generate(String.class), generator.generate(int.class),
            generator.generate(new TypeToken<List<String>>() {}));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(
            new TypeToken<RowSortedTable<String, Integer, List<String>>>() {}));
    assertFreshInstance(new TypeToken<RowSortedTable<EmptyEnum, String, Integer>>() {});
  }

  public void testTreeBasedTable() {
    assertFreshInstance(new TypeToken<TreeBasedTable<String, ?, ?>>() {});
    FreshValueGenerator generator = new FreshValueGenerator();
    TreeBasedTable<String, Integer, List<String>> expected = TreeBasedTable.create();
    expected.put(generator.generate(String.class), generator.generate(int.class),
            generator.generate(new TypeToken<List<String>>() {}));
    assertValueAndTypeEquals(expected,
        new FreshValueGenerator().generate(
            new TypeToken<TreeBasedTable<String, Integer, List<String>>>() {}));
  }

  public void testObject() {
    assertEquals(new FreshValueGenerator().generate(String.class),
        new FreshValueGenerator().generate(Object.class));
  }

  public void testEnums() {
    assertEqualInstance(EmptyEnum.class, null);
    assertEqualInstance(OneConstantEnum.class, OneConstantEnum.CONSTANT1);
    assertFreshInstance(TwoConstantEnum.class);
  }

  public void testAddSampleInstances_twoInstances() {
    FreshValueGenerator generator = new FreshValueGenerator();
    generator.addSampleInstances(String.class, ImmutableList.of("a", "b"));
    assertEquals("a", generator.generate(String.class));
    assertEquals("b", generator.generate(String.class));
    assertEquals("a", generator.generate(String.class));
  }

  public void testAddSampleInstances_oneInstance() {
    FreshValueGenerator generator = new FreshValueGenerator();
    generator.addSampleInstances(String.class, ImmutableList.of("a"));
    assertEquals("a", generator.generate(String.class));
    assertEquals("a", generator.generate(String.class));
  }

  public void testAddSampleInstances_noInstance() {
    FreshValueGenerator generator = new FreshValueGenerator();
    generator.addSampleInstances(String.class, ImmutableList.<String>of());
    assertEquals(new FreshValueGenerator().generate(String.class),
        generator.generate(String.class));
  }

  public void testFreshCurrency() {
    FreshValueGenerator generator = new FreshValueGenerator();
    // repeat a few times to make sure we don't stumble upon a bad Locale
    assertNotNull(generator.generate(Currency.class));
    assertNotNull(generator.generate(Currency.class));
    assertNotNull(generator.generate(Currency.class));
  }

  public void testNulls() throws Exception {
    new ClassSanityTester()
        .setDefault(Method.class, FreshValueGeneratorTest.class.getDeclaredMethod("testNulls"))
        .testNulls(FreshValueGenerator.class);
  }

  private static void assertFreshInstances(Class<?>... types) {
    for (Class<?> type : types) {
      assertFreshInstance(type);
    }
  }

  private static <T> void assertFreshInstance(TypeToken<T> type) {
    FreshValueGenerator generator = new FreshValueGenerator();
    T value1 = generator.generate(type);
    T value2 = generator.generate(type);
    assertNotNull("Null returned for " + type, value1);
    assertFalse("Equal instance " + value1 + " returned for " + type, value1.equals(value2));
  }

  private static <T> void assertFreshInstance(Class<T> type) {
    assertFreshInstance(TypeToken.of(type));
  }

  private static <T> void assertEqualInstance(Class<T> type, T value) {
    FreshValueGenerator generator = new FreshValueGenerator();
    assertEquals(value, generator.generate(type));
    assertEquals(value, generator.generate(type));
  }

  private enum EmptyEnum {}

  private enum OneConstantEnum {
    CONSTANT1
  }

  private enum TwoConstantEnum {
    CONSTANT1, CONSTANT2
  }

  private static void assertValueAndTypeEquals(Object expected, Object actual) {
    assertEquals(expected, actual);
    assertEquals(expected.getClass(), actual.getClass());
  }

  private static <E> LinkedHashSet<E> newLinkedHashSet(E element) {
    LinkedHashSet<E> set = Sets.newLinkedHashSet();
    set.add(element);
    return set;
  }

  private static <E> LinkedList<E> newLinkedList(E element) {
    LinkedList<E> list = Lists.newLinkedList();
    list.add(element);
    return list;
  }
}
