3.2.1 • Published 6 months ago

@morenabarboni/sumo v3.2.1

Weekly downloads
-
License
-
Repository
github
Last release
6 months ago

SuMo

SuMo is a mutation testing tool for Solidity Smart Contracts.

SuMo was designed to run mutation testing on Solidity projects in a NodeJS environment. It can run test using Hardhat, Brownie and Forge, hybrid test suites, and custom test scripts.

Table of Contents

Installation

To install sumo run npm install @morenabarboni/sumo

Configuration āš™ļø

Before using SuMo you must specify your desired configuration in a sumo-config.js in the root directory of your project. The sumo-config.js is automatically generated when SuMo is installed.

Here's a simple example of sumo-config.js:

module.exports = {  
      buildDir: "auto",                               //build directory of the SUT (auto detect)
      contractsDir: "auto",                           //contract directory of the SUT (auto detect)
      testDir: "auto",                                //test directory of the SUT (auto detect)
      skipContracts: ["interfaces", "mock", "test"],  // Relative paths from contractsDir
      skipTests: [],                                  // Relative paths from testsDir
      testingFramework: "auto",                      //testing framework (auto detect)
      minimalOperators: false,                       // use minimal mutation rules
      randomSampling: false,                         //use random mutant sampling
      randomMutants: 100,                            //if random sampling is enabled, generate 100 mutants max
      testingTimeOutInSec: 500                       //testing time-out for a mutant
}

1) SUT directories

SuMo will try to automatically find your project directories based on standard naming conventions (e.g., /contracts and /test). These can be overriden in the sumo-config.js file. Contracts and test files/folders to be ignored by SuMo can be specified as well.

FieldDescriptionDefault Value
contractsDirrelative path to the directory of the contracts to be mutatedauto
testDirrelative path to the directory of the tests to be evaluatedauto
buildDirrelative path to the directory of the compilation artifactsauto
skipContractsblacklist of relative paths to contract files (or folders)["interfaces", "mock", "test"]
skipTestsblacklist of relative paths to test files (or folders)[]

2) Testing Frameworks Configuration šŸ”—

These fields allow to customize the testing frameworks used by SuMo.

By default, testingFramework is set to auto: SuMo will automatically select the testing framework(s) to be used based on the configuration files (e.g., foundry.toml) present in your workspace. If multiple configuration files are found, SuMo will try to run a hybrid testing process.

FieldDescriptionAvailable OptionsDefault Value
testingFrameworkthe testing framework to be used for compiling and testing the smart contractsauto, brownie, forge, hardhat, customauto

Brownie

When choosing brownie:

  • SuMo will rely on a local/global brownie installation;
  • The smart contracts will be compiled with a minimal compile command (e.g., brownie compile );
  • The smart contracts will be tested with a minimal test command and (optionally) by a list of test files to be executed (e.g., brownie test ...testFiles --exitfirst) .

Forge

When choosing forge :

  • SuMo will rely on the global installation of foundry;
  • The smart contracts will be compiled with a minimal compile command (e.g., forge build);
  • The smart contracts will be tested with a minimal test command and (optionally) by a list of test files to be executed (e.g., forge test ...testFiles --fail-fast).
  • Make sure that your forge installation is up-to-date to enable --fail-fast.

Custom

If you set testingFramework to custom, SuMo will invoke the compile and test script defined in your package.json. This allows you to customize both scripts and have more control over the testing process. For example, you can define the scripts as follows:

//package.json
 scripts: {
    compile: "hardhat compile",
    test "hardhat test --bail && forge test --fail-fast"
 }

//sumo-config.js
 project: {
    buildDir: "artifacts",
 }

Additionally, you must also explicitly define a buildDir (matching your compile command) in your sumo-config.js.

āš ļø Limitations of Custom Test Scripts:
     * buildDir: must be explicitly specified it in the sumo-config.js
     * skipTests: will be ignored. You have to specify them in your custom script.

3) Mutation Testing Process Configuration

These fields allow you to further customize the mutation testing process:

FieldDescriptionDefault Value
minimalOperatorsuse minimal mutation rulesfalse
randomSamplinguse Random Mutant Samplingfalse
randomMutantsthe maximum number of mutants to be tested (only if randomSampling is enabled)100
testingTimeOutInSecseconds after which a mutant is marked as timed-out during testing500

CLI Usage

Selecting the Mutation Operators

Before starting the mutation process you can choose which mutation operators to use:

CommandDescriptionUsageExample
listShows the enabled mutation operators.npx/yarn sumo list$ npx sumo list
enableEnables one or more mutation operators. If no operator IDs are specified, all of them are enabled.npx/yarn sumo enable [...ID]$ npx sumo enable $ npx sumo enable AOR BOR
disableDisables one or more mutation operators. If no operator IDs are specified, all of them are disabled.npx/yarn sumo disable [...ID]$ npx sumo disable $ npx sumo disable FVR

Viewing the available mutations

CommandDescriptionUsageExample
lookupGenerates the mutations and creates reports without starting mutation testing.npx/yarn sumo lookup$ npx sumo lookup
mutateGenerates the mutations and saves a copy of each .sol mutant to to ./sumo/mutants.npx/yarn sumo mutate$ npx sumo mutate

Running Mutation Testing

