Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public class PowerMockWhiteboxToJavaReflection extends Recipe {
private static final String WHITEBOX_FQN = "org.powermock.reflect.Whitebox";
private static final MethodMatcher SET_INTERNAL_STATE =
new MethodMatcher("org.powermock.reflect.Whitebox setInternalState(java.lang.Object, java.lang.String, java.lang.Object)");
private static final MethodMatcher SET_INTERNAL_STATE_WHERE =
new MethodMatcher("org.powermock.reflect.Whitebox setInternalState(java.lang.Object, java.lang.String, java.lang.Object, java.lang.Class)");
private static final MethodMatcher GET_INTERNAL_STATE =
new MethodMatcher("org.powermock.reflect.Whitebox getInternalState(java.lang.Object, java.lang.String)");
private static final MethodMatcher INVOKE_METHOD =
Expand Down Expand Up @@ -125,7 +127,7 @@ public J.Block visitBlock(J.Block block, ExecutionContext ctx) {
templateArgs
);
getCursor().putMessageOnFirstEnclosing(J.MethodDeclaration.class, WHITEBOX_REPLACED, true);
if (SET_INTERNAL_STATE.matches(mi) || GET_INTERNAL_STATE.matches(mi)) {
if (SET_INTERNAL_STATE.matches(mi) || SET_INTERNAL_STATE_WHERE.matches(mi) || GET_INTERNAL_STATE.matches(mi)) {
getCursor().putMessageOnFirstEnclosing(J.MethodDeclaration.class, NEEDS_FIELD_IMPORT, true);
}
if (INVOKE_METHOD.matches(mi)) {
Expand All @@ -144,7 +146,10 @@ public J.Block visitBlock(J.Block block, ExecutionContext ctx) {
List<Expression> args = mi.getArguments();

if (SET_INTERNAL_STATE.matches(mi) && args.size() == 3) {
return buildSetInternalStateTemplate(args, scope);
return buildSetInternalStateTemplate(args, scope, false);
}
if (SET_INTERNAL_STATE_WHERE.matches(mi) && args.size() == 4) {
return buildSetInternalStateTemplate(args, scope, true);
}
if (GET_INTERNAL_STATE.matches(mi) && args.size() == 2) {
return buildGetInternalStateTemplate(args, statement, scope);
Expand All @@ -162,6 +167,10 @@ private Object[] buildTemplateArgs(J.MethodInvocation mi, JavaType.@Nullable Met
// target, fieldName, target, value
return new Object[]{args.get(0), args.get(1), args.get(0), args.get(2)};
}
if (SET_INTERNAL_STATE_WHERE.matches(mi) && args.size() == 4) {
// whereClass, fieldName, target, value
return new Object[]{args.get(3), args.get(1), args.get(0), args.get(2)};
}
if (GET_INTERNAL_STATE.matches(mi) && args.size() == 2) {
// target, fieldName, target
return new Object[]{args.get(0), args.get(1), args.get(0)};
Expand All @@ -172,13 +181,16 @@ private Object[] buildTemplateArgs(J.MethodInvocation mi, JavaType.@Nullable Met
return new Object[0];
}

private @Nullable String buildSetInternalStateTemplate(List<Expression> args, Cursor scope) {
private @Nullable String buildSetInternalStateTemplate(List<Expression> args, Cursor scope, boolean where) {
String fieldName = extractStringLiteral(args.get(1));
if (fieldName == null) {
return null;
}
String varName = generateVariableName(fieldName + "Field", scope, INCREMENT_NUMBER);
return "Field " + varName + " = #{any(java.lang.Object)}.getClass().getDeclaredField(#{any(java.lang.String)});\n" +
String lookup = where ?
"Field " + varName + " = #{any(java.lang.Class)}.getDeclaredField(#{any(java.lang.String)});\n" :
"Field " + varName + " = #{any(java.lang.Object)}.getClass().getDeclaredField(#{any(java.lang.String)});\n";
return lookup +
varName + ".setAccessible(true);\n" +
varName + ".set(#{any(java.lang.Object)}, #{any(java.lang.Object)});";
}
Expand Down Expand Up @@ -325,7 +337,8 @@ private Object[] buildInvokeMethodArgs(List<Expression> args, JavaType.@Nullable
private J.@Nullable MethodInvocation extractWhiteboxInvocation(Statement statement) {
if (statement instanceof J.MethodInvocation) {
J.MethodInvocation mi = (J.MethodInvocation) statement;
if (SET_INTERNAL_STATE.matches(mi) || GET_INTERNAL_STATE.matches(mi) || INVOKE_METHOD.matches(mi)) {
if (SET_INTERNAL_STATE.matches(mi) || SET_INTERNAL_STATE_WHERE.matches(mi) ||
GET_INTERNAL_STATE.matches(mi) || INVOKE_METHOD.matches(mi)) {
return mi;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,96 @@ void testSetField() throws Exception {
);
}

@Test
void setInternalStateWithWhereClass() {
//language=java
rewriteRun(
java(
"""
class Parent {
private String name;
}
"""
),
java(
"""
class Child extends Parent {
}
"""
),
java(
"""
import org.powermock.reflect.Whitebox;

class MyServiceTest {
void test() {
Child child = new Child();
Whitebox.setInternalState(child, "name", "newValue", Parent.class);
}
}
""",
"""
import java.lang.reflect.Field;

class MyServiceTest {
void test() throws Exception {
Child child = new Child();
Field nameField = Parent.class.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(child, "newValue");
}
}
"""
)
);
}

@Test
void setInternalStateWithWhereClassVariable() {
//language=java
rewriteRun(
java(
"""
class Parent {
private String name;
}
"""
),
java(
"""
class Child extends Parent {
}
"""
),
java(
"""
import org.powermock.reflect.Whitebox;

class MyServiceTest {
void test() {
Child child = new Child();
Class<?> where = Parent.class;
Whitebox.setInternalState(child, "name", "newValue", where);
}
}
""",
"""
import java.lang.reflect.Field;

class MyServiceTest {
void test() throws Exception {
Child child = new Child();
Class<?> where = Parent.class;
Field nameField = where.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(child, "newValue");
}
}
"""
)
);
}

@Test
void getInternalStateWithAssignment() {
//language=java
Expand Down
Loading