/* * Copyright (c) 2007 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.internal.creation; import org.mockito.MockSettings; import org.mockito.internal.creation.settings.CreationSettings; import org.mockito.internal.debugging.VerboseMockInvocationLogger; import org.mockito.internal.util.Checks; import org.mockito.internal.util.MockCreationValidator; import org.mockito.internal.util.MockNameImpl; import org.mockito.listeners.InvocationListener; import org.mockito.listeners.VerificationStartedListener; import org.mockito.mock.MockCreationSettings; import org.mockito.mock.MockName; import org.mockito.mock.SerializableMode; import org.mockito.stubbing.Answer; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import static org.mockito.internal.exceptions.Reporter.defaultAnswerDoesNotAcceptNullParameter; import static org.mockito.internal.exceptions.Reporter.extraInterfacesAcceptsOnlyInterfaces; import static org.mockito.internal.exceptions.Reporter.extraInterfacesDoesNotAcceptNullParameters; import static org.mockito.internal.exceptions.Reporter.extraInterfacesRequiresAtLeastOneInterface; import static org.mockito.internal.exceptions.Reporter.invocationListenersRequiresAtLeastOneListener; import static org.mockito.internal.exceptions.Reporter.methodDoesNotAcceptParameter; import static org.mockito.internal.util.collections.Sets.newSet; @SuppressWarnings("unchecked") public class MockSettingsImpl extends CreationSettings implements MockSettings, MockCreationSettings { private static final long serialVersionUID = 4475297236197939569L; private boolean useConstructor; private Object outerClassInstance; private Object[] constructorArgs; @Override public MockSettings serializable() { return serializable(SerializableMode.BASIC); } @Override public MockSettings serializable(SerializableMode mode) { this.serializableMode = mode; return this; } @Override public MockSettings extraInterfaces(Class... extraInterfaces) { if (extraInterfaces == null || extraInterfaces.length == 0) { throw extraInterfacesRequiresAtLeastOneInterface(); } for (Class i : extraInterfaces) { if (i == null) { throw extraInterfacesDoesNotAcceptNullParameters(); } else if (!i.isInterface()) { throw extraInterfacesAcceptsOnlyInterfaces(i); } } this.extraInterfaces = newSet(extraInterfaces); return this; } @Override public MockName getMockName() { return mockName; } @Override public Set> getExtraInterfaces() { return extraInterfaces; } @Override public Object getSpiedInstance() { return spiedInstance; } @Override public MockSettings name(String name) { this.name = name; return this; } @Override public MockSettings spiedInstance(Object spiedInstance) { this.spiedInstance = spiedInstance; return this; } @Override public MockSettings defaultAnswer(Answer defaultAnswer) { this.defaultAnswer = defaultAnswer; if (defaultAnswer == null) { throw defaultAnswerDoesNotAcceptNullParameter(); } return this; } @Override public Answer getDefaultAnswer() { return defaultAnswer; } @Override public MockSettingsImpl stubOnly() { this.stubOnly = true; return this; } @Override public MockSettings useConstructor(Object... constructorArgs) { Checks.checkNotNull(constructorArgs, "constructorArgs", "If you need to pass null, please cast it to the right type, e.g.: useConstructor((String) null)"); this.useConstructor = true; this.constructorArgs = constructorArgs; return this; } @Override public MockSettings outerInstance(Object outerClassInstance) { this.outerClassInstance = outerClassInstance; return this; } @Override public MockSettings withoutAnnotations() { stripAnnotations = true; return this; } @Override public boolean isUsingConstructor() { return useConstructor; } @Override public Object getOuterClassInstance() { return outerClassInstance; } @Override public Object[] getConstructorArgs() { if (outerClassInstance == null) { return constructorArgs; } List resultArgs = new ArrayList(constructorArgs.length + 1); resultArgs.add(outerClassInstance); resultArgs.addAll(Arrays.asList(constructorArgs)); return resultArgs.toArray(new Object[constructorArgs.length + 1]); } @Override public boolean isStubOnly() { return this.stubOnly; } @Override public MockSettings verboseLogging() { if (!invocationListenersContainsType(VerboseMockInvocationLogger.class)) { invocationListeners(new VerboseMockInvocationLogger()); } return this; } @Override public MockSettings invocationListeners(InvocationListener... listeners) { if (listeners == null || listeners.length == 0) { throw invocationListenersRequiresAtLeastOneListener(); } addListeners(listeners, invocationListeners, "invocationListeners"); return this; } private static void addListeners(T[] listeners, List container, String method) { if (listeners == null) { throw methodDoesNotAcceptParameter(method, "null vararg array."); } for (T listener : listeners) { if (listener == null) { throw methodDoesNotAcceptParameter(method, "null listeners."); } container.add(listener); } } @Override public MockSettings verificationStartedListeners(VerificationStartedListener... listeners) { addListeners(listeners, this.verificationStartedListeners, "verificationStartedListeners"); return this; } private boolean invocationListenersContainsType(Class clazz) { for (InvocationListener listener : invocationListeners) { if (listener.getClass().equals(clazz)) { return true; } } return false; } @Override public List getInvocationListeners() { return this.invocationListeners; } public boolean hasInvocationListeners() { return !invocationListeners.isEmpty(); } @Override public Class getTypeToMock() { return typeToMock; } @Override public MockCreationSettings build(Class typeToMock) { return validatedSettings(typeToMock, (CreationSettings) this); } @Override public MockSettings lenient() { this.lenient = true; return this; } private static CreationSettings validatedSettings(Class typeToMock, CreationSettings source) { MockCreationValidator validator = new MockCreationValidator(); validator.validateType(typeToMock); validator.validateExtraInterfaces(typeToMock, source.getExtraInterfaces()); validator.validateMockedType(typeToMock, source.getSpiedInstance()); //TODO SF - add this validation and also add missing coverage // validator.validateDelegatedInstance(classToMock, settings.getDelegatedInstance()); validator.validateConstructorUse(source.isUsingConstructor(), source.getSerializableMode()); //TODO SF - I don't think we really need CreationSettings type //TODO do we really need to copy the entire settings every time we create mock object? it does not seem necessary. CreationSettings settings = new CreationSettings(source); settings.setMockName(new MockNameImpl(source.getName(), typeToMock)); settings.setTypeToMock(typeToMock); settings.setExtraInterfaces(prepareExtraInterfaces(source)); return settings; } private static Set> prepareExtraInterfaces(CreationSettings settings) { Set> interfaces = new HashSet>(settings.getExtraInterfaces()); if(settings.isSerializable()) { interfaces.add(Serializable.class); } return interfaces; } }