Skip to content

Commit 9c69c05

Browse files
committed
Merge pull request #1236 from tgerring/ethtest
ethtest improvements
2 parents 53e042f + d1e5892 commit 9c69c05

20 files changed

+1138
-866
lines changed

build/test-global-coverage.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ for pkg in $(go list ./...); do
1616
# drop the namespace prefix.
1717
dir=${pkg##github.com/ethereum/go-ethereum/}
1818

19-
if [[ $dir != "tests/vm" ]]; then
19+
if [[ $dir != "tests" ]]; then
2020
go test -covermode=count -coverprofile=$dir/profile.tmp $pkg
2121
fi
2222
if [[ -f $dir/profile.tmp ]]; then

cmd/ethtest/main.go

Lines changed: 147 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -17,217 +17,201 @@
1717
/**
1818
* @authors:
1919
* Jeffrey Wilcke <[email protected]>
20+
* Taylor Gerring <[email protected]>
2021
*/
2122

2223
package main
2324

2425
import (
25-
"bytes"
26-
"encoding/json"
26+
"fmt"
2727
"io"
2828
"io/ioutil"
29-
"log"
30-
"math/big"
3129
"os"
32-
"strconv"
30+
"path/filepath"
3331
"strings"
3432

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"
4134
"github.com/ethereum/go-ethereum/logger/glog"
42-
"github.com/ethereum/go-ethereum/tests/helper"
35+
"github.com/ethereum/go-ethereum/tests"
4336
)
4437

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+
)
5170

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)
5985
}
60-
return t
61-
}
6286

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+
}
6990

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+
}
7393

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
76100
}
77-
obj.SetCode(common.Hex2Bytes(account.Code))
78-
obj.SetNonce(common.Big(account.Nonce).Uint64())
101+
defer f.Close()
79102

80-
return obj
81-
}
103+
fi, err := f.Stat()
104+
if err != nil {
105+
return nil, err
106+
}
82107

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+
}
95123

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
103125
}
104126

105-
func RunVmTest(r io.Reader) (failed int) {
106-
tests := make(map[string]VmTest)
127+
func runSuite(test, file string) {
128+
var tests []string
107129

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}
112134
}
113135

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))
124142

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))
133143
} else {
134-
env["currentTimestamp"] = test.Env.CurrentTimestamp.(string)
144+
files, err = getFiles(file)
135145
}
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)
144148
}
145149

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 {
149156
continue
150157
}
151158

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)
157162
}
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)
166171
}
167172
}
168-
}
169173

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
175174
}
175+
}
176+
}
176177

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), " ")
213184

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)
216190
}
217191

218-
logger.Flush()
219192
}
220-
221-
return
222193
}
223194

224195
func main() {
225-
helper.Logger.SetLogLevel(5)
226-
vm.Debug = true
196+
glog.SetToStderr(true)
227197

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,
232211
}
212+
213+
if err := app.Run(os.Args); err != nil {
214+
glog.Fatalln(err)
215+
}
216+
233217
}

cmd/geth/blocktestcmd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ func runBlockTest(ctx *cli.Context) {
8686
}
8787

8888
func runOneBlockTest(ctx *cli.Context, test *tests.BlockTest) (*eth.Ethereum, error) {
89+
// TODO remove in favor of logic contained in tests package
8990
cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
9091
cfg.NewDB = func(path string) (common.Database, error) { return ethdb.NewMemDatabase() }
9192
cfg.MaxPeers = 0 // disable network

0 commit comments

Comments
 (0)