/* * Copyright 2009 the original author or 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 org.powermock.api.mockito.internal.invocation; import org.powermock.core.spi.support.InvocationSubstitute; import org.powermock.reflect.Whitebox; import java.util.regex.Matcher; public class InvocationControlAssertionError { private static final String AT = "at"; private static final String ERROR_LOCATION_MARKER = "->"; private static final String COLON_NEWLINE = ":\n"; private static final String HERE_TEXT = "here:\n"; private static final String UNDESIRED_INVOCATION_TEXT = " Undesired invocation:"; private static final String POWER_MOCKITO_CLASS_NAME = "org.powermock.api.mockito.PowerMockito"; public static void updateErrorMessageForVerifyNoMoreInteractions(AssertionError errorToUpdate) { /* * VerifyNoMoreInteractions failed, we need to update the error message. */ String verifyNoMoreInteractionsInvocation = null; StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); for (int i = stackTrace.length - 1; i >= 0; i--) { final StackTraceElement stackTraceElement = stackTrace[i]; if (stackTraceElement.getClassName().equals(POWER_MOCKITO_CLASS_NAME) && stackTraceElement.getMethodName().equals("verifyNoMoreInteractions")) { final int invocationStackTraceIndex; if (stackTrace[i + 1].getClassName().equals(POWER_MOCKITO_CLASS_NAME) && stackTrace[i + 1].getMethodName().equals("verifyZeroInteractions")) { invocationStackTraceIndex = i + 2; } else { invocationStackTraceIndex = i + 1; } verifyNoMoreInteractionsInvocation = stackTrace[invocationStackTraceIndex].toString(); } } if (verifyNoMoreInteractionsInvocation == null) { // Something unexpected happened, just return return; } String message = errorToUpdate.getMessage(); StringBuilder builder = new StringBuilder(); builder.append(message); final int indexOfFirstAt = message.indexOf(AT); final int startOfVerifyNoMoreInteractionsInvocation = indexOfFirstAt + AT.length() + 1; final int endOfVerifyNoMoreInteractionsInvocation = message.indexOf('\n', indexOfFirstAt + AT.length()); builder.replace(startOfVerifyNoMoreInteractionsInvocation, endOfVerifyNoMoreInteractionsInvocation, verifyNoMoreInteractionsInvocation); builder.delete(builder.indexOf("\n", endOfVerifyNoMoreInteractionsInvocation + 1), builder.lastIndexOf("\n")); Whitebox.setInternalState(errorToUpdate, builder.toString()); } public static void updateErrorMessageForMethodInvocation(AssertionError errorToUpdate) { /* * We failed to verify the new substitution mock. This happens when, for * example, the user has done something like * whenNew(MyClass.class).thenReturn(myMock).times(3) when in fact an * instance of MyClass has been created less or more times than 3. */ Whitebox.setInternalState(errorToUpdate, "\n" + changeMessageContent(errorToUpdate.getMessage())); } public static void throwAssertionErrorForNewSubstitutionFailure(AssertionError oldError, Class<?> type) { /* * We failed to verify the new substitution mock. This happens when, for * example, the user has done something like * whenNew(MyClass.class).thenReturn(myMock).times(3) when in fact an * instance of MyClass has been created less or more times than 3. */ final String newSubsitutionClassName = InvocationSubstitute.class.getSimpleName(); final String newSubsitutionClassNameInMockito = newSubsitutionClassName.substring(0, 1).toLowerCase() + newSubsitutionClassName.substring(1); String message = oldError.getMessage(); final String newSubsitutionMethodName = InvocationSubstitute.class.getDeclaredMethods()[0].getName(); message = message.replaceAll(newSubsitutionClassNameInMockito + "." + newSubsitutionMethodName, Matcher .quoteReplacement(type.getName())); message = message.replaceAll("method", "constructor"); throw new AssertionError(changeMessageContent(message)); } private static String changeMessageContent(String message) { /* * Temp fix: Remove powermock internal "at locations" (points to which * line the expectation went wrong in Mockito). We should try to find * the real ones instead */ StringBuilder builder = removeFailureLocations(message); // Remove "Undesired invocation:" removeText(builder, UNDESIRED_INVOCATION_TEXT); removeAndReplaceText(builder, HERE_TEXT, ' '); removeAndReplaceText(builder, COLON_NEWLINE, ' '); final String finalMessage = builder.toString().trim(); return finalMessage; } private static StringBuilder removeFailureLocations(String message) { StringBuilder builder = new StringBuilder(); builder.append(message); int indexOfBeginLocation = builder.indexOf(ERROR_LOCATION_MARKER); while (indexOfBeginLocation > 0) { int indexOfLocationEnd = builder.indexOf("\n", indexOfBeginLocation); builder.delete(indexOfBeginLocation, indexOfLocationEnd < 0 ? builder.length() : indexOfLocationEnd + 1); indexOfBeginLocation = builder.indexOf(ERROR_LOCATION_MARKER); } return builder; } private static void removeAndReplaceText(StringBuilder builder, String text, char appender) { int currentTextIndex = builder.indexOf(text); int previousTextIndex = 0; boolean isSingleConcat = true; while (currentTextIndex > 0) { previousTextIndex = currentTextIndex; builder.delete(currentTextIndex, currentTextIndex + text.length()); currentTextIndex = builder.indexOf(text); if (isLastFinding(currentTextIndex) && !isSingleConcat) { builder.replace(builder.length(), builder.length(), "."); } else { builder.replace(previousTextIndex, previousTextIndex + 1, String.valueOf( builder.charAt(previousTextIndex)).toLowerCase()); builder.insert(previousTextIndex, String.valueOf(appender)); currentTextIndex++; isSingleConcat = false; } } } private static boolean isLastFinding(int index) { return index < 0; } private static void removeText(StringBuilder builder, String text) { int textIndex = builder.indexOf(text); while (textIndex > 0) { builder.delete(textIndex, textIndex + text.length()); textIndex = builder.indexOf(text); } } }