From 1bf6ab3b21eb5397e84364a3e5b0c1962153182b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=88=9F?= Date: Sun, 29 Nov 2020 10:39:55 +0800 Subject: [PATCH] fit test case name fetcher for gradle build --- .../testable/core/constant/ConstPool.java | 10 -- .../testable/core/matcher/InvokeVerifier.java | 2 +- .../testable/core/util/TestableUtil.java | 96 ++++++++++++------- .../testable/core/util/TestableUtilTest.java | 23 +++++ 4 files changed, 84 insertions(+), 47 deletions(-) delete mode 100644 testable-core/src/main/java/com/alibaba/testable/core/constant/ConstPool.java create mode 100644 testable-core/src/test/java/com/alibaba/testable/core/util/TestableUtilTest.java diff --git a/testable-core/src/main/java/com/alibaba/testable/core/constant/ConstPool.java b/testable-core/src/main/java/com/alibaba/testable/core/constant/ConstPool.java deleted file mode 100644 index 78684ae..0000000 --- a/testable-core/src/main/java/com/alibaba/testable/core/constant/ConstPool.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.alibaba.testable.core.constant; - -/** - * @author flin - */ -public final class ConstPool { - - public static final String TEST_POSTFIX = "Test"; - -} diff --git a/testable-core/src/main/java/com/alibaba/testable/core/matcher/InvokeVerifier.java b/testable-core/src/main/java/com/alibaba/testable/core/matcher/InvokeVerifier.java index add69e0..d50c02c 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/matcher/InvokeVerifier.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/matcher/InvokeVerifier.java @@ -108,7 +108,7 @@ public class InvokeVerifier { public InvokeVerifier times(int count) { if (lastVerification == null) { // when used independently, equals to `withTimes()` - System.out.println("Warning: [" + TestableUtil.getPreviousStackLocation() + "] using \"times()\" method " + System.out.println("Warning: [" + TestableUtil.previousStackLocation() + "] using \"times()\" method " + "without \"with()\" or \"withInOrder()\" is not recommended, please use \"withTimes()\" instead."); return withTimes(count); } diff --git a/testable-core/src/main/java/com/alibaba/testable/core/util/TestableUtil.java b/testable-core/src/main/java/com/alibaba/testable/core/util/TestableUtil.java index bc85364..cf24302 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/util/TestableUtil.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/util/TestableUtil.java @@ -1,27 +1,32 @@ package com.alibaba.testable.core.util; -import com.alibaba.testable.core.constant.ConstPool; - +import java.util.Set; /** * @author flin */ public class TestableUtil { + /** + * [0]Thread.getStackTrace() → [1]currentSourceMethodName() → [2]MockMethod -> [3]SourceMethod + */ + private static final int INDEX_OF_SOURCE_METHOD = 3; + /** + * [0]Thread.getStackTrace() → [1]previousStackLocation() → [2]Invoker -> [3]Caller of invoker + */ + private static final int INDEX_OF_CALLER_METHOD = 3; + /** + * Just a special number to identify test worker thread + */ + private static final int TEST_WORKER_THREAD_PRIORITY = 55555; + /** * 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) { - Class testClass = testClassRef.getClass(); - String testClassName = getRealClassName(testClass); - String sourceClassName = testClassName.substring(0, testClassName.length() - ConstPool.TEST_POSTFIX.length()); - String sourceMethod = findLastMethodFromSourceClass(sourceClassName, getMainThread().getStackTrace()); - if (sourceMethod.isEmpty()) { - return findLastMethodFromSourceClass(sourceClassName, Thread.currentThread().getStackTrace()); - } - return sourceMethod; + return Thread.currentThread().getStackTrace()[INDEX_OF_SOURCE_METHOD].getMethodName(); } /** @@ -31,7 +36,7 @@ public class TestableUtil { */ public static String currentTestCaseName(Object testClassRef) { Class testClass = testClassRef.getClass(); - String testClassName = getRealClassName(testClass); + String testClassName = getOuterClassName(testClass.getName()); return currentTestCaseName(testClassName); } @@ -41,10 +46,28 @@ public class TestableUtil { * @return method name */ public static String currentTestCaseName(String testClassName) { - String testCaseName = findFirstMethodFromTestClass(testClassName, getMainThread().getStackTrace()); + // try current thread + String testCaseName = findFirstMethodFromTestClass(testClassName, Thread.currentThread().getStackTrace()); if (testCaseName.isEmpty()) { - return findFirstMethodFromTestClass(testClassName, Thread.currentThread().getStackTrace()); + Set threads = Thread.getAllStackTraces().keySet(); + // try find previously marked thread + Thread testWorkerThread = findTestWorkerThread(threads); + if (testWorkerThread != null) { + testCaseName = findFirstMethodFromTestClass(testClassName, testWorkerThread.getStackTrace()); + if (!testCaseName.isEmpty()) { + return testCaseName; + } + } + // travel all possible threads + for (Thread t : threads) { + testCaseName = findFirstMethodFromTestClass(testClassName, t.getStackTrace()); + if (!testCaseName.isEmpty()) { + t.setPriority(TEST_WORKER_THREAD_PRIORITY); + return testCaseName; + } + } } + System.err.println("testCaseName: " + testCaseName); return testCaseName; } @@ -52,44 +75,45 @@ public class TestableUtil { * Get file name and line number of where current method was called * @return in "filename:linenumber" format */ - public static String getPreviousStackLocation() { - // 0 - Thread.getStackTrace(), 1 - this method, 2 - code call this method, 3 - code call the caller method - StackTraceElement stack = getMainThread().getStackTrace()[3]; + public static String previousStackLocation() { + StackTraceElement stack = Thread.currentThread().getStackTrace()[INDEX_OF_CALLER_METHOD]; return stack.getFileName() + ":" + stack.getLineNumber(); } - private static String findLastMethodFromSourceClass(String sourceClassName, StackTraceElement[] stack) { - for (StackTraceElement element : stack) { - if (element.getClassName().equals(sourceClassName)) { - return element.getMethodName(); + private static Thread findTestWorkerThread(Set threads) { + for (Thread t : threads) { + if (t.getPriority() == TEST_WORKER_THREAD_PRIORITY) { + return t; } } - return ""; + return null; } private static String findFirstMethodFromTestClass(String testClassName, StackTraceElement[] stack) { for (int i = stack.length - 1; i >= 0; i--) { - if (stack[i].getClassName().equals(testClassName)) { - return stack[i].getMethodName(); + if (getOuterClassName(stack[i].getClassName()).equals(testClassName)) { + return stack[i].getClassName().indexOf('$') > 0 ? + // test case using async call + getMethodNameFromLambda(stack[i].getClassName()) : + // in case of lambda method + getMethodNameFromLambda(stack[i].getMethodName()); } } return ""; } - private static String getRealClassName(Class testClass) { - String className = testClass.getName(); - int posOfInnerClass = className.lastIndexOf('$'); + private static String getMethodNameFromLambda(String originName) { + int beginOfMethodName = originName.indexOf('$'); + if (beginOfMethodName < 0) { + return originName; + } + int endOfMethodName = originName.indexOf('$', beginOfMethodName + 1); + return originName.substring(beginOfMethodName + 1, endOfMethodName); + } + + private static String getOuterClassName(String className) { + int posOfInnerClass = className.indexOf('$'); return posOfInnerClass > 0 ? className.substring(0, posOfInnerClass) : className; } - private static Thread getMainThread() { - for (Thread t : Thread.getAllStackTraces().keySet()) { - if (t.getId() == 1L) { - return t; - } - } - // usually impossible to go here - return Thread.currentThread(); - } - } diff --git a/testable-core/src/test/java/com/alibaba/testable/core/util/TestableUtilTest.java b/testable-core/src/test/java/com/alibaba/testable/core/util/TestableUtilTest.java new file mode 100644 index 0000000..e796017 --- /dev/null +++ b/testable-core/src/test/java/com/alibaba/testable/core/util/TestableUtilTest.java @@ -0,0 +1,23 @@ +package com.alibaba.testable.core.util; + +import com.alibaba.testable.core.accessor.PrivateAccessor; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class TestableUtilTest { + + @Test + void should_get_outer_class_name() { + assertEquals("com.alibaba.testable.demo.DemoMockTest", + PrivateAccessor.invokeStatic(TestableUtil.class, "getOuterClassName", "com.alibaba.testable.demo.DemoMockTest$should_able_to_get_source_method_name$1")); + } + + @Test + void should_get_async_method_name_from_inner_class_name() { + assertEquals("should_able_to_get_source_method_name", + PrivateAccessor.invokeStatic(TestableUtil.class, "getAsyncMethodName", "com.alibaba.testable.demo.DemoMockTest$should_able_to_get_source_method_name$1")); + assertEquals("should_able_to_get_source_method_name", + PrivateAccessor.invokeStatic(TestableUtil.class, "getAsyncMethodName", "lambda$should_able_to_get_source_method_name$0")); + } +}