Skip to content

Commit

Permalink
fix exit codes and max-warnings on quiet
Browse files Browse the repository at this point in the history
  • Loading branch information
dbale-altoros committed May 11, 2024
1 parent 1ffde5e commit 8ab9533
Show file tree
Hide file tree
Showing 7 changed files with 460 additions and 303 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [5.0.0] - 2024-05-11
### BREAKING CHANGES


## [4.5.4] - 2024-04-10
### Fixed
- `gas-custom-errors` improved logic to ranged pragma versions [#573](https://github.com/protofire/solhint/pull/573)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Options:
-V, --version output the version number
-f, --formatter [name] report formatter name (stylish, table, tap, unix, json, compact, sarif)
-w, --max-warnings [maxWarningsNumber] number of allowed warnings
-w, --max-warnings [maxWarningsNumber] number of allowed warnings, works in quiet mode as well
-c, --config [file_name] file to use as your .solhint.json
-q, --quiet report errors only - default: false
--ignore-path [file_name] file to use as your .solhintignore
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
FROM node:20-alpine
LABEL maintainer="[email protected]"
ENV VERSION=4.5.4
ENV VERSION=5.0.0

RUN npm install -g solhint@"$VERSION"
484 changes: 273 additions & 211 deletions e2e/formatters-test.js

Large diffs are not rendered by default.

178 changes: 123 additions & 55 deletions e2e/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,82 +6,114 @@ const os = require('os')
const path = require('path')
const shell = require('shelljs')

function useFixture(dir) {
beforeEach(`switch to ${dir}`, function () {
const fixturePath = path.join(__dirname, dir)

const tmpDirContainer = os.tmpdir()
this.testDirPath = path.join(tmpDirContainer, `solhint-tests-${dir}`)

fs.ensureDirSync(this.testDirPath)
fs.emptyDirSync(this.testDirPath)

fs.copySync(fixturePath, this.testDirPath)

shell.cd(this.testDirPath)
})
const EXIT_CODES = { BAD_OPTIONS: 255, OK: 0, REPORTED_ERRORS: 1 }

// ==================================================================
// use these lines to execute locally in TEST directory
// ==================================================================
// const E2E = false
// const NODE = 'node '

// ==================================================================
// use these lines to E2E
// ==================================================================
const E2E = true
const NODE = ''

function prepareContext(path) {
let PREFIX
let SUFFIX

if (E2E) {
useFixture(path)
PREFIX = ''
SUFFIX = ' --disc'
} else {
PREFIX = `./e2e/${path}/`
SUFFIX = `-c ${PREFIX}.solhint.json --disc`
}
return { PREFIX, SUFFIX }
}

describe('e2e', function () {
if (!E2E) shell.exec(`rm ./.solhint.json`)

describe('no config', function () {
useFixture('01-no-config')
const PATH = '01-no-config'
prepareContext(PATH)

it('should fail', function () {
const { code } = shell.exec('solhint Foo.sol')
it('should fail when config file does not exists', function () {
const { code, stderr } = shell.exec(`${NODE}solhint Foo.sol -c ./noconfig/.solhint.json`)

expect(code).to.equal(1)
expect(code).to.equal(EXIT_CODES.BAD_OPTIONS)
expect(stderr).to.include('couldnt be found')
})

it('should create an initial config with --init', function () {
const { code } = shell.exec('solhint --init')
let solhintConfigPath
if (E2E) solhintConfigPath = path.join(this.testDirPath, '.solhint.json')
else solhintConfigPath = './.solhint.json'

expect(code).to.equal(0)
const { code } = shell.exec(`${NODE}solhint --init`)

const solhintConfigPath = path.join(this.testDirPath, '.solhint.json')
if (E2E) solhintConfigPath = path.join(this.testDirPath, '.solhint.json')
else solhintConfigPath = './.solhint.json'

expect(code).to.equal(EXIT_CODES.OK)
expect(fs.existsSync(solhintConfigPath)).to.be.true
})

it('should print usage if called without arguments', function () {
const { code, stdout } = shell.exec('solhint')
const { code, stdout } = shell.exec(`${NODE}solhint`)

expect(code).to.equal(0)
expect(code).to.equal(EXIT_CODES.OK)
expect(stdout).to.include('Usage: solhint [options]')
expect(stdout).to.include('Linter for Solidity programming language')
expect(stdout).to.include('linting of source code data provided to STDIN')
})
})

describe('empty-config', function () {
useFixture('02-empty-solhint-json')
const PATH = '02-empty-solhint-json'
const { PREFIX, SUFFIX } = prepareContext(PATH)

it('should print nothing', function () {
const { code, stdout } = shell.exec('solhint Foo.sol')
const { code, stdout } = shell.exec(`${NODE}solhint ${PREFIX}Foo.sol ${SUFFIX}`)

expect(code).to.equal(0)
expect(code).to.equal(EXIT_CODES.OK)
expect(stdout.trim()).to.equal('')
})

it('should show warning when using --init', function () {
const { code, stdout } = shell.exec('solhint --init')
const { code, stdout } = shell.exec(`${NODE}solhint --init`)

expect(code).to.equal(0)
expect(code).to.equal(EXIT_CODES.BAD_OPTIONS)
expect(stdout.trim()).to.equal('Configuration file already exists')
})
})

describe('no-empty-blocks', function () {
useFixture('03-no-empty-blocks')
const PATH = '03-no-empty-blocks'
const { PREFIX, SUFFIX } = prepareContext(PATH)

describe('No contracts to lint', function () {
it('should fail with appropiate message', function () {
const { code, stderr } = shell.exec(`${NODE}solhint Foo.sol ${SUFFIX}`)

expect(code).to.equal(EXIT_CODES.BAD_OPTIONS)
expect(stderr).to.include('No files to lint!')
})
})

it('should end correctly (exit w/0), found 1 error', function () {
const { code, stdout } = shell.exec('solhint Foo.sol')
it('should end with REPORTED_ERRORS = 1 because report contains errors', function () {
const { code, stdout } = shell.exec(`${NODE}solhint ${PREFIX}Foo.sol ${SUFFIX}`)

expect(code).to.equal(0)
expect(code).to.equal(EXIT_CODES.REPORTED_ERRORS)
expect(stdout.trim()).to.contain('Code contains empty blocks')
})

it('should work with stdin, exit 0, found 1 error', async function () {
const child = cp.exec('solhint stdin')
const child = cp.exec(`${NODE}solhint stdin ${PREFIX}Foo.sol ${SUFFIX}`)

const stdoutPromise = getStream(child.stdout)

Expand All @@ -95,78 +127,114 @@ describe('e2e', function () {
child.stdin.end()

const code = await codePromise

expect(code).to.equal(0)

const stdout = await stdoutPromise

expect(code).to.equal(EXIT_CODES.REPORTED_ERRORS)
expect(stdout.trim()).to.contain('Code contains empty blocks')
})
})

describe('.sol on path', function () {
useFixture('04-dotSol-on-path')
const PATH = '04-dotSol-on-path'
const { PREFIX, SUFFIX } = prepareContext(PATH)

it('should handle directory names that end with .sol', function () {
const { code } = shell.exec('solhint contracts/**/*.sol')
expect(code).to.equal(0)
const { code } = shell.exec(`${NODE}solhint ${PREFIX}contracts/**/*.sol ${SUFFIX}`)
expect(code).to.equal(EXIT_CODES.OK)
})
})

describe('--max-warnings parameter tests', function () {
const PATH = '05-max-warnings'
const { PREFIX, SUFFIX } = prepareContext(PATH)

// Foo contract has 6 warnings
// Foo2 contract has 1 error and 14 warnings
useFixture('05-max-warnings')
const warningExceededMsg = 'Solhint found more warnings than the maximum specified'
const errorFound =
'Error/s found on rules! [max-warnings] param is ignored. Fixing errors enables max-warnings'

it('should not display [warnings exceeded] for max 7 warnings', function () {
const { code, stdout } = shell.exec('solhint contracts/Foo.sol --max-warnings 7')
expect(code).to.equal(0)
const { code, stdout } = shell.exec(
`${NODE}solhint ${PREFIX}contracts/Foo.sol --max-warnings 7 ${SUFFIX}`
)
expect(code).to.equal(EXIT_CODES.OK)
expect(stdout.trim()).to.not.contain(warningExceededMsg)
})

it('should display [warnings exceeded] for max 3 warnings and exit with 0', function () {
const { code, stdout } = shell.exec('solhint contracts/Foo.sol --max-warnings 3')
it('should display [warnings exceeded] for max 3 warnings and exit with 1', function () {
const { code, stdout } = shell.exec(
`${NODE}solhint ${PREFIX}contracts/Foo.sol --max-warnings 3 ${SUFFIX}`
)

expect(code).to.equal(1)
expect(code).to.equal(EXIT_CODES.REPORTED_ERRORS)
expect(stdout.trim()).to.contain(warningExceededMsg)
})

it('should return error for Compiler version rule, ignoring 3 --max-warnings', function () {
const { code, stdout } = shell.exec('solhint contracts/Foo2.sol --max-warnings 3')
const { code, stdout } = shell.exec(
`${NODE}solhint ${PREFIX}contracts/Foo2.sol --max-warnings 3 ${SUFFIX}`
)

expect(code).to.equal(0)
expect(code).to.equal(EXIT_CODES.REPORTED_ERRORS)
expect(stdout.trim()).to.contain(errorFound)
})

it('should return error for Compiler version rule. No message for max-warnings', function () {
const { code, stdout } = shell.exec('solhint contracts/Foo2.sol --max-warnings 27')
expect(code).to.equal(0)
const { code, stdout } = shell.exec(
`${NODE}solhint ${PREFIX}contracts/Foo2.sol --max-warnings 27 ${SUFFIX}`
)
expect(code).to.equal(EXIT_CODES.REPORTED_ERRORS)
expect(stdout.trim()).to.not.contain(errorFound)
})

it('should NOT display warningns nor error but exit with 1 because max is 3 warnings', function () {
const { code } = shell.exec(
`${NODE}solhint ${PREFIX}contracts/Foo.sol --max-warnings 3 ${SUFFIX} -q`
)

expect(code).to.equal(EXIT_CODES.REPORTED_ERRORS)
})
})

describe('Linter - foundry-test-functions with shell', () => {
const PATH = '07-foundry-test'
const { PREFIX, SUFFIX } = prepareContext(PATH)

// Foo contract has 1 warning
// FooTest contract has 1 error
useFixture('07-foundry-test')

it(`should raise error for empty blocks only`, () => {
const { code, stdout } = shell.exec('solhint contracts/Foo.sol')
const { code, stdout } = shell.exec(`${NODE}solhint ${PREFIX}contracts/Foo.sol ${SUFFIX}`)

expect(code).to.equal(0)
expect(code).to.equal(EXIT_CODES.OK)
expect(stdout.trim()).to.contain('Code contains empty blocks')
})

it(`should raise error for wrongFunctionDefinitionName() only`, () => {
const { code, stdout } = shell.exec('solhint -c test/.solhint.json test/FooTest.sol')
const SUFFIX2 = `-c ${PREFIX}test/.solhint.json --disc`
const { code, stdout } = shell.exec(`${NODE}solhint ${PREFIX}test/FooTest.sol ${SUFFIX2}`)

expect(code).to.equal(0)
expect(code).to.equal(EXIT_CODES.OK)
expect(stdout.trim()).to.contain(
'Function wrongFunctionDefinitionName() must match Foundry test naming convention'
'Function wrongFunctionDefinitionName() must match Foundry test naming convention '
)
})
})
})

function useFixture(dir) {
beforeEach(`switch to ${dir}`, function () {
const fixturePath = path.join(__dirname, dir)

const tmpDirContainer = os.tmpdir()
this.testDirPath = path.join(tmpDirContainer, `solhint-tests-${dir}`)

fs.ensureDirSync(this.testDirPath)
fs.emptyDirSync(this.testDirPath)

fs.copySync(fixturePath, this.testDirPath)

shell.cd(this.testDirPath)
})
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "solhint",
"version": "4.5.4",
"version": "5.0.0",
"description": "Solidity Code Linter",
"main": "lib/index.js",
"keywords": [
Expand Down
Loading

0 comments on commit 8ab9533

Please sign in to comment.