github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/tests/init_test.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:45</date> 10 //</624450122301837312> 11 12 13 package tests 14 15 import ( 16 "encoding/json" 17 "fmt" 18 "io" 19 "io/ioutil" 20 "os" 21 "path/filepath" 22 "reflect" 23 "regexp" 24 "runtime" 25 "sort" 26 "strings" 27 "testing" 28 29 "github.com/ethereum/go-ethereum/params" 30 ) 31 32 var ( 33 baseDir = filepath.Join(".", "testdata") 34 blockTestDir = filepath.Join(baseDir, "BlockchainTests") 35 stateTestDir = filepath.Join(baseDir, "GeneralStateTests") 36 transactionTestDir = filepath.Join(baseDir, "TransactionTests") 37 vmTestDir = filepath.Join(baseDir, "VMTests") 38 rlpTestDir = filepath.Join(baseDir, "RLPTests") 39 difficultyTestDir = filepath.Join(baseDir, "BasicTests") 40 ) 41 42 func readJSON(reader io.Reader, value interface{}) error { 43 data, err := ioutil.ReadAll(reader) 44 if err != nil { 45 return fmt.Errorf("error reading JSON file: %v", err) 46 } 47 if err = json.Unmarshal(data, &value); err != nil { 48 if syntaxerr, ok := err.(*json.SyntaxError); ok { 49 line := findLine(data, syntaxerr.Offset) 50 return fmt.Errorf("JSON syntax error at line %v: %v", line, err) 51 } 52 return err 53 } 54 return nil 55 } 56 57 func readJSONFile(fn string, value interface{}) error { 58 file, err := os.Open(fn) 59 if err != nil { 60 return err 61 } 62 defer file.Close() 63 64 err = readJSON(file, value) 65 if err != nil { 66 return fmt.Errorf("%s in file %s", err.Error(), fn) 67 } 68 return nil 69 } 70 71 //findline将给定偏移量的行号返回到数据中。 72 func findLine(data []byte, offset int64) (line int) { 73 line = 1 74 for i, r := range string(data) { 75 if int64(i) >= offset { 76 return 77 } 78 if r == '\n' { 79 line++ 80 } 81 } 82 return 83 } 84 85 //TestMatcher控制对测试的跳过和链配置分配。 86 type testMatcher struct { 87 configpat []testConfig 88 failpat []testFailure 89 skiploadpat []*regexp.Regexp 90 slowpat []*regexp.Regexp 91 whitelistpat *regexp.Regexp 92 } 93 94 type testConfig struct { 95 p *regexp.Regexp 96 config params.ChainConfig 97 } 98 99 type testFailure struct { 100 p *regexp.Regexp 101 reason string 102 } 103 104 //使用-short标志时,SkipShortMode跳过匹配的测试。 105 func (tm *testMatcher) slow(pattern string) { 106 tm.slowpat = append(tm.slowpat, regexp.MustCompile(pattern)) 107 } 108 109 //SkipLoad跳过与模式匹配的测试的JSON加载。 110 func (tm *testMatcher) skipLoad(pattern string) { 111 tm.skiploadpat = append(tm.skiploadpat, regexp.MustCompile(pattern)) 112 } 113 114 //失败为匹配模式的测试添加预期失败。 115 func (tm *testMatcher) fails(pattern string, reason string) { 116 if reason == "" { 117 panic("empty fail reason") 118 } 119 tm.failpat = append(tm.failpat, testFailure{regexp.MustCompile(pattern), reason}) 120 } 121 122 func (tm *testMatcher) whitelist(pattern string) { 123 tm.whitelistpat = regexp.MustCompile(pattern) 124 } 125 126 //config为匹配模式的测试定义链配置。 127 func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) { 128 tm.configpat = append(tm.configpat, testConfig{regexp.MustCompile(pattern), cfg}) 129 } 130 131 //findskip根据测试跳过模式匹配名称。 132 func (tm *testMatcher) findSkip(name string) (reason string, skipload bool) { 133 isWin32 := runtime.GOARCH == "386" && runtime.GOOS == "windows" 134 for _, re := range tm.slowpat { 135 if re.MatchString(name) { 136 if testing.Short() { 137 return "skipped in -short mode", false 138 } 139 if isWin32 { 140 return "skipped on 32bit windows", false 141 } 142 } 143 } 144 for _, re := range tm.skiploadpat { 145 if re.MatchString(name) { 146 return "skipped by skipLoad", true 147 } 148 } 149 return "", false 150 } 151 152 //findconfig返回与定义的模式匹配的链配置。 153 func (tm *testMatcher) findConfig(name string) *params.ChainConfig { 154 //todo(fjl):当min go版本为1.8时,可以从testing.t派生名称。 155 for _, m := range tm.configpat { 156 if m.p.MatchString(name) { 157 return &m.config 158 } 159 } 160 return new(params.ChainConfig) 161 } 162 163 //checkfailure检查是否需要失败。 164 func (tm *testMatcher) checkFailure(t *testing.T, name string, err error) error { 165 //todo(fjl):当min go版本为1.8时,可以从t派生名称 166 failReason := "" 167 for _, m := range tm.failpat { 168 if m.p.MatchString(name) { 169 failReason = m.reason 170 break 171 } 172 } 173 if failReason != "" { 174 t.Logf("expected failure: %s", failReason) 175 if err != nil { 176 t.Logf("error: %v", err) 177 return nil 178 } 179 return fmt.Errorf("test succeeded unexpectedly") 180 } 181 return err 182 } 183 184 //walk为给定目录中的所有子测试调用其runtest参数。 185 // 186 //runtest应该是func类型的函数(t*testing.t,name string,x<testtype>), 187 //其中test type是包含在测试文件中的测试类型。 188 func (tm *testMatcher) walk(t *testing.T, dir string, runTest interface{}) { 189 //浏览目录。 190 dirinfo, err := os.Stat(dir) 191 if os.IsNotExist(err) || !dirinfo.IsDir() { 192 fmt.Fprintf(os.Stderr, "can't find test files in %s, did you clone the tests submodule?\n", dir) 193 t.Skip("missing test files") 194 } 195 err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 196 name := filepath.ToSlash(strings.TrimPrefix(path, dir+string(filepath.Separator))) 197 if info.IsDir() { 198 if _, skipload := tm.findSkip(name + "/"); skipload { 199 return filepath.SkipDir 200 } 201 return nil 202 } 203 if filepath.Ext(path) == ".json" { 204 t.Run(name, func(t *testing.T) { tm.runTestFile(t, path, name, runTest) }) 205 } 206 return nil 207 }) 208 if err != nil { 209 t.Fatal(err) 210 } 211 } 212 213 func (tm *testMatcher) runTestFile(t *testing.T, path, name string, runTest interface{}) { 214 if r, _ := tm.findSkip(name); r != "" { 215 t.Skip(r) 216 } 217 if tm.whitelistpat != nil { 218 if !tm.whitelistpat.MatchString(name) { 219 t.Skip("Skipped by whitelist") 220 } 221 } 222 t.Parallel() 223 224 //将文件作为map[string]<testtype>加载。 225 m := makeMapFromTestFunc(runTest) 226 if err := readJSONFile(path, m.Addr().Interface()); err != nil { 227 t.Fatal(err) 228 } 229 230 //从映射中运行所有测试。如果文件中只有一个测试,则不要在子测试中换行。 231 keys := sortedMapKeys(m) 232 if len(keys) == 1 { 233 runTestFunc(runTest, t, name, m, keys[0]) 234 } else { 235 for _, key := range keys { 236 name := name + "/" + key 237 t.Run(key, func(t *testing.T) { 238 if r, _ := tm.findSkip(name); r != "" { 239 t.Skip(r) 240 } 241 runTestFunc(runTest, t, name, m, key) 242 }) 243 } 244 } 245 } 246 247 func makeMapFromTestFunc(f interface{}) reflect.Value { 248 stringT := reflect.TypeOf("") 249 testingT := reflect.TypeOf((*testing.T)(nil)) 250 ftyp := reflect.TypeOf(f) 251 if ftyp.Kind() != reflect.Func || ftyp.NumIn() != 3 || ftyp.NumOut() != 0 || ftyp.In(0) != testingT || ftyp.In(1) != stringT { 252 panic(fmt.Sprintf("bad test function type: want func(*testing.T, string, <TestType>), have %s", ftyp)) 253 } 254 testType := ftyp.In(2) 255 mp := reflect.New(reflect.MapOf(stringT, testType)) 256 return mp.Elem() 257 } 258 259 func sortedMapKeys(m reflect.Value) []string { 260 keys := make([]string, m.Len()) 261 for i, k := range m.MapKeys() { 262 keys[i] = k.String() 263 } 264 sort.Strings(keys) 265 return keys 266 } 267 268 func runTestFunc(runTest interface{}, t *testing.T, name string, m reflect.Value, key string) { 269 reflect.ValueOf(runTest).Call([]reflect.Value{ 270 reflect.ValueOf(t), 271 reflect.ValueOf(name), 272 m.MapIndex(reflect.ValueOf(key)), 273 }) 274 } 275