CommandDescriptionUsageExample
pretestRuns the test suite on the original smart contracts to check if all tests pass and can be successfully evaluated. Pretest is automatically run when sumo test is executed.npx/yarn sumo pretest$ npx sumo pretest
testStarts the mutation testing process. You can also choose a single mutant / an interval of mutants to be tested by sepcifying <startHash> and (optionally) <endHash>.npx/yarn sumo test <startHash> <endHash>$ npx sumo test $ npx sumo test mbc5e8f56 mbg5t86o6
restoreRestores the SUT files to a clean version. This should be executed if you suddenly interrupt the mutation process. Note that the restore command overwrites your codebase with the files stored in the sumo/baseline folder. If you need to restore the project files, make sure to do so before performing other operations as the baseline is automatically refreshed on subsequent preflight or test runs.$ npx/yarn sumo restore$ npx sumo restore

Viewing the results

SuMo automatically creates a sumo\results folder in the root directory of the project with the following reports:

  • mutations.json: List of mutations in json format, synchronoysly updated during testing.
  • index.html: A simple web display of the results (you can view this using VSCode extensions like Live Server). From here, you can also download a csv with the results.
  • \mutants: Folder with mutated .sol source files (only if generated with sumo mutate)

Mutation Operators šŸ‘¾

SuMo includes the following Traditional and Solidity-specific operators. Note that not all mutation operators are enabled by default.

Traditional Mutation Operators

OperatorNameMutation ExampleEnabled by DefaultMinimal Available
ACMArgument Change of overloaded Method calloverloadedFunc(a,b); → overloadedFunc(a,b,c);YN
AORAssignment Operator Replacement+= → =YN
BCRDBreak and Continue Replacement and Deletionbreak → continue → breakYN
BLRBoolean Literal Replacementtrue → falseYN
BORBinary Operator Replacement+ → - < → >=YY
CBDCatch Block Deletioncatch{} → YN
CSCConditional Statement Changeif(condition) → if(false) else{} → YN
EREnum Replacemetenum.member1 → enum.member2YY
ECSExplicit Conversion to Smaller typeuint256 → uint8YN
FCDFunction Call Deletionfoo() → YN
HLRHexadecimal Literal Replacementhex\"01\" → hex\"random\"YN
ILRInteger Literal Replacement1 → 0YN
LCSLoop Statement Changewhile(condition) → while(false)YN
OLFDOverloaded Function Deletionfunction overloadedF(){} → YN
ORFDOverridden Function Deletionfunction f() override {} → YN
SKRSuper Keyword Replacementx = getData() → x = super.getData()YN
SLRString Literal Replacement"string" → ""YN
UORDUnary Operator Replacement and Deletion++ → -- ! → YY

Solidity Mutation Operators

OperatorNameMutation ExampleEnabled by DefaultMinimal version available
AVRAddress Value Replacement0x67ED2e5dD3d0... → address.this()YY
CCDContract Constructor Deletionconstructor(){} → YN
DLRData Location Keyword Replacementmemory → storageNN
DODDelete Operator Deletiondelete →YN
ETREther Transfer function Replacementdelegatecall() → call()YY
EEDEvent Emission Deletionemit Deposit(...) → /*emit Deposit(...)*/YN
EHDException Handling Deletionrequire(...) → /*require(...)*/YN
FVRFunction Visibility Replacementfunction f() public → function f() privateNY
GVRGlobal Variable Replacementmsg.value() → tx.gasprice()YY
MCRMathematical and Cryptographic function Replacementaddmod → mulmod keccak256 → basha256YY
MODModifier Deletionfunction f() onlyOwner → function f()YY
MOIModifier Insertionfunction f() → function f() onlyOwnerNY
OMDOverridden Modifier Deletionmodifier m() override {} → YN
PKDPayable Keyword Deletionfunction f() payable → function f()YN
RSDReturn Statement Deletionreturn amount; → //return amount;YN
RVSReturn Values Swapreturn (1, "msg", 100); → return (100, "msg", 1);YY
SCDSelfdestruct Call Deletionselfdestruct(); → //selfdestruct();YN
SFRSafeMath Function ReplacementSafeMath.add → SafeMath.subYY
SCECSwitch Call Expression CastingContract c = Contract(0x86C9...); → Contract c = Contract(0x67ED...);YN
TORTransaction Origin Replacementmsg.sender → tx.originYN
VURVariable Unit Replacementwei → ether minutes → hoursYY
VVRVariable Visibility Replacementuint private data; → uint public data;NY

Minimal Mutation Rules

Some mutation operators foresee a minimal version:

  • The extended operators generate a more comprehensive set of mutants. These guarantee a more in-depth test adequacy assessment, but they can generate more than one replacement per target (e.g., + is mutated in both - and *), which can lead to longer execution times.
  • The minimal operators define simplified rules that only inject one replacement per target (e.g., + is mutated in -), limiting the generation of subsumed mutants and speeding up the testing process.

By default, SuMo employs the extended operators. However, you can enable the minimal rules in the sumo-config.js file.

Publications

To cite SuMo, please use the following:

@article{BARBONI2022111445,
  title = {SuMo: A mutation testing approach and tool for the Ethereum blockchain},
  journal = {Journal of Systems and Software},
  volume = {193},
  pages = {111445},
  year = {2022},
  issn = {0164-1212},
  doi = {https://doi.org/10.1016/j.jss.2022.111445},
  author = {Morena Barboni and Andrea Morichetta and Andrea Polini}
}
3.2.1

6 months ago

3.2.0

7 months ago

3.1.1

7 months ago

3.0.1

7 months ago

3.0.0

7 months ago

2.3.0

2 years ago

2.2.1

2 years ago

2.4.1

2 years ago

2.4.0

2 years ago

2.4.3

2 years ago

2.4.2

2 years ago

2.5.4

2 years ago

2.4.4

2 years ago

2.1.1

2 years ago

2.1.0

2 years ago

1.2.0

2 years ago

1.1.1

3 years ago

2.0.0

2 years ago

1.2.1

2 years ago

1.1.2

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago

0.0.1

3 years ago