Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply spotless to all java projects #3184

Merged
merged 2 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 1 addition & 22 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ allprojects { proj ->
plugins.withId('java') {
proj.apply from: "$rootDir/gradle/errorprone.gradle"
proj.apply from: "$rootDir/gradle/jfr.gradle"
proj.apply from: "$rootDir/gradle/spotless.gradle"
}
tasks.withType(JavaCompile).configureEach {
//I don't believe those warnings add value given modern IDEs
Expand Down Expand Up @@ -118,25 +119,3 @@ animalsniffer {
'java.lang.StackWalker$Option'
]
}

spotless {
// We run the check separately on CI, so don't run this by default
enforceCheck = false

java {
licenseHeaderFile rootProject.file('config/spotless/spotless.header')

custom 'google-java-format', { source ->
com.google.googlejavaformat.java.JavaFormatterOptions options = new com.google.googlejavaformat.java.AutoValue_JavaFormatterOptions.Builder()
.style(com.google.googlejavaformat.java.JavaFormatterOptions.Style.AOSP)
.formatJavadoc(false)
.reorderModifiers(true)
.build()
com.google.googlejavaformat.java.Formatter formatter = new com.google.googlejavaformat.java.Formatter(options)
return formatter.formatSource(source)
}

// This test contains emulation of same-line stubbings. The formatter would put them on a separate line.
targetExclude 'src/test/java/org/mockitousage/internal/junit/UnusedStubbingsFinderTest.java'
}
}
33 changes: 33 additions & 0 deletions gradle/spotless.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
buildscript {
repositories {
gradlePluginPortal()
}

dependencies {
classpath 'com.google.googlejavaformat:google-java-format:1.18.1'
}
}

apply plugin: 'com.diffplug.spotless'

spotless {
// We run the check separately on CI, so don't run this by default
enforceCheck = false

java {
licenseHeaderFile rootProject.file('config/spotless/spotless.header')

custom 'google-java-format', { source ->
com.google.googlejavaformat.java.JavaFormatterOptions options = new com.google.googlejavaformat.java.AutoValue_JavaFormatterOptions.Builder()
.style(com.google.googlejavaformat.java.JavaFormatterOptions.Style.AOSP)
.formatJavadoc(false)
.reorderModifiers(true)
.build()
com.google.googlejavaformat.java.Formatter formatter = new com.google.googlejavaformat.java.Formatter(options)
return formatter.formatSource(source)
}

// This test contains emulation of same-line stubbings. The formatter would put them on a separate line.
targetExclude 'src/test/java/org/mockitousage/internal/junit/UnusedStubbingsFinderTest.java'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@ public AndroidByteBuddyMockMaker() {
if (Platform.isAndroid() || Platform.isAndroidMockMakerRequired()) {
delegate = new SubclassByteBuddyMockMaker(new AndroidLoadingStrategy());
} else {
Plugins.getMockitoLogger().log(join(
"IMPORTANT NOTE FROM MOCKITO:",
"",
"You included the 'mockito-android' dependency in a non-Android environment.",
"The Android mock maker was disabled. You should only include the latter in your 'androidTestCompile' configuration",
"If disabling was a mistake, you can set the 'org.mockito.mock.android' property to 'true' to override this detection.",
"",
"Visit https://javadoc.io/page/org.mockito/mockito-core/latest/org/mockito/Mockito.html#0.1 for more information"
));
Plugins.getMockitoLogger()
.log(
join(
"IMPORTANT NOTE FROM MOCKITO:",
"",
"You included the 'mockito-android' dependency in a non-Android environment.",
"The Android mock maker was disabled. You should only include the latter in your 'androidTestCompile' configuration",
"If disabling was a mistake, you can set the 'org.mockito.mock.android' property to 'true' to override this detection.",
"",
"Visit https://javadoc.io/page/org.mockito/mockito-core/latest/org/mockito/Mockito.html#0.1 for more information"));
delegate = new SubclassByteBuddyMockMaker();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,21 @@ public boolean isDisrespectingOpenness() {
}

@Override
public ClassLoadingStrategy<ClassLoader> resolveStrategy(Class<?> mockedType, ClassLoader classLoader, boolean localMock) {
public ClassLoadingStrategy<ClassLoader> resolveStrategy(
Class<?> mockedType, ClassLoader classLoader, boolean localMock) {
File target = AndroidTempFileLocator.target;
if (target == null) {
throw new MockitoException(join(
"Could not look up implicit location for storing generated classes",
"",
"You can configure an explicit location by setting the system property",
"'org.mockito.android.target' to a folder for storing generated class files",
"This location must be in private scope for most API versions, for example:",
"",
"MyActivity.this.getDir(\"target\", Context.MODE_PRIVATE)",
"or",
"getInstrumentation().getTargetContext().getCacheDir().getPath()"
));
throw new MockitoException(
join(
"Could not look up implicit location for storing generated classes",
"",
"You can configure an explicit location by setting the system property",
"'org.mockito.android.target' to a folder for storing generated class files",
"This location must be in private scope for most API versions, for example:",
"",
"MyActivity.this.getDir(\"target\", Context.MODE_PRIVATE)",
"or",
"getInstrumentation().getTargetContext().getCacheDir().getPath()"));
}
return new AndroidClassLoadingStrategy.Injecting(target);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

class AndroidTempFileLocator {

final static File target;
static final File target;

static {
File t = null;
Expand All @@ -23,7 +23,9 @@ class AndroidTempFileLocator {
} catch (Throwable ignored) {
}
if (t == null) {
t = getCacheDirFromInstrumentationRegistry("android.support.test.InstrumentationRegistry");
t =
getCacheDirFromInstrumentationRegistry(
"android.support.test.InstrumentationRegistry");
}
if (t == null) {
t = getCacheDirFromInstrumentationRegistry("androidx.test.InstrumentationRegistry");
Expand All @@ -33,7 +35,8 @@ class AndroidTempFileLocator {
Class<?> clazz = Class.forName("dalvik.system.PathClassLoader");
Field pathField = clazz.getDeclaredField("path");
pathField.setAccessible(true);
String pathFromThisClassLoader = (String) pathField.get(AndroidTempFileLocator.class.getClassLoader());
String pathFromThisClassLoader =
(String) pathField.get(AndroidTempFileLocator.class.getClassLoader());
File[] results = guessPath(pathFromThisClassLoader);
if (results.length > 0) {
t = results[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,103 +27,107 @@

/** Base for {@link BugChecker}s that detect issues with any() matchers and primitive types. */
public abstract class AbstractMockitoAnyForPrimitiveType extends BugChecker
implements MethodInvocationTreeMatcher {

protected abstract Matcher<? super MethodInvocationTree> matcher();

protected abstract String formatMessage(
String expectedTypeAsString, Type matcherType, String replacementName);

@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (!matcher().matches(tree, state)) {
return NO_MATCH;
}

MethodSymbol method = ASTHelpers.getSymbol(tree);
Type matcherType = method.getReturnType();

// It is expected that the call to anyX() is itself the argument to another call which is
// the one being mocked, e.g. something like this:
// when(mock.call(..., anyInt(), ...))...
TreePath path = state.getPath();
Tree parentTree = path.getParentPath().getLeaf();
if (!(parentTree instanceof MethodInvocationTree)) {
// Ignore calls that are not arguments to another method call.
// TODO: Report this as a problem because it makes little sense.
// TODO: Support casting.
return NO_MATCH;
}

MethodInvocationTree parentCall = (MethodInvocationTree) parentTree;
MethodSymbol parentMethod = ASTHelpers.getSymbol(parentCall);

// Find the index of the argument in the parent call.
int argumentIndex = -1;
List<? extends ExpressionTree> parentArguments = parentCall.getArguments();
for (int i = 0; i < parentArguments.size(); i++) {
ExpressionTree argumentTree = parentArguments.get(i);
if (argumentTree == tree) {
argumentIndex = i;
break;
}
}
if (argumentIndex == -1) {
throw new IllegalStateException(
"Cannot find argument " + state.getSourceForNode(tree) + " in argument list from " + state.getSourceForNode(parentTree));
}

Type parameterType = getParameterType(parentMethod, argumentIndex);

TypeKind parameterTypeKind = parameterType.getKind();
if (parameterTypeKind.isPrimitive() && parameterTypeKind != matcherType.getKind()) {
String expectedTypeAsString = parameterType.toString();
String replacementName =
"any"
+ Character.toUpperCase(expectedTypeAsString.charAt(0))
+ expectedTypeAsString.substring(1);

String message = formatMessage(expectedTypeAsString, matcherType, replacementName);

SuggestedFix.Builder fixBuilder = SuggestedFix.builder();

ExpressionTree methodSelect = tree.getMethodSelect();
String replacement;
if (methodSelect instanceof MemberSelectTree) {
MemberSelectTree qualifier = (MemberSelectTree) methodSelect;
replacement = state.getSourceForNode(qualifier.getExpression()) + "." + replacementName;
} else {
replacement = replacementName;
String staticImport = method.owner + "." + replacementName;
fixBuilder.addStaticImport(staticImport);
}

SuggestedFix fix = fixBuilder.replace(tree, replacement + "()").build();

return buildDescription(tree).setMessage(message).addFix(fix).build();
implements MethodInvocationTreeMatcher {

protected abstract Matcher<? super MethodInvocationTree> matcher();

protected abstract String formatMessage(
String expectedTypeAsString, Type matcherType, String replacementName);

@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (!matcher().matches(tree, state)) {
return NO_MATCH;
}

MethodSymbol method = ASTHelpers.getSymbol(tree);
Type matcherType = method.getReturnType();

// It is expected that the call to anyX() is itself the argument to another call which is
// the one being mocked, e.g. something like this:
// when(mock.call(..., anyInt(), ...))...
TreePath path = state.getPath();
Tree parentTree = path.getParentPath().getLeaf();
if (!(parentTree instanceof MethodInvocationTree)) {
// Ignore calls that are not arguments to another method call.
// TODO: Report this as a problem because it makes little sense.
// TODO: Support casting.
return NO_MATCH;
}

MethodInvocationTree parentCall = (MethodInvocationTree) parentTree;
MethodSymbol parentMethod = ASTHelpers.getSymbol(parentCall);

// Find the index of the argument in the parent call.
int argumentIndex = -1;
List<? extends ExpressionTree> parentArguments = parentCall.getArguments();
for (int i = 0; i < parentArguments.size(); i++) {
ExpressionTree argumentTree = parentArguments.get(i);
if (argumentTree == tree) {
argumentIndex = i;
break;
}
}
if (argumentIndex == -1) {
throw new IllegalStateException(
"Cannot find argument "
+ state.getSourceForNode(tree)
+ " in argument list from "
+ state.getSourceForNode(parentTree));
}

Type parameterType = getParameterType(parentMethod, argumentIndex);

TypeKind parameterTypeKind = parameterType.getKind();
if (parameterTypeKind.isPrimitive() && parameterTypeKind != matcherType.getKind()) {
String expectedTypeAsString = parameterType.toString();
String replacementName =
"any"
+ Character.toUpperCase(expectedTypeAsString.charAt(0))
+ expectedTypeAsString.substring(1);

String message = formatMessage(expectedTypeAsString, matcherType, replacementName);

SuggestedFix.Builder fixBuilder = SuggestedFix.builder();

ExpressionTree methodSelect = tree.getMethodSelect();
String replacement;
if (methodSelect instanceof MemberSelectTree) {
MemberSelectTree qualifier = (MemberSelectTree) methodSelect;
replacement =
state.getSourceForNode(qualifier.getExpression()) + "." + replacementName;
} else {
replacement = replacementName;
String staticImport = method.owner + "." + replacementName;
fixBuilder.addStaticImport(staticImport);
}

SuggestedFix fix = fixBuilder.replace(tree, replacement + "()").build();

return buildDescription(tree).setMessage(message).addFix(fix).build();
}

return NO_MATCH;
}

return NO_MATCH;
}

/**
* Get the type of the parameter for a supplied argument.
*
* @param method the method symbol that is being called.
* @param argumentIndex the index of the argument, can be greater than the number of parameters
* for a var arg method.
* @return the type of the associated parameter.
*/
private Type getParameterType(MethodSymbol method, int argumentIndex) {
List<VarSymbol> parameters = method.getParameters();
Type parameterType;
int parameterCount = parameters.size();
if (argumentIndex >= parameterCount && method.isVarArgs()) {
VarSymbol varArgParameter = parameters.get(parameterCount - 1);
parameterType = ((ArrayType) varArgParameter.asType()).getComponentType();
} else {
parameterType = parameters.get(argumentIndex).asType();
/**
* Get the type of the parameter for a supplied argument.
*
* @param method the method symbol that is being called.
* @param argumentIndex the index of the argument, can be greater than the number of parameters
* for a var arg method.
* @return the type of the associated parameter.
*/
private Type getParameterType(MethodSymbol method, int argumentIndex) {
List<VarSymbol> parameters = method.getParameters();
Type parameterType;
int parameterCount = parameters.size();
if (argumentIndex >= parameterCount && method.isVarArgs()) {
VarSymbol varArgParameter = parameters.get(parameterCount - 1);
parameterType = ((ArrayType) varArgParameter.asType()).getComponentType();
} else {
parameterType = parameters.get(argumentIndex).asType();
}
return parameterType;
}
return parameterType;
}
}