All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.testing.ArbitraryInstances Maven / Gradle / Ivy

Go to download

Guava testlib is a set of java classes used for more convenient unit testing - particularly to assist the tests for Guava itself.

There is a newer version: 33.3.1-jre
Show newest version
/*
 * 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 static com.google.common.base.Preconditions.checkArgument;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;

import com.google.common.annotations.GwtIncompatible;
import com.google.common.annotations.J2ktIncompatible;
import com.google.common.base.CharMatcher;
import com.google.common.base.Defaults;
import com.google.common.base.Equivalence;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Splitter;
import com.google.common.base.Stopwatch;
import com.google.common.base.Ticker;
import com.google.common.collect.BiMap;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableClassToInstanceMap;
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.Iterators;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Multiset;
import com.google.common.collect.Ordering;
import com.google.common.collect.PeekingIterator;
import com.google.common.collect.Range;
import com.google.common.collect.RowSortedTable;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.collect.SortedMapDifference;
import com.google.common.collect.SortedMultiset;
import com.google.common.collect.SortedSetMultimap;
import com.google.common.collect.Table;
import com.google.common.collect.Tables;
import com.google.common.collect.TreeBasedTable;
import com.google.common.collect.TreeMultimap;
import com.google.common.io.ByteSink;
import com.google.common.io.ByteSource;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharSink;
import com.google.common.io.CharSource;
import com.google.common.primitives.Primitives;
import com.google.common.primitives.UnsignedInteger;
import com.google.common.primitives.UnsignedLong;
import com.google.errorprone.annotations.Keep;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
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.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Currency;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.UUID;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * Supplies an arbitrary "default" instance for a wide range of types, often useful in testing
 * utilities.
 *
 * 

Covers arrays, enums and common types defined in {@code java.lang}, {@code java.lang.reflect}, * {@code java.io}, {@code java.nio}, {@code java.math}, {@code java.util}, {@code * java.util.concurrent}, {@code java.util.regex}, {@code com.google.common.base}, {@code * com.google.common.collect} and {@code com.google.common.primitives}. In addition, if the type * exposes at least one public static final constant of the same type, one of the constants will be * used; or if the class exposes a public parameter-less constructor then it will be "new"d and * returned. * *

All default instances returned by {@link #get} are generics-safe. Clients won't get type * errors for using {@code get(Comparator.class)} as a {@code Comparator}, for example. * Immutable empty instances are returned for collection types; {@code ""} for string; {@code 0} for * number types; reasonable default instance for other stateless types. For mutable types, a fresh * instance is created each time {@code get()} is called. * * @author Kevin Bourrillion * @author Ben Yu * @since 12.0 */ @GwtIncompatible @J2ktIncompatible @ElementTypesAreNonnullByDefault public final class ArbitraryInstances { private static final Ordering BY_FIELD_NAME = new Ordering() { @Override public int compare(Field left, Field right) { return left.getName().compareTo(right.getName()); } }; /** * Returns a new {@code MatchResult} that corresponds to a successful match. Apache Harmony (used * in Android) requires a successful match in order to generate a {@code MatchResult}: * https://cs.android.com/android/platform/superproject/+/android-2.3.7_r1:libcore/luni/src/main/java/java/util/regex/Matcher.java;l=550;drc=5850271b4ab93ebc27c1d49169a348c6be3c7f04 */ private static MatchResult createMatchResult() { Matcher matcher = Pattern.compile(".").matcher("X"); matcher.find(); return matcher.toMatchResult(); } private static final ClassToInstanceMap DEFAULTS = ImmutableClassToInstanceMap.builder() // primitives .put(Object.class, "") .put(Number.class, 0) .put(UnsignedInteger.class, UnsignedInteger.ZERO) .put(UnsignedLong.class, UnsignedLong.ZERO) .put(BigInteger.class, BigInteger.ZERO) .put(BigDecimal.class, BigDecimal.ZERO) .put(CharSequence.class, "") .put(String.class, "") .put(Pattern.class, Pattern.compile("")) .put(MatchResult.class, createMatchResult()) .put(TimeUnit.class, TimeUnit.SECONDS) .put(Charset.class, UTF_8) .put(Currency.class, Currency.getInstance(Locale.US)) .put(Locale.class, Locale.US) .put(Optional.class, Optional.empty()) .put(OptionalInt.class, OptionalInt.empty()) .put(OptionalLong.class, OptionalLong.empty()) .put(OptionalDouble.class, OptionalDouble.empty()) .put(UUID.class, UUID.randomUUID()) // common.base .put(CharMatcher.class, CharMatcher.none()) .put(Joiner.class, Joiner.on(',')) .put(Splitter.class, Splitter.on(',')) .put(com.google.common.base.Optional.class, com.google.common.base.Optional.absent()) .put(Predicate.class, Predicates.alwaysTrue()) .put(Equivalence.class, Equivalence.equals()) .put(Ticker.class, Ticker.systemTicker()) .put(Stopwatch.class, Stopwatch.createUnstarted()) // io types .put(InputStream.class, new ByteArrayInputStream(new byte[0])) .put(ByteArrayInputStream.class, new ByteArrayInputStream(new byte[0])) .put(Readable.class, new StringReader("")) .put(Reader.class, new StringReader("")) .put(StringReader.class, new StringReader("")) .put(Buffer.class, ByteBuffer.allocate(0)) .put(CharBuffer.class, CharBuffer.allocate(0)) .put(ByteBuffer.class, ByteBuffer.allocate(0)) .put(ShortBuffer.class, ShortBuffer.allocate(0)) .put(IntBuffer.class, IntBuffer.allocate(0)) .put(LongBuffer.class, LongBuffer.allocate(0)) .put(FloatBuffer.class, FloatBuffer.allocate(0)) .put(DoubleBuffer.class, DoubleBuffer.allocate(0)) .put(File.class, new File("")) .put(ByteSource.class, ByteSource.empty()) .put(CharSource.class, CharSource.empty()) .put(ByteSink.class, NullByteSink.INSTANCE) .put(CharSink.class, NullByteSink.INSTANCE.asCharSink(UTF_8)) // All collections are immutable empty. So safe for any type parameter. .put(Iterator.class, ImmutableSet.of().iterator()) .put(PeekingIterator.class, Iterators.peekingIterator(ImmutableSet.of().iterator())) .put(ListIterator.class, ImmutableList.of().listIterator()) .put(Iterable.class, ImmutableSet.of()) .put(Collection.class, ImmutableList.of()) .put(ImmutableCollection.class, ImmutableList.of()) .put(List.class, ImmutableList.of()) .put(ImmutableList.class, ImmutableList.of()) .put(Set.class, ImmutableSet.of()) .put(ImmutableSet.class, ImmutableSet.of()) .put(SortedSet.class, ImmutableSortedSet.of()) .put(ImmutableSortedSet.class, ImmutableSortedSet.of()) .put(NavigableSet.class, Sets.unmodifiableNavigableSet(Sets.newTreeSet())) .put(Map.class, ImmutableMap.of()) .put(ImmutableMap.class, ImmutableMap.of()) .put(SortedMap.class, ImmutableSortedMap.of()) .put(ImmutableSortedMap.class, ImmutableSortedMap.of()) .put(NavigableMap.class, Maps.unmodifiableNavigableMap(Maps.newTreeMap())) .put(Multimap.class, ImmutableMultimap.of()) .put(ImmutableMultimap.class, ImmutableMultimap.of()) .put(ListMultimap.class, ImmutableListMultimap.of()) .put(ImmutableListMultimap.class, ImmutableListMultimap.of()) .put(SetMultimap.class, ImmutableSetMultimap.of()) .put(ImmutableSetMultimap.class, ImmutableSetMultimap.of()) .put( SortedSetMultimap.class, Multimaps.unmodifiableSortedSetMultimap(TreeMultimap.create())) .put(Multiset.class, ImmutableMultiset.of()) .put(ImmutableMultiset.class, ImmutableMultiset.of()) .put(SortedMultiset.class, ImmutableSortedMultiset.of()) .put(ImmutableSortedMultiset.class, ImmutableSortedMultiset.of()) .put(BiMap.class, ImmutableBiMap.of()) .put(ImmutableBiMap.class, ImmutableBiMap.of()) .put(Table.class, ImmutableTable.of()) .put(ImmutableTable.class, ImmutableTable.of()) .put(RowSortedTable.class, Tables.unmodifiableRowSortedTable(TreeBasedTable.create())) .put(ClassToInstanceMap.class, ImmutableClassToInstanceMap.builder().build()) .put(ImmutableClassToInstanceMap.class, ImmutableClassToInstanceMap.builder().build()) .put(Comparable.class, ByToString.INSTANCE) .put(Comparator.class, AlwaysEqual.INSTANCE) .put(Ordering.class, AlwaysEqual.INSTANCE) .put(Range.class, Range.all()) .put(MapDifference.class, Maps.difference(ImmutableMap.of(), ImmutableMap.of())) .put( SortedMapDifference.class, Maps.difference(ImmutableSortedMap.of(), ImmutableSortedMap.of())) // reflect .put(AnnotatedElement.class, Object.class) .put(GenericDeclaration.class, Object.class) .put(Type.class, Object.class) .build(); /** * type → implementation. Inherently mutable interfaces and abstract classes are mapped to their * default implementations and are "new"d upon get(). */ private static final ConcurrentMap, Class> implementations = Maps.newConcurrentMap(); private static void setImplementation(Class type, Class implementation) { checkArgument(type != implementation, "Don't register %s to itself!", type); checkArgument( !DEFAULTS.containsKey(type), "A default value was already registered for %s", type); checkArgument( implementations.put(type, implementation) == null, "Implementation for %s was already registered", type); } static { setImplementation(Appendable.class, StringBuilder.class); setImplementation(BlockingQueue.class, LinkedBlockingDeque.class); setImplementation(BlockingDeque.class, LinkedBlockingDeque.class); setImplementation(ConcurrentMap.class, ConcurrentHashMap.class); setImplementation(ConcurrentNavigableMap.class, ConcurrentSkipListMap.class); setImplementation(CountDownLatch.class, Dummies.DummyCountDownLatch.class); setImplementation(Deque.class, ArrayDeque.class); setImplementation(OutputStream.class, ByteArrayOutputStream.class); setImplementation(PrintStream.class, Dummies.InMemoryPrintStream.class); setImplementation(PrintWriter.class, Dummies.InMemoryPrintWriter.class); setImplementation(Queue.class, ArrayDeque.class); setImplementation(Random.class, Dummies.DeterministicRandom.class); setImplementation( ScheduledThreadPoolExecutor.class, Dummies.DummyScheduledThreadPoolExecutor.class); setImplementation(ThreadPoolExecutor.class, Dummies.DummyScheduledThreadPoolExecutor.class); setImplementation(Writer.class, StringWriter.class); setImplementation(Runnable.class, Dummies.DummyRunnable.class); setImplementation(ThreadFactory.class, Dummies.DummyThreadFactory.class); setImplementation(Executor.class, Dummies.DummyExecutor.class); } @SuppressWarnings("unchecked") // it's a subtype map private static @Nullable Class getImplementation(Class type) { return (Class) implementations.get(type); } private static final Logger logger = Logger.getLogger(ArbitraryInstances.class.getName()); /** * Returns an arbitrary instance for {@code type}, or {@code null} if no arbitrary instance can be * determined. */ public static @Nullable T get(Class type) { T defaultValue = DEFAULTS.getInstance(type); if (defaultValue != null) { return defaultValue; } Class implementation = getImplementation(type); if (implementation != null) { return get(implementation); } if (type == Stream.class) { return type.cast(Stream.empty()); } if (type.isEnum()) { T[] enumConstants = type.getEnumConstants(); return (enumConstants == null || enumConstants.length == 0) ? null : enumConstants[0]; } if (type.isArray()) { return createEmptyArray(type); } T jvmDefault = Defaults.defaultValue(Primitives.unwrap(type)); if (jvmDefault != null) { return jvmDefault; } if (Modifier.isAbstract(type.getModifiers()) || !Modifier.isPublic(type.getModifiers())) { return arbitraryConstantInstanceOrNull(type); } final Constructor constructor; try { constructor = type.getConstructor(); } catch (NoSuchMethodException e) { return arbitraryConstantInstanceOrNull(type); } constructor.setAccessible(true); // accessibility check is too slow try { return constructor.newInstance(); } catch (InstantiationException | IllegalAccessException impossible) { throw new AssertionError(impossible); } catch (InvocationTargetException e) { logger.log(Level.WARNING, "Exception while invoking default constructor.", e.getCause()); return arbitraryConstantInstanceOrNull(type); } } private static @Nullable T arbitraryConstantInstanceOrNull(Class type) { Field[] fields = type.getDeclaredFields(); Arrays.sort(fields, BY_FIELD_NAME); for (Field field : fields) { if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) { if (field.getGenericType() == field.getType() && type.isAssignableFrom(field.getType())) { field.setAccessible(true); try { T constant = type.cast(field.get(null)); if (constant != null) { return constant; } } catch (IllegalAccessException impossible) { throw new AssertionError(impossible); } } } } return null; } private static T createEmptyArray(Class arrayType) { // getComponentType() is non-null because we call createEmptyArray only with an array type. return arrayType.cast(Array.newInstance(requireNonNull(arrayType.getComponentType()), 0)); } // Internal implementations of some classes, with public default constructor that get() needs. private static final class Dummies { public static final class InMemoryPrintStream extends PrintStream { public InMemoryPrintStream() { super(new ByteArrayOutputStream()); } } public static final class InMemoryPrintWriter extends PrintWriter { public InMemoryPrintWriter() { super(new StringWriter()); } } public static final class DeterministicRandom extends Random { @Keep public DeterministicRandom() { super(0); } } public static final class DummyScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor { public DummyScheduledThreadPoolExecutor() { super(1); } } public static final class DummyCountDownLatch extends CountDownLatch { public DummyCountDownLatch() { super(0); } } public static final class DummyRunnable implements Runnable, Serializable { @Override public void run() {} } public static final class DummyThreadFactory implements ThreadFactory, Serializable { @Override public Thread newThread(Runnable r) { return new Thread(r); } } public static final class DummyExecutor implements Executor, Serializable { @Override public void execute(Runnable command) {} } } private static final class NullByteSink extends ByteSink implements Serializable { private static final NullByteSink INSTANCE = new NullByteSink(); @Override public OutputStream openStream() { return ByteStreams.nullOutputStream(); } } // Compare by toString() to satisfy 2 properties: // 1. compareTo(null) should throw NullPointerException // 2. the order is deterministic and easy to understand, for debugging purpose. @SuppressWarnings("ComparableType") private static final class ByToString implements Comparable, Serializable { private static final ByToString INSTANCE = new ByToString(); @Override public int compareTo(Object o) { return toString().compareTo(o.toString()); } @Override public String toString() { return "BY_TO_STRING"; } private Object readResolve() { return INSTANCE; } } // Always equal is a valid total ordering. And it works for any Object. private static final class AlwaysEqual extends Ordering<@Nullable Object> implements Serializable { private static final AlwaysEqual INSTANCE = new AlwaysEqual(); @Override @SuppressWarnings("UnusedVariable") // intentionally weird Comparator public int compare(@Nullable Object o1, @Nullable Object o2) { return 0; } @Override public String toString() { return "ALWAYS_EQUAL"; } private Object readResolve() { return INSTANCE; } } private ArbitraryInstances() {} }