mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-03-26 01:30:24 +08:00
method invocation could contain LabelNode and LineNumberNode
This commit is contained in:
parent
179e71c7c2
commit
28bde0b85a
@ -41,8 +41,24 @@ class DemoMatcher {
|
||||
val longArray = arrayOf(1L, 2L)
|
||||
methodToBeMocked(1, 2)
|
||||
methodToBeMocked(1L, 2.0)
|
||||
|
||||
// below two invocations are equivalent
|
||||
methodToBeMocked(listOf(1), setOf(1.0f))
|
||||
// multiple lines method invocation
|
||||
methodToBeMocked(object : ArrayList<Int?>() {
|
||||
init {
|
||||
add(1)
|
||||
}
|
||||
}, object : HashSet<Float?>() {
|
||||
init {
|
||||
add(1.0f)
|
||||
}
|
||||
})
|
||||
|
||||
// below two invocations are equivalent
|
||||
methodToBeMocked(1.0, mapOf(1 to 1.0f))
|
||||
methodToBeMocked(1.0, object : HashMap<Int?, Float?>(2) { init { put(1, 1.0f) } })
|
||||
|
||||
methodToBeMocked(floatList, floatList)
|
||||
methodToBeMocked(longArray)
|
||||
methodToBeMocked(arrayOf(1.0, 2.0))
|
||||
|
@ -44,8 +44,8 @@ internal class DemoMatcherTest {
|
||||
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.anyInt(), 2)
|
||||
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.anyLong(), InvokeMatcher.anyNumber())
|
||||
// Note: Must use `::class.javaObjectType` for primary types check in Kotlin
|
||||
InvokeVerifier.verify("methodWithArguments").with(1.0, InvokeMatcher.anyMapOf(Int::class.javaObjectType, Float::class.javaObjectType))
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyList(), InvokeMatcher.anySetOf(Float::class.javaObjectType))
|
||||
InvokeVerifier.verify("methodWithArguments").with(1.0, InvokeMatcher.anyMapOf(Int::class.javaObjectType, Float::class.javaObjectType)).times(2)
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyList(), InvokeMatcher.anySetOf(Float::class.javaObjectType)).times(2)
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyList(), InvokeMatcher.anyListOf(Float::class.javaObjectType))
|
||||
InvokeVerifier.verify("methodWithArrayArgument").with(InvokeMatcher.anyArrayOf(Long::class.javaObjectType))
|
||||
InvokeVerifier.verify("methodWithArrayArgument").with(InvokeMatcher.anyArray())
|
||||
@ -70,12 +70,12 @@ internal class DemoMatcherTest {
|
||||
@Test
|
||||
fun should_match_with_times() {
|
||||
demoMatcher.callMethodWithNumberArguments()
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyNumber(), InvokeMatcher.any()).times(3)
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyNumber(), InvokeMatcher.any()).times(4)
|
||||
|
||||
demoMatcher.callMethodWithNumberArguments()
|
||||
var gotError = false
|
||||
try {
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyNumber(), InvokeMatcher.any()).times(4)
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyNumber(), InvokeMatcher.any()).times(5)
|
||||
} catch (e: VerifyFailedError) {
|
||||
gotError = true
|
||||
}
|
||||
|
@ -2,8 +2,6 @@ package com.alibaba.testable.demo
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockConstructor
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.annotation.MockWith
|
||||
import com.alibaba.testable.core.model.MockDiagnose
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.util.*
|
||||
@ -12,7 +10,6 @@ import java.util.*
|
||||
* 演示模板方法可以被Mock
|
||||
* Demonstrate template method can be mocked
|
||||
*/
|
||||
@MockWith(diagnose = MockDiagnose.ENABLE)
|
||||
internal class DemoTemplateTest {
|
||||
|
||||
private val demoTemplate = DemoTemplate()
|
||||
|
@ -67,6 +67,8 @@ public class SourceClassHandler extends BaseClassHandler {
|
||||
instructions = replaceMemberCallOps(cn, mn, memberInjectMethodName, instructions,
|
||||
node.owner, node.getOpcode(), rangeStart, i);
|
||||
i = rangeStart;
|
||||
} else {
|
||||
LogUtil.warn("Potential missed mocking at %s:%s", mn.name, getLineNum(instructions, i));
|
||||
}
|
||||
} else if (ConstPool.CONSTRUCTOR.equals(node.name)) {
|
||||
// it's a new operation
|
||||
@ -119,28 +121,9 @@ public class SourceClassHandler extends BaseClassHandler {
|
||||
}
|
||||
|
||||
private int getMemberMethodStart(AbstractInsnNode[] instructions, int rangeEnd) {
|
||||
int stackLevel = ClassUtil.getParameterTypes(((MethodInsnNode)instructions[rangeEnd]).desc).size();
|
||||
int stackLevel = getInitialStackLevel((MethodInsnNode)instructions[rangeEnd]);
|
||||
for (int i = rangeEnd - 1; i >= 0; i--) {
|
||||
switch (instructions[i].getOpcode()) {
|
||||
case Opcodes.INVOKESPECIAL:
|
||||
case Opcodes.INVOKEVIRTUAL:
|
||||
case Opcodes.INVOKEINTERFACE:
|
||||
stackLevel += stackEffectOfInvocation(instructions[i]) + 1;
|
||||
if (((MethodInsnNode)instructions[i]).name.equals(ConstPool.CONSTRUCTOR)) {
|
||||
// constructor must be INVOKESPECIAL and implicitly pop 1 more stack
|
||||
stackLevel++;
|
||||
}
|
||||
break;
|
||||
case Opcodes.INVOKESTATIC:
|
||||
case Opcodes.INVOKEDYNAMIC:
|
||||
stackLevel += stackEffectOfInvocation(instructions[i]);
|
||||
break;
|
||||
case -1:
|
||||
// reach LineNumberNode or LabelNode
|
||||
return i + 1;
|
||||
default:
|
||||
stackLevel -= BytecodeUtil.stackEffect(instructions[i].getOpcode());
|
||||
}
|
||||
stackLevel += getStackLevelChange(instructions[i]);
|
||||
if (stackLevel < 0) {
|
||||
return i;
|
||||
}
|
||||
@ -148,6 +131,38 @@ public class SourceClassHandler extends BaseClassHandler {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int getInitialStackLevel(MethodInsnNode instruction) {
|
||||
int stackLevel = ClassUtil.getParameterTypes((instruction).desc).size();
|
||||
switch (instruction.getOpcode()) {
|
||||
case Opcodes.INVOKESPECIAL:
|
||||
case Opcodes.INVOKEVIRTUAL:
|
||||
case Opcodes.INVOKEINTERFACE:
|
||||
return stackLevel;
|
||||
case Opcodes.INVOKESTATIC:
|
||||
case Opcodes.INVOKEDYNAMIC:
|
||||
return stackLevel - 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private int getStackLevelChange(AbstractInsnNode instruction) {
|
||||
switch (instruction.getOpcode()) {
|
||||
case Opcodes.INVOKESPECIAL:
|
||||
case Opcodes.INVOKEVIRTUAL:
|
||||
case Opcodes.INVOKEINTERFACE:
|
||||
return stackEffectOfInvocation(instruction) + 1;
|
||||
case Opcodes.INVOKESTATIC:
|
||||
case Opcodes.INVOKEDYNAMIC:
|
||||
return stackEffectOfInvocation(instruction);
|
||||
case -1:
|
||||
// either LabelNode or LineNumberNode
|
||||
return 0;
|
||||
default:
|
||||
return -BytecodeUtil.stackEffect(instruction.getOpcode());
|
||||
}
|
||||
}
|
||||
|
||||
private int stackEffectOfInvocation(AbstractInsnNode instruction) {
|
||||
String desc = ((MethodInsnNode)instruction).desc;
|
||||
return ClassUtil.getParameterTypes(desc).size() - (ClassUtil.getReturnType(desc).isEmpty() ? 0 : 1);
|
||||
|
Loading…
Reference in New Issue
Block a user