/*
* Copyright (c) 2016 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito.internal.stubbing.defaultanswers;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
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.Map;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import org.mockito.internal.util.MockUtil;
import org.mockito.internal.util.Primitives;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.mock.MockName;
import org.mockito.stubbing.Answer;
import static org.mockito.internal.util.ObjectMethodsGuru.isCompareToMethod;
import static org.mockito.internal.util.ObjectMethodsGuru.isToStringMethod;
/**
* Default answer of every Mockito mock.
* <ul>
* <li>Returns appropriate primitive for primitive-returning methods</li>
* <li>Returns consistent values for primitive wrapper classes (e.g. int-returning method returns 0 <strong>and</strong>
* Integer-returning method returns 0, too)</li>
* <li>Returns empty collection for collection-returning methods (works for most commonly used collection types)</li>
* <li>Returns empty array for array-returning methods</li>
* <li>Returns description of mock for toString() method</li>
* <li>Returns zero if references are equals otherwise non-zero for Comparable#compareTo(T other) method (see issue 184)</li>
* <li>Returns an {@link java.util.Optional#empty() empty Optional} for Optional. Similarly for primitive optional variants.</li>
* <li>Returns an {@link java.util.stream.Stream#empty() empty Stream} for Stream. Similarly for primitive stream variants.</li>
* <li>Returns null for everything else</li>
* </ul>
*/
public class ReturnsEmptyValues implements Answer<Object>, Serializable {
private static final long serialVersionUID = 1998191268711234347L;
public Object answer(InvocationOnMock invocation) {
if (isToStringMethod(invocation.getMethod())) {
Object mock = invocation.getMock();
MockName name = MockUtil.getMockName(mock);
if (name.isDefault()) {
return "Mock for " + MockUtil.getMockSettings(mock).getTypeToMock().getSimpleName() + ", hashCode: " + mock.hashCode();
} else {
return name.toString();
}
} else if (isCompareToMethod(invocation.getMethod())) {
//see issue 184.
//mocks by default should return 0 if references are the same, otherwise some other value because they are not the same. Hence we return 1 (anything but 0 is good).
//Only for compareTo() method by the Comparable interface
return invocation.getMock() == invocation.getArgument(0) ? 0 : 1;
}
Class<?> returnType = invocation.getMethod().getReturnType();
return returnValueFor(returnType);
}
Object returnValueFor(Class<?> type) {
if (Primitives.isPrimitiveOrWrapper(type)) {
return Primitives.defaultValue(type);
//new instances are used instead of Collections.emptyList(), etc.
//to avoid UnsupportedOperationException if code under test modifies returned collection
} else if (type.isArray()) {
Class<?> componentType = type.getComponentType();
return Array.newInstance(componentType, 0);
} else if (type == Iterable.class) {
return new ArrayList<Object>(0);
} else if (type == Collection.class) {
return new LinkedList<Object>();
} else if (type == Set.class) {
return new HashSet<Object>();
} else if (type == HashSet.class) {
return new HashSet<Object>();
} else if (type == SortedSet.class) {
return new TreeSet<Object>();
} else if (type == TreeSet.class) {
return new TreeSet<Object>();
} else if (type == LinkedHashSet.class) {
return new LinkedHashSet<Object>();
} else if (type == List.class) {
return new LinkedList<Object>();
} else if (type == LinkedList.class) {
return new LinkedList<Object>();
} else if (type == ArrayList.class) {
return new ArrayList<Object>();
} else if (type == Map.class) {
return new HashMap<Object, Object>();
} else if (type == HashMap.class) {
return new HashMap<Object, Object>();
} else if (type == SortedMap.class) {
return new TreeMap<Object, Object>();
} else if (type == TreeMap.class) {
return new TreeMap<Object, Object>();
} else if (type == LinkedHashMap.class) {
return new LinkedHashMap<Object, Object>();
} else if (type == Optional.class) {
return Optional.empty();
} else if (type == OptionalDouble.class) {
return OptionalDouble.empty();
} else if (type == OptionalInt.class) {
return OptionalInt.empty();
} else if (type == OptionalLong.class) {
return OptionalLong.empty();
} else if (type == Stream.class) {
return Stream.empty();
} else if (type == DoubleStream.class) {
return DoubleStream.empty();
} else if (type == IntStream.class) {
return IntStream.empty();
} else if (type == LongStream.class) {
return LongStream.empty();
}
//Let's not care about the rest of collections.
return null;
}
}