A Cairo test generator based on the Branching Tree Technique.
APACHE-2.0 License
A Cairo test generator based on the Branching Tree Technique and bulloak.
cargo install poinciana --git https://github.com/ericnordelo/poinciana
The following VSCode extensions are not essential but they are recommended for a better user experience:
poinciana
implements two commands:
poinciana scaffold
poinciana check
(in the roadmap)Say you have a foo.tree
file with the following contents:
FooTest
└── When stuff is called // Comments are supported.
└── When a condition is met
└── It should revert.
└── Because we shouldn't allow it.
You can use poinciana scaffold
to generate a Cairo contract containing
modifiers and tests that match the spec described in foo.tree
. The following
will be printed to stdout
:
/// Generated by poinciana using BTT
fn when_stuff_is_called() {
// code
}
#[test]
fn test_panic_when_a_condition_is_met() {
when_stuff_is_called();
// It should revert.
// Because we shouldn't allow it.
panic!("NOT IMPLEMENTED");
}
ponciana scaffold
scaffolds Cairo test files based on .tree
specifications
that follow the
Branching Tree Technique. The tree parser implementation is currently coming from bulloak
, which is Solidity optimized. In the future we may update the tree syntax to better fit the Cairo ecosystem.
Currently, there is on-going discussion on how to handle different edge-cases to better empower the Solidity community. This section is a description of the current implementation of the bulloak compiler.
when/given
branches of a tree.it
branches of a tree.Each tree
file should describe at least one function under test. Trees follow
these rules:
poinciana
expects you to use ├
and └
characters to denote branches.when
or given
, it is a condition.
when
and given
are interchangeable.it
, it is an action.
it
is the same as It
and IT
.//
is a comment and will be stripped from theTake the following Cairo function:
fn hash_pair(a: u256, b: u256) -> u256 {
if (a < b) {
hash(a, b)
} else {
hash(b, a)
}
}
A reasonable spec for the above function would be:
HashPairTest
├── It should never revert.
├── When first arg is smaller than second arg
│ └── It should match the result of `hash(a, b)`.
└── When first arg is bigger than second arg
└── It should match the result of `hash(b, a)`.
There is a top-level action that will generate a test to check the function invariant that it should never revert.
Then, we have the two possible preconditions: a < b
and a >= b
. Both
branches end in an action that will make poinciana scaffold
generate the
respective test.
Note the following things:
poinciana
alsoSuppose you have additional Cairo functions that you want to test in the same
test contract, say Utils
within utils.t.cairo
:
fn min(a: u256, b: u256) -> u256 {
if a < b { a } else { b }
}
fn max(a: u256, b: u256) -> u256 {
if a > b { a } else { b }
}
The full spec for all the above functions would be:
Utils::hash_pair
├── It should never revert.
├── When first arg is smaller than second arg
│ └── It should match the result of `hash(a, b)`.
└── When first arg is bigger than second arg
└── It should match the result of `hash(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`.
Note the following things:
poinciana
to error.test_min_should_never_revert
).This project is licensed under either of: