init_test.go 8.3 KB

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