|
17 | 17 | /**
|
18 | 18 | * @authors:
|
19 | 19 | * Jeffrey Wilcke <[email protected]>
|
| 20 | + * Taylor Gerring <[email protected]> |
20 | 21 | */
|
21 | 22 |
|
22 | 23 | package main
|
23 | 24 |
|
24 | 25 | import (
|
25 |
| - "bytes" |
26 |
| - "encoding/json" |
| 26 | + "fmt" |
27 | 27 | "io"
|
28 | 28 | "io/ioutil"
|
29 |
| - "log" |
30 |
| - "math/big" |
31 | 29 | "os"
|
32 |
| - "strconv" |
| 30 | + "path/filepath" |
33 | 31 | "strings"
|
34 | 32 |
|
35 |
| - "github.com/ethereum/go-ethereum/common" |
36 |
| - "github.com/ethereum/go-ethereum/core/state" |
37 |
| - "github.com/ethereum/go-ethereum/core/types" |
38 |
| - "github.com/ethereum/go-ethereum/core/vm" |
39 |
| - "github.com/ethereum/go-ethereum/ethdb" |
40 |
| - "github.com/ethereum/go-ethereum/logger" |
| 33 | + "github.com/codegangsta/cli" |
41 | 34 | "github.com/ethereum/go-ethereum/logger/glog"
|
42 |
| - "github.com/ethereum/go-ethereum/tests/helper" |
| 35 | + "github.com/ethereum/go-ethereum/tests" |
43 | 36 | )
|
44 | 37 |
|
45 |
| -type Log struct { |
46 |
| - AddressF string `json:"address"` |
47 |
| - DataF string `json:"data"` |
48 |
| - TopicsF []string `json:"topics"` |
49 |
| - BloomF string `json:"bloom"` |
50 |
| -} |
| 38 | +var ( |
| 39 | + continueOnError = false |
| 40 | + testExtension = ".json" |
| 41 | + defaultTest = "all" |
| 42 | + defaultDir = "." |
| 43 | + allTests = []string{"BlockTests", "StateTests", "TransactionTests", "VMTests"} |
| 44 | + skipTests = []string{} |
| 45 | + |
| 46 | + TestFlag = cli.StringFlag{ |
| 47 | + Name: "test", |
| 48 | + Usage: "Test type (string): VMTests, TransactionTests, StateTests, BlockTests", |
| 49 | + Value: defaultTest, |
| 50 | + } |
| 51 | + FileFlag = cli.StringFlag{ |
| 52 | + Name: "file", |
| 53 | + Usage: "Test file or directory. Directories are searched for .json files 1 level deep", |
| 54 | + Value: defaultDir, |
| 55 | + EnvVar: "ETHEREUM_TEST_PATH", |
| 56 | + } |
| 57 | + ContinueOnErrorFlag = cli.BoolFlag{ |
| 58 | + Name: "continue", |
| 59 | + Usage: "Continue running tests on error (true) or [default] exit immediately (false)", |
| 60 | + } |
| 61 | + ReadStdInFlag = cli.BoolFlag{ |
| 62 | + Name: "stdin", |
| 63 | + Usage: "Accept input from stdin instead of reading from file", |
| 64 | + } |
| 65 | + SkipTestsFlag = cli.StringFlag{ |
| 66 | + Name: "skip", |
| 67 | + Usage: "Tests names to skip", |
| 68 | + } |
| 69 | +) |
51 | 70 |
|
52 |
| -func (self Log) Address() []byte { return common.Hex2Bytes(self.AddressF) } |
53 |
| -func (self Log) Data() []byte { return common.Hex2Bytes(self.DataF) } |
54 |
| -func (self Log) RlpData() interface{} { return nil } |
55 |
| -func (self Log) Topics() [][]byte { |
56 |
| - t := make([][]byte, len(self.TopicsF)) |
57 |
| - for i, topic := range self.TopicsF { |
58 |
| - t[i] = common.Hex2Bytes(topic) |
| 71 | +func runTestWithReader(test string, r io.Reader) error { |
| 72 | + glog.Infoln("runTest", test) |
| 73 | + var err error |
| 74 | + switch strings.ToLower(test) { |
| 75 | + case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests": |
| 76 | + err = tests.RunBlockTestWithReader(r, skipTests) |
| 77 | + case "st", "state", "statetest", "statetests": |
| 78 | + err = tests.RunStateTestWithReader(r, skipTests) |
| 79 | + case "tx", "transactiontest", "transactiontests": |
| 80 | + err = tests.RunTransactionTestsWithReader(r, skipTests) |
| 81 | + case "vm", "vmtest", "vmtests": |
| 82 | + err = tests.RunVmTestWithReader(r, skipTests) |
| 83 | + default: |
| 84 | + err = fmt.Errorf("Invalid test type specified: %v", test) |
59 | 85 | }
|
60 |
| - return t |
61 |
| -} |
62 | 86 |
|
63 |
| -type Account struct { |
64 |
| - Balance string |
65 |
| - Code string |
66 |
| - Nonce string |
67 |
| - Storage map[string]string |
68 |
| -} |
| 87 | + if err != nil { |
| 88 | + return err |
| 89 | + } |
69 | 90 |
|
70 |
| -func StateObjectFromAccount(db common.Database, addr string, account Account) *state.StateObject { |
71 |
| - obj := state.NewStateObject(common.HexToAddress(addr), db) |
72 |
| - obj.SetBalance(common.Big(account.Balance)) |
| 91 | + return nil |
| 92 | +} |
73 | 93 |
|
74 |
| - if common.IsHex(account.Code) { |
75 |
| - account.Code = account.Code[2:] |
| 94 | +func getFiles(path string) ([]string, error) { |
| 95 | + glog.Infoln("getFiles", path) |
| 96 | + var files []string |
| 97 | + f, err := os.Open(path) |
| 98 | + if err != nil { |
| 99 | + return nil, err |
76 | 100 | }
|
77 |
| - obj.SetCode(common.Hex2Bytes(account.Code)) |
78 |
| - obj.SetNonce(common.Big(account.Nonce).Uint64()) |
| 101 | + defer f.Close() |
79 | 102 |
|
80 |
| - return obj |
81 |
| -} |
| 103 | + fi, err := f.Stat() |
| 104 | + if err != nil { |
| 105 | + return nil, err |
| 106 | + } |
82 | 107 |
|
83 |
| -type VmTest struct { |
84 |
| - Callcreates interface{} |
85 |
| - Env Env |
86 |
| - Exec map[string]string |
87 |
| - Transaction map[string]string |
88 |
| - Logs []Log |
89 |
| - Gas string |
90 |
| - Out string |
91 |
| - Post map[string]Account |
92 |
| - Pre map[string]Account |
93 |
| - PostStateRoot string |
94 |
| -} |
| 108 | + switch mode := fi.Mode(); { |
| 109 | + case mode.IsDir(): |
| 110 | + fi, _ := ioutil.ReadDir(path) |
| 111 | + files = make([]string, len(fi)) |
| 112 | + for i, v := range fi { |
| 113 | + // only go 1 depth and leave directory entires blank |
| 114 | + if !v.IsDir() && v.Name()[len(v.Name())-len(testExtension):len(v.Name())] == testExtension { |
| 115 | + files[i] = filepath.Join(path, v.Name()) |
| 116 | + glog.Infoln("Found file", files[i]) |
| 117 | + } |
| 118 | + } |
| 119 | + case mode.IsRegular(): |
| 120 | + files = make([]string, 1) |
| 121 | + files[0] = path |
| 122 | + } |
95 | 123 |
|
96 |
| -type Env struct { |
97 |
| - CurrentCoinbase string |
98 |
| - CurrentDifficulty string |
99 |
| - CurrentGasLimit string |
100 |
| - CurrentNumber string |
101 |
| - CurrentTimestamp interface{} |
102 |
| - PreviousHash string |
| 124 | + return files, nil |
103 | 125 | }
|
104 | 126 |
|
105 |
| -func RunVmTest(r io.Reader) (failed int) { |
106 |
| - tests := make(map[string]VmTest) |
| 127 | +func runSuite(test, file string) { |
| 128 | + var tests []string |
107 | 129 |
|
108 |
| - data, _ := ioutil.ReadAll(r) |
109 |
| - err := json.Unmarshal(data, &tests) |
110 |
| - if err != nil { |
111 |
| - log.Fatalln(err) |
| 130 | + if test == defaultTest { |
| 131 | + tests = allTests |
| 132 | + } else { |
| 133 | + tests = []string{test} |
112 | 134 | }
|
113 | 135 |
|
114 |
| - vm.Debug = true |
115 |
| - glog.SetV(4) |
116 |
| - glog.SetToStderr(true) |
117 |
| - for name, test := range tests { |
118 |
| - db, _ := ethdb.NewMemDatabase() |
119 |
| - statedb := state.New(common.Hash{}, db) |
120 |
| - for addr, account := range test.Pre { |
121 |
| - obj := StateObjectFromAccount(db, addr, account) |
122 |
| - statedb.SetStateObject(obj) |
123 |
| - } |
| 136 | + for _, curTest := range tests { |
| 137 | + glog.Infoln("runSuite", curTest, file) |
| 138 | + var err error |
| 139 | + var files []string |
| 140 | + if test == defaultTest { |
| 141 | + files, err = getFiles(filepath.Join(file, curTest)) |
124 | 142 |
|
125 |
| - env := make(map[string]string) |
126 |
| - env["currentCoinbase"] = test.Env.CurrentCoinbase |
127 |
| - env["currentDifficulty"] = test.Env.CurrentDifficulty |
128 |
| - env["currentGasLimit"] = test.Env.CurrentGasLimit |
129 |
| - env["currentNumber"] = test.Env.CurrentNumber |
130 |
| - env["previousHash"] = test.Env.PreviousHash |
131 |
| - if n, ok := test.Env.CurrentTimestamp.(float64); ok { |
132 |
| - env["currentTimestamp"] = strconv.Itoa(int(n)) |
133 | 143 | } else {
|
134 |
| - env["currentTimestamp"] = test.Env.CurrentTimestamp.(string) |
| 144 | + files, err = getFiles(file) |
135 | 145 | }
|
136 |
| - |
137 |
| - ret, logs, _, _ := helper.RunState(statedb, env, test.Transaction) |
138 |
| - statedb.Sync() |
139 |
| - |
140 |
| - rexp := helper.FromHex(test.Out) |
141 |
| - if bytes.Compare(rexp, ret) != 0 { |
142 |
| - glog.V(logger.Info).Infof("%s's return failed. Expected %x, got %x\n", name, rexp, ret) |
143 |
| - failed = 1 |
| 146 | + if err != nil { |
| 147 | + glog.Fatalln(err) |
144 | 148 | }
|
145 | 149 |
|
146 |
| - for addr, account := range test.Post { |
147 |
| - obj := statedb.GetStateObject(common.HexToAddress(addr)) |
148 |
| - if obj == nil { |
| 150 | + if len(files) == 0 { |
| 151 | + glog.Warningln("No files matched path") |
| 152 | + } |
| 153 | + for _, curFile := range files { |
| 154 | + // Skip blank entries |
| 155 | + if len(curFile) == 0 { |
149 | 156 | continue
|
150 | 157 | }
|
151 | 158 |
|
152 |
| - if len(test.Exec) == 0 { |
153 |
| - if obj.Balance().Cmp(common.Big(account.Balance)) != 0 { |
154 |
| - glog.V(logger.Info).Infof("%s's : (%x) balance failed. Expected %v, got %v => %v\n", name, obj.Address().Bytes()[:4], account.Balance, obj.Balance(), new(big.Int).Sub(common.Big(account.Balance), obj.Balance())) |
155 |
| - failed = 1 |
156 |
| - } |
| 159 | + r, err := os.Open(curFile) |
| 160 | + if err != nil { |
| 161 | + glog.Fatalln(err) |
157 | 162 | }
|
158 |
| - |
159 |
| - for addr, value := range account.Storage { |
160 |
| - v := obj.GetState(common.HexToHash(addr)).Bytes() |
161 |
| - vexp := helper.FromHex(value) |
162 |
| - |
163 |
| - if bytes.Compare(v, vexp) != 0 { |
164 |
| - glog.V(logger.Info).Infof("%s's : (%x: %s) storage failed. Expected %x, got %x (%v %v)\n", name, obj.Address().Bytes()[0:4], addr, vexp, v, common.BigD(vexp), common.BigD(v)) |
165 |
| - failed = 1 |
| 163 | + defer r.Close() |
| 164 | + |
| 165 | + err = runTestWithReader(curTest, r) |
| 166 | + if err != nil { |
| 167 | + if continueOnError { |
| 168 | + glog.Errorln(err) |
| 169 | + } else { |
| 170 | + glog.Fatalln(err) |
166 | 171 | }
|
167 | 172 | }
|
168 |
| - } |
169 | 173 |
|
170 |
| - statedb.Sync() |
171 |
| - //if !bytes.Equal(common.Hex2Bytes(test.PostStateRoot), statedb.Root()) { |
172 |
| - if common.HexToHash(test.PostStateRoot) != statedb.Root() { |
173 |
| - glog.V(logger.Info).Infof("%s's : Post state root failed. Expected %s, got %x", name, test.PostStateRoot, statedb.Root()) |
174 |
| - failed = 1 |
175 | 174 | }
|
| 175 | + } |
| 176 | +} |
176 | 177 |
|
177 |
| - if len(test.Logs) > 0 { |
178 |
| - if len(test.Logs) != len(logs) { |
179 |
| - glog.V(logger.Info).Infof("log length failed. Expected %d, got %d", len(test.Logs), len(logs)) |
180 |
| - failed = 1 |
181 |
| - } else { |
182 |
| - for i, log := range test.Logs { |
183 |
| - if common.HexToAddress(log.AddressF) != logs[i].Address { |
184 |
| - glog.V(logger.Info).Infof("'%s' log address failed. Expected %v got %x", name, log.AddressF, logs[i].Address) |
185 |
| - failed = 1 |
186 |
| - } |
187 |
| - |
188 |
| - if !bytes.Equal(logs[i].Data, helper.FromHex(log.DataF)) { |
189 |
| - glog.V(logger.Info).Infof("'%s' log data failed. Expected %v got %x", name, log.DataF, logs[i].Data) |
190 |
| - failed = 1 |
191 |
| - } |
192 |
| - |
193 |
| - if len(log.TopicsF) != len(logs[i].Topics) { |
194 |
| - glog.V(logger.Info).Infof("'%s' log topics length failed. Expected %d got %d", name, len(log.TopicsF), logs[i].Topics) |
195 |
| - failed = 1 |
196 |
| - } else { |
197 |
| - for j, topic := range log.TopicsF { |
198 |
| - if common.HexToHash(topic) != logs[i].Topics[j] { |
199 |
| - glog.V(logger.Info).Infof("'%s' log topic[%d] failed. Expected %v got %x", name, j, topic, logs[i].Topics[j]) |
200 |
| - failed = 1 |
201 |
| - } |
202 |
| - } |
203 |
| - } |
204 |
| - genBloom := common.LeftPadBytes(types.LogsBloom(state.Logs{logs[i]}).Bytes(), 256) |
205 |
| - |
206 |
| - if !bytes.Equal(genBloom, common.Hex2Bytes(log.BloomF)) { |
207 |
| - glog.V(logger.Info).Infof("'%s' bloom failed.", name) |
208 |
| - failed = 1 |
209 |
| - } |
210 |
| - } |
211 |
| - } |
212 |
| - } |
| 178 | +func setupApp(c *cli.Context) { |
| 179 | + flagTest := c.GlobalString(TestFlag.Name) |
| 180 | + flagFile := c.GlobalString(FileFlag.Name) |
| 181 | + continueOnError = c.GlobalBool(ContinueOnErrorFlag.Name) |
| 182 | + useStdIn := c.GlobalBool(ReadStdInFlag.Name) |
| 183 | + skipTests = strings.Split(c.GlobalString(SkipTestsFlag.Name), " ") |
213 | 184 |
|
214 |
| - if failed == 1 { |
215 |
| - glog.V(logger.Info).Infoln(string(statedb.Dump())) |
| 185 | + if !useStdIn { |
| 186 | + runSuite(flagTest, flagFile) |
| 187 | + } else { |
| 188 | + if err := runTestWithReader(flagTest, os.Stdin); err != nil { |
| 189 | + glog.Fatalln(err) |
216 | 190 | }
|
217 | 191 |
|
218 |
| - logger.Flush() |
219 | 192 | }
|
220 |
| - |
221 |
| - return |
222 | 193 | }
|
223 | 194 |
|
224 | 195 | func main() {
|
225 |
| - helper.Logger.SetLogLevel(5) |
226 |
| - vm.Debug = true |
| 196 | + glog.SetToStderr(true) |
227 | 197 |
|
228 |
| - if len(os.Args) > 1 { |
229 |
| - os.Exit(RunVmTest(strings.NewReader(os.Args[1]))) |
230 |
| - } else { |
231 |
| - os.Exit(RunVmTest(os.Stdin)) |
| 198 | + app := cli.NewApp() |
| 199 | + app.Name = "ethtest" |
| 200 | + app.Usage = "go-ethereum test interface" |
| 201 | + app.Action = setupApp |
| 202 | + app.Version = "0.2.0" |
| 203 | + app.Author = "go-ethereum team" |
| 204 | + |
| 205 | + app.Flags = []cli.Flag{ |
| 206 | + TestFlag, |
| 207 | + FileFlag, |
| 208 | + ContinueOnErrorFlag, |
| 209 | + ReadStdInFlag, |
| 210 | + SkipTestsFlag, |
232 | 211 | }
|
| 212 | + |
| 213 | + if err := app.Run(os.Args); err != nil { |
| 214 | + glog.Fatalln(err) |
| 215 | + } |
| 216 | + |
233 | 217 | }
|
0 commit comments