Commit 24cb20d4 authored by Mark Tyneway's avatar Mark Tyneway

contracts-bedrock: remove glob dependency

The script that ensures that all tests have the correct
naming scheme used an external package named glob. This
package was not actually necessary, altho it was convenient
to use. This commit refactors the script to not use the glob
dependency. It also now logs all of the files it successfully
processes, so that we can be sure that it is running on all
of the expected files. It uses the `IS_TEST` getter in the
abi to determine if something is a test, which comes from
forge-std `Test` contract. It also now uses `forge config --json`
to parse the `out` location for the artifacts, so that it can
be changed without needing to update the script.
parent 6c19d7f1
......@@ -48,7 +48,6 @@
"@typescript-eslint/parser": "^5.60.1",
"ds-test": "github:dapphub/ds-test#c9ce3f25bde29fc5eb9901842bf02850dfd2d084",
"forge-std": "github:foundry-rs/forge-std#e8a047e3f40f13fa37af6fe14e6e06283d9a060e",
"glob": "^7.1.6",
"solhint": "^3.4.1",
"solhint-plugin-prettier": "^0.0.5",
"ts-node": "^10.9.1",
......
import fs from 'fs'
import { glob } from 'glob'
import path from 'path'
import { execSync } from 'child_process'
/**
* Series of function name checks.
......@@ -9,77 +9,117 @@ const checks: Array<{
check: (parts: string[]) => boolean
error: string
}> = [
{
error: 'test name parts should be in camelCase',
check: (parts: string[]): boolean => {
return parts.every((part) => {
return part[0] === part[0].toLowerCase()
})
{
error: 'test name parts should be in camelCase',
check: (parts: string[]): boolean => {
return parts.every((part) => {
return part[0] === part[0].toLowerCase()
})
},
},
},
{
error:
'test names should have either 3 or 4 parts, each separated by underscores',
check: (parts: string[]): boolean => {
return parts.length === 3 || parts.length === 4
{
error:
'test names should have either 3 or 4 parts, each separated by underscores',
check: (parts: string[]): boolean => {
return parts.length === 3 || parts.length === 4
},
},
},
{
error: 'test names should begin with "test", "testFuzz", or "testDiff"',
check: (parts: string[]): boolean => {
return ['test', 'testFuzz', 'testDiff'].includes(parts[0])
{
error: 'test names should begin with "test", "testFuzz", or "testDiff"',
check: (parts: string[]): boolean => {
return ['test', 'testFuzz', 'testDiff'].includes(parts[0])
},
},
},
{
error:
'test names should end with either "succeeds", "reverts", "fails", "works" or "benchmark[_num]"',
check: (parts: string[]): boolean => {
return (
['succeeds', 'reverts', 'fails', 'benchmark', 'works'].includes(
parts[parts.length - 1]
) ||
(parts[parts.length - 2] === 'benchmark' &&
!isNaN(parseInt(parts[parts.length - 1], 10)))
)
{
error:
'test names should end with either "succeeds", "reverts", "fails", "works" or "benchmark[_num]"',
check: (parts: string[]): boolean => {
return (
['succeeds', 'reverts', 'fails', 'benchmark', 'works'].includes(
parts[parts.length - 1]
) ||
(parts[parts.length - 2] === 'benchmark' &&
!isNaN(parseInt(parts[parts.length - 1], 10)))
)
},
},
},
{
error:
'failure tests should have 4 parts, third part should indicate the reason for failure',
check: (parts: string[]): boolean => {
return (
parts.length === 4 ||
!['reverts', 'fails'].includes(parts[parts.length - 1])
)
{
error:
'failure tests should have 4 parts, third part should indicate the reason for failure',
check: (parts: string[]): boolean => {
return (
parts.length === 4 ||
!['reverts', 'fails'].includes(parts[parts.length - 1])
)
},
},
},
]
]
/**
* Script for checking that all test functions are named correctly.
*/
const main = async () => {
const result = execSync('forge config --json')
const config = JSON.parse(result.toString())
const out = config.out || 'out'
const paths = []
const readFilesRecursively = (dir: string) => {
const files = fs.readdirSync(dir)
for (const file of files) {
const filePath = path.join(dir, file)
const fileStat = fs.statSync(filePath)
if (fileStat.isDirectory()) {
readFilesRecursively(filePath)
} else {
paths.push(filePath)
}
}
}
readFilesRecursively(out)
console.log('Success:')
const errors: string[] = []
const files = glob.sync('./forge-artifacts/**/*.t.sol/*Test*.json')
for (const file of files) {
const artifact = JSON.parse(fs.readFileSync(file, 'utf8'))
for (const filepath of paths) {
const artifact = JSON.parse(fs.readFileSync(filepath, 'utf8'))
let isTest = false
for (const element of artifact.abi) {
// Skip non-functions and functions that don't start with "test".
if (element.type !== 'function' || !element.name.startsWith('test')) {
continue
if (element.name === 'IS_TEST') {
isTest = true
break
}
}
if (isTest) {
let success = true
for (const element of artifact.abi) {
// Skip non-functions and functions that don't start with "test".
if (element.type !== 'function' || !element.name.startsWith('test')) {
continue
}
// Check the rest.
for (const { check, error } of checks) {
if (!check(element.name.split('_'))) {
errors.push(`in ${file} function ${element.name}: ${error}`)
// Check the rest.
for (const { check, error } of checks) {
if (!check(element.name.split('_'))) {
errors.push(`${filepath} function ${element.name}: ${error}`)
success = false
}
}
}
if (success) {
console.log(` - ${path.parse(filepath).name}`)
}
}
}
if (errors.length > 0) {
console.error(...errors)
console.error(errors.join('\n'))
process.exit(1)
}
}
......
......@@ -277,9 +277,6 @@ importers:
forge-std:
specifier: github:foundry-rs/forge-std#e8a047e3f40f13fa37af6fe14e6e06283d9a060e
version: github.com/foundry-rs/forge-std/e8a047e3f40f13fa37af6fe14e6e06283d9a060e
glob:
specifier: ^7.1.6
version: 7.1.6
solhint:
specifier: ^3.4.1
version: 3.4.1
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment