|
| 1 | +""" |
| 2 | +@custom:ape-fuzzer-max-examples 100 |
| 3 | +@custom:ape-stateful-step-count 50 |
| 4 | +@custom:ape-stateful-bundles a b c |
| 5 | +""" |
| 6 | + |
| 7 | +secret: public(uint256) |
| 8 | + |
| 9 | + |
| 10 | +@external |
| 11 | +def setUp(): |
| 12 | + self.secret = 703895692105206524502680346056234 |
| 13 | + |
| 14 | + |
| 15 | +@external |
| 16 | +def initialize_bundleA() -> DynArray[uint256, 10]: |
| 17 | + """ |
| 18 | + @notice |
| 19 | + Add some initial values to a bundle. "Initializers" are called exactly once at the |
| 20 | + beginning of a test (before any `rule`s are called), but could be called in any order. |
| 21 | + They are mostly used to initialize "Bundles" with values for the rest of the test. |
| 22 | +
|
| 23 | + @dev |
| 24 | + Same as `@initializes` in Hypothesis, allowing to set up initial test state (incl Bundles). |
| 25 | + The return value is injected into the bundle specified by `@custom:test:stateful:targets`. |
| 26 | + A single return (1 value) or array return (multiple values) are supported for conveinence. |
| 27 | +
|
| 28 | + @custom:ape-stateful-targets a |
| 29 | + """ |
| 30 | + return [1, 2, 3, 5, 7, 11, 13, 17, 19, 23] |
| 31 | + |
| 32 | + |
| 33 | +@external |
| 34 | +def rule_add(a: uint256) -> uint256: |
| 35 | + """ |
| 36 | + @notice |
| 37 | + A rule is an action that MAY be called by the test harness as one step in the test. |
| 38 | +
|
| 39 | + Rules can also return Bundles values, which add more choices to the associated bundle. |
| 40 | +
|
| 41 | + @dev |
| 42 | + A rule is selected at random, and follows the same rules as normal tests with regard to arguments. |
| 43 | +
|
| 44 | + If you wish to avoid calling a rule except under a particular scenario, add a precondition. |
| 45 | +
|
| 46 | + @custom:ape-stateful-precondition self.secret() + a + b < 2**256 |
| 47 | + @custom:ape-stateful-targets b |
| 48 | + """ |
| 49 | + # NOTE: Due to precondition, will **never** fail |
| 50 | + self.secret += a |
| 51 | + |
| 52 | + return a % 100 |
| 53 | + |
| 54 | + |
| 55 | +@external |
| 56 | +def rule_subtract(a: DynArray[uint256, 10], b: uint256): |
| 57 | + """ |
| 58 | + @notice |
| 59 | + If a failure occurs when executing a rule, that will automatically raise a test failure. |
| 60 | +
|
| 61 | + This may indicate a legitimate bug in what you are testing, or a design flaw in your test. |
| 62 | +
|
| 63 | + @dev |
| 64 | + Each argument that has a name matching a bundle "pulls" values from the associated bundle. |
| 65 | + If `@custom:test:stateful:consumes` is present, then that value will instead be "consumed" |
| 66 | + by the rule, and therefore removed from the associated bundle (e.g. no longer available) |
| 67 | +
|
| 68 | + To pull multiple values from a bundle, use an array (selection size is chosen at random). |
| 69 | +
|
| 70 | + @custom:ape-stateful-consumes b |
| 71 | + """ |
| 72 | + # NOTE: This will likely fail after a few calls |
| 73 | + |
| 74 | + for val: uint256 in a: |
| 75 | + self.secret -= val % b |
| 76 | + |
| 77 | + |
| 78 | + |
| 79 | +@view |
| 80 | +@external |
| 81 | +def invariant_secret_not_found(): |
| 82 | + """ |
| 83 | + @notice |
| 84 | + An invariant is called after every rule invocation, to check consistency of internal state. |
| 85 | + If it fails, it will automatically raise a test failure, likely indicating a legitimate bug. |
| 86 | +
|
| 87 | + @dev |
| 88 | + An invariant **MUST** be `view`/`pure` mutability or it will be ignored. |
| 89 | + """ |
| 90 | + assert self.secret != 2378945823475283674509246524589 |
0 commit comments