github.com/jimmyx0x/go-ethereum@v1.10.28/tests/init_test.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser 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 // The go-ethereum library 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 Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package tests 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "io" 23 "os" 24 "path/filepath" 25 "reflect" 26 "regexp" 27 "runtime" 28 "sort" 29 "strings" 30 "testing" 31 32 "github.com/ethereum/go-ethereum/params" 33 ) 34 35 var ( 36 baseDir = filepath.Join(".", "testdata") 37 blockTestDir = filepath.Join(baseDir, "BlockchainTests") 38 stateTestDir = filepath.Join(baseDir, "GeneralStateTests") 39 legacyStateTestDir = filepath.Join(baseDir, "LegacyTests", "Constantinople", "GeneralStateTests") 40 transactionTestDir = filepath.Join(baseDir, "TransactionTests") 41 rlpTestDir = filepath.Join(baseDir, "RLPTests") 42 difficultyTestDir = filepath.Join(baseDir, "BasicTests") 43 benchmarksDir = filepath.Join(".", "evm-benchmarks", "benchmarks") 44 ) 45 46 func readJSON(reader io.Reader, value interface{}) error { 47 data, err := io.ReadAll(reader) 48 if err != nil { 49 return fmt.Errorf("error reading JSON file: %v", err) 50 } 51 if err = json.Unmarshal(data, &value); err != nil { 52 if syntaxerr, ok := err.(*json.SyntaxError); ok { 53 line := findLine(data, syntaxerr.Offset) 54 return fmt.Errorf("JSON syntax error at line %v: %v", line, err) 55 } 56 return err 57 } 58 return nil 59 } 60 61 func readJSONFile(fn string, value interface{}) error { 62 file, err := os.Open(fn) 63 if err != nil { 64 return err 65 } 66 defer file.Close() 67 68 err = readJSON(file, value) 69 if err != nil { 70 return fmt.Errorf("%s in file %s", err.Error(), fn) 71 } 72 return nil 73 } 74 75 // findLine returns the line number for the given offset into data. 76 func findLine(data []byte, offset int64) (line int) { 77 line = 1 78 for i, r := range string(data) { 79 if int64(i) >= offset { 80 return 81 } 82 if r == '\n' { 83 line++ 84 } 85 } 86 return 87 } 88 89 // testMatcher controls skipping and chain config assignment to tests. 90 type testMatcher struct { 91 configpat []testConfig 92 failpat []testFailure 93 skiploadpat []*regexp.Regexp 94 slowpat []*regexp.Regexp 95 runonlylistpat *regexp.Regexp 96 } 97 98 type testConfig struct { 99 p *regexp.Regexp 100 config params.ChainConfig 101 } 102 103 type testFailure struct { 104 p *regexp.Regexp 105 reason string 106 } 107 108 // skipShortMode skips tests matching when the -short flag is used. 109 func (tm *testMatcher) slow(pattern string) { 110 tm.slowpat = append(tm.slowpat, regexp.MustCompile(pattern)) 111 } 112 113 // skipLoad skips JSON loading of tests matching the pattern. 114 func (tm *testMatcher) skipLoad(pattern string) { 115 tm.skiploadpat = append(tm.skiploadpat, regexp.MustCompile(pattern)) 116 } 117 118 // fails adds an expected failure for tests matching the pattern. 119 // 120 //nolint:unused 121 func (tm *testMatcher) fails(pattern string, reason string) { 122 if reason == "" { 123 panic("empty fail reason") 124 } 125 tm.failpat = append(tm.failpat, testFailure{regexp.MustCompile(pattern), reason}) 126 } 127 128 func (tm *testMatcher) runonly(pattern string) { 129 tm.runonlylistpat = regexp.MustCompile(pattern) 130 } 131 132 // config defines chain config for tests matching the pattern. 133 func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) { 134 tm.configpat = append(tm.configpat, testConfig{regexp.MustCompile(pattern), cfg}) 135 } 136 137 // findSkip matches name against test skip patterns. 138 func (tm *testMatcher) findSkip(name string) (reason string, skipload bool) { 139 isWin32 := runtime.GOARCH == "386" && runtime.GOOS == "windows" 140 for _, re := range tm.slowpat { 141 if re.MatchString(name) { 142 if testing.Short() { 143 return "skipped in -short mode", false 144 } 145 if isWin32 { 146 return "skipped on 32bit windows", false 147 } 148 } 149 } 150 for _, re := range tm.skiploadpat { 151 if re.MatchString(name) { 152 return "skipped by skipLoad", true 153 } 154 } 155 return "", false 156 } 157 158 // findConfig returns the chain config matching defined patterns. 159 func (tm *testMatcher) findConfig(t *testing.T) *params.ChainConfig { 160 for _, m := range tm.configpat { 161 if m.p.MatchString(t.Name()) { 162 return &m.config 163 } 164 } 165 return new(params.ChainConfig) 166 } 167 168 // checkFailure checks whether a failure is expected. 169 func (tm *testMatcher) checkFailure(t *testing.T, err error) error { 170 failReason := "" 171 for _, m := range tm.failpat { 172 if m.p.MatchString(t.Name()) { 173 failReason = m.reason 174 break 175 } 176 } 177 if failReason != "" { 178 t.Logf("expected failure: %s", failReason) 179 if err != nil { 180 t.Logf("error: %v", err) 181 return nil 182 } 183 return fmt.Errorf("test succeeded unexpectedly") 184 } 185 return err 186 } 187 188 // walk invokes its runTest argument for all subtests in the given directory. 189 // 190 // runTest should be a function of type func(t *testing.T, name string, x <TestType>), 191 // where TestType is the type of the test contained in test files. 192 func (tm *testMatcher) walk(t *testing.T, dir string, runTest interface{}) { 193 // Walk the directory. 194 dirinfo, err := os.Stat(dir) 195 if os.IsNotExist(err) || !dirinfo.IsDir() { 196 fmt.Fprintf(os.Stderr, "can't find test files in %s, did you clone the tests submodule?\n", dir) 197 t.Skip("missing test files") 198 } 199 err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 200 name := filepath.ToSlash(strings.TrimPrefix(path, dir+string(filepath.Separator))) 201 if info.IsDir() { 202 if _, skipload := tm.findSkip(name + "/"); skipload { 203 return filepath.SkipDir 204 } 205 return nil 206 } 207 if filepath.Ext(path) == ".json" { 208 t.Run(name, func(t *testing.T) { tm.runTestFile(t, path, name, runTest) }) 209 } 210 return nil 211 }) 212 if err != nil { 213 t.Fatal(err) 214 } 215 } 216 217 func (tm *testMatcher) runTestFile(t *testing.T, path, name string, runTest interface{}) { 218 if r, _ := tm.findSkip(name); r != "" { 219 t.Skip(r) 220 } 221 if tm.runonlylistpat != nil { 222 if !tm.runonlylistpat.MatchString(name) { 223 t.Skip("Skipped by runonly") 224 } 225 } 226 t.Parallel() 227 228 // Load the file as map[string]<testType>. 229 m := makeMapFromTestFunc(runTest) 230 if err := readJSONFile(path, m.Addr().Interface()); err != nil { 231 t.Fatal(err) 232 } 233 234 // Run all tests from the map. Don't wrap in a subtest if there is only one test in the file. 235 keys := sortedMapKeys(m) 236 if len(keys) == 1 { 237 runTestFunc(runTest, t, name, m, keys[0]) 238 } else { 239 for _, key := range keys { 240 name := name + "/" + key 241 t.Run(key, func(t *testing.T) { 242 if r, _ := tm.findSkip(name); r != "" { 243 t.Skip(r) 244 } 245 runTestFunc(runTest, t, name, m, key) 246 }) 247 } 248 } 249 } 250 251 func makeMapFromTestFunc(f interface{}) reflect.Value { 252 stringT := reflect.TypeOf("") 253 testingT := reflect.TypeOf((*testing.T)(nil)) 254 ftyp := reflect.TypeOf(f) 255 if ftyp.Kind() != reflect.Func || ftyp.NumIn() != 3 || ftyp.NumOut() != 0 || ftyp.In(0) != testingT || ftyp.In(1) != stringT { 256 panic(fmt.Sprintf("bad test function type: want func(*testing.T, string, <TestType>), have %s", ftyp)) 257 } 258 testType := ftyp.In(2) 259 mp := reflect.New(reflect.MapOf(stringT, testType)) 260 return mp.Elem() 261 } 262 263 func sortedMapKeys(m reflect.Value) []string { 264 keys := make([]string, m.Len()) 265 for i, k := range m.MapKeys() { 266 keys[i] = k.String() 267 } 268 sort.Strings(keys) 269 return keys 270 } 271 272 func runTestFunc(runTest interface{}, t *testing.T, name string, m reflect.Value, key string) { 273 reflect.ValueOf(runTest).Call([]reflect.Value{ 274 reflect.ValueOf(t), 275 reflect.ValueOf(name), 276 m.MapIndex(reflect.ValueOf(key)), 277 }) 278 } 279 280 func TestMatcherRunonlylist(t *testing.T) { 281 t.Parallel() 282 tm := new(testMatcher) 283 tm.runonly("invalid*") 284 tm.walk(t, rlpTestDir, func(t *testing.T, name string, test *RLPTest) { 285 if name[:len("invalidRLPTest.json")] != "invalidRLPTest.json" { 286 t.Fatalf("invalid test found: %s != invalidRLPTest.json", name) 287 } 288 }) 289 }