// SPDX-License-Identifier: MITpragmasolidity ^0.8.18;contract Bit {// Find most significant bit using binary searchfunctionmostSignificantBit(uint256 x)externalpurereturns (uint256 msb) {// x >= 2 ** 128if (x >=0x100000000000000000000000000000000) { x >>=128; msb +=128; }// x >= 2 ** 64if (x >=0x10000000000000000) { x >>=64; msb +=64; }// x >= 2 ** 32if (x >=0x100000000) { x >>=32; msb +=32; }// x >= 2 ** 16if (x >=0x10000) { x >>=16; msb +=16; }// x >= 2 ** 8if (x >=0x100) { x >>=8; msb +=8; }// x >= 2 ** 4if (x >=0x10) { x >>=4; msb +=4; }// x >= 2 ** 2if (x >=0x4) { x >>=2; msb +=2; }// x >= 2 ** 1if (x >=0x2) msb +=1; }}
Test file:
// SPDX-License-Identifier: MITpragmasolidity ^0.8.18;import"forge-std/Test.sol";import {Bit} from"../src/Bit.sol";// forge test --match-path test/Fuzz.t.sol// Topics// - fuzz// - assume and bound// - stats// (runs: 256, μ: 18301, ~: 10819)// runs - number of tests// μ - mean gas used// ~ - median gas usedcontractFuzzTestisTest { Bit public b;functionsetUp() public { b =newBit(); }functionmostSignificantBit(uint256 x) privatepurereturns (uint256) {uint256 i =0;while ((x >>=1) >0) { i++; }return i; }functiontestMostSignificantBitManual() public {assertEq(b.mostSignificantBit(0),0);assertEq(b.mostSignificantBit(1),0);assertEq(b.mostSignificantBit(2),1);assertEq(b.mostSignificantBit(4),2);assertEq(b.mostSignificantBit(8),3);assertEq(b.mostSignificantBit(type(uint256).max),255); }functiontestMostSignificantBitFuzz(uint256 x) public {// assume - If false, the fuzzer will discard the current fuzz inputs// and start a new fuzz run// Skip x = 0// vm.assume(x > 0);// assertGt(x, 0);// bound(input, min, max) - bound input between min and max// Bound x =bound(x,1,10);// assertGe(x, 1);// assertLe(x, 10);uint256 i = b.mostSignificantBit(x);assertEq(i,mostSignificantBit(x)); }}
What is fuzzing?
Fuzzing basically means generating a bunch of random inputs and feeding them into a function. If the input violates the assertion, then we just found a bug.
A usual test case in a Foundry test does not have input parameter. If there is input parameter, then Foundry will consider it as a fuzzing scenario. For example:
Back to the Bit contract. If we want to limit the value of x in a certain range, we can use vm.assume(). This cheatcode is going to set a constrait on x generated by the fuzzer: if the generated input does not satisfy the condition, fuzzer is going to drop it and start another round of fuzzing. For example:
vm.assume(x >0);
If x <= 0 then it will be dropped.
Another way of setting a range for x is using bound(). It sets lower bound and upper bound for x:
x =bound(x,1,10);
In the end we would feed x into the function and test its result:
uint256 i = b.mostSignificantBit(x);assertEq(i,mostSignificantBit(x));