More unit tests

This commit is contained in:
Nullptr 2022-02-19 16:35:23 +08:00
parent 68c6b5f96b
commit d99a17b449
4 changed files with 91 additions and 39 deletions

View File

@ -9,38 +9,66 @@ import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class UnitTest {
private static final List<Hooker> hookers = new ArrayList<>();
@Test
public void t00_initTest() {
public void t00_init() {
Assert.assertTrue(LSPTest.initHooker());
}
@Test
public void t01_hookTest() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
public void t01_staticMethod() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
var staticMethod = LSPTest.class.getDeclaredMethod("staticMethod");
var staticMethodReplacement = Replacement.class.getDeclaredMethod("staticMethodReplacement");
var staticMethodReplacement = Replacement.class.getDeclaredMethod("staticMethodReplacement", Hooker.MethodCallback.class);
Assert.assertFalse(LSPTest.staticMethod());
Hooker hooker1 = Hooker.hook(staticMethod, staticMethodReplacement);
hookers.add(hooker1);
Assert.assertNotNull(hooker1);
Hooker hooker = Hooker.hook(staticMethod, staticMethodReplacement, null);
Assert.assertNotNull(hooker);
Assert.assertTrue(LSPTest.staticMethod());
Assert.assertFalse((Boolean) ((Method) hooker1.backup).invoke(null));
Assert.assertFalse((boolean) hooker.backup.invoke(null));
Assert.assertTrue(hooker.unhook());
Assert.assertFalse(LSPTest.staticMethod());
}
@Test
public void t02_unhookTest() {
for (Hooker hooker : hookers) {
Assert.assertTrue(hooker.unhook());
}
Assert.assertFalse(LSPTest.staticMethod());
public void t02_normalMethod() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
var normalMethod = LSPTest.class.getDeclaredMethod("normalMethod", String.class, int.class, long.class);
var normalMethodReplacement = Replacement.class.getDeclaredMethod("normalMethodReplacement", Hooker.MethodCallback.class);
var a = "test";
var b = 114514;
var c = 1919810L;
var o = a + b + c;
var r = a + b + c + "replace";
LSPTest test = new LSPTest();
Assert.assertEquals(o, test.normalMethod(a, b, c));
Hooker hooker = Hooker.hook(normalMethod, normalMethodReplacement, new Replacement());
Assert.assertNotNull(hooker);
Assert.assertEquals(r, test.normalMethod(a, b, c));
Assert.assertEquals(o, hooker.backup.invoke(test, a, b, c));
Assert.assertTrue(hooker.unhook());
Assert.assertEquals(o, test.normalMethod(a, b, c));
}
@Test
public void t03_constructor() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
var constructor = LSPTest.class.getDeclaredConstructor();
var constructorReplacement = Replacement.class.getDeclaredMethod("constructorReplacement", Hooker.MethodCallback.class);
Assert.assertFalse(new LSPTest().field);
Assert.assertFalse(constructor.newInstance().field);
Hooker hooker = Hooker.hook(constructor, constructorReplacement, new Replacement());
Assert.assertNotNull(hooker);
Assert.assertTrue(new LSPTest().field);
Assert.assertTrue(constructor.newInstance().field);
Assert.assertTrue(hooker.unhook());
Assert.assertFalse(new LSPTest().field);
Assert.assertFalse(constructor.newInstance().field);
}
}

View File

@ -1,54 +1,53 @@
package org.lsposed.lsplant;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
public class Hooker {
public Executable backup;
static class MethodCallback {
Method backup;
Object[] args;
private boolean isStatic;
private Executable target, replacement;
MethodCallback(Method backup, Object[] args) {
this.backup = backup;
this.args = args;
}
}
public Method backup;
private Executable target;
private Method replacement;
private Object owner = null;
private Hooker() {
}
private native Executable doHook(Executable original, Executable callback);
private native Method doHook(Executable original, Method callback);
private native boolean doUnhook(Executable target);
public Object callback(Object[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException {
if (replacement instanceof Method) {
if (isStatic) {
return ((Method) replacement).invoke(null, args);
} else {
return ((Method) replacement).invoke(args[0], Arrays.copyOfRange(args, 1, args.length));
}
} else if (replacement instanceof Constructor) {
return ((Constructor<?>) replacement).newInstance(args);
} else {
throw new IllegalArgumentException("Unsupported executable type");
}
public Object callback(Object[] args) throws InvocationTargetException, IllegalAccessException {
var methodCallback = new MethodCallback(backup, args);
return replacement.invoke(owner, methodCallback);
}
public boolean unhook() {
return doUnhook(target);
}
public static Hooker hook(Executable target, Executable replacement) {
public static Hooker hook(Executable target, Method replacement, Object owner) {
Hooker hooker = new Hooker();
try {
var callbackMethod = Hooker.class.getDeclaredMethod("callback", Object[].class);
var result = hooker.doHook(target, callbackMethod);
if (result == null) return null;
hooker.isStatic = (replacement.getModifiers() & Modifier.STATIC) != 0;
hooker.backup = result;
hooker.target = target;
hooker.replacement = replacement;
hooker.owner = owner;
} catch (NoSuchMethodException ignored) {
}
return hooker;

View File

@ -6,9 +6,19 @@ public class LSPTest {
System.loadLibrary("test");
}
boolean field;
LSPTest() {
field = false;
}
native static boolean initHooker();
static boolean staticMethod() {
return false;
}
String normalMethod(String a, int b, long c) {
return a + b + c;
}
}

View File

@ -1,8 +1,23 @@
package org.lsposed.lsplant;
import java.lang.reflect.InvocationTargetException;
public class Replacement {
static boolean staticMethodReplacement() {
static boolean staticMethodReplacement(Hooker.MethodCallback callback) {
return true;
}
String normalMethodReplacement(Hooker.MethodCallback callback) {
var a = (String) callback.args[1];
var b = (int) callback.args[2];
var c = (long) callback.args[3];
return a + b + c + "replace";
}
void constructorReplacement(Hooker.MethodCallback callback) throws InvocationTargetException, IllegalAccessException {
var test = (LSPTest) callback.args[0];
callback.backup.invoke(test);
test.field = true;
}
}