cmd/geth, tests: enable running multiple tests from a single file
This commit also changes the block test loading so tests containing invalid RLP blocks can be loaded and return an error only when they are run. (cherry picked from commit 898ba87984791249586b97c9ce340dd087b79d67)
This commit is contained in:
parent
2ce21cefdc
commit
235ed7ecb9
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
@ -12,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var blocktestCmd = cli.Command{
|
var blocktestCmd = cli.Command{
|
||||||
Action: runblocktest,
|
Action: runBlockTest,
|
||||||
Name: "blocktest",
|
Name: "blocktest",
|
||||||
Usage: `loads a block test file`,
|
Usage: `loads a block test file`,
|
||||||
Description: `
|
Description: `
|
||||||
@ -25,27 +26,78 @@ be able to interact with the chain defined by the test.
|
|||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
func runblocktest(ctx *cli.Context) {
|
func runBlockTest(ctx *cli.Context) {
|
||||||
if len(ctx.Args()) != 3 {
|
var (
|
||||||
utils.Fatalf("Usage: ethereum blocktest <path-to-test-file> <test-name> {rpc, norpc}")
|
file, testname string
|
||||||
|
rpc bool
|
||||||
|
)
|
||||||
|
args := ctx.Args()
|
||||||
|
switch {
|
||||||
|
case len(args) == 1:
|
||||||
|
file = args[0]
|
||||||
|
case len(args) == 2:
|
||||||
|
file, testname = args[0], args[1]
|
||||||
|
case len(args) == 3:
|
||||||
|
file, testname = args[0], args[1]
|
||||||
|
rpc = true
|
||||||
|
default:
|
||||||
|
utils.Fatalf(`Usage: ethereum blocktest <path-to-test-file> [ <test-name> [ "rpc" ] ]`)
|
||||||
}
|
}
|
||||||
file, testname, startrpc := ctx.Args()[0], ctx.Args()[1], ctx.Args()[2]
|
|
||||||
|
|
||||||
bt, err := tests.LoadBlockTests(file)
|
bt, err := tests.LoadBlockTests(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("%v", err)
|
utils.Fatalf("%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// run all tests if no test name is specified
|
||||||
|
if testname == "" {
|
||||||
|
ecode := 0
|
||||||
|
for name, test := range bt {
|
||||||
|
fmt.Printf("----------------- Running Block Test %q\n", name)
|
||||||
|
ethereum, err := runOneBlockTest(ctx, test)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Println("FAIL")
|
||||||
|
ecode = 1
|
||||||
|
}
|
||||||
|
if ethereum != nil {
|
||||||
|
ethereum.Stop()
|
||||||
|
ethereum.WaitForShutdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.Exit(ecode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// otherwise, run the given test
|
||||||
test, ok := bt[testname]
|
test, ok := bt[testname]
|
||||||
if !ok {
|
if !ok {
|
||||||
utils.Fatalf("Test file does not contain test named %q", testname)
|
utils.Fatalf("Test file does not contain test named %q", testname)
|
||||||
}
|
}
|
||||||
|
ethereum, err := runOneBlockTest(ctx, test)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
defer ethereum.Stop()
|
||||||
|
if rpc {
|
||||||
|
fmt.Println("Block Test post state validated, starting RPC interface.")
|
||||||
|
startEth(ctx, ethereum)
|
||||||
|
utils.StartRPC(ethereum, ctx)
|
||||||
|
ethereum.WaitForShutdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runOneBlockTest(ctx *cli.Context, test *tests.BlockTest) (*eth.Ethereum, error) {
|
||||||
cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
|
cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
|
||||||
cfg.NewDB = func(path string) (common.Database, error) { return ethdb.NewMemDatabase() }
|
cfg.NewDB = func(path string) (common.Database, error) { return ethdb.NewMemDatabase() }
|
||||||
cfg.MaxPeers = 0 // disable network
|
cfg.MaxPeers = 0 // disable network
|
||||||
|
cfg.Shh = false // disable whisper
|
||||||
|
cfg.NAT = nil // disable port mapping
|
||||||
|
|
||||||
ethereum, err := eth.New(cfg)
|
ethereum, err := eth.New(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("%v", err)
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := ethereum.Start(); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// import the genesis block
|
// import the genesis block
|
||||||
@ -54,27 +106,16 @@ func runblocktest(ctx *cli.Context) {
|
|||||||
// import pre accounts
|
// import pre accounts
|
||||||
statedb, err := test.InsertPreState(ethereum.StateDb())
|
statedb, err := test.InsertPreState(ethereum.StateDb())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("could not insert genesis accounts: %v", err)
|
return ethereum, fmt.Errorf("InsertPreState: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert the test blocks, which will execute all transactions
|
// insert the test blocks, which will execute all transactions
|
||||||
chain := ethereum.ChainManager()
|
if err := test.InsertBlocks(ethereum.ChainManager()); err != nil {
|
||||||
if err := chain.InsertChain(test.Blocks); err != nil {
|
return ethereum, fmt.Errorf("Block Test load error: %v %T", err, err)
|
||||||
utils.Fatalf("Block Test load error: %v %T", err, err)
|
|
||||||
} else {
|
|
||||||
fmt.Println("Block Test chain loaded")
|
|
||||||
}
|
}
|
||||||
|
fmt.Println("chain loaded")
|
||||||
if err := test.ValidatePostState(statedb); err != nil {
|
if err := test.ValidatePostState(statedb); err != nil {
|
||||||
utils.Fatalf("post state validation failed: %v", err)
|
return ethereum, fmt.Errorf("post state validation failed: %v", err)
|
||||||
}
|
|
||||||
fmt.Println("Block Test post state validated, starting ethereum.")
|
|
||||||
|
|
||||||
if startrpc == "rpc" {
|
|
||||||
startEth(ctx, ethereum)
|
|
||||||
utils.StartRPC(ethereum, ctx)
|
|
||||||
ethereum.WaitForShutdown()
|
|
||||||
} else {
|
|
||||||
startEth(ctx, ethereum)
|
|
||||||
}
|
}
|
||||||
|
return ethereum, nil
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
@ -73,8 +74,8 @@ type btBlock struct {
|
|||||||
|
|
||||||
type BlockTest struct {
|
type BlockTest struct {
|
||||||
Genesis *types.Block
|
Genesis *types.Block
|
||||||
Blocks []*types.Block
|
|
||||||
|
|
||||||
|
json *btJSON
|
||||||
preAccounts map[string]btAccount
|
preAccounts map[string]btAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +89,7 @@ func LoadBlockTests(file string) (map[string]*BlockTest, error) {
|
|||||||
for name, in := range bt {
|
for name, in := range bt {
|
||||||
var err error
|
var err error
|
||||||
if out[name], err = convertTest(in); err != nil {
|
if out[name], err = convertTest(in); err != nil {
|
||||||
return nil, fmt.Errorf("bad test %q: %v", err)
|
return out, fmt.Errorf("bad test %q: %v", name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
@ -124,6 +125,15 @@ func (t *BlockTest) InsertPreState(db common.Database) (*state.StateDB, error) {
|
|||||||
return statedb, nil
|
return statedb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InsertBlocks loads the test's blocks into the given chain.
|
||||||
|
func (t *BlockTest) InsertBlocks(chain *core.ChainManager) error {
|
||||||
|
blocks, err := t.convertBlocks()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return chain.InsertChain(blocks)
|
||||||
|
}
|
||||||
|
|
||||||
func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error {
|
func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error {
|
||||||
for addrString, acct := range t.preAccounts {
|
for addrString, acct := range t.preAccounts {
|
||||||
// XXX: is is worth it checking for errors here?
|
// XXX: is is worth it checking for errors here?
|
||||||
@ -149,6 +159,21 @@ func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *BlockTest) convertBlocks() (blocks []*types.Block, err error) {
|
||||||
|
// the conversion handles errors by catching panics.
|
||||||
|
// you might consider this ugly, but the alternative (passing errors)
|
||||||
|
// would be much harder to read.
|
||||||
|
defer func() {
|
||||||
|
if recovered := recover(); recovered != nil {
|
||||||
|
buf := make([]byte, 64<<10)
|
||||||
|
buf = buf[:runtime.Stack(buf, false)]
|
||||||
|
err = fmt.Errorf("%v\n%s", recovered, buf)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
blocks = mustConvertBlocks(t.json.Blocks)
|
||||||
|
return blocks, nil
|
||||||
|
}
|
||||||
|
|
||||||
func convertTest(in *btJSON) (out *BlockTest, err error) {
|
func convertTest(in *btJSON) (out *BlockTest, err error) {
|
||||||
// the conversion handles errors by catching panics.
|
// the conversion handles errors by catching panics.
|
||||||
// you might consider this ugly, but the alternative (passing errors)
|
// you might consider this ugly, but the alternative (passing errors)
|
||||||
@ -160,9 +185,8 @@ func convertTest(in *btJSON) (out *BlockTest, err error) {
|
|||||||
err = fmt.Errorf("%v\n%s", recovered, buf)
|
err = fmt.Errorf("%v\n%s", recovered, buf)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
out = &BlockTest{preAccounts: in.Pre}
|
out = &BlockTest{preAccounts: in.Pre, json: in}
|
||||||
out.Genesis = mustConvertGenesis(in.GenesisBlockHeader)
|
out.Genesis = mustConvertGenesis(in.GenesisBlockHeader)
|
||||||
out.Blocks = mustConvertBlocks(in.Blocks)
|
|
||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +227,7 @@ func mustConvertBlocks(testBlocks []btBlock) []*types.Block {
|
|||||||
var b types.Block
|
var b types.Block
|
||||||
r := bytes.NewReader(mustConvertBytes(inb.Rlp))
|
r := bytes.NewReader(mustConvertBytes(inb.Rlp))
|
||||||
if err := rlp.Decode(r, &b); err != nil {
|
if err := rlp.Decode(r, &b); err != nil {
|
||||||
panic(fmt.Errorf("invalid block %d: %q", i, inb.Rlp))
|
panic(fmt.Errorf("invalid block %d: %q\nerror: %v", i, inb.Rlp, err))
|
||||||
}
|
}
|
||||||
out = append(out, &b)
|
out = append(out, &b)
|
||||||
}
|
}
|
||||||
@ -293,11 +317,18 @@ func findLine(data []byte, offset int64) (line int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func unfuckCPPHexInts(s string) string {
|
func unfuckCPPHexInts(s string) string {
|
||||||
if s == "0x" { // no respect for the empty value :(
|
switch {
|
||||||
|
case s == "0x":
|
||||||
|
// no respect for the empty value :(
|
||||||
return "0x00"
|
return "0x00"
|
||||||
}
|
case len(s) == 0:
|
||||||
if (len(s) % 2) != 0 { // motherfucking nibbles
|
return "0x00"
|
||||||
|
case len(s) == 1:
|
||||||
|
return "0x0" + s[:1]
|
||||||
|
case len(s)%2 != 0:
|
||||||
|
// motherfucking nibbles
|
||||||
return "0x0" + s[2:]
|
return "0x0" + s[2:]
|
||||||
|
default:
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user