github.com/ethereum/go-ethereum@v1.16.1/cmd/workload/testsuite.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "embed" 21 "fmt" 22 "io/fs" 23 "os" 24 25 "github.com/ethereum/go-ethereum/core/history" 26 "github.com/ethereum/go-ethereum/internal/flags" 27 "github.com/ethereum/go-ethereum/internal/utesting" 28 "github.com/ethereum/go-ethereum/log" 29 "github.com/ethereum/go-ethereum/params" 30 "github.com/ethereum/go-ethereum/rpc" 31 "github.com/urfave/cli/v2" 32 ) 33 34 //go:embed queries 35 var builtinTestFiles embed.FS 36 37 var ( 38 runTestCommand = &cli.Command{ 39 Name: "test", 40 Usage: "Runs workload tests against an RPC endpoint", 41 ArgsUsage: "<RPC endpoint URL>", 42 Action: runTestCmd, 43 Flags: []cli.Flag{ 44 testPatternFlag, 45 testTAPFlag, 46 testSlowFlag, 47 testArchiveFlag, 48 testSepoliaFlag, 49 testMainnetFlag, 50 filterQueryFileFlag, 51 historyTestFileFlag, 52 traceTestFileFlag, 53 traceTestInvalidOutputFlag, 54 }, 55 } 56 testPatternFlag = &cli.StringFlag{ 57 Name: "run", 58 Usage: "Pattern of test suite(s) to run", 59 Category: flags.TestingCategory, 60 } 61 testTAPFlag = &cli.BoolFlag{ 62 Name: "tap", 63 Usage: "Output test results in TAP format", 64 Category: flags.TestingCategory, 65 } 66 testSlowFlag = &cli.BoolFlag{ 67 Name: "slow", 68 Usage: "Enable slow tests", 69 Value: false, 70 Category: flags.TestingCategory, 71 } 72 testArchiveFlag = &cli.BoolFlag{ 73 Name: "archive", 74 Usage: "Enable archive tests", 75 Value: false, 76 Category: flags.TestingCategory, 77 } 78 testSepoliaFlag = &cli.BoolFlag{ 79 Name: "sepolia", 80 Usage: "Use test cases for sepolia network", 81 Category: flags.TestingCategory, 82 } 83 testMainnetFlag = &cli.BoolFlag{ 84 Name: "mainnet", 85 Usage: "Use test cases for mainnet network", 86 Category: flags.TestingCategory, 87 } 88 ) 89 90 // testConfig holds the parameters for testing. 91 type testConfig struct { 92 client *client 93 fsys fs.FS 94 filterQueryFile string 95 historyTestFile string 96 historyPruneBlock *uint64 97 traceTestFile string 98 } 99 100 var errPrunedHistory = fmt.Errorf("attempt to access pruned history") 101 102 // validateHistoryPruneErr checks whether the given error is caused by access 103 // to history before the pruning threshold block (it is an rpc.Error with code 4444). 104 // In this case, errPrunedHistory is returned. 105 // If the error is a pruned history error that occurs when accessing a block past the 106 // historyPrune block, an error is returned. 107 // Otherwise, the original value of err is returned. 108 func validateHistoryPruneErr(err error, blockNum uint64, historyPruneBlock *uint64) error { 109 if err != nil { 110 if rpcErr, ok := err.(rpc.Error); ok && rpcErr.ErrorCode() == 4444 { 111 if historyPruneBlock != nil && blockNum > *historyPruneBlock { 112 return fmt.Errorf("pruned history error returned after pruning threshold") 113 } 114 return errPrunedHistory 115 } 116 } 117 return err 118 } 119 120 func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) { 121 flags.CheckExclusive(ctx, testMainnetFlag, testSepoliaFlag) 122 if (ctx.IsSet(testMainnetFlag.Name) || ctx.IsSet(testSepoliaFlag.Name)) && ctx.IsSet(filterQueryFileFlag.Name) { 123 exit(filterQueryFileFlag.Name + " cannot be used with " + testMainnetFlag.Name + " or " + testSepoliaFlag.Name) 124 } 125 126 // configure ethclient 127 cfg.client = makeClient(ctx) 128 129 // configure test files 130 switch { 131 case ctx.Bool(testMainnetFlag.Name): 132 cfg.fsys = builtinTestFiles 133 cfg.filterQueryFile = "queries/filter_queries_mainnet.json" 134 cfg.historyTestFile = "queries/history_mainnet.json" 135 cfg.historyPruneBlock = new(uint64) 136 *cfg.historyPruneBlock = history.PrunePoints[params.MainnetGenesisHash].BlockNumber 137 cfg.traceTestFile = "queries/trace_mainnet.json" 138 case ctx.Bool(testSepoliaFlag.Name): 139 cfg.fsys = builtinTestFiles 140 cfg.filterQueryFile = "queries/filter_queries_sepolia.json" 141 cfg.historyTestFile = "queries/history_sepolia.json" 142 cfg.historyPruneBlock = new(uint64) 143 *cfg.historyPruneBlock = history.PrunePoints[params.SepoliaGenesisHash].BlockNumber 144 cfg.traceTestFile = "queries/trace_sepolia.json" 145 default: 146 cfg.fsys = os.DirFS(".") 147 cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name) 148 cfg.historyTestFile = ctx.String(historyTestFileFlag.Name) 149 cfg.traceTestFile = ctx.String(traceTestFileFlag.Name) 150 } 151 return cfg 152 } 153 154 // workloadTest represents a single test in the workload. It's a wrapper 155 // of utesting.Test by adding a few additional attributes. 156 type workloadTest struct { 157 utesting.Test 158 159 archive bool // Flag whether the archive node (full state history) is required for this test 160 } 161 162 func newWorkLoadTest(name string, fn func(t *utesting.T)) workloadTest { 163 return workloadTest{ 164 Test: utesting.Test{ 165 Name: name, 166 Fn: fn, 167 }, 168 } 169 } 170 171 func newSlowWorkloadTest(name string, fn func(t *utesting.T)) workloadTest { 172 t := newWorkLoadTest(name, fn) 173 t.Slow = true 174 return t 175 } 176 177 func newArchiveWorkloadTest(name string, fn func(t *utesting.T)) workloadTest { 178 t := newWorkLoadTest(name, fn) 179 t.archive = true 180 return t 181 } 182 183 func filterTests(tests []workloadTest, pattern string, filterFn func(t workloadTest) bool) []utesting.Test { 184 var utests []utesting.Test 185 for _, t := range tests { 186 if filterFn(t) { 187 utests = append(utests, t.Test) 188 } 189 } 190 if pattern == "" { 191 return utests 192 } 193 return utesting.MatchTests(utests, pattern) 194 } 195 196 func runTestCmd(ctx *cli.Context) error { 197 cfg := testConfigFromCLI(ctx) 198 filterSuite := newFilterTestSuite(cfg) 199 historySuite := newHistoryTestSuite(cfg) 200 traceSuite := newTraceTestSuite(cfg, ctx) 201 202 // Filter test cases. 203 tests := filterSuite.allTests() 204 tests = append(tests, historySuite.allTests()...) 205 tests = append(tests, traceSuite.allTests()...) 206 207 utests := filterTests(tests, ctx.String(testPatternFlag.Name), func(t workloadTest) bool { 208 if t.Slow && !ctx.Bool(testSlowFlag.Name) { 209 return false 210 } 211 if t.archive && !ctx.Bool(testArchiveFlag.Name) { 212 return false 213 } 214 return true 215 }) 216 217 // Disable logging unless explicitly enabled. 218 if !ctx.IsSet("verbosity") && !ctx.IsSet("vmodule") { 219 log.SetDefault(log.NewLogger(log.DiscardHandler())) 220 } 221 222 // Run the tests. 223 var run = utesting.RunTests 224 if ctx.Bool(testTAPFlag.Name) { 225 run = utesting.RunTAP 226 } 227 results := run(utests, os.Stdout) 228 if utesting.CountFailures(results) > 0 { 229 os.Exit(1) 230 } 231 return nil 232 }