-
Notifications
You must be signed in to change notification settings - Fork 3.5k
/
Copy pathcheck-test-names.ts
130 lines (115 loc) · 3.16 KB
/
check-test-names.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import fs from 'fs'
import path from 'path'
import { execSync } from 'child_process'
type Check = (parts: string[]) => boolean
type Checks = Array<{
check: Check
error: string
}>
/**
* Series of function name checks.
*/
const checks: Checks = [
{
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 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:
'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[] = []
for (const filepath of paths) {
const artifact = JSON.parse(fs.readFileSync(filepath, 'utf8'))
let isTest = false
for (const element of artifact.abi) {
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(`${filepath}#${element.name}: ${error}`)
success = false
}
}
}
if (success) {
console.log(` - ${path.parse(filepath).name}`)
}
}
}
if (errors.length > 0) {
console.error(errors.join('\n'))
process.exit(1)
}
}
main()