Add stdin option

This commit is contained in:
Taylor Gerring 2015-06-14 17:55:03 -04:00
parent a86452d22c
commit 01ec4dbb12
6 changed files with 422 additions and 237 deletions

View File

@ -24,6 +24,7 @@ package main
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
@ -55,28 +56,37 @@ var (
Name: "continue",
Usage: "Continue running tests on error (true) or [default] exit immediately (false)",
}
ReadStdInFlag = cli.BoolFlag{
Name: "stdin",
Usage: "Accept input from stdin instead of reading from file",
}
)
func runTest(test, file string) error {
// glog.Infoln("runTest", test, file)
func runTestWithReader(test string, r io.Reader) error {
glog.Infoln("runTest", test)
var err error
switch test {
case "bc", "BlockTest", "BlockTests", "BlockChainTest":
err = tests.RunBlockTest(file)
case "bt", "BlockTest", "BlockTests", "BlockChainTest":
err = tests.RunBlockTestWithReader(r)
case "st", "state", "StateTest", "StateTests":
err = tests.RunStateTest(file)
err = tests.RunStateTestWithReader(r)
case "tx", "TransactionTest", "TransactionTests":
err = tests.RunTransactionTests(file)
err = tests.RunTransactionTestsWithReader(r)
case "vm", "VMTest", "VMTests":
err = tests.RunVmTest(file)
err = tests.RunVmTestWithReader(r)
default:
err = fmt.Errorf("Invalid test type specified:", test)
err = fmt.Errorf("Invalid test type specified: %v", test)
}
if err != nil {
return err
}
return nil
}
func getFiles(path string) ([]string, error) {
// glog.Infoln("getFiles", path)
glog.Infoln("getFiles", path)
var files []string
f, err := os.Open(path)
if err != nil {
@ -97,7 +107,7 @@ func getFiles(path string) ([]string, error) {
// only go 1 depth and leave directory entires blank
if !v.IsDir() && v.Name()[len(v.Name())-len(testExtension):len(v.Name())] == testExtension {
files[i] = filepath.Join(path, v.Name())
// glog.Infoln("Found file", files[i])
glog.Infoln("Found file", files[i])
}
}
case mode.IsRegular():
@ -118,7 +128,7 @@ func runSuite(test, file string) {
}
for _, curTest := range tests {
// glog.Infoln("runSuite", curTest, file)
glog.Infoln("runSuite", curTest, file)
var err error
var files []string
if test == defaultTest {
@ -134,16 +144,19 @@ func runSuite(test, file string) {
if len(files) == 0 {
glog.Warningln("No files matched path")
}
for _, testfile := range files {
for _, curFile := range files {
// Skip blank entries
if len(testfile) == 0 {
if len(curFile) == 0 {
continue
}
// TODO allow io.Reader to be passed so Stdin can be piped
// RunVmTest(strings.NewReader(os.Args[2]))
// RunVmTest(os.Stdin)
err := runTest(curTest, testfile)
r, err := os.Open(curFile)
if err != nil {
glog.Fatalln(err)
}
defer r.Close()
err = runTestWithReader(curTest, r)
if err != nil {
if continueOnError {
glog.Errorln(err)
@ -160,8 +173,16 @@ func setupApp(c *cli.Context) {
flagTest := c.GlobalString(TestFlag.Name)
flagFile := c.GlobalString(FileFlag.Name)
continueOnError = c.GlobalBool(ContinueOnErrorFlag.Name)
useStdIn := c.GlobalBool(ReadStdInFlag.Name)
if !useStdIn {
runSuite(flagTest, flagFile)
} else {
if err := runTestWithReader(flagTest, os.Stdin); err != nil {
glog.Fatalln(err)
}
}
}
func main() {
@ -178,6 +199,7 @@ func main() {
TestFlag,
FileFlag,
ContinueOnErrorFlag,
ReadStdInFlag,
}
if err := app.Run(os.Args); err != nil {

View File

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/hex"
"fmt"
"io"
"math/big"
"path/filepath"
"runtime"
@ -85,15 +86,42 @@ type btTransaction struct {
Value string
}
func RunBlockTest(filepath string) error {
bt, err := LoadBlockTests(filepath)
func RunBlockTestWithReader(r io.Reader) error {
btjs := make(map[string]*btJSON)
if err := readJson(r, &btjs); err != nil {
return err
}
bt, err := convertBlockTests(btjs)
if err != nil {
return err
}
// map skipped tests to boolean set
skipTest := make(map[string]bool, len(blockSkipTests))
for _, name := range blockSkipTests {
if err := runBlockTests(bt); err != nil {
return err
}
return nil
}
func RunBlockTest(file string) error {
btjs := make(map[string]*btJSON)
if err := readJsonFile(file, &btjs); err != nil {
return err
}
bt, err := convertBlockTests(btjs)
if err != nil {
return err
}
if err := runBlockTests(bt); err != nil {
return err
}
return nil
}
func runBlockTests(bt map[string]*BlockTest) error {
skipTest := make(map[string]bool, len(BlockSkipTests))
for _, name := range BlockSkipTests {
skipTest[name] = true
}
@ -103,17 +131,19 @@ func RunBlockTest(filepath string) error {
glog.Infoln("Skipping block test", name)
return nil
}
// test the block
if err := testBlock(test); err != nil {
if err := runBlockTest(test); err != nil {
return err
}
glog.Infoln("Block test passed: ", name)
}
return nil
}
func testBlock(test *BlockTest) error {
cfg := testEthConfig()
}
func runBlockTest(test *BlockTest) error {
cfg := test.makeEthConfig()
ethereum, err := eth.New(cfg)
if err != nil {
return err
@ -144,7 +174,7 @@ func testBlock(test *BlockTest) error {
return nil
}
func testEthConfig() *eth.Config {
func (test *BlockTest) makeEthConfig() *eth.Config {
ks := crypto.NewKeyStorePassphrase(filepath.Join(common.DefaultDataDir(), "keystore"))
return &eth.Config{
@ -230,7 +260,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error {
if b.BlockHeader == nil {
return fmt.Errorf("Block insertion should have failed")
}
err = validateBlockHeader(b.BlockHeader, cb.Header())
err = t.validateBlockHeader(b.BlockHeader, cb.Header())
if err != nil {
return fmt.Errorf("Block header validation failed: ", err)
}
@ -238,7 +268,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error {
return nil
}
func validateBlockHeader(h *btHeader, h2 *types.Header) error {
func (s *BlockTest) validateBlockHeader(h *btHeader, h2 *types.Header) error {
expectedBloom := mustConvertBytes(h.Bloom)
if !bytes.Equal(expectedBloom, h2.Bloom.Bytes()) {
return fmt.Errorf("Bloom: expected: %v, decoded: %v", expectedBloom, h2.Bloom.Bytes())
@ -341,7 +371,18 @@ func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error {
return nil
}
func convertTest(in *btJSON) (out *BlockTest, err error) {
func convertBlockTests(in map[string]*btJSON) (map[string]*BlockTest, error) {
out := make(map[string]*BlockTest)
for name, test := range in {
var err error
if out[name], err = convertBlockTest(test); err != nil {
return out, fmt.Errorf("bad test %q: %v", name, err)
}
}
return out, nil
}
func convertBlockTest(in *btJSON) (out *BlockTest, 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.
@ -450,19 +491,12 @@ func mustConvertUint(in string, base int) uint64 {
}
func LoadBlockTests(file string) (map[string]*BlockTest, error) {
bt := make(map[string]*btJSON)
if err := readTestFile(file, &bt); err != nil {
btjs := make(map[string]*btJSON)
if err := readJsonFile(file, &btjs); err != nil {
return nil, err
}
out := make(map[string]*BlockTest)
for name, in := range bt {
var err error
if out[name], err = convertTest(in); err != nil {
return out, fmt.Errorf("bad test %q: %v", name, err)
}
}
return out, nil
return convertBlockTests(btjs)
}
// Nothing to see here, please move along...

View File

@ -8,6 +8,8 @@ import (
"net/http"
"os"
"path/filepath"
// "github.com/ethereum/go-ethereum/logger/glog"
)
var (
@ -17,13 +19,40 @@ var (
transactionTestDir = filepath.Join(baseDir, "TransactionTests")
vmTestDir = filepath.Join(baseDir, "VMTests")
blockSkipTests = []string{"SimpleTx3"}
transSkipTests = []string{"TransactionWithHihghNonce256"}
stateSkipTests = []string{"mload32bitBound_return", "mload32bitBound_return2"}
vmSkipTests = []string{}
BlockSkipTests = []string{"SimpleTx3"}
TransSkipTests = []string{"TransactionWithHihghNonce256"}
StateSkipTests = []string{"mload32bitBound_return", "mload32bitBound_return2"}
VmSkipTests = []string{}
)
func readJSON(reader io.Reader, value interface{}) error {
// type TestRunner interface {
// // LoadTest()
// RunTest() error
// }
// func RunTests(bt map[string]TestRunner, skipTests []string) error {
// // map skipped tests to boolean set
// skipTest := make(map[string]bool, len(skipTests))
// for _, name := range skipTests {
// skipTest[name] = true
// }
// for name, test := range bt {
// // if the test should be skipped, return
// if skipTest[name] {
// glog.Infoln("Skipping block test", name)
// return nil
// }
// // test the block
// if err := test.RunTest(); err != nil {
// return err
// }
// glog.Infoln("Block test passed: ", name)
// }
// return nil
// }
func readJson(reader io.Reader, value interface{}) error {
data, err := ioutil.ReadAll(reader)
if err != nil {
return fmt.Errorf("Error reading JSON file", err.Error())
@ -39,6 +68,34 @@ func readJSON(reader io.Reader, value interface{}) error {
return nil
}
func readJsonHttp(uri string, value interface{}) error {
resp, err := http.Get(uri)
if err != nil {
return err
}
defer resp.Body.Close()
err = readJson(resp.Body, value)
if err != nil {
return err
}
return nil
}
func readJsonFile(fn string, value interface{}) error {
file, err := os.Open(fn)
if err != nil {
return err
}
defer file.Close()
err = readJson(file, value)
if err != nil {
return fmt.Errorf("%s in file %s", err.Error(), fn)
}
return nil
}
// findLine returns the line number for the given offset into data.
func findLine(data []byte, offset int64) (line int) {
line = 1
@ -52,31 +109,3 @@ func findLine(data []byte, offset int64) (line int) {
}
return
}
func readHttpFile(uri string, value interface{}) error {
resp, err := http.Get(uri)
if err != nil {
return err
}
defer resp.Body.Close()
err = readJSON(resp.Body, value)
if err != nil {
return err
}
return nil
}
func readTestFile(fn string, value interface{}) error {
file, err := os.Open(fn)
if err != nil {
return err
}
defer file.Close()
err = readJSON(file, value)
if err != nil {
return fmt.Errorf("%s in file %s", err.Error(), fn)
}
return nil
}

View File

@ -3,6 +3,7 @@ package tests
import (
"bytes"
"fmt"
"io"
"math/big"
"strconv"
@ -15,27 +16,64 @@ import (
"github.com/ethereum/go-ethereum/logger/glog"
)
func RunStateTest(p string) error {
skipTest := make(map[string]bool, len(stateSkipTests))
for _, name := range stateSkipTests {
skipTest[name] = true
func RunStateTestWithReader(r io.Reader) error {
tests := make(map[string]VmTest)
if err := readJson(r, &tests); err != nil {
return err
}
if err := runStateTests(tests); err != nil {
return err
}
return nil
}
func RunStateTest(p string) error {
tests := make(map[string]VmTest)
readTestFile(p, &tests)
if err := readJsonFile(p, &tests); err != nil {
return err
}
if err := runStateTests(tests); err != nil {
return err
}
return nil
}
func runStateTests(tests map[string]VmTest) error {
skipTest := make(map[string]bool, len(StateSkipTests))
for _, name := range StateSkipTests {
skipTest[name] = true
}
for name, test := range tests {
if skipTest[name] {
glog.Infoln("Skipping state test", name)
return nil
}
if err := runStateTest(test); err != nil {
return fmt.Errorf("%s: %s\n", name, err.Error())
}
glog.Infoln("State test passed: ", name)
//fmt.Println(string(statedb.Dump()))
}
return nil
}
func runStateTest(test VmTest) error {
db, _ := ethdb.NewMemDatabase()
statedb := state.New(common.Hash{}, db)
for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj)
for a, v := range account.Storage {
obj.SetState(common.HexToHash(a), common.HexToHash(s))
obj.SetState(common.HexToHash(a), common.HexToHash(v))
}
}
@ -64,7 +102,7 @@ func RunStateTest(p string) error {
// // Compare expected and actual return
rexp := common.FromHex(test.Out)
if bytes.Compare(rexp, ret) != 0 {
return fmt.Errorf("%s's return failed. Expected %x, got %x\n", name, rexp, ret)
return fmt.Errorf("return failed. Expected %x, got %x\n", rexp, ret)
}
// check post state
@ -75,11 +113,11 @@ func RunStateTest(p string) error {
}
if obj.Balance().Cmp(common.Big(account.Balance)) != 0 {
return fmt.Errorf("%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()))
return fmt.Errorf("(%x) balance failed. Expected %v, got %v => %v\n", obj.Address().Bytes()[:4], account.Balance, obj.Balance(), new(big.Int).Sub(common.Big(account.Balance), obj.Balance()))
}
if obj.Nonce() != common.String2Big(account.Nonce).Uint64() {
return fmt.Errorf("%s's : (%x) nonce failed. Expected %v, got %v\n", name, obj.Address().Bytes()[:4], account.Nonce, obj.Nonce())
return fmt.Errorf("(%x) nonce failed. Expected %v, got %v\n", obj.Address().Bytes()[:4], account.Nonce, obj.Nonce())
}
for addr, value := range account.Storage {
@ -87,7 +125,7 @@ func RunStateTest(p string) error {
vexp := common.FromHex(value)
if bytes.Compare(v, vexp) != 0 {
return fmt.Errorf("%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))
return fmt.Errorf("(%x: %s) storage failed. Expected %x, got %x (%v %v)\n", obj.Address().Bytes()[0:4], addr, vexp, v, common.BigD(vexp), common.BigD(v))
}
}
}
@ -95,20 +133,16 @@ func RunStateTest(p string) error {
statedb.Sync()
//if !bytes.Equal(common.Hex2Bytes(test.PostStateRoot), statedb.Root()) {
if common.HexToHash(test.PostStateRoot) != statedb.Root() {
return fmt.Errorf("%s's : Post state root error. Expected %s, got %x", name, test.PostStateRoot, statedb.Root())
return fmt.Errorf("Post state root error. Expected %s, got %x", test.PostStateRoot, statedb.Root())
}
// check logs
if len(test.Logs) > 0 {
lerr := checkLogs(test.Logs, logs)
if lerr != nil {
return fmt.Errorf("'%s' ", name, lerr.Error())
if err := checkLogs(test.Logs, logs); err != nil {
return err
}
}
glog.Infoln("State test passed: ", name)
//fmt.Println(string(statedb.Dump()))
}
return nil
}

View File

@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"fmt"
"io"
"runtime"
"github.com/ethereum/go-ethereum/common"
@ -31,14 +32,14 @@ type TransactionTest struct {
Transaction TtTransaction
}
func RunTransactionTests(file string) error {
skipTest := make(map[string]bool, len(transSkipTests))
for _, name := range transSkipTests {
func RunTransactionTestsWithReader(r io.Reader) error {
skipTest := make(map[string]bool, len(TransSkipTests))
for _, name := range TransSkipTests {
skipTest[name] = true
}
bt := make(map[string]TransactionTest)
if err := readTestFile(file, &bt); err != nil {
if err := readJson(r, &bt); err != nil {
return err
}
@ -49,7 +50,7 @@ func RunTransactionTests(file string) error {
return nil
}
// test the block
if err := runTest(test); err != nil {
if err := runTransactionTest(test); err != nil {
return err
}
glog.Infoln("Transaction test passed: ", name)
@ -58,7 +59,35 @@ func RunTransactionTests(file string) error {
return nil
}
func runTest(txTest TransactionTest) (err error) {
func RunTransactionTests(file string) error {
skipTest := make(map[string]bool, len(TransSkipTests))
for _, name := range TransSkipTests {
skipTest[name] = true
}
bt := make(map[string]TransactionTest)
if err := readJsonFile(file, &bt); err != nil {
return err
}
for name, test := range bt {
// if the test should be skipped, return
if skipTest[name] {
glog.Infoln("Skipping transaction test", name)
return nil
}
// test the block
if err := runTransactionTest(test); err != nil {
return err
}
glog.Infoln("Transaction test passed: ", name)
}
return nil
}
func runTransactionTest(txTest TransactionTest) (err error) {
tx := new(types.Transaction)
err = rlp.DecodeBytes(mustConvertBytes(txTest.Rlp), tx)

View File

@ -3,6 +3,7 @@ package tests
import (
"bytes"
"fmt"
"io"
"math/big"
"strconv"
@ -13,23 +14,62 @@ import (
"github.com/ethereum/go-ethereum/logger/glog"
)
func RunVmTest(p string) error {
skipTest := make(map[string]bool, len(vmSkipTests))
for _, name := range vmSkipTests {
skipTest[name] = true
}
func RunVmTestWithReader(r io.Reader) error {
tests := make(map[string]VmTest)
err := readTestFile(p, &tests)
err := readJson(r, &tests)
if err != nil {
return err
}
if err != nil {
return err
}
if err := runVmTests(tests); err != nil {
return err
}
return nil
}
func RunVmTest(p string) error {
tests := make(map[string]VmTest)
err := readJsonFile(p, &tests)
if err != nil {
return err
}
if err := runVmTests(tests); err != nil {
return err
}
return nil
}
func runVmTests(tests map[string]VmTest) error {
skipTest := make(map[string]bool, len(VmSkipTests))
for _, name := range VmSkipTests {
skipTest[name] = true
}
for name, test := range tests {
if skipTest[name] {
glog.Infoln("Skipping VM test", name)
return nil
}
if err := runVmTest(test); err != nil {
return fmt.Errorf("%s %s", name, err.Error())
}
glog.Infoln("VM test passed: ", name)
//fmt.Println(string(statedb.Dump()))
}
return nil
}
func runVmTest(test VmTest) error {
db, _ := ethdb.NewMemDatabase()
statedb := state.New(common.Hash{}, db)
for addr, account := range test.Pre {
@ -65,16 +105,16 @@ func RunVmTest(p string) error {
// Compare expectedand actual return
rexp := common.FromHex(test.Out)
if bytes.Compare(rexp, ret) != 0 {
return fmt.Errorf("%s's return failed. Expected %x, got %x\n", name, rexp, ret)
return fmt.Errorf("return failed. Expected %x, got %x\n", rexp, ret)
}
// Check gas usage
if len(test.Gas) == 0 && err == nil {
return fmt.Errorf("%s's gas unspecified, indicating an error. VM returned (incorrectly) successfull", name)
return fmt.Errorf("gas unspecified, indicating an error. VM returned (incorrectly) successfull")
} else {
gexp := common.Big(test.Gas)
if gexp.Cmp(gas) != 0 {
return fmt.Errorf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas)
return fmt.Errorf("gas failed. Expected %v, got %v\n", gexp, gas)
}
}
@ -90,7 +130,7 @@ func RunVmTest(p string) error {
vexp := common.HexToHash(value)
if v != vexp {
return t.Errorf("%s's : (%x: %s) storage failed. Expected %x, got %x (%v %v)\n", name, obj.Address().Bytes()[0:4], addr, vexp, v, vexp.Big(), v.Big())
return fmt.Errorf("(%x: %s) storage failed. Expected %x, got %x (%v %v)\n", obj.Address().Bytes()[0:4], addr, vexp, v, vexp.BigD(vexp), v.Big(v))
}
}
}
@ -99,13 +139,10 @@ func RunVmTest(p string) error {
if len(test.Logs) > 0 {
lerr := checkLogs(test.Logs, logs)
if lerr != nil {
return fmt.Errorf("'%s' ", name, lerr.Error())
return lerr
}
}
glog.Infoln("VM test passed: ", name)
//fmt.Println(string(statedb.Dump()))
}
return nil
}