github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/tests/init_test.go (about)

     1  package tests
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"reflect"
    11  	"regexp"
    12  	"sort"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/neatlab/neatio/params"
    17  )
    18  
    19  var (
    20  	baseDir            = filepath.Join(".", "testdata")
    21  	blockTestDir       = filepath.Join(baseDir, "BlockchainTests")
    22  	stateTestDir       = filepath.Join(baseDir, "GeneralStateTests")
    23  	transactionTestDir = filepath.Join(baseDir, "TransactionTests")
    24  	vmTestDir          = filepath.Join(baseDir, "VMTests")
    25  	rlpTestDir         = filepath.Join(baseDir, "RLPTests")
    26  	difficultyTestDir  = filepath.Join(baseDir, "BasicTests")
    27  )
    28  
    29  func readJson(reader io.Reader, value interface{}) error {
    30  	data, err := ioutil.ReadAll(reader)
    31  	if err != nil {
    32  		return fmt.Errorf("error reading JSON file: %v", err)
    33  	}
    34  	if err = json.Unmarshal(data, &value); err != nil {
    35  		if syntaxerr, ok := err.(*json.SyntaxError); ok {
    36  			line := findLine(data, syntaxerr.Offset)
    37  			return fmt.Errorf("JSON syntax error at line %v: %v", line, err)
    38  		}
    39  		return err
    40  	}
    41  	return nil
    42  }
    43  
    44  func readJsonFile(fn string, value interface{}) error {
    45  	file, err := os.Open(fn)
    46  	if err != nil {
    47  		return err
    48  	}
    49  	defer file.Close()
    50  
    51  	err = readJson(file, value)
    52  	if err != nil {
    53  		return fmt.Errorf("%s in file %s", err.Error(), fn)
    54  	}
    55  	return nil
    56  }
    57  
    58  func findLine(data []byte, offset int64) (line int) {
    59  	line = 1
    60  	for i, r := range string(data) {
    61  		if int64(i) >= offset {
    62  			return
    63  		}
    64  		if r == '\n' {
    65  			line++
    66  		}
    67  	}
    68  	return
    69  }
    70  
    71  type testMatcher struct {
    72  	configpat    []testConfig
    73  	failpat      []testFailure
    74  	skiploadpat  []*regexp.Regexp
    75  	skipshortpat []*regexp.Regexp
    76  }
    77  
    78  type testConfig struct {
    79  	p      *regexp.Regexp
    80  	config params.ChainConfig
    81  }
    82  
    83  type testFailure struct {
    84  	p      *regexp.Regexp
    85  	reason string
    86  }
    87  
    88  func (tm *testMatcher) skipShortMode(pattern string) {
    89  	tm.skipshortpat = append(tm.skipshortpat, regexp.MustCompile(pattern))
    90  }
    91  
    92  func (tm *testMatcher) skipLoad(pattern string) {
    93  	tm.skiploadpat = append(tm.skiploadpat, regexp.MustCompile(pattern))
    94  }
    95  
    96  func (tm *testMatcher) fails(pattern string, reason string) {
    97  	if reason == "" {
    98  		panic("empty fail reason")
    99  	}
   100  	tm.failpat = append(tm.failpat, testFailure{regexp.MustCompile(pattern), reason})
   101  }
   102  
   103  func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) {
   104  	tm.configpat = append(tm.configpat, testConfig{regexp.MustCompile(pattern), cfg})
   105  }
   106  
   107  func (tm *testMatcher) findSkip(name string) (reason string, skipload bool) {
   108  	if testing.Short() {
   109  		for _, re := range tm.skipshortpat {
   110  			if re.MatchString(name) {
   111  				return "skipped in -short mode", false
   112  			}
   113  		}
   114  	}
   115  	for _, re := range tm.skiploadpat {
   116  		if re.MatchString(name) {
   117  			return "skipped by skipLoad", true
   118  		}
   119  	}
   120  	return "", false
   121  }
   122  
   123  func (tm *testMatcher) findConfig(name string) *params.ChainConfig {
   124  
   125  	for _, m := range tm.configpat {
   126  		if m.p.MatchString(name) {
   127  			return &m.config
   128  		}
   129  	}
   130  	return new(params.ChainConfig)
   131  }
   132  
   133  func (tm *testMatcher) checkFailure(t *testing.T, name string, err error) error {
   134  
   135  	failReason := ""
   136  	for _, m := range tm.failpat {
   137  		if m.p.MatchString(name) {
   138  			failReason = m.reason
   139  			break
   140  		}
   141  	}
   142  	if failReason != "" {
   143  		t.Logf("expected failure: %s", failReason)
   144  		if err != nil {
   145  			t.Logf("error: %v", err)
   146  			return nil
   147  		} else {
   148  			return fmt.Errorf("test succeeded unexpectedly")
   149  		}
   150  	}
   151  	return err
   152  }
   153  
   154  func (tm *testMatcher) walk(t *testing.T, dir string, runTest interface{}) {
   155  
   156  	dirinfo, err := os.Stat(dir)
   157  	if os.IsNotExist(err) || !dirinfo.IsDir() {
   158  		fmt.Fprintf(os.Stderr, "can't find test files in %s, did you clone the tests submodule?\n", dir)
   159  		t.Skip("missing test files")
   160  	}
   161  	err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
   162  		name := filepath.ToSlash(strings.TrimPrefix(path, dir+string(filepath.Separator)))
   163  		if info.IsDir() {
   164  			if _, skipload := tm.findSkip(name + "/"); skipload {
   165  				return filepath.SkipDir
   166  			}
   167  			return nil
   168  		}
   169  		if filepath.Ext(path) == ".json" {
   170  			t.Run(name, func(t *testing.T) { tm.runTestFile(t, path, name, runTest) })
   171  		}
   172  		return nil
   173  	})
   174  	if err != nil {
   175  		t.Fatal(err)
   176  	}
   177  }
   178  
   179  func (tm *testMatcher) runTestFile(t *testing.T, path, name string, runTest interface{}) {
   180  	if r, _ := tm.findSkip(name); r != "" {
   181  		t.Skip(r)
   182  	}
   183  	t.Parallel()
   184  
   185  	m := makeMapFromTestFunc(runTest)
   186  	if err := readJsonFile(path, m.Addr().Interface()); err != nil {
   187  		t.Fatal(err)
   188  	}
   189  
   190  	keys := sortedMapKeys(m)
   191  	if len(keys) == 1 {
   192  		runTestFunc(runTest, t, name, m, keys[0])
   193  	} else {
   194  		for _, key := range keys {
   195  			name := name + "/" + key
   196  			t.Run(key, func(t *testing.T) {
   197  				if r, _ := tm.findSkip(name); r != "" {
   198  					t.Skip(r)
   199  				}
   200  				runTestFunc(runTest, t, name, m, key)
   201  			})
   202  		}
   203  	}
   204  }
   205  
   206  func makeMapFromTestFunc(f interface{}) reflect.Value {
   207  	stringT := reflect.TypeOf("")
   208  	testingT := reflect.TypeOf((*testing.T)(nil))
   209  	ftyp := reflect.TypeOf(f)
   210  	if ftyp.Kind() != reflect.Func || ftyp.NumIn() != 3 || ftyp.NumOut() != 0 || ftyp.In(0) != testingT || ftyp.In(1) != stringT {
   211  		panic(fmt.Sprintf("bad test function type: want func(*testing.T, string, <TestType>), have %s", ftyp))
   212  	}
   213  	testType := ftyp.In(2)
   214  	mp := reflect.New(reflect.MapOf(stringT, testType))
   215  	return mp.Elem()
   216  }
   217  
   218  func sortedMapKeys(m reflect.Value) []string {
   219  	keys := make([]string, m.Len())
   220  	for i, k := range m.MapKeys() {
   221  		keys[i] = k.String()
   222  	}
   223  	sort.Strings(keys)
   224  	return keys
   225  }
   226  
   227  func runTestFunc(runTest interface{}, t *testing.T, name string, m reflect.Value, key string) {
   228  	reflect.ValueOf(runTest).Call([]reflect.Value{
   229  		reflect.ValueOf(t),
   230  		reflect.ValueOf(name),
   231  		m.MapIndex(reflect.ValueOf(key)),
   232  	})
   233  }