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