fix bytecode order and async test case

This commit is contained in:
金戟 2020-10-21 21:45:12 +08:00
parent 2fda70d7b4
commit 26712108d6
7 changed files with 81 additions and 22 deletions

View File

@ -83,10 +83,11 @@ public class TestClassHandler extends BaseClassHandler {
private AbstractInsnNode[] replaceTestableUtilField(MethodNode mn, AbstractInsnNode[] instructions, private AbstractInsnNode[] replaceTestableUtilField(MethodNode mn, AbstractInsnNode[] instructions,
String fieldName, int pos) { String fieldName, int pos) {
InsnList insnNodes = new InsnList(); InsnList insnNodes = new InsnList();
insnNodes.insert(new VarInsnNode(ALOAD, 0)); // NOTE: will insert in reversed order
insnNodes.insert(new MethodInsnNode(INVOKESTATIC, CLASS_TESTABLE_UTIL, FIELD_TO_METHOD_MAPPING.get(fieldName), insnNodes.insert(new MethodInsnNode(INVOKESTATIC, CLASS_TESTABLE_UTIL, FIELD_TO_METHOD_MAPPING.get(fieldName),
SIGNATURE_TESTABLE_UTIL_METHOD, false)); SIGNATURE_TESTABLE_UTIL_METHOD, false));
mn.instructions.insertBefore(instructions[pos], insnNodes); insnNodes.insert(new VarInsnNode(ALOAD, 0));
mn.instructions.insert(instructions[pos], insnNodes);
mn.instructions.remove(instructions[pos]); mn.instructions.remove(instructions[pos]);
return mn.instructions.toArray(); return mn.instructions.toArray();
} }

View File

@ -5,7 +5,14 @@ package com.alibaba.testable.core.tool;
*/ */
public class TestableTool { public class TestableTool {
/**
* Name of current test case method
*/
public static String TEST_CASE; public static String TEST_CASE;
/**
* Name of the last visited method in source class
*/
public static String SOURCE_METHOD; public static String SOURCE_METHOD;
} }

View File

@ -7,6 +7,10 @@ import java.io.*;
*/ */
public class ResourceUtil { public class ResourceUtil {
/**
* Read content of a text file from resource folder
* @param filePath file to read
*/
public static String fetchText(String filePath) { public static String fetchText(String filePath) {
InputStream in = ResourceUtil.class.getResourceAsStream("/" + filePath); InputStream in = ResourceUtil.class.getResourceAsStream("/" + filePath);
BufferedReader reader = new BufferedReader(new InputStreamReader(in)); BufferedReader reader = new BufferedReader(new InputStreamReader(in));
@ -24,6 +28,10 @@ public class ResourceUtil {
} }
} }
/**
* Read content of a binary file from resource folder
* @param filePath file to read
*/
public static byte[] fetchBinary(String filePath) { public static byte[] fetchBinary(String filePath) {
InputStream in = ResourceUtil.class.getResourceAsStream("/" + filePath); InputStream in = ResourceUtil.class.getResourceAsStream("/" + filePath);
if (in == null) { if (in == null) {

View File

@ -7,6 +7,11 @@ import java.util.List;
*/ */
public class StringUtil { public class StringUtil {
/**
* Join strings
* @param list strings to join
* @param conjunction connection character
*/
static public String join(List<String> list, String conjunction) static public String join(List<String> list, String conjunction)
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();

View File

@ -7,19 +7,27 @@ import com.alibaba.testable.core.constant.ConstPool;
*/ */
public class TestableUtil { public class TestableUtil {
/**
* Get the last visit method in source file
* @param testClassRef usually `this` variable of the test class
* @return method name
*/
public static String currentSourceMethodName(Object testClassRef) { public static String currentSourceMethodName(Object testClassRef) {
Class<?> testClass = testClassRef.getClass(); Class<?> testClass = testClassRef.getClass();
StackTraceElement[] stack = getMainThread().getStackTrace();
String testClassName = getRealClassName(testClass); String testClassName = getRealClassName(testClass);
String sourceClassName = testClassName.substring(0, testClassName.length() - ConstPool.TEST_POSTFIX.length()); String sourceClassName = testClassName.substring(0, testClassName.length() - ConstPool.TEST_POSTFIX.length());
for (int i = stack.length - 1; i >= 0; i--) { String sourceMethod = findLastMethodFromSourceClass(sourceClassName, getMainThread().getStackTrace());
if (stack[i].getClassName().equals(sourceClassName)) { if (sourceMethod.isEmpty()) {
return stack[i].getMethodName(); return findLastMethodFromSourceClass(sourceClassName, Thread.currentThread().getStackTrace());
} }
} return sourceMethod;
return "";
} }
/**
* Get current test case method
* @param testClassRef usually `this` variable of the test class
* @return method name
*/
public static String currentTestCaseName(Object testClassRef) { public static String currentTestCaseName(Object testClassRef) {
Class<?> testClass = testClassRef.getClass(); Class<?> testClass = testClassRef.getClass();
StackTraceElement[] stack = getMainThread().getStackTrace(); StackTraceElement[] stack = getMainThread().getStackTrace();
@ -32,6 +40,15 @@ public class TestableUtil {
return ""; return "";
} }
private static String findLastMethodFromSourceClass(String sourceClassName, StackTraceElement[] stack) {
for (StackTraceElement element : stack) {
if (element.getClassName().equals(sourceClassName)) {
return element.getMethodName();
}
}
return "";
}
private static String getRealClassName(Class<?> testClass) { private static String getRealClassName(Class<?> testClass) {
String className = testClass.getName(); String className = testClass.getName();
int posOfInnerClass = className.lastIndexOf('$'); int posOfInnerClass = className.lastIndexOf('$');
@ -44,6 +61,7 @@ public class TestableUtil {
return t; return t;
} }
} }
// usually impossible to go here
return Thread.currentThread(); return Thread.currentThread();
} }

View File

@ -6,6 +6,7 @@ import com.alibaba.testable.core.annotation.TestableInject;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import static com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD; import static com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD;
import static com.alibaba.testable.core.tool.TestableTool.TEST_CASE; import static com.alibaba.testable.core.tool.TestableTool.TEST_CASE;
@ -41,6 +42,9 @@ class DemoServiceTest {
@TestableInject @TestableInject
private String callFromDifferentMethod() { private String callFromDifferentMethod() {
if (TEST_CASE.equals("should_able_to_get_test_case_name")) {
return "mock_special";
}
switch (SOURCE_METHOD) { switch (SOURCE_METHOD) {
case "callerOne": return "mock_one"; case "callerOne": return "mock_one";
default: return "mock_others"; default: return "mock_others";
@ -81,15 +85,19 @@ class DemoServiceTest {
@Test @Test
void should_able_to_get_source_method_name() throws Exception { void should_able_to_get_source_method_name() throws Exception {
assertEquals("mock_one_mock_others", demoService.callerTwo() + "_" + demoService.callerOne()); // synchronous
assertEquals("mock_one_mock_others", ((Callable<String>)() -> assertEquals("mock_one_mock_others", demoService.callerOne() + "_" + demoService.callerTwo());
demoService.callerOne() + "_" + demoService.callerTwo()).call()); // asynchronous
assertEquals("mock_one_mock_others",
Executors.newSingleThreadExecutor().submit(() -> demoService.callerOne() + "_" + demoService.callerTwo()).get());
} }
@Test @Test
void should_able_to_get_test_case_name() throws Exception { void should_able_to_get_test_case_name() throws Exception {
assertEquals("should_able_to_get_test_case_name", TEST_CASE); // synchronous
assertEquals("should_able_to_get_test_case_name", ((Callable<String>)() -> TEST_CASE).call()); assertEquals("mock_special", demoService.callerOne());
// asynchronous
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit(() -> demoService.callerOne()).get());
} }
} }

View File

@ -7,7 +7,7 @@ import com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD
import com.alibaba.testable.core.tool.TestableTool.TEST_CASE import com.alibaba.testable.core.tool.TestableTool.TEST_CASE
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import java.util.concurrent.Callable import java.util.concurrent.Executors
@EnableTestable @EnableTestable
@ -29,10 +29,16 @@ internal class DemoServiceTest {
private fun startsWith(self: BlackBox, s: String) = false private fun startsWith(self: BlackBox, s: String) = false
@TestableInject @TestableInject
private fun callFromDifferentMethod() = when (SOURCE_METHOD) { private fun callFromDifferentMethod(): String {
return if (TEST_CASE == "should_able_to_get_test_case_name") {
"mock_special"
} else {
when (SOURCE_METHOD) {
"callerOne" -> "mock_one" "callerOne" -> "mock_one"
else -> "mock_others" else -> "mock_others"
} }
}
}
private val demoService = DemoService() private val demoService = DemoService()
@ -65,15 +71,21 @@ internal class DemoServiceTest {
@Test @Test
fun should_able_to_get_source_method_name() { fun should_able_to_get_source_method_name() {
assertEquals("mock_one_mock_others", demoService.callerTwo() + "_" + demoService.callerOne()) // synchronous
assertEquals("mock_one_mock_others", Callable<String> { assertEquals("mock_one_mock_others", demoService.callerOne() + "_" + demoService.callerTwo())
// asynchronous
assertEquals("mock_one_mock_others", Executors.newSingleThreadExecutor().submit<String> {
demoService.callerOne() + "_" + demoService.callerTwo() demoService.callerOne() + "_" + demoService.callerTwo()
}.call()) }.get())
} }
@Test @Test
fun should_able_to_get_test_case_name() { fun should_able_to_get_test_case_name() {
assertEquals("should_able_to_get_test_case_name", TEST_CASE) // synchronous
assertEquals("should_able_to_get_test_case_name", Callable<String> { TEST_CASE }.call()) assertEquals("mock_special", demoService.callerOne())
// asynchronous
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit<String> {
demoService.callerOne()
}.get())
} }
} }