20个不稳定版本 (4个重大更改)
0.8.0 | 2024年7月27日 |
---|---|
0.7.2 | 2024年6月10日 |
0.7.0 | 2024年2月25日 |
0.6.1 | 2023年12月9日 |
0.5.4 | 2023年11月16日 |
#1183 in 魔法豆
每月下载量287次
290KB
5.5K SLoC
bulloak
基于分支树技术的Solidity测试生成器。
[!WARNING] 注意,
bulloak
仍处于0.*.*
版本,因此可能随时发生破坏性更改。如果您必须依赖bulloak
,我们建议将其锁定到特定版本,即=0.y.z
。
安装
cargo install bulloak
VSCode
以下VSCode扩展不是必需的,但它们可以提供更好的用户体验
- Solidity Inspector - 为
.tree
文件提供语法高亮显示 - Ascii Tree Generator:方便生成ASCII树
使用方法
bulloak
实现了两个命令
bulloak scaffold
bulloak check
生成Solidity文件
假设您有一个包含以下内容的foo.tree
文件
FooTest
└── When stuff is called // Comments are supported.
└── When a condition is met
└── It should revert.
└── Because we shouldn't allow it.
您可以使用bulloak scaffold
生成一个Solidity合约,其中包含与foo.tree
中描述的规范相匹配的修饰符和测试。以下内容将打印到stdout
// $ bulloak scaffold foo.tree
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.0;
contract FooTest {
modifier whenStuffIsCalled() {
_;
}
function test_RevertWhen_AConditionIsMet() external whenStuffIsCalled {
// It should revert.
// Because we shouldn't allow it.
}
}
您可以使用-w
选项将生成的合约写入文件系统。假设我们在当前工作目录中有一堆.tree
文件。如果我们运行以下
$ bulloak scaffold -w ./**/*.tree
bulloak
将为每个 .tree
文件创建一个 .t.sol
文件,并将生成的内容写入其中。
如果一个 .t.sol
文件的标题与同一目录中的 .tree
匹配,那么 bulloak
将跳过写入该文件。但是,您可以使用 -f
标志来覆盖此行为。这将强制 bulloak
覆盖文件的内容。
$ bulloak scaffold -wf ./**/*.tree
请注意,当测试体为空时,所有测试都显示为通过。为防止这种情况,您可以使用 -S
(或 --vm-skip
)选项在每个测试函数的开始处添加一个 vm.skip(true);
。此选项还将添加对 forge-std 的 Test.sol
的导入,并且所有测试合约都将继承它。
您可以通过传递 -m
(或 --skip--modifiers
)选项来跳过修改符的生成。这样,生成的文件将只包含测试函数。
检查您的代码和规范是否匹配
您可以使用 bulloak check
确保您的 Solidity 文件与规范匹配。例如,任何缺失的测试都会向您报告。
如果您有如下规范
HashPairTest
├── It should never revert.
├── When first arg is smaller than second arg
│ └── It should match the result of `keccak256(abi.encodePacked(a,b))`.
└── When first arg is bigger than second arg
└── It should match the result of `keccak256(abi.encodePacked(b,a))`.
以及相应的 Solidity 文件
pragma solidity 0.8.0;
contract HashPairTest {
function test_ShouldNeverRevert() external {
// It should never revert.
}
function test_WhenFirstArgIsSmallerThanSecondArg() external {
// It should match the result of `keccak256(abi.encodePacked(a,b))`.
}
}
此 Solidity 文件缺少分支 When first arg is bigger than second arg
的测试,这将运行 bulloak check tests/scaffold/basic.tree
后报告。
warn: function "test_WhenFirstArgIsBiggerThanSecondArg" is missing in .sol
+ fix: run `bulloak check --fix tests/scaffold/basic.tree`
--> tests/scaffold/basic.tree:5
warn: 1 check failed (run `bulloak check --fix <.tree files>` to apply 1 fix)
如上所述,bulloak
可以自动修复此问题。如果我们使用带有 --stdout
标志的命令运行,输出将是
--> tests/scaffold/basic.t.sol
pragma solidity 0.8.0;
contract HashPairTest {
function test_ShouldNeverRevert() external {
// It should never revert.
}
function test_WhenFirstArgIsSmallerThanSecondArg() external {
// It should match the result of `keccak256(abi.encodePacked(a,b))`.
}
function test_WhenFirstArgIsBiggerThanSecondArg() external {
// It should match the result of `keccak256(abi.encodePacked(b,a))`.
}
}
<--
success: 1 issue fixed.
不带 --stdout
标志运行命令将使用修复后的内容覆盖 Solidity 文件的内容。请注意,并非所有问题都可以自动修复,bulloak 的输出将反映这一点。
warn: 13 checks failed (run `bulloak check --fix <.tree files>` to apply 11 fixes)
您可以通过传递 -m
(或 --skip--modifiers
)选项来跳过检查修改符的存在。这样,bulloak
将不会警告生成文件中缺少修改符。
规则
以下规则目前正在实施
- 匹配的规范文件必须存在且可读。
- 规范和 Solidity 文件匹配,如果它们的名字之间的区别只是
.tree
&.t.sol
。
- 规范和 Solidity 文件匹配,如果它们的名字之间的区别只是
- Solidity 文件中有一个合同,其名称与规范根节点匹配。
- 每个构造,如由
bulloak scaffold
生成,都必须存在于 Solidity 文件中。 - 每个构造的顺序,如由
bulloak scaffold
生成,必须与规范顺序匹配。- 允许任何有效的 Solidity 构造,并且只检查由
bulloak scaffold
生成的构造。这意味着可以添加任意数量的额外函数、修改符等。到文件中。
- 允许任何有效的 Solidity 构造,并且只检查由
编译器错误
bulloak
的另一个功能是报告输入树中的错误。
例如,假设您有一个存在错误的 foo.tree
文件,它缺少一个 └
字符。运行 bulloak scaffold foo.tree
会报告如下错误
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
bulloak error: unexpected `when` keyword
── when the id references a null stream
^^^^
--- (line 2, column 4) ---
file: foo.tree
树
bulloak scaffold
根据遵循 分支树技术 的 .tree
规范构建 Solidity 测试文件。
目前,有关如何处理不同边缘情况以更好地赋能 Solidity 社区的讨论正在进行中。本节是关于编译器当前实现的描述。
术语
- 条件:树的
when/given
分支。 - 动作:树的
it
分支。 - 动作描述:动作的子节点。
规范
每个 tree
文件应描述至少一个待测试函数。树遵循以下规则
- 第一行是根树标识符,由合约和函数名组成,应以双冒号分隔。
bulloak
预期您使用├
和└
字符来表示分支。- 如果一个分支以
when
或given
开头,则它是一个条件。when
和given
可以互换。
- 如果一个分支以
it
开头,则它是一个动作。- 动作的任何子分支都称为动作描述。
- 关键字不区分大小写:
it
与It
和IT
相同。 - 以
//
开头的任何内容都是注释,并将从输出中删除。 - 可以在同一文件中定义多个树,以描述不同的函数,按照相同的规则进行,用两个新行分隔。
以下是一个 Solidity 函数
function hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? hash(a, b) : hash(b, a);
}
上述函数的合理规范可能是
HashPairTest
├── It should never revert.
├── When first arg is smaller than second arg
│ └── It should match the result of `keccak256(abi.encodePacked(a,b))`.
└── When first arg is bigger than second arg
└── It should match the result of `keccak256(abi.encodePacked(b,a))`.
存在一个顶层动作,它将生成一个测试来检查函数不变量,即它永远不会回滚。
然后,我们有两种可能的先决条件:a < b
和 a >= b
。两个分支都以一个动作结束,该动作将使 bulloak scaffold
生成相应的测试。
请注意以下几点
- 动作以句点结尾,但条件不是。这是因为动作支持任何字符,但条件不支持。由于条件被转换为修饰符,它们必须是有效的 Solidity 标识符。
- 可以没有条件地进行顶层动作。目前,
bulloak
也支持具有兄弟条件的动作,但根据此 讨论,这可能会在未来版本中删除。 - 树的根将被输出为测试合约的名称。
假设您有其他您想在同一测试合约中测试的 Solidity 函数,例如 Utils
在 utils.t.sol
中的 Utils
function min(uint256 a, uint256 b) private pure returns (uint256) {
return a < b ? a : b;
}
function max(uint256 a, uint256 b) private pure returns (uint256) {
return a > b ? a : b;
}
上述所有函数的完整规范可能是
Utils::hashPair
├── It should never revert.
├── When first arg is smaller than second arg
│ └── It should match the result of `keccak256(abi.encodePacked(a,b))`.
└── When first arg is bigger than second arg
└── It should match the result of `keccak256(abi.encodePacked(b,a))`.
Utils::min
├── It should never revert.
├── When first arg is smaller than second arg
│ └── It should match the value of `a`.
└── When first arg is bigger than second arg
└── It should match the value of `b`.
Utils::max
├── It should never revert.
├── When first arg is smaller than second arg
│ └── It should match the value of `b`.
└── When first arg is bigger than second arg
└── It should match the value of `a`.
请注意以下几点
- 合约标识符必须在所有根中存在。
- 如果后续树中缺少合约标识符,或与第一个树的根标识符不匹配,则会导致
bulloak
错误。这种违规行为目前无法通过bulloak check --fix
进行修复,因此需要手动更正。 - 在转换为Solidity修饰符时,不同树之间的重复条件将被去重。
- 每个树的根标识符的部分函数将被作为Solidity测试名称的一部分发出(例如,
test_MinShouldNeverRevert
)。
输出
关于生成的Solidity测试,有以下几点需要注意
- 合约文件名与
.tree
相同,但文件扩展名为.t.sol
。例如,test.tree
对应于test.t.sol
。 - 测试的发出顺序与它们在
.tree
文件中对应动作出现的顺序相同。 - 除叶子条件节点外,我们为每个条件生成一个修饰符。
- 测试名称遵循Foundry的最佳实践。
许可
本项目许可协议为以下之一
- Apache License,版本2.0,(LICENSE-APACHE或https://apache.ac.cn/licenses/LICENSE-2.0)。
- MIT许可协议(LICENSE-MIT或https://opensource.org/licenses/MIT)。
依赖项
~27–44MB
~735K SLoC