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