diff --git a/CHANGELOG.md b/CHANGELOG.md
index f666c172..59d23848 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## [6.0.2] - 2025-09-18
+
+๐งน `Chore`: `foundry-test-functions` is deprecated and will be removed in v7.0.0. Please rename to `foundry-test-function-naming`.
+WILL BE REPLACED IN v7
+
+
+
## [6.0.1] - 2025-08-22
๐ ๏ธ `Fix`: `no-unused-vars` for modifiers
diff --git a/conf/rulesets/solhint-all.js b/conf/rulesets/solhint-all.js
index c251f20f..35178955 100644
--- a/conf/rulesets/solhint-all.js
+++ b/conf/rulesets/solhint-all.js
@@ -72,6 +72,7 @@ module.exports = Object.freeze({
'const-name-snakecase': 'warn',
'contract-name-capwords': 'warn',
'event-name-capwords': 'warn',
+ 'foundry-test-function-naming': ['warn', ['setUp']],
'foundry-test-functions': ['warn', ['setUp']],
'func-name-mixedcase': 'warn',
'func-named-parameters': ['warn', 4],
diff --git a/docs/rules.md b/docs/rules.md
index 2bf7b701..fb8a9158 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -27,27 +27,28 @@ title: "Rule Index of Solhint"
## Style Guide Rules
-| Rule Id | Error | Recommended | Deprecated |
-| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ------------ | ---------- |
-| [interface-starts-with-i](./rules/naming/interface-starts-with-i.md) | Solidity Interfaces names should start with an `I` | $~~~~~~~~$โ๏ธ | |
-| [duplicated-imports](./rules/miscellaneous/duplicated-imports.md) | Check if an import is done twice in the same file and there is no alias | $~~~~~~~~$โ๏ธ | |
-| [const-name-snakecase](./rules/naming/const-name-snakecase.md) | Constant name must be in capitalized SNAKE_CASE. (Does not check IMMUTABLES, use immutable-vars-naming) | $~~~~~~~~$โ๏ธ | |
-| [contract-name-capwords](./rules/naming/contract-name-capwords.md) | Contract, Structs and Enums should be in CapWords. | $~~~~~~~~$โ๏ธ | |
-| [event-name-capwords](./rules/naming/event-name-capwords.md) | Event name must be in CapWords. | $~~~~~~~~$โ๏ธ | |
-| [foundry-test-functions](./rules/naming/foundry-test-functions.md) | Enforce naming convention on functions for Foundry test cases | | |
-| [func-name-mixedcase](./rules/naming/func-name-mixedcase.md) | Function name must be in mixedCase. | $~~~~~~~~$โ๏ธ | |
-| [func-named-parameters](./rules/naming/func-named-parameters.md) | Enforce named parameters for function calls with 4 or more arguments. This rule may have some false positives | | |
-| [func-param-name-mixedcase](./rules/naming/func-param-name-mixedcase.md) | Function param name must be in mixedCase. | | |
-| [immutable-vars-naming](./rules/naming/immutable-vars-naming.md) | Check Immutable variables. Capitalized SNAKE_CASE or mixedCase depending on configuration. | $~~~~~~~~$โ๏ธ | |
-| [modifier-name-mixedcase](./rules/naming/modifier-name-mixedcase.md) | Modifier name must be in mixedCase. | | |
-| [named-parameters-mapping](./rules/naming/named-parameters-mapping.md) | Solidity v0.8.18 introduced named parameters on the mappings definition. | | |
-| [private-vars-leading-underscore](./rules/naming/private-vars-leading-underscore.md) | Non-external functions and state variables should start with a single underscore. Others, shouldn't | | |
-| [use-forbidden-name](./rules/naming/use-forbidden-name.md) | Avoid to use letters 'I', 'l', 'O' as identifiers. | $~~~~~~~~$โ๏ธ | |
-| [var-name-mixedcase](./rules/naming/var-name-mixedcase.md) | Variable names must be in mixedCase. (Does not check IMMUTABLES nor CONSTANTS (use inherent rules for that) | $~~~~~~~~$โ๏ธ | |
-| [imports-on-top](./rules/order/imports-on-top.md) | Import statements must be on top. | $~~~~~~~~$โ๏ธ | |
-| [imports-order](./rules/order/imports-order.md) | Order the imports of the contract to follow a certain hierarchy (read "Notes section") | | |
-| [ordering](./rules/order/ordering.md) | Check order of elements in file and inside each contract, according to the style guide | | |
-| [visibility-modifier-order](./rules/order/visibility-modifier-order.md) | Visibility modifier must be first in list of modifiers. | $~~~~~~~~$โ๏ธ | |
+| Rule Id | Error | Recommended | Deprecated |
+| ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | ------------ | ---------- |
+| [interface-starts-with-i](./rules/naming/interface-starts-with-i.md) | Solidity Interfaces names should start with an `I` | $~~~~~~~~$โ๏ธ | |
+| [duplicated-imports](./rules/miscellaneous/duplicated-imports.md) | Check if an import is done twice in the same file and there is no alias | $~~~~~~~~$โ๏ธ | |
+| [const-name-snakecase](./rules/naming/const-name-snakecase.md) | Constant name must be in capitalized SNAKE_CASE. (Does not check IMMUTABLES, use immutable-vars-naming) | $~~~~~~~~$โ๏ธ | |
+| [contract-name-capwords](./rules/naming/contract-name-capwords.md) | Contract, Structs and Enums should be in CapWords. | $~~~~~~~~$โ๏ธ | |
+| [event-name-capwords](./rules/naming/event-name-capwords.md) | Event name must be in CapWords. | $~~~~~~~~$โ๏ธ | |
+| [foundry-test-function-naming](./rules/naming/foundry-test-function-naming.md) | Enforce naming convention on functions for Foundry test cases | | |
+| [foundry-test-functions](./rules/naming/foundry-test-functions.md) | Enforce naming convention on functions for Foundry test cases (DEPRECATED, use `foundry-test-functions-naming`) | | |
+| [func-name-mixedcase](./rules/naming/func-name-mixedcase.md) | Function name must be in mixedCase. | $~~~~~~~~$โ๏ธ | |
+| [func-named-parameters](./rules/naming/func-named-parameters.md) | Enforce named parameters for function calls with 4 or more arguments. This rule may have some false positives | | |
+| [func-param-name-mixedcase](./rules/naming/func-param-name-mixedcase.md) | Function param name must be in mixedCase. | | |
+| [immutable-vars-naming](./rules/naming/immutable-vars-naming.md) | Check Immutable variables. Capitalized SNAKE_CASE or mixedCase depending on configuration. | $~~~~~~~~$โ๏ธ | |
+| [modifier-name-mixedcase](./rules/naming/modifier-name-mixedcase.md) | Modifier name must be in mixedCase. | | |
+| [named-parameters-mapping](./rules/naming/named-parameters-mapping.md) | Solidity v0.8.18 introduced named parameters on the mappings definition. | | |
+| [private-vars-leading-underscore](./rules/naming/private-vars-leading-underscore.md) | Non-external functions and state variables should start with a single underscore. Others, shouldn't | | |
+| [use-forbidden-name](./rules/naming/use-forbidden-name.md) | Avoid to use letters 'I', 'l', 'O' as identifiers. | $~~~~~~~~$โ๏ธ | |
+| [var-name-mixedcase](./rules/naming/var-name-mixedcase.md) | Variable names must be in mixedCase. (Does not check IMMUTABLES nor CONSTANTS (use inherent rules for that) | $~~~~~~~~$โ๏ธ | |
+| [imports-on-top](./rules/order/imports-on-top.md) | Import statements must be on top. | $~~~~~~~~$โ๏ธ | |
+| [imports-order](./rules/order/imports-order.md) | Order the imports of the contract to follow a certain hierarchy (read "Notes section") | | |
+| [ordering](./rules/order/ordering.md) | Check order of elements in file and inside each contract, according to the style guide | | |
+| [visibility-modifier-order](./rules/order/visibility-modifier-order.md) | Visibility modifier must be first in list of modifiers. | $~~~~~~~~$โ๏ธ | |
## Gas Consumption Rules
diff --git a/docs/rules/naming/foundry-test-function-naming.md b/docs/rules/naming/foundry-test-function-naming.md
new file mode 100644
index 00000000..e2efe3c7
--- /dev/null
+++ b/docs/rules/naming/foundry-test-function-naming.md
@@ -0,0 +1,79 @@
+---
+warning: "This is a dynamically generated file. Do not edit manually."
+layout: "default"
+title: "foundry-test-function-naming | Solhint"
+---
+
+# foundry-test-function-naming
+
+
+
+## Description
+Enforce naming convention on functions for Foundry test cases
+
+## Options
+This rule accepts an array of options:
+
+| Index | Description | Default Value |
+| ----- | ----------------------------------------------------- | ------------- |
+| 0 | Rule severity. Must be one of "error", "warn", "off". | warn |
+| 1 | Array of required Foundry test hook function names. | setUp |
+
+
+### Example Config
+```json
+{
+ "rules": {
+ "foundry-test-function-naming": [
+ "warn",
+ [
+ "setUp"
+ ]
+ ]
+ }
+}
+```
+
+### Notes
+- This rule can be configured to skip certain function names in the SKIP array. In Example Config. ```setUp``` function will be skipped
+- Supported Regex: ```test(Fork)?(Fuzz)?(Fail)?_(Revert(If_|When_){1})?\w{1,}```
+- This rule should be executed in a separate folder with a separate .solhint.json => ```solhint --config .solhint.json testFolder/**/*.sol```
+- This rule applies only to `external` and `public` functions
+- This rule skips the `setUp()` function by default
+
+## Examples
+### ๐ Examples of **correct** code for this rule
+
+#### Foundry test case with correct Function declaration
+
+```solidity
+function test_NumberIs42() public {}
+```
+
+#### Foundry test case with correct Function declaration
+
+```solidity
+function testFail_Subtract43() public {}
+```
+
+#### Foundry test case with correct Function declaration
+
+```solidity
+function testFuzz_FuzzyTest() public {}
+```
+
+### ๐ Examples of **incorrect** code for this rule
+
+#### Foundry test case with incorrect Function declaration
+
+```solidity
+function numberIs42() public {}
+```
+
+## Version
+This rule was introduced in the latest version.
+
+## Resources
+- [Rule source](https://github.com/protofire/solhint/blob/master/lib/rules/naming/foundry-test-function-naming.js)
+- [Document source](https://github.com/protofire/solhint/blob/master/docs/rules/naming/foundry-test-function-naming.md)
+- [Test cases](https://github.com/protofire/solhint/blob/master/test/rules/naming/foundry-test-function-naming.js)
diff --git a/docs/rules/naming/foundry-test-functions.md b/docs/rules/naming/foundry-test-functions.md
index 1fa0272d..c58a5afb 100644
--- a/docs/rules/naming/foundry-test-functions.md
+++ b/docs/rules/naming/foundry-test-functions.md
@@ -9,7 +9,7 @@ title: "foundry-test-functions | Solhint"

## Description
-Enforce naming convention on functions for Foundry test cases
+Enforce naming convention on functions for Foundry test cases (DEPRECATED, use `foundry-test-functions-naming`)
## Options
This rule accepts an array of options:
diff --git a/lib/common/identifier-naming.js b/lib/common/identifier-naming.js
index 368490ae..b0850b55 100644
--- a/lib/common/identifier-naming.js
+++ b/lib/common/identifier-naming.js
@@ -32,14 +32,20 @@ module.exports = {
},
isFoundryTestCase(text) {
- // this one checks CamelCase after test keyword
+ // PREVIOUS VERSION
+ // โ strict version: requires that after `test...` the first character must be uppercase (CamelCase enforced)
// const regexTest = /^test(Fork)?(Fuzz)?(Fail)?(_)?[A-Z](Revert(If_|When_){1})?\w{1,}$/
+ // PREVIOUS VERSION
+ // โ strict version: requires that after `invariant` or `statefulFuzz` the first character must be uppercase (CamelCase enforced)
+ // const regexInvariant = /^(invariant|statefulFuzz)(_)?[A-Z]\w{1,}$/
+ // this one checks test functions with optional suffixes (Fork, Fuzz, Fail, RevertIf_, RevertWhen_, etc.)
+ // now it only requires that after those, at least one word character exists (no CamelCase enforcement)
const regexTest = /^test(Fork)?(Fuzz)?(Fail)?(_)?(Revert(If_|When_){1})?\w{1,}$/
const matchRegexTest = match(text, regexTest)
- // this one checks CamelCase after test keyword
- // const regexInvariant = /^(invariant|statefulFuzz)(_)?[A-Z]\w{1,}$/
+ // this one checks invariant or statefulFuzz functions, with optional underscore,
+ // followed by at least one word character (no CamelCase enforcement)
const regexInvariant = /^(invariant|statefulFuzz)(_)?\w{1,}$/
const matchRegexInvariant = match(text, regexInvariant)
diff --git a/lib/rules/naming/foundry-test-function-naming.js b/lib/rules/naming/foundry-test-function-naming.js
new file mode 100644
index 00000000..e7a995aa
--- /dev/null
+++ b/lib/rules/naming/foundry-test-function-naming.js
@@ -0,0 +1,100 @@
+const BaseChecker = require('../base-checker')
+const naming = require('../../common/identifier-naming')
+const { severityDescription } = require('../../doc/utils')
+
+const DEFAULT_SEVERITY = 'warn'
+const DEFAULT_SKIP_FUNCTIONS = ['setUp']
+
+const ruleId = 'foundry-test-function-naming'
+const meta = {
+ type: 'naming',
+
+ docs: {
+ description: `Enforce naming convention on functions for Foundry test cases`,
+ category: 'Style Guide Rules',
+ options: [
+ {
+ description: severityDescription,
+ default: DEFAULT_SEVERITY,
+ },
+ {
+ description: 'Array of required Foundry test hook function names.',
+ default: DEFAULT_SKIP_FUNCTIONS,
+ },
+ ],
+ examples: {
+ good: [
+ {
+ description: 'Foundry test case with correct Function declaration',
+ code: 'function test_NumberIs42() public {}',
+ },
+ {
+ description: 'Foundry test case with correct Function declaration',
+ code: 'function testFail_Subtract43() public {}',
+ },
+ {
+ description: 'Foundry test case with correct Function declaration',
+ code: 'function testFuzz_FuzzyTest() public {}',
+ },
+ ],
+ bad: [
+ {
+ description: 'Foundry test case with incorrect Function declaration',
+ code: 'function numberIs42() public {}',
+ },
+ ],
+ },
+ notes: [
+ {
+ note: 'This rule can be configured to skip certain function names in the SKIP array. In Example Config. ```setUp``` function will be skipped',
+ },
+ { note: 'Supported Regex: ```test(Fork)?(Fuzz)?(Fail)?_(Revert(If_|When_){1})?\\w{1,}```' },
+ {
+ note: 'This rule should be executed in a separate folder with a separate .solhint.json => ```solhint --config .solhint.json testFolder/**/*.sol```',
+ },
+ {
+ note: 'This rule applies only to `external` and `public` functions',
+ },
+ {
+ note: 'This rule skips the `setUp()` function by default',
+ },
+ ],
+ },
+
+ recommended: false,
+ defaultSetup: [DEFAULT_SEVERITY, DEFAULT_SKIP_FUNCTIONS],
+
+ schema: {
+ type: 'array',
+ description: 'Array of function names to skip from the check',
+ items: { type: 'string', errorMessage: 'Each item must be a string' },
+ },
+}
+
+class FoundryTestFunctionNaming extends BaseChecker {
+ constructor(reporter, config) {
+ super(reporter, ruleId, meta)
+ this.skippedFunctions = config
+ ? config.getArray(ruleId, DEFAULT_SKIP_FUNCTIONS)
+ : DEFAULT_SKIP_FUNCTIONS
+ }
+
+ FunctionDefinition(node) {
+ // function name should not be in skipped functions array
+ // should be external or public
+ if (
+ !this.searchInArray(this.skippedFunctions, node.name) &&
+ (node.visibility === 'public' || node.visibility === 'external')
+ ) {
+ if (!naming.isFoundryTestCase(node.name)) {
+ this.error(node, `Function ${node.name}() must match Foundry test naming convention`)
+ }
+ }
+ }
+
+ searchInArray(array, searchString) {
+ return array.indexOf(searchString) !== -1
+ }
+}
+
+module.exports = FoundryTestFunctionNaming
diff --git a/lib/rules/naming/foundry-test-functions.js b/lib/rules/naming/foundry-test-functions.js
index 06996d7b..eb6127dc 100644
--- a/lib/rules/naming/foundry-test-functions.js
+++ b/lib/rules/naming/foundry-test-functions.js
@@ -10,7 +10,8 @@ const meta = {
type: 'naming',
docs: {
- description: `Enforce naming convention on functions for Foundry test cases`,
+ description:
+ 'Enforce naming convention on functions for Foundry test cases (DEPRECATED, use `foundry-test-functions-naming`)',
category: 'Style Guide Rules',
options: [
{
@@ -77,6 +78,9 @@ class FoundryTestFunctionsChecker extends BaseChecker {
this.skippedFunctions = config
? config.getArray(ruleId, DEFAULT_SKIP_FUNCTIONS)
: DEFAULT_SKIP_FUNCTIONS
+
+ // avoid spamming the deprecation warning
+ this._deprecationWarned = false
}
FunctionDefinition(node) {
@@ -87,7 +91,10 @@ class FoundryTestFunctionsChecker extends BaseChecker {
(node.visibility === 'public' || node.visibility === 'external')
) {
if (!naming.isFoundryTestCase(node.name)) {
- this.error(node, `Function ${node.name}() must match Foundry test naming convention`)
+ this.error(
+ node,
+ `[DEPRECATED] rule. Use "foundry-test-function-naming". Function ${node.name}() must match Foundry test naming convention`
+ )
}
}
}
diff --git a/lib/rules/naming/index.js b/lib/rules/naming/index.js
index 8399b7f5..9d2bb79c 100644
--- a/lib/rules/naming/index.js
+++ b/lib/rules/naming/index.js
@@ -10,7 +10,11 @@ const VarNameMixedcaseChecker = require('./var-name-mixedcase')
const NamedParametersMappingChecker = require('./named-parameters-mapping')
const ImmutableVarsNamingChecker = require('./immutable-vars-naming')
const FunctionNamedParametersChecker = require('./func-named-parameters')
+
+// ๐ old (alias with deprecation)
const FoundryTestFunctionsChecker = require('./foundry-test-functions')
+// ๐ new name
+const FoundryTestFunctionNaming = require('./foundry-test-function-naming')
module.exports = function checkers(reporter, config) {
return [
@@ -26,6 +30,9 @@ module.exports = function checkers(reporter, config) {
new NamedParametersMappingChecker(reporter),
new ImmutableVarsNamingChecker(reporter, config),
new FunctionNamedParametersChecker(reporter, config),
+
+ // ๐ call both
new FoundryTestFunctionsChecker(reporter, config),
+ new FoundryTestFunctionNaming(reporter, config),
]
}
diff --git a/test/rules/gas-consumption/gas-named-return-values.js b/test/rules/gas-consumption/gas-named-return-values.js
index 4b603c81..216edaca 100644
--- a/test/rules/gas-consumption/gas-named-return-values.js
+++ b/test/rules/gas-consumption/gas-named-return-values.js
@@ -76,6 +76,7 @@ describe('Linter - gas-named-return-values', () => {
'compiler-version': 'off',
'comprehensive-interface': 'off',
'foundry-test-functions': 'off',
+ 'foundry-test-function-naming': 'off',
'use-natspec': 'off',
},
})
diff --git a/test/rules/naming/foundry-test-function-naming.js b/test/rules/naming/foundry-test-function-naming.js
new file mode 100644
index 00000000..c016d170
--- /dev/null
+++ b/test/rules/naming/foundry-test-function-naming.js
@@ -0,0 +1,231 @@
+const assert = require('assert')
+const linter = require('../../../lib/index')
+const contractWith = require('../../common/contract-builder').contractWith
+const { assertErrorCount, assertNoErrors, assertErrorMessage } = require('../../common/asserts')
+
+const ALLOWED_FUNCTION_NAMES = [
+ 'test',
+ 'test_',
+ 'testFork_',
+ 'testFuzz_',
+ 'testFail_',
+ 'test_Revert_',
+ 'test_If_',
+ 'test_When_',
+ 'testFail_Revert_',
+ 'testFail_If_',
+ 'testFail_When_',
+ 'testFork_Revert_',
+ 'testFork_If_',
+ 'testFork_When_',
+ 'testFuzz_Revert_',
+ 'testFuzz_If_',
+ 'testFuzz_When_',
+ 'invariant',
+ 'invariant_',
+ 'invariantA',
+ 'statefulFuzz',
+ 'statefulFuzz_',
+]
+
+const DISALLOWED_FUNCTION_NAMES = ['Test_', 'Test', '', 'any', 'setUp', 'other', '_']
+
+const composeFunctionName = (prefix, name, visibility) =>
+ 'function ' + prefix + name + ' ' + visibility + ' { testNumber = 42; }'
+
+describe('Linter - foundry-test-function-naming', () => {
+ for (const prefix of DISALLOWED_FUNCTION_NAMES) {
+ it(`should raise error for DISALLOWED_FUNCTION_NAMES [${prefix}] when PUBLIC`, () => {
+ const functionDefinition = composeFunctionName(prefix, 'FunctionName()', 'public')
+ const code = contractWith(functionDefinition)
+
+ const report = linter.processStr(code, {
+ rules: { 'foundry-test-function-naming': ['error', ['setUp', 'finish']] },
+ })
+
+ assertErrorCount(report, 1)
+ assertErrorMessage(
+ report,
+ `Function ${prefix + 'FunctionName()'} must match Foundry test naming convention`
+ )
+ })
+ }
+
+ for (const prefix of DISALLOWED_FUNCTION_NAMES) {
+ it(`should NOT raise error for DISALLOWED_FUNCTION_NAMES [${prefix}] when INTERNAL`, () => {
+ const functionDefinition = composeFunctionName(prefix, 'FunctionName()', 'internal')
+ const code = contractWith(functionDefinition)
+
+ const report = linter.processStr(code, {
+ rules: { 'foundry-test-function-naming': ['error', ['setUp', 'finish']] },
+ })
+
+ assertNoErrors(report)
+ })
+ }
+
+ for (const prefix of ALLOWED_FUNCTION_NAMES) {
+ it(`should NOT raise error for ALLOWED_FUNCTION_NAMES [${prefix}] when PUBLIC`, () => {
+ const functionDefinition = composeFunctionName(prefix, 'FunctionName()', 'public')
+ const code = contractWith(functionDefinition)
+
+ const report = linter.processStr(code, {
+ rules: { 'foundry-test-function-naming': ['error', ['setUp', 'finish']] },
+ })
+
+ assertNoErrors(report)
+ })
+ }
+
+ for (const prefix of ALLOWED_FUNCTION_NAMES) {
+ it(`should NOT raise error for ALLOWED_FUNCTION_NAMES [${prefix}] when EXTERNAL`, () => {
+ const functionDefinition = composeFunctionName(prefix, 'FunctionName()', 'external')
+ const code = contractWith(functionDefinition)
+
+ const report = linter.processStr(code, {
+ rules: { 'foundry-test-function-naming': ['error', ['setUp', 'finish']] },
+ })
+
+ assertNoErrors(report)
+ })
+ }
+
+ for (const prefix of ALLOWED_FUNCTION_NAMES) {
+ it(`should NOT raise error for ALLOWED_FUNCTION_NAMES [${prefix}] when INTERNAL`, () => {
+ const functionDefinition = composeFunctionName(prefix, 'FunctionName()', 'external')
+ const code = contractWith(functionDefinition)
+
+ const report = linter.processStr(code, {
+ rules: { 'foundry-test-function-naming': ['error', ['setUp', 'finish']] },
+ })
+
+ assertNoErrors(report)
+ })
+ }
+
+ it(`should NOT raise error for setUp function, since is configured as SKIPPED`, () => {
+ const code = contractWith(
+ 'function setUp() public { testNumber = 42; } function finish() public { testNumber = 42; }'
+ )
+
+ const report = linter.processStr(code, {
+ rules: { 'foundry-test-function-naming': ['error', ['setUp', 'finish']] },
+ })
+
+ assertNoErrors(report)
+ })
+
+ it(`should NOT raise error for setUp and finish functions but RAISE for the other two functions`, () => {
+ const code = contractWith(`
+ function setUp() public { testNumber = 42; }
+ function finish() public { testNumber = 43; }
+ function invalidFunction1() external { testNumber = 44; }
+ function invalidFunction2() external { testNumber = 45; }`)
+
+ const report = linter.processStr(code, {
+ rules: { 'foundry-test-function-naming': ['error', ['setUp', 'finish']] },
+ })
+
+ assertErrorCount(report, 2)
+ assert.equal(
+ report.reports[0].message,
+ `Function invalidFunction1() must match Foundry test naming convention`
+ )
+ assert.equal(
+ report.reports[1].message,
+ 'Function invalidFunction2() must match Foundry test naming convention'
+ )
+ })
+
+ it('should NOT raise error when recommended rules are configured', () => {
+ const code = contractWith(`
+ function setUp() public { testNumber = 42; }
+ function finish() public { testNumber = 43; }
+ function invalidFunction1() external { testNumber = 44; }
+ function invalidFunction2() external { testNumber = 45; }`)
+
+ const report = linter.processStr(code, {
+ extends: 'solhint:recommended',
+ rules: { 'compiler-version': 'off' },
+ })
+
+ assertNoErrors(report)
+ })
+
+ it('should raise 2 errors when all rules are configured and setUp is skipped', () => {
+ const code = contractWith(`
+ function setUp() public { testNumber = 42; }
+ function invalidFunction1() external { testNumber = 44; }
+ function invalidFunction2() external { testNumber = 45; }`)
+
+ const report = linter.processStr(code, {
+ extends: 'solhint:recommended',
+ rules: {
+ 'compiler-version': 'off',
+ 'use-natspec': 'off',
+ 'foundry-test-function-naming': ['error', ['setUp', 'finish']],
+ },
+ })
+
+ assertErrorCount(report, 2)
+ assert.equal(
+ report.reports[0].message,
+ `Function invalidFunction1() must match Foundry test naming convention`
+ )
+ assert.equal(
+ report.reports[1].message,
+ 'Function invalidFunction2() must match Foundry test naming convention'
+ )
+ })
+
+ it(`should NOT raise error only for setUp when rule is just on 'error' (setUp is default)`, () => {
+ const code = contractWith(`
+ function setUp() public { testNumber = 42; }
+ function finish() public { testNumber = 43; }
+ function invalidFunction1() external { testNumber = 44; }
+ function invalidFunction2() external { testNumber = 45; }`)
+
+ const report = linter.processStr(code, {
+ rules: { 'foundry-test-function-naming': 'error' },
+ })
+
+ assertErrorCount(report, 3)
+ assert.equal(
+ report.reports[0].message,
+ 'Function finish() must match Foundry test naming convention'
+ )
+ assert.equal(
+ report.reports[1].message,
+ `Function invalidFunction1() must match Foundry test naming convention`
+ )
+ assert.equal(
+ report.reports[2].message,
+ 'Function invalidFunction2() must match Foundry test naming convention'
+ )
+ })
+
+ it(`should raise error for all functions when rule SKIP array is empty`, () => {
+ const code = contractWith(`
+ function setUp() public { testNumber = 42; }
+ function finish() public { testNumber = 43; }
+ function invalidFunction() external { testNumber = 44; }`)
+
+ const report = linter.processStr(code, {
+ rules: { 'foundry-test-function-naming': ['error', []] },
+ })
+
+ assertErrorCount(report, 3)
+ assert.equal(
+ report.reports[0].message,
+ 'Function setUp() must match Foundry test naming convention'
+ )
+ assert.equal(
+ report.reports[1].message,
+ `Function finish() must match Foundry test naming convention`
+ )
+ assert.equal(
+ report.reports[2].message,
+ 'Function invalidFunction() must match Foundry test naming convention'
+ )
+ })
+})
diff --git a/test/rules/naming/foundry-test-functions.js b/test/rules/naming/foundry-test-functions.js
index 18b6d1a7..f95b6518 100644
--- a/test/rules/naming/foundry-test-functions.js
+++ b/test/rules/naming/foundry-test-functions.js
@@ -30,6 +30,8 @@ const ALLOWED_FUNCTION_NAMES = [
const DISALLOWED_FUNCTION_NAMES = ['Test_', 'Test', '', 'any', 'setUp', 'other', '_']
+const DEPRECATED_ALERT = '[DEPRECATED] rule. Use "foundry-test-function-naming". '
+
const composeFunctionName = (prefix, name, visibility) =>
'function ' + prefix + name + ' ' + visibility + ' { testNumber = 42; }'
@@ -46,7 +48,8 @@ describe('Linter - foundry-test-functions', () => {
assertErrorCount(report, 1)
assertErrorMessage(
report,
- `Function ${prefix + 'FunctionName()'} must match Foundry test naming convention`
+ DEPRECATED_ALERT +
+ `Function ${prefix + 'FunctionName()'} must match Foundry test naming convention`
)
})
}
@@ -103,7 +106,7 @@ describe('Linter - foundry-test-functions', () => {
})
}
- it(`should NOT raise error for setUp function, since is confired as SKIPPED`, () => {
+ it(`should NOT raise error for setUp function, since is configured as SKIPPED`, () => {
const code = contractWith(
'function setUp() public { testNumber = 42; } function finish() public { testNumber = 42; }'
)
@@ -129,11 +132,11 @@ describe('Linter - foundry-test-functions', () => {
assertErrorCount(report, 2)
assert.equal(
report.reports[0].message,
- `Function invalidFunction1() must match Foundry test naming convention`
+ DEPRECATED_ALERT + 'Function invalidFunction1() must match Foundry test naming convention'
)
assert.equal(
report.reports[1].message,
- 'Function invalidFunction2() must match Foundry test naming convention'
+ DEPRECATED_ALERT + 'Function invalidFunction2() must match Foundry test naming convention'
)
})
@@ -170,11 +173,11 @@ describe('Linter - foundry-test-functions', () => {
assertErrorCount(report, 2)
assert.equal(
report.reports[0].message,
- `Function invalidFunction1() must match Foundry test naming convention`
+ DEPRECATED_ALERT + 'Function invalidFunction1() must match Foundry test naming convention'
)
assert.equal(
report.reports[1].message,
- 'Function invalidFunction2() must match Foundry test naming convention'
+ DEPRECATED_ALERT + 'Function invalidFunction2() must match Foundry test naming convention'
)
})
@@ -192,15 +195,15 @@ describe('Linter - foundry-test-functions', () => {
assertErrorCount(report, 3)
assert.equal(
report.reports[0].message,
- 'Function finish() must match Foundry test naming convention'
+ DEPRECATED_ALERT + 'Function finish() must match Foundry test naming convention'
)
assert.equal(
report.reports[1].message,
- `Function invalidFunction1() must match Foundry test naming convention`
+ DEPRECATED_ALERT + 'Function invalidFunction1() must match Foundry test naming convention'
)
assert.equal(
report.reports[2].message,
- 'Function invalidFunction2() must match Foundry test naming convention'
+ DEPRECATED_ALERT + 'Function invalidFunction2() must match Foundry test naming convention'
)
})
@@ -217,15 +220,15 @@ describe('Linter - foundry-test-functions', () => {
assertErrorCount(report, 3)
assert.equal(
report.reports[0].message,
- 'Function setUp() must match Foundry test naming convention'
+ DEPRECATED_ALERT + 'Function setUp() must match Foundry test naming convention'
)
assert.equal(
report.reports[1].message,
- `Function finish() must match Foundry test naming convention`
+ DEPRECATED_ALERT + `Function finish() must match Foundry test naming convention`
)
assert.equal(
report.reports[2].message,
- 'Function invalidFunction() must match Foundry test naming convention'
+ DEPRECATED_ALERT + 'Function invalidFunction() must match Foundry test naming convention'
)
})
})
diff --git a/test/rules/naming/func-named-parameters.js b/test/rules/naming/func-named-parameters.js
index a5f7504a..c4f6f50e 100644
--- a/test/rules/naming/func-named-parameters.js
+++ b/test/rules/naming/func-named-parameters.js
@@ -71,6 +71,7 @@ describe('Linter - func-named-parameters', () => {
'compiler-version': 'off',
'comprehensive-interface': 'off',
'foundry-test-functions': 'off',
+ 'foundry-test-function-naming': 'off',
'use-natspec': 'off',
},
})