diff --git a/hugegraph-common/src/main/java/com/baidu/hugegraph/testutil/Whitebox.java b/hugegraph-common/src/main/java/com/baidu/hugegraph/testutil/Whitebox.java index 0d7c7c426..b9473142e 100644 --- a/hugegraph-common/src/main/java/com/baidu/hugegraph/testutil/Whitebox.java +++ b/hugegraph-common/src/main/java/com/baidu/hugegraph/testutil/Whitebox.java @@ -22,6 +22,7 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Objects; @@ -32,13 +33,18 @@ public class Whitebox { public static final char SEPARATOR = '.'; - public static void setInternalState(Object target, String fieldName, - Object value) { + public static void setInternalState(Object target, + String fieldName, Object value) { assert target != null; assert fieldName != null; int sep = fieldName.lastIndexOf(SEPARATOR); if (sep > 0) { - target = getInternalState(target, fieldName.substring(0, sep)); + String prefix = fieldName.substring(0, sep); + Object result = getInternalState(target, prefix); + E.checkArgument(result != null, + "Can't set value on null field: `%s.%s`", + target, prefix); + target = result; fieldName = fieldName.substring(sep + 1); } @@ -80,6 +86,42 @@ public static T getInternalState(Object target, String fieldName) { } } + public static void setInternalFinalState(Object target, + String fieldName, Object value) { + Class c = target instanceof Class ? + (Class) target : target.getClass(); + try { + Field field = c.getDeclaredField(fieldName); + + // Remove final like FieldUtils.removeFinalModifier() + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + int oldModifiers = field.getModifiers(); + int nonFinal = oldModifiers & ~Modifier.FINAL; + modifiersField.setInt(field, nonFinal); + + // Reset overrideFieldAccessor + Field overrideFAField = Field.class.getDeclaredField("overrideFieldAccessor"); + overrideFAField.setAccessible(true); + if (overrideFAField.get(field) != null) { + setInternalState(field, "overrideFieldAccessor", null); + } + + try { + field.setAccessible(true); + field.set(target, value); + } finally { + // Resume old modifiers (final) + modifiersField.setInt(field, oldModifiers); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(String.format( + "Can't set value of '%s' against object '%s'", + fieldName, target), e); + } + } + private static Field getFieldFromHierarchy(Class clazz, String field) { Field f = getField(clazz, field); while (f == null && clazz != Object.class) { diff --git a/hugegraph-common/src/test/java/com/baidu/hugegraph/testutil/WhiteboxTest.java b/hugegraph-common/src/test/java/com/baidu/hugegraph/testutil/WhiteboxTest.java index 92ab1aff1..855c6adbd 100644 --- a/hugegraph-common/src/test/java/com/baidu/hugegraph/testutil/WhiteboxTest.java +++ b/hugegraph-common/src/test/java/com/baidu/hugegraph/testutil/WhiteboxTest.java @@ -75,6 +75,7 @@ public void testSetInternalState() { Whitebox.setInternalState(test1, "ivalue", 11); Assert.assertEquals(11, Whitebox.getInternalState(test1, "ivalue")); + Assert.assertEquals(11, test1.ivalue); Whitebox.setInternalState(test1, "test2.fvalue", 22f); Assert.assertEquals(22f, Whitebox.getInternalState(test1, @@ -97,6 +98,52 @@ public void testSetInternalState() { }); } + @Test + public void testSetInternalFinalState() { + Test1 test1 = newTest(); + Assert.assertEquals(1, test1.ivalueFinal); + + Whitebox.setInternalState(test1, "ivalueFinal", 2); + Assert.assertEquals(2, Whitebox.getInternalState(test1, "ivalueFinal")); + // FIXME: seems don't take effect!!! + Assert.assertEquals(1, test1.ivalueFinal); + + Whitebox.setInternalFinalState(test1, "ivalueFinal", 3); + Assert.assertEquals(3, Whitebox.getInternalState(test1, "ivalueFinal")); + // FIXME: seems don't take effect!!! + Assert.assertEquals(1, test1.ivalueFinal); + } + + @Test + public void testSetInternalStaticFinalState() { +// Assert.assertThrows(RuntimeException.class, () -> { +// Whitebox.setInternalState(Test4.class, "staticFinalValue", 11); +// }, e -> { +// Assert.assertContains("Can't set value of 'staticFinalValue'", +// e.getMessage()); +// }); + + Assert.assertEquals(1, Test4.staticFinalValue); + // Can only call at the first reflect time + Whitebox.setInternalFinalState(Test4.class, "staticFinalValue", 11); + Assert.assertEquals(11, Whitebox.getInternalState(Test4.class, + "staticFinalValue")); + + Whitebox.setInternalFinalState(Test4.class, "staticFinalValue", 12); + Assert.assertEquals(12, Whitebox.getInternalState(Test4.class, + "staticFinalValue")); + + // FIXME: seems don't take effect!!! + Assert.assertEquals(1, Test4.staticFinalValue); + + Assert.assertThrows(RuntimeException.class, () -> { + Whitebox.setInternalState(Test4.class, "staticFinalValue", 1); + }, e -> { + Assert.assertContains("Can't set value of 'staticFinalValue'", + e.getMessage()); + }); + } + @Test public void testInvokeStatic() { Assert.assertEquals(1, Whitebox.invokeStatic(Test1.class, "svalue")); @@ -159,7 +206,10 @@ private static Test1 newTest() { private static class Test1 { private static int staticValue = 1; + private int ivalue = 1; + private final int ivalueFinal = 1; + private Test2 test2; private TestSubClass test4; @@ -218,6 +268,11 @@ private String value() { } } + private static class Test4 { + + private static final int staticFinalValue = 1; + } + @SuppressWarnings("unused") private static class TestSubClass extends Test1 {