github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/scripts/checks/check-test-names.ts (about)

     1  import fs from 'fs'
     2  import path from 'path'
     3  import { execSync } from 'child_process'
     4  
     5  type Check = (parts: string[]) => boolean
     6  type Checks = Array<{
     7    check: Check
     8    error: string
     9  }>
    10  
    11  /**
    12   * Series of function name checks.
    13   */
    14  const checks: Checks = [
    15    {
    16      error: 'test name parts should be in camelCase',
    17      check: (parts: string[]): boolean => {
    18        return parts.every((part) => {
    19          return part[0] === part[0].toLowerCase()
    20        })
    21      },
    22    },
    23    {
    24      error:
    25        'test names should have either 3 or 4 parts, each separated by underscores',
    26      check: (parts: string[]): boolean => {
    27        return parts.length === 3 || parts.length === 4
    28      },
    29    },
    30    {
    31      error: 'test names should begin with "test", "testFuzz", or "testDiff"',
    32      check: (parts: string[]): boolean => {
    33        return ['test', 'testFuzz', 'testDiff'].includes(parts[0])
    34      },
    35    },
    36    {
    37      error:
    38        'test names should end with either "succeeds", "reverts", "fails", "works" or "benchmark[_num]"',
    39      check: (parts: string[]): boolean => {
    40        return (
    41          ['succeeds', 'reverts', 'fails', 'benchmark', 'works'].includes(
    42            parts[parts.length - 1]
    43          ) ||
    44          (parts[parts.length - 2] === 'benchmark' &&
    45            !isNaN(parseInt(parts[parts.length - 1], 10)))
    46        )
    47      },
    48    },
    49    {
    50      error:
    51        'failure tests should have 4 parts, third part should indicate the reason for failure',
    52      check: (parts: string[]): boolean => {
    53        return (
    54          parts.length === 4 ||
    55          !['reverts', 'fails'].includes(parts[parts.length - 1])
    56        )
    57      },
    58    },
    59  ]
    60  
    61  /**
    62   * Script for checking that all test functions are named correctly.
    63   */
    64  const main = async () => {
    65    const result = execSync('forge config --json')
    66    const config = JSON.parse(result.toString())
    67    const out = config.out || 'out'
    68  
    69    const paths = []
    70  
    71    const readFilesRecursively = (dir: string) => {
    72      const files = fs.readdirSync(dir)
    73  
    74      for (const file of files) {
    75        const filePath = path.join(dir, file)
    76        const fileStat = fs.statSync(filePath)
    77  
    78        if (fileStat.isDirectory()) {
    79          readFilesRecursively(filePath)
    80        } else {
    81          paths.push(filePath)
    82        }
    83      }
    84    }
    85  
    86    readFilesRecursively(out)
    87  
    88    console.log('Success:')
    89    const errors: string[] = []
    90  
    91    for (const filepath of paths) {
    92      const artifact = JSON.parse(fs.readFileSync(filepath, 'utf8'))
    93  
    94      let isTest = false
    95      for (const element of artifact.abi) {
    96        if (element.name === 'IS_TEST') {
    97          isTest = true
    98          break
    99        }
   100      }
   101  
   102      if (isTest) {
   103        let success = true
   104        for (const element of artifact.abi) {
   105          // Skip non-functions and functions that don't start with "test".
   106          if (element.type !== 'function' || !element.name.startsWith('test')) {
   107            continue
   108          }
   109  
   110          // Check the rest.
   111          for (const { check, error } of checks) {
   112            if (!check(element.name.split('_'))) {
   113              errors.push(`${filepath}#${element.name}: ${error}`)
   114              success = false
   115            }
   116          }
   117        }
   118        if (success) {
   119          console.log(` - ${path.parse(filepath).name}`)
   120        }
   121      }
   122    }
   123  
   124    if (errors.length > 0) {
   125      console.error(errors.join('\n'))
   126      process.exit(1)
   127    }
   128  }
   129  
   130  main()