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