neo-vm icon indicating copy to clipboard operation
neo-vm copied to clipboard

Create a better unittest for NEOVM

Open lightszero opened this issue 5 years ago • 9 comments

NeoVM's unittest is hard to use for now.

how about we use a ASM like string to define the NEF, like


{
    PUSH 1//push 1 number
    PUSH 2
    CALL method1
    RET;
}
method1:
{
label1:
    ADD
88:
    RET
}

lightszero avatar Feb 20 '20 03:02 lightszero

It's good but this will be inside the json format (as script), or you have plans for other format?

shargon avatar Feb 20 '20 08:02 shargon

首先我们定义一个文本形式的汇编语言 因为定位为UnitTest中使用的脚本,我们就在这里讨论?还是要写一个标准

@erikzhang @shargon @Tommo-L

一、这是一个例子

这种脚本定位为UnitTest中方便测试使用,需要编译迅速,所以仅仅是简单的NEF OPCODE加入了标签,从第一条指令开始执行

    PUSH 1//push 1 number
debugpoint1:
    PUSH 2
    CALL method1
    RET;
method1:
{
    ADD
    JMP ”88“
88:
    RET
}

二、使用方法

byte[] nef = FromTestScript(asmstr); 在Unittest中封装一个这样的方法,实现转换过程,主要用于方便实现NEOMV的UnitTest

在执行时增加一个ExecuteToLabel的功能 比如ExecuteToLabel("debugpoint1"); 可以直接让UnitTest执行到标签对应的地址,这样检查起来方便多了。

后续可以集成到json之类

三、脚本规则

1.注释类符号

允许 //注释大括号{}封号;,他们不产生任何作用,仅为了便于约定 //注释 双斜杠之后到换行所有的内容都为注释 大括号{} 只是为了把代码括起来容易看,不做语法检查 封号; 只是为了防止c#程序员手误,额外兼容,不做语法检查

2.元素

除注释符号外允许空行,另外只有两种元素:指令和标签行,指令和标签都不允许换行书写。 但指令和标签前后都允许各种注释类符号和空格。

3.标签

标签独占一行,可以是数字与字符串下划线的组合,以 冒号: 结尾 标签名称不得重复,允许出现两个连续的标签行 标签名称不区分大小写

4.指令

所有指令都是OPCODE 加空格 加 参数的格式,无参数留空,参数 支持 数字 数组 字符串 比如 PUSHINT16 [0x02,0x55] PUSHINT16 21762 PUSHINT16 "AA" 指令名称不区分大小写

5.虚拟指令PUSH

5.增加一条虚拟指令 PUSH,PUSH根据参数自动生成对应的指令 比如上边的PUSHINT16 直接用PUSH “AA"就可以

6.有跳转地址的指令

所有含有地址跳转的指令JMP CALL以及PUSHA,都改为使用标签的名字做为参数,生成NEF时自动处理 比如,有多个地址的逗号隔开 JMP ”88“ CALL method01 TRY offsetcatch,offsetfinal

7.可以不写_L指令

比如JMP_L, 在脚本里写JMP_L可以,在脚本里写JMP也可以,如果跳转地址很远,生成NEF时自动会处理为JMP_L

lightszero avatar Feb 22 '20 10:02 lightszero

PUSHINT16 [0x02,0x55] it's the same as PUSHINT16 0x0255 ?

The asm format that you define seems good, but it is not clear to me how you want to integrate it with the unit tests.

shargon avatar Feb 22 '20 11:02 shargon

same as PUSHINT16 0x5502 same as PUSHINT16 21762

first ,we could write c# unittest code for it

  string asm=@"
 xxx
x
xxx
abcd:
xx
xx
aaa:
 "
byte[] nef = FromTestScript(asmstr);
var addr = FrormTestScript(asmstr,"abcd");
TestEngine.ExecuteTo("abcd");
//add testcode
TestEngine.ExecuteTo("aaa");
//add testcode

and nextstep,we can add a unittest txt file for test,

[TESTSCRIPT]
 xxx
x
xxx
abcd:
xx
xx
aaa:

[TESTSTEP]
ExecuteTo:"abcd"
Assert:Evalstack.xxx=xxx
...
..

[TESTSTEP]
ExecuteTo:"aaa"
...
...
..
[TESTSTEP]
...
..

lightszero avatar Feb 22 '20 11:02 lightszero

ExecuteTo:"abcd" i like it, "abcd" it's a label, right?

shargon avatar Feb 22 '20 11:02 shargon

yes

lightszero avatar Feb 22 '20 11:02 lightszero

I agree that we may need a more simply and clear script format.

Tommo-L avatar Feb 22 '20 14:02 Tommo-L

What do you think about something like this:

{
    PUSH 1//push 1 number
    PUSH 2
    CALL method1
    RET;
}
method1:
{
label1:
    [Assert:CurrentContext.EvaluationStack.Count=1]
    [Assert:CurrentContext.EvaluationStack[0]={Integer:123}]
    ADD
88:
    RET
}

shargon avatar Feb 25 '20 17:02 shargon

What do you think about something like this:

It would be nice if the JSON tests actually remains usable for other implementations as well. The above (specifically):

Assert:CurrentContext.EvaluationStack[0].Count

Seems to be a direct mapping to the C# implementation which form a portability standpoint is bad.

I like some of the suggestions by @lightszero

  1. Instructions format <OpCode> <Parameters> 👍 . Allowing non-json standard features like comments (//) 👎 (breaks some strict JSON parsers) , allowing inconsistent instruction naming (=case-insensitive) and inconsistent parameter descriptions (= e.g. appending or not appending "0x" to bytearrays) 👎
  2. Not a big fan of the virtual instruction. I believe tests should be explicit instead of implicit which this feature creates.
  3. ExecuteToLabel ("debugpoint1"); Good idea. There might be some things lost in Google translate, but how about adding a breakPoints key to the step instead? e.g.
 "steps": [
    {
        "breakPoints": 
        {
            "add": [123, 111], // array of IP location
            "remove": []
        },
        "actions": [
            "execute"
        ],
        "result": {
        ...
        }
    }
    { /* step 2 */}
]

That should be able to get the same results no?

I could not follow the translation of the JMP(_L) instructions 🤷‍♂

ixje avatar Mar 17 '20 08:03 ixje