aboutsummaryrefslogtreecommitdiffstats
path: root/packages/sol-tracing-utils/test
diff options
context:
space:
mode:
Diffstat (limited to 'packages/sol-tracing-utils/test')
-rw-r--r--packages/sol-tracing-utils/test/collect_coverage_entries_test.ts155
-rw-r--r--packages/sol-tracing-utils/test/fixtures/contracts/AllSolidityFeatures.sol413
-rw-r--r--packages/sol-tracing-utils/test/fixtures/contracts/SimpleStorage.sol11
-rw-r--r--packages/sol-tracing-utils/test/fixtures/contracts/Simplest.sol2
-rw-r--r--packages/sol-tracing-utils/test/fixtures/contracts/SolcovIgnore.sol22
-rw-r--r--packages/sol-tracing-utils/test/instructions_test.ts19
-rw-r--r--packages/sol-tracing-utils/test/sol_compiler_artifact_adapter_test.ts29
-rw-r--r--packages/sol-tracing-utils/test/source_maps_test.ts71
-rw-r--r--packages/sol-tracing-utils/test/trace_test.ts55
-rw-r--r--packages/sol-tracing-utils/test/utils_test.ts53
10 files changed, 830 insertions, 0 deletions
diff --git a/packages/sol-tracing-utils/test/collect_coverage_entries_test.ts b/packages/sol-tracing-utils/test/collect_coverage_entries_test.ts
new file mode 100644
index 000000000..7832ec316
--- /dev/null
+++ b/packages/sol-tracing-utils/test/collect_coverage_entries_test.ts
@@ -0,0 +1,155 @@
+import * as chai from 'chai';
+import * as fs from 'fs';
+import * as _ from 'lodash';
+import 'mocha';
+import * as path from 'path';
+
+import { collectCoverageEntries } from '../src/collect_coverage_entries';
+import { utils } from '../src/utils';
+
+const expect = chai.expect;
+
+describe('Collect coverage entries', () => {
+ describe('#collectCoverageEntries', () => {
+ it('correctly collects coverage entries for Simplest contract', () => {
+ const simplestContractBaseName = 'Simplest.sol';
+ const simplestContractFileName = path.resolve(__dirname, 'fixtures/contracts', simplestContractBaseName);
+ const simplestContract = fs.readFileSync(simplestContractFileName).toString();
+ const coverageEntries = collectCoverageEntries(simplestContract);
+ expect(coverageEntries.fnMap).to.be.deep.equal({});
+ expect(coverageEntries.branchMap).to.be.deep.equal({});
+ expect(coverageEntries.statementMap).to.be.deep.equal({});
+ expect(coverageEntries.modifiersStatementIds).to.be.deep.equal([]);
+ });
+ it('correctly collects coverage entries for SimpleStorage contract', () => {
+ const simpleStorageContractBaseName = 'SimpleStorage.sol';
+ const simpleStorageContractFileName = path.resolve(
+ __dirname,
+ 'fixtures/contracts',
+ simpleStorageContractBaseName,
+ );
+ const simpleStorageContract = fs.readFileSync(simpleStorageContractFileName).toString();
+ const coverageEntries = collectCoverageEntries(simpleStorageContract);
+ const fnIds = _.keys(coverageEntries.fnMap);
+ expect(coverageEntries.fnMap[fnIds[0]].name).to.be.equal('set');
+ // tslint:disable-next-line:custom-no-magic-numbers
+ expect(coverageEntries.fnMap[fnIds[0]].line).to.be.equal(5);
+ const setFunction = `function set(uint x) {
+ storedData = x;
+ }`;
+ expect(utils.getRange(simpleStorageContract, coverageEntries.fnMap[fnIds[0]].loc)).to.be.equal(setFunction);
+ expect(coverageEntries.fnMap[fnIds[1]].name).to.be.equal('get');
+ // tslint:disable-next-line:custom-no-magic-numbers
+ expect(coverageEntries.fnMap[fnIds[1]].line).to.be.equal(8);
+ const getFunction = `function get() constant returns (uint retVal) {
+ return storedData;
+ }`;
+ expect(utils.getRange(simpleStorageContract, coverageEntries.fnMap[fnIds[1]].loc)).to.be.equal(getFunction);
+ expect(coverageEntries.branchMap).to.be.deep.equal({});
+ const statementIds = _.keys(coverageEntries.statementMap);
+ expect(utils.getRange(simpleStorageContract, coverageEntries.statementMap[statementIds[1]])).to.be.equal(
+ 'storedData = x',
+ );
+ expect(utils.getRange(simpleStorageContract, coverageEntries.statementMap[statementIds[3]])).to.be.equal(
+ 'return storedData;',
+ );
+ expect(coverageEntries.modifiersStatementIds).to.be.deep.equal([]);
+ });
+ it('correctly collects coverage entries for AllSolidityFeatures contract', () => {
+ const simpleStorageContractBaseName = 'AllSolidityFeatures.sol';
+ const simpleStorageContractFileName = path.resolve(
+ __dirname,
+ 'fixtures/contracts',
+ simpleStorageContractBaseName,
+ );
+ const simpleStorageContract = fs.readFileSync(simpleStorageContractFileName).toString();
+ const coverageEntries = collectCoverageEntries(simpleStorageContract);
+ const fnDescriptions = _.values(coverageEntries.fnMap);
+ const fnNames = _.map(fnDescriptions, fnDescription => fnDescription.name);
+ const expectedFnNames = [
+ 'f',
+ 'c',
+ 'test',
+ 'getChoice',
+ 'Base',
+ 'Derived',
+ 'f',
+ 'f',
+ '',
+ 'g',
+ 'setData',
+ 'getData',
+ 'sendHalf',
+ 'insert',
+ 'remove',
+ 'contains',
+ 'iterate_start',
+ 'iterate_valid',
+ 'iterate_advance',
+ 'iterate_get',
+ 'insert',
+ 'sum',
+ 'restricted',
+ 'DualIndex',
+ 'set',
+ 'transfer_ownership',
+ 'lookup',
+ '',
+ '',
+ 'sum',
+ 'someFunction',
+ 'fun',
+ 'at',
+ 'test',
+ 'get',
+ 'returnNumber',
+ 'alloc',
+ 'ham',
+ 'getMyTuple',
+ 'ham',
+ 'abstain',
+ 'foobar',
+ 'foobar',
+ 'a',
+ ];
+ expect(fnNames).to.be.deep.equal(expectedFnNames);
+
+ const branchDescriptions = _.values(coverageEntries.branchMap);
+ const branchLines = _.map(branchDescriptions, branchDescription => branchDescription.line);
+ // tslint:disable-next-line:custom-no-magic-numbers
+ expect(branchLines).to.be.deep.equal([94, 115, 119, 130, 151, 187]);
+ const branchTypes = _.map(branchDescriptions, branchDescription => branchDescription.type);
+ expect(branchTypes).to.be.deep.equal(['if', 'if', 'if', 'if', 'binary-expr', 'if']);
+ });
+
+ it('correctly ignores all coverage entries for Ignore contract', () => {
+ const solcovIgnoreContractBaseName = 'SolcovIgnore.sol';
+ const solcovIgnoreContractFileName = path.resolve(
+ __dirname,
+ 'fixtures/contracts',
+ solcovIgnoreContractBaseName,
+ );
+ const solcovIgnoreContract = fs.readFileSync(solcovIgnoreContractFileName).toString();
+ const coverageEntries = collectCoverageEntries(solcovIgnoreContract);
+ const fnIds = _.keys(coverageEntries.fnMap);
+
+ expect(fnIds.length).to.be.equal(1);
+ expect(coverageEntries.fnMap[fnIds[0]].name).to.be.equal('set');
+ // tslint:disable-next-line:custom-no-magic-numbers
+ expect(coverageEntries.fnMap[fnIds[0]].line).to.be.equal(6);
+ const setFunction = `function set(uint x) public {
+ /* solcov ignore next */
+ storedData = x;
+ }`;
+ expect(utils.getRange(solcovIgnoreContract, coverageEntries.fnMap[fnIds[0]].loc)).to.be.equal(setFunction);
+
+ expect(coverageEntries.branchMap).to.be.deep.equal({});
+ const statementIds = _.keys(coverageEntries.statementMap);
+ expect(utils.getRange(solcovIgnoreContract, coverageEntries.statementMap[statementIds[0]])).to.be.equal(
+ setFunction,
+ );
+ expect(statementIds.length).to.be.equal(1);
+ expect(coverageEntries.modifiersStatementIds.length).to.be.equal(0);
+ });
+ });
+});
diff --git a/packages/sol-tracing-utils/test/fixtures/contracts/AllSolidityFeatures.sol b/packages/sol-tracing-utils/test/fixtures/contracts/AllSolidityFeatures.sol
new file mode 100644
index 000000000..21137347e
--- /dev/null
+++ b/packages/sol-tracing-utils/test/fixtures/contracts/AllSolidityFeatures.sol
@@ -0,0 +1,413 @@
+// Examples taken from the Solidity documentation online.
+
+// for pragma version numbers, see https://docs.npmjs.com/misc/semver#versions
+pragma solidity 0.4.0;
+pragma solidity ^0.4.0;
+
+import "SomeFile.sol";
+import "SomeFile.sol" as SomeOtherFile;
+import * as SomeSymbol from "AnotherFile.sol";
+import {symbol1 as alias, symbol2} from "File.sol";
+
+interface i {
+ function f();
+}
+
+contract c {
+ function c()
+ {
+ val1 = 1 wei; // 1
+ val2 = 1 szabo; // 1 * 10 ** 12
+ val3 = 1 finney; // 1 * 10 ** 15
+ val4 = 1 ether; // 1 * 10 ** 18
+ }
+ uint256 val1;
+ uint256 val2;
+ uint256 val3;
+ uint256 val4;
+}
+
+contract test {
+ enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
+
+ function test()
+ {
+ choices = ActionChoices.GoStraight;
+ }
+ function getChoice() returns (uint d)
+ {
+ d = uint256(choices);
+ }
+ ActionChoices choices;
+}
+
+contract Base {
+ function Base(uint i)
+ {
+ m_i = i;
+ }
+ uint public m_i;
+}
+contract Derived is Base(0) {
+ function Derived(uint i) Base(i) {}
+}
+
+contract C {
+ uint248 x; // 31 bytes: slot 0, offset 0
+ uint16 y; // 2 bytes: slot 1, offset 0 (does not fit in slot 0)
+ uint240 z; // 30 bytes: slot 1, offset 2 bytes
+ uint8 a; // 1 byte: slot 2, offset 0 bytes
+ struct S {
+ uint8 a; // 1 byte, slot +0, offset 0 bytes
+ uint256 b; // 32 bytes, slot +1, offset 0 bytes (does not fit)
+ }
+ S structData; // 2 slots, slot 3, offset 0 bytes (does not really apply)
+ uint8 alpha; // 1 byte, slot 4 (start new slot after struct)
+ uint16[3] beta; // 3*16 bytes, slots 5+6 (start new slot for array)
+ uint8 gamma; // 1 byte, slot 7 (start new slot after array)
+}
+
+contract test {
+ function f(uint x, uint y) returns (uint z) {
+ var c = x + 3;
+ var b = 7 + (c * (8 - 7)) - x;
+ return -(-b | 0);
+ }
+}
+
+contract test {
+ function f(uint x, uint y) returns (uint z) {
+ return 10;
+ }
+}
+
+contract c {
+ function () returns (uint) { return g(8); }
+ function g(uint pos) internal returns (uint) { setData(pos, 8); return getData(pos); }
+ function setData(uint pos, uint value) internal { data[pos] = value; }
+ function getData(uint pos) internal { return data[pos]; }
+ mapping(uint => uint) data;
+}
+
+contract Sharer {
+ function sendHalf(address addr) returns (uint balance) {
+ if (!addr.send(msg.value/2))
+ throw; // also reverts the transfer to Sharer
+ return address(this).balance;
+ }
+}
+
+/// @dev Models a modifiable and iterable set of uint values.
+library IntegerSet
+{
+ struct data
+ {
+ /// Mapping item => index (or zero if not present)
+ mapping(uint => uint) index;
+ /// Items by index (index 0 is invalid), items with index[item] == 0 are invalid.
+ uint[] items;
+ /// Number of stored items.
+ uint size;
+ }
+ function insert(data storage self, uint value) returns (bool alreadyPresent)
+ {
+ uint index = self.index[value];
+ if (index > 0)
+ return true;
+ else
+ {
+ if (self.items.length == 0) self.items.length = 1;
+ index = self.items.length++;
+ self.items[index] = value;
+ self.index[value] = index;
+ self.size++;
+ return false;
+ }
+ }
+ function remove(data storage self, uint value) returns (bool success)
+ {
+ uint index = self.index[value];
+ if (index == 0)
+ return false;
+ delete self.index[value];
+ delete self.items[index];
+ self.size --;
+ }
+ function contains(data storage self, uint value) returns (bool)
+ {
+ return self.index[value] > 0;
+ }
+ function iterate_start(data storage self) returns (uint index)
+ {
+ return iterate_advance(self, 0);
+ }
+ function iterate_valid(data storage self, uint index) returns (bool)
+ {
+ return index < self.items.length;
+ }
+ function iterate_advance(data storage self, uint index) returns (uint r_index)
+ {
+ index++;
+ while (iterate_valid(self, index) && self.index[self.items[index]] == index)
+ index++;
+ return index;
+ }
+ function iterate_get(data storage self, uint index) returns (uint value)
+ {
+ return self.items[index];
+ }
+}
+
+/// How to use it:
+contract User
+{
+ /// Just a struct holding our data.
+ IntegerSet.data data;
+ /// Insert something
+ function insert(uint v) returns (uint size)
+ {
+ /// Sends `data` via reference, so IntegerSet can modify it.
+ IntegerSet.insert(data, v);
+ /// We can access members of the struct - but we should take care not to mess with them.
+ return data.size;
+ }
+ /// Computes the sum of all stored data.
+ function sum() returns (uint s)
+ {
+ for (var i = IntegerSet.iterate_start(data); IntegerSet.iterate_valid(data, i); i = IntegerSet.iterate_advance(data, i))
+ s += IntegerSet.iterate_get(data, i);
+ }
+}
+
+// This broke it at one point (namely the modifiers).
+contract DualIndex {
+ mapping(uint => mapping(uint => uint)) data;
+ address public admin;
+
+ modifier restricted { if (msg.sender == admin) _; }
+
+ function DualIndex() {
+ admin = msg.sender;
+ }
+
+ function set(uint key1, uint key2, uint value) restricted {
+ uint[2][4] memory defaults; // "memory" broke things at one time.
+ data[key1][key2] = value;
+ }
+
+ function transfer_ownership(address _admin) restricted {
+ admin = _admin;
+ }
+
+ function lookup(uint key1, uint key2) returns(uint) {
+ return data[key1][key2];
+ }
+}
+
+contract A {
+
+}
+
+contract B {
+
+}
+
+contract C is A, B {
+
+}
+
+contract TestPrivate
+{
+ uint private value;
+}
+
+contract TestInternal
+{
+ uint internal value;
+}
+
+contract FromSolparse is A, B, TestPrivate, TestInternal {
+ function() {
+ uint a = 6 ** 9;
+ var (x) = 100;
+ uint y = 2 days;
+ }
+}
+
+contract CommentedOutFunction {
+ // FYI: This empty function, as well as the commented
+ // out function below (bad code) is important to this test.
+ function() {
+
+ }
+
+ // function something()
+ // uint x = 10;
+ // }
+}
+
+library VarHasBrackets {
+ string constant specialRight = "}";
+ //string storage specialLeft = "{";
+}
+
+library UsingExampleLibrary {
+ function sum(uint[] storage self) returns (uint s) {
+ for (uint i = 0; i < self.length; i++)
+ s += self[i];
+ }
+}
+
+contract UsingExampleContract {
+ using UsingExampleLibrary for uint[];
+}
+
+contract NewStuff {
+ uint[] b;
+
+ function someFunction() payable {
+ string storage a = hex"ab1248fe";
+ b[2+2];
+ }
+}
+
+// modifier with expression
+contract MyContract {
+ function fun() mymodifier(foo.bar()) {}
+}
+
+library GetCode {
+ function at(address _addr) returns (bytes o_code) {
+ assembly {
+ // retrieve the size of the code, this needs assembly
+ let size := extcodesize(_addr)
+ // allocate output byte array - this could also be done without assembly
+ // by using o_code = new bytes(size)
+ o_code := mload(0x40)
+ // new "memory end" including padding
+ mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
+ // store length in memory
+ mstore(o_code, size)
+ // actually retrieve the code, this needs assembly
+ extcodecopy(_addr, add(o_code, 0x20), 0, size)
+ }
+ }
+}
+
+contract assemblyLocalBinding {
+ function test(){
+ assembly {
+ let v := 1
+ let x := 0x00
+ let y := x
+ let z := "hello"
+ }
+ }
+}
+
+contract assemblyReturn {
+ uint a = 10;
+
+ function get() constant returns(uint) {
+ assembly {
+ mstore(0x40, sload(0))
+ byte(0)
+ address(0)
+ return(0x40,32)
+ }
+ }
+}
+
+contract usesConst {
+ uint const = 0;
+}
+
+contract memoryArrays {
+ uint seven = 7;
+
+ function returnNumber(uint number) returns (uint){
+ return number;
+ }
+
+ function alloc() {
+ uint[] memory a = new uint[](7);
+ uint[] memory b = new uint[](returnNumber(seven));
+ }
+}
+
+contract DeclarativeExpressions {
+ uint a;
+ uint b = 7;
+ uint b2=0;
+ uint public c;
+ uint constant public d;
+ uint public constant e;
+ uint private constant f = 7;
+ struct S { uint q;}
+
+ function ham(S storage s1, uint[] storage arr) internal {
+ uint x;
+ uint y = 7;
+ S storage s2 = s1;
+ uint[] memory stor;
+ uint[] storage stor2 = arr;
+ }
+}
+
+contract VariableDeclarationTuple {
+ function getMyTuple() returns (bool, bool){
+ return (true, false);
+ }
+
+ function ham (){
+ var (x, y) = (10, 20);
+ var (a, b) = getMyTuple();
+ var (,c) = (10, 20);
+ var (d,,) = (10, 20, 30);
+ var (,e,,f,) = (10, 20, 30, 40, 50);
+
+ var (
+ num1, num2,
+ num3, ,num5
+ ) = (10, 20, 30, 40, 50);
+ }
+}
+
+contract TypeIndexSpacing {
+ uint [ 7 ] x;
+ uint [] y;
+}
+
+contract Ballot {
+
+ struct Voter {
+ uint weight;
+ bool voted;
+ }
+
+ function abstain() returns (bool) {
+ return false;
+ }
+
+ function foobar() payable owner (myPrice) returns (uint[], address myAdd, string[] names) {}
+ function foobar() payable owner (myPrice) returns (uint[], address myAdd, string[] names);
+
+ Voter you = Voter(1, true);
+
+ Voter me = Voter({
+ weight: 2,
+ voted: abstain()
+ });
+
+ Voter airbnb = Voter({
+ weight: 2,
+ voted: true,
+ });
+}
+
+contract multilineReturn {
+ function a() returns (uint x) {
+ return
+ 5;
+ }
+}
diff --git a/packages/sol-tracing-utils/test/fixtures/contracts/SimpleStorage.sol b/packages/sol-tracing-utils/test/fixtures/contracts/SimpleStorage.sol
new file mode 100644
index 000000000..e4b4ac246
--- /dev/null
+++ b/packages/sol-tracing-utils/test/fixtures/contracts/SimpleStorage.sol
@@ -0,0 +1,11 @@
+pragma solidity ^0.4.21;
+
+contract SimpleStorage {
+ uint public storedData;
+ function set(uint x) {
+ storedData = x;
+ }
+ function get() constant returns (uint retVal) {
+ return storedData;
+ }
+}
diff --git a/packages/sol-tracing-utils/test/fixtures/contracts/Simplest.sol b/packages/sol-tracing-utils/test/fixtures/contracts/Simplest.sol
new file mode 100644
index 000000000..d71016e07
--- /dev/null
+++ b/packages/sol-tracing-utils/test/fixtures/contracts/Simplest.sol
@@ -0,0 +1,2 @@
+contract Simplest {
+}
diff --git a/packages/sol-tracing-utils/test/fixtures/contracts/SolcovIgnore.sol b/packages/sol-tracing-utils/test/fixtures/contracts/SolcovIgnore.sol
new file mode 100644
index 000000000..a7977ffb4
--- /dev/null
+++ b/packages/sol-tracing-utils/test/fixtures/contracts/SolcovIgnore.sol
@@ -0,0 +1,22 @@
+pragma solidity ^0.4.21;
+
+contract SolcovIgnore {
+ uint public storedData;
+
+ function set(uint x) public {
+ /* solcov ignore next */
+ storedData = x;
+ }
+
+ /* solcov ignore next */
+ function get() constant public returns (uint retVal) {
+ return storedData;
+ }
+}
+
+/* solcov ignore next */
+contract Ignore {
+ function ignored() public returns (bool) {
+ return false;
+ }
+}
diff --git a/packages/sol-tracing-utils/test/instructions_test.ts b/packages/sol-tracing-utils/test/instructions_test.ts
new file mode 100644
index 000000000..058053cf9
--- /dev/null
+++ b/packages/sol-tracing-utils/test/instructions_test.ts
@@ -0,0 +1,19 @@
+import * as chai from 'chai';
+import 'mocha';
+
+import { constants } from '../src/constants';
+import { getPcToInstructionIndexMapping } from '../src/instructions';
+
+const expect = chai.expect;
+
+describe('instructions', () => {
+ describe('#getPcToInstructionIndexMapping', () => {
+ it('correctly maps pcs to instruction indexed', () => {
+ // tslint:disable-next-line:custom-no-magic-numbers
+ const bytecode = new Uint8Array([constants.PUSH1, 42, constants.PUSH2, 1, 2, constants.TIMESTAMP]);
+ const pcToInstruction = getPcToInstructionIndexMapping(bytecode);
+ const expectedPcToInstruction = { '0': 0, '2': 1, '5': 2 };
+ expect(pcToInstruction).to.be.deep.equal(expectedPcToInstruction);
+ });
+ });
+});
diff --git a/packages/sol-tracing-utils/test/sol_compiler_artifact_adapter_test.ts b/packages/sol-tracing-utils/test/sol_compiler_artifact_adapter_test.ts
new file mode 100644
index 000000000..9c58d2cef
--- /dev/null
+++ b/packages/sol-tracing-utils/test/sol_compiler_artifact_adapter_test.ts
@@ -0,0 +1,29 @@
+import * as chai from 'chai';
+import * as _ from 'lodash';
+import 'mocha';
+import * as path from 'path';
+
+import { SolCompilerArtifactAdapter } from '../src/artifact_adapters/sol_compiler_artifact_adapter';
+
+const expect = chai.expect;
+
+describe('SolCompilerArtifactAdapter', () => {
+ describe('#collectContractsData', () => {
+ it('correctly collects contracts data', async () => {
+ const artifactsPath = path.resolve(__dirname, 'fixtures/artifacts');
+ const sourcesPath = path.resolve(__dirname, 'fixtures/contracts');
+ const zeroExArtifactsAdapter = new SolCompilerArtifactAdapter(artifactsPath, sourcesPath);
+ const contractsData = await zeroExArtifactsAdapter.collectContractsDataAsync();
+ _.forEach(contractsData, contractData => {
+ expect(contractData).to.have.keys([
+ 'sourceCodes',
+ 'sources',
+ 'sourceMap',
+ 'sourceMapRuntime',
+ 'bytecode',
+ 'runtimeBytecode',
+ ]);
+ });
+ });
+ });
+});
diff --git a/packages/sol-tracing-utils/test/source_maps_test.ts b/packages/sol-tracing-utils/test/source_maps_test.ts
new file mode 100644
index 000000000..5820bedd7
--- /dev/null
+++ b/packages/sol-tracing-utils/test/source_maps_test.ts
@@ -0,0 +1,71 @@
+import * as chai from 'chai';
+import * as fs from 'fs';
+import * as _ from 'lodash';
+import 'mocha';
+import * as path from 'path';
+
+import { getLocationByOffset, parseSourceMap } from '../src/source_maps';
+
+const expect = chai.expect;
+
+const simplestContractBaseName = 'Simplest.sol';
+const simplestContractFileName = path.resolve(__dirname, 'fixtures/contracts', simplestContractBaseName);
+const simplestContract = fs.readFileSync(simplestContractFileName).toString();
+
+describe('source maps', () => {
+ describe('#getLocationByOffset', () => {
+ it('correctly computes location by offset', () => {
+ const locationByOffset = getLocationByOffset(simplestContract);
+ const expectedLocationByOffset = {
+ '0': { line: 1, column: 0 },
+ '1': { line: 1, column: 1 },
+ '2': { line: 1, column: 2 },
+ '3': { line: 1, column: 3 },
+ '4': { line: 1, column: 4 },
+ '5': { line: 1, column: 5 },
+ '6': { line: 1, column: 6 },
+ '7': { line: 1, column: 7 },
+ '8': { line: 1, column: 8 },
+ '9': { line: 1, column: 9 },
+ '10': { line: 1, column: 10 },
+ '11': { line: 1, column: 11 },
+ '12': { line: 1, column: 12 },
+ '13': { line: 1, column: 13 },
+ '14': { line: 1, column: 14 },
+ '15': { line: 1, column: 15 },
+ '16': { line: 1, column: 16 },
+ '17': { line: 1, column: 17 },
+ '18': { line: 1, column: 18 },
+ '19': { line: 1, column: 19 },
+ '20': { line: 2, column: 0 },
+ '21': { line: 2, column: 1 },
+ '22': { line: 3, column: 0 },
+ };
+ expect(locationByOffset).to.be.deep.equal(expectedLocationByOffset);
+ });
+ });
+ describe('#parseSourceMap', () => {
+ it('correctly parses the source map', () => {
+ // This is the source map and bytecode for an empty contract like Example.sol
+ const srcMap = '0:21:0:-;;;;;;;;;;;;;;;;;';
+ const bytecodeHex =
+ '60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00a165627a7a72305820377cdef690e46589f40efeef14d8ef73504af059fb3fd46f1da3cd2fc52ef7890029';
+ const sources = [simplestContractBaseName];
+ const pcToSourceRange = parseSourceMap([simplestContract], srcMap, bytecodeHex, sources);
+ const expectedSourceRange = {
+ location: {
+ start: { line: 1, column: 0 },
+ end: { line: 2, column: 1 },
+ },
+ fileName: simplestContractBaseName,
+ };
+ _.forEach(pcToSourceRange, sourceRange => {
+ // Solidity source maps are too short and we map some instructions to undefined
+ // Source: https://github.com/ethereum/solidity/issues/3741
+ if (!_.isUndefined(sourceRange)) {
+ expect(sourceRange).to.be.deep.equal(expectedSourceRange);
+ }
+ });
+ });
+ });
+});
diff --git a/packages/sol-tracing-utils/test/trace_test.ts b/packages/sol-tracing-utils/test/trace_test.ts
new file mode 100644
index 000000000..7a034362c
--- /dev/null
+++ b/packages/sol-tracing-utils/test/trace_test.ts
@@ -0,0 +1,55 @@
+import * as chai from 'chai';
+import { OpCode, StructLog } from 'ethereum-types';
+import * as _ from 'lodash';
+import 'mocha';
+
+import { getTracesByContractAddress } from '../src/trace';
+
+const expect = chai.expect;
+
+const DEFAULT_STRUCT_LOG: StructLog = {
+ depth: 0,
+ error: '',
+ gas: 0,
+ gasCost: 0,
+ memory: [],
+ op: OpCode.Invalid,
+ pc: 0,
+ stack: [],
+ storage: {},
+};
+
+function addDefaultStructLogFields(compactStructLog: Partial<StructLog> & { op: OpCode; depth: number }): StructLog {
+ return { ...DEFAULT_STRUCT_LOG, ...compactStructLog };
+}
+
+describe('Trace', () => {
+ describe('#getTracesByContractAddress', () => {
+ it('correctly splits trace by contract address', () => {
+ const delegateCallAddress = '0x0000000000000000000000000000000000000002';
+ const trace = [
+ {
+ op: OpCode.DelegateCall,
+ stack: [delegateCallAddress, '0x'],
+ depth: 0,
+ },
+ {
+ op: OpCode.Return,
+ depth: 1,
+ },
+ {
+ op: OpCode.Return,
+ depth: 0,
+ },
+ ];
+ const fullTrace = _.map(trace, compactStructLog => addDefaultStructLogFields(compactStructLog));
+ const startAddress = '0x0000000000000000000000000000000000000001';
+ const traceByContractAddress = getTracesByContractAddress(fullTrace, startAddress);
+ const expectedTraceByContractAddress = {
+ [startAddress]: [fullTrace[0], fullTrace[2]],
+ [delegateCallAddress]: [fullTrace[1]],
+ };
+ expect(traceByContractAddress).to.be.deep.equal(expectedTraceByContractAddress);
+ });
+ });
+});
diff --git a/packages/sol-tracing-utils/test/utils_test.ts b/packages/sol-tracing-utils/test/utils_test.ts
new file mode 100644
index 000000000..6fc8fcfe1
--- /dev/null
+++ b/packages/sol-tracing-utils/test/utils_test.ts
@@ -0,0 +1,53 @@
+import * as chai from 'chai';
+import * as dirtyChai from 'dirty-chai';
+import 'mocha';
+
+import { utils } from '../src/utils';
+
+chai.use(dirtyChai);
+const expect = chai.expect;
+
+describe('utils', () => {
+ describe('#compareLineColumn', () => {
+ it('correctly compares LineColumns', () => {
+ expect(utils.compareLineColumn({ line: 1, column: 3 }, { line: 1, column: 4 })).to.be.lessThan(0);
+ expect(utils.compareLineColumn({ line: 1, column: 4 }, { line: 1, column: 3 })).to.be.greaterThan(0);
+ expect(utils.compareLineColumn({ line: 1, column: 3 }, { line: 1, column: 3 })).to.be.equal(0);
+ expect(utils.compareLineColumn({ line: 0, column: 2 }, { line: 1, column: 0 })).to.be.lessThan(0);
+ expect(utils.compareLineColumn({ line: 1, column: 0 }, { line: 0, column: 2 })).to.be.greaterThan(0);
+ });
+ });
+
+ describe('#isRangeInside', () => {
+ it('returns true if inside', () => {
+ expect(
+ utils.isRangeInside(
+ { start: { line: 1, column: 3 }, end: { line: 1, column: 4 } },
+ { start: { line: 1, column: 2 }, end: { line: 1, column: 5 } },
+ ),
+ ).to.be.true();
+ });
+ it('returns true if the same', () => {
+ expect(
+ utils.isRangeInside(
+ { start: { line: 1, column: 3 }, end: { line: 1, column: 4 } },
+ { start: { line: 1, column: 3 }, end: { line: 1, column: 4 } },
+ ),
+ ).to.be.true();
+ });
+ it('returns false if not inside', () => {
+ expect(
+ utils.isRangeInside(
+ { start: { line: 1, column: 3 }, end: { line: 1, column: 4 } },
+ { start: { line: 1, column: 4 }, end: { line: 1, column: 4 } },
+ ),
+ ).to.be.false();
+ expect(
+ utils.isRangeInside(
+ { start: { line: 1, column: 3 }, end: { line: 1, column: 4 } },
+ { start: { line: 1, column: 4 }, end: { line: 1, column: 5 } },
+ ),
+ ).to.be.false();
+ });
+ });
+});