Permalink
Browse files

Fix #673: Add support for setting private static final fields

* setInternalState for public static final
* Restore fields modifiers after setInternalState
* Cannot set value to private static final primitive field
* Cannot set value to static final String field
* updated changelog.txt setting private static final
  • Loading branch information...
1 parent a2a6dbb commit bc2b791b7c7c84953af69ea4863eb733c6d35b01 @andreicristianpetcu andreicristianpetcu committed with thekingnothing Jun 27, 2016
View
@@ -6,4 +6,5 @@ target
.project
.settings
.springBeans
-repo
+repo
+.idea
View
@@ -2,6 +2,7 @@ Change log next version
-----------------------
* Fix #668 @Mock fields are not injected anymore (1.6.5 regression)
* Renamed org.powermock.reflect.internal.WhiteboxImpl.getAllMetodsExcept to org.powermock.reflect.internal.WhiteboxImpl.getAllMethodsExcept (typo)
+* Added support for setting private static final fields in Whitebox.setInternalState
Change log 1.6.5 (2015-04-30)
-----------------------------
@@ -2305,12 +2305,61 @@ private static Field findFieldOrThrowException(Class<?> fieldType, Class<?> wher
private static void setField(Object object, Object value, Field foundField) {
foundField.setAccessible(true);
try {
+ int fieldModifiersMask = foundField.getModifiers();
+ removeFinalModifierIfPresent(foundField);
foundField.set(object, value);
+ restoreModifiersToFieldIfChanged(fieldModifiersMask, foundField);
} catch (IllegalAccessException e) {
throw new RuntimeException("Internal error: Failed to set field in method setInternalState.", e);
}
}
+ private static void removeFinalModifierIfPresent(Field fieldToRemoveFinalFrom) throws IllegalAccessException {
+ int fieldModifiersMask = fieldToRemoveFinalFrom.getModifiers();
+ boolean isFinalModifierPresent = (fieldModifiersMask & Modifier.FINAL) == Modifier.FINAL;
+ if (isFinalModifierPresent) {
+ checkIfCanSetNewValue(fieldToRemoveFinalFrom);
+ int fieldModifiersMaskWithoutFinal = fieldModifiersMask & ~Modifier.FINAL;
+ sedModifiersToField(fieldToRemoveFinalFrom, fieldModifiersMaskWithoutFinal);
+ }
+ }
+
+ private static void checkIfCanSetNewValue(Field fieldToSetNewValueTo) {
+ int fieldModifiersMask = fieldToSetNewValueTo.getModifiers();
+ boolean isFinalModifierPresent = (fieldModifiersMask & Modifier.FINAL) == Modifier.FINAL;
+ boolean isStaticModifierPresent = (fieldModifiersMask & Modifier.STATIC) == Modifier.STATIC;
+
+ if(isFinalModifierPresent && isStaticModifierPresent){
+ boolean fieldTypeIsPrimitive = fieldToSetNewValueTo.getType().isPrimitive();
+ if (fieldTypeIsPrimitive) {
+ throw new IllegalArgumentException("You are trying to set a private static final primitive. Try using an object like Integer instead of int!");
+ }
+ boolean fieldTypeIsString = fieldToSetNewValueTo.getType().equals(String.class);
+ if (fieldTypeIsString) {
+ throw new IllegalArgumentException("You are trying to set a private static final String. Cannot set such fields!");
+ }
+ }
+ }
+
+ private static void restoreModifiersToFieldIfChanged(int initialFieldModifiersMask, Field fieldToRestoreModifiersTo) throws IllegalAccessException {
+ int newFieldModifiersMask = fieldToRestoreModifiersTo.getModifiers();
+ if(initialFieldModifiersMask != newFieldModifiersMask){
+ sedModifiersToField(fieldToRestoreModifiersTo, initialFieldModifiersMask);
+ }
+ }
+
+ private static void sedModifiersToField(Field fieldToRemoveFinalFrom, int fieldModifiersMaskWithoutFinal) throws IllegalAccessException {
+ try {
+ Field modifiersField = Field.class.getDeclaredField("modifiers");
+ boolean accessibleBeforeSet = modifiersField.isAccessible();
+ modifiersField.setAccessible(true);
+ modifiersField.setInt(fieldToRemoveFinalFrom, fieldModifiersMaskWithoutFinal);
+ modifiersField.setAccessible(accessibleBeforeSet);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException("Internal error: Failed to find the \"modifiers\" field in method setInternalState.", e);
+ }
+ }
+
/**
* Concatenate strings.
*
@@ -16,7 +16,9 @@
package org.powermock.reflect;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.powermock.reflect.context.ClassFieldsNotInTargetContext;
import org.powermock.reflect.context.InstanceFieldsNotInTargetContext;
import org.powermock.reflect.context.MyContext;
@@ -79,6 +81,9 @@
*/
public class WhiteBoxTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
static {
RegisterProxyFramework.registerProxyFramework(new ProxyFramework() {
public Class<?> getUnproxiedType(Class<?> type) {
@@ -269,10 +274,32 @@ public void testStaticState() {
assertEquals(expected, Whitebox.getInternalState(ClassWithInternalState.class, "staticState"));
}
- @Test(expected = RuntimeException.class)
- public void testStaticFinalState() {
- Whitebox.setInternalState(ClassWithInternalState.class, "staticFinalState", 123);
- fail("Static final is not possible to change");
+ @Test
+ public void testStaticFinalPrimitiveState() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("You are trying to set a private static final primitive. Try using an object like Integer instead of int!");
+
+ Whitebox.setInternalState(ClassWithInternalState.class, "staticFinalIntState", 123);
+ }
+
+ @Test
+ public void testStaticFinalStringState() throws NoSuchFieldException {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("You are trying to set a private static final String. Cannot set such fields!");
+
+ Whitebox.setInternalState(ClassWithInternalState.class, "staticFinalStringState", "Brand new string");
+ }
+
+ @Test
+ public void testStaticFinalObject() throws NoSuchFieldException {
+ int modifiersBeforeSet = ClassWithInternalState.class.getDeclaredField("staticFinalIntegerState").getModifiers();
+ Integer newValue = ClassWithInternalState.getStaticFinalIntegerState() + 1;
+
+ Whitebox.setInternalState(ClassWithInternalState.class, "staticFinalIntegerState", newValue);
+
+ int modifiersAfterSet = ClassWithInternalState.class.getDeclaredField("staticFinalIntegerState").getModifiers();
+ assertEquals(newValue, ClassWithInternalState.getStaticFinalIntegerState());
+ assertEquals(modifiersBeforeSet, modifiersAfterSet);
}
/**
@@ -530,7 +557,7 @@ public void testGetAllStaticFields_assertNoFieldsFromParent() throws Exception {
@Test
public void testGetAllStaticFields() throws Exception {
Set<Field> allFields = Whitebox.getAllStaticFields(ClassWithInternalState.class);
- assertEquals(2, allFields.size());
+ assertEquals(4, allFields.size());
}
@Test
@@ -23,7 +23,11 @@
private static int staticState = 5;
- private static final int staticFinalState = 15;
+ private static final int staticFinalIntState = 15;
+
+ private static final Integer staticFinalIntegerState = 15;
+
+ private static final String staticFinalStringState = "Some String";
private int internalState = 0;
@@ -59,8 +63,8 @@ public static int getStaticState() {
return staticState;
}
- public static int getStaticFinalState() {
- return staticFinalState;
+ public static Integer getStaticFinalIntegerState() {
+ return staticFinalIntegerState;
}
public ClassWithPrivateMethods getClassWithPrivateMethods() {

0 comments on commit bc2b791

Please sign in to comment.