mirror of https://github.com/status-im/op-geth.git
cmd/geth: add tests for account commands
This commit is contained in:
parent
6cb08d8328
commit
ee1682ffe6
|
@ -0,0 +1 @@
|
||||||
|
{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3}
|
|
@ -0,0 +1,21 @@
|
||||||
|
This directory contains accounts for testing.
|
||||||
|
The passphrase that unlocks them is "foobar".
|
||||||
|
|
||||||
|
The "good" key files which are supposed to be loadable are:
|
||||||
|
|
||||||
|
- File: UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
|
||||||
|
Address: 0x7ef5a6135f1fd6a02593eedc869c6d41d934aef8
|
||||||
|
- File: UTC--2016-03-23T09-30-22.528630983Z--f466859ead1932d743d622cb74fc058882e8648a
|
||||||
|
Address: 0xf466859ead1932d743d622cb74fc058882e8648a
|
||||||
|
- File: UTC--2016-03-23T09-30-26.532308523Z--289d485d9771714cce91d3393d764e1311907acc
|
||||||
|
Address: 0x289d485d9771714cce91d3393d764e1311907acc
|
||||||
|
|
||||||
|
The other files (including this README) are broken in various ways
|
||||||
|
and should not be picked up by package accounts:
|
||||||
|
|
||||||
|
- File: no-address (missing address field, otherwise same as "aaa")
|
||||||
|
- File: garbage (file with random data)
|
||||||
|
- File: empty (file with no content)
|
||||||
|
- File: swapfile~ (should be skipped)
|
||||||
|
- File: .hiddenfile (should be skipped)
|
||||||
|
- File: foo/... (should be skipped because it is a directory)
|
|
@ -0,0 +1 @@
|
||||||
|
{"address":"7ef5a6135f1fd6a02593eedc869c6d41d934aef8","crypto":{"cipher":"aes-128-ctr","ciphertext":"1d0839166e7a15b9c1333fc865d69858b22df26815ccf601b28219b6192974e1","cipherparams":{"iv":"8df6caa7ff1b00c4e871f002cb7921ed"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"e5e6ef3f4ea695f496b643ebd3f75c0aa58ef4070e90c80c5d3fb0241bf1595c"},"mac":"6d16dfde774845e4585357f24bce530528bc69f4f84e1e22880d34fa45c273e5"},"id":"950077c7-71e3-4c44-a4a1-143919141ed4","version":3}
|
|
@ -0,0 +1 @@
|
||||||
|
{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3}
|
|
@ -0,0 +1 @@
|
||||||
|
{"address":"289d485d9771714cce91d3393d764e1311907acc","crypto":{"cipher":"aes-128-ctr","ciphertext":"faf32ca89d286b107f5e6d842802e05263c49b78d46eac74e6109e9a963378ab","cipherparams":{"iv":"558833eec4a665a8c55608d7d503407d"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"d571fff447ffb24314f9513f5160246f09997b857ac71348b73e785aab40dc04"},"mac":"21edb85ff7d0dab1767b9bf498f2c3cb7be7609490756bd32300bb213b59effe"},"id":"3279afcf-55ba-43ff-8997-02dcc46a6525","version":3}
|
|
@ -0,0 +1 @@
|
||||||
|
{"address":"fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e","crypto":{"cipher":"aes-128-ctr","ciphertext":"8124d5134aa4a927c79fd852989e4b5419397566f04b0936a1eb1d168c7c68a5","cipherparams":{"iv":"e2febe17176414dd2cda28287947eb2f"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":4096,"p":6,"r":8,"salt":"44b415ede89f3bdd6830390a21b78965f571b347a589d1d943029f016c5e8bd5"},"mac":"5e149ff25bfd9dd45746a84bb2bcd2f015f2cbca2b6d25c5de8c29617f71fe5b"},"id":"d6ac5452-2b2c-4d3c-ad80-4bf0327d971c","version":3}
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
{"crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3}
|
|
@ -0,0 +1 @@
|
||||||
|
{"address":"0000000000000000000000000000000000000000","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3}
|
|
@ -23,6 +23,8 @@ import (
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -180,6 +182,7 @@ func unlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i
|
||||||
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
|
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
|
||||||
password := getPassPhrase(prompt, false, i, passwords)
|
password := getPassPhrase(prompt, false, i, passwords)
|
||||||
if err := accman.Unlock(account, password); err == nil {
|
if err := accman.Unlock(account, password); err == nil {
|
||||||
|
glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
|
||||||
return account, password
|
return account, password
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,7 +202,9 @@ func getPassPhrase(prompt string, confirmation bool, i int, passwords []string)
|
||||||
return passwords[len(passwords)-1]
|
return passwords[len(passwords)-1]
|
||||||
}
|
}
|
||||||
// Otherwise prompt the user for the password
|
// Otherwise prompt the user for the password
|
||||||
fmt.Println(prompt)
|
if prompt != "" {
|
||||||
|
fmt.Println(prompt)
|
||||||
|
}
|
||||||
password, err := utils.Stdin.PasswordPrompt("Passphrase: ")
|
password, err := utils.Stdin.PasswordPrompt("Passphrase: ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to read passphrase: %v", err)
|
utils.Fatalf("Failed to read passphrase: %v", err)
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
// Copyright 2016 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/cespare/cp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// These tests are 'smoke tests' for the account related
|
||||||
|
// subcommands and flags.
|
||||||
|
//
|
||||||
|
// For most tests, the test files from package accounts
|
||||||
|
// are copied into a temporary keystore directory.
|
||||||
|
|
||||||
|
func tmpDatadirWithKeystore(t *testing.T) string {
|
||||||
|
datadir := tmpdir(t)
|
||||||
|
keystore := filepath.Join(datadir, "keystore")
|
||||||
|
source := filepath.Join("..", "..", "accounts", "testdata", "keystore")
|
||||||
|
if err := cp.CopyAll(keystore, source); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return datadir
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountListEmpty(t *testing.T) {
|
||||||
|
geth := runGeth(t, "account")
|
||||||
|
geth.expectExit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountList(t *testing.T) {
|
||||||
|
datadir := tmpDatadirWithKeystore(t)
|
||||||
|
geth := runGeth(t, "--datadir", datadir, "account")
|
||||||
|
defer geth.expectExit()
|
||||||
|
geth.expect(`
|
||||||
|
Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8}
|
||||||
|
Account #1: {f466859ead1932d743d622cb74fc058882e8648a}
|
||||||
|
Account #2: {289d485d9771714cce91d3393d764e1311907acc}
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountNew(t *testing.T) {
|
||||||
|
geth := runGeth(t, "--lightkdf", "account", "new")
|
||||||
|
defer geth.expectExit()
|
||||||
|
geth.expect(`
|
||||||
|
Your new account is locked with a password. Please give a password. Do not forget this password.
|
||||||
|
!! Unsupported terminal, password will be echoed.
|
||||||
|
Passphrase: {{.InputLine "foobar"}}
|
||||||
|
Repeat passphrase: {{.InputLine "foobar"}}
|
||||||
|
`)
|
||||||
|
geth.expectRegexp(`Address: \{[0-9a-f]{40}\}\n`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountNewBadRepeat(t *testing.T) {
|
||||||
|
geth := runGeth(t, "--lightkdf", "account", "new")
|
||||||
|
defer geth.expectExit()
|
||||||
|
geth.expect(`
|
||||||
|
Your new account is locked with a password. Please give a password. Do not forget this password.
|
||||||
|
!! Unsupported terminal, password will be echoed.
|
||||||
|
Passphrase: {{.InputLine "something"}}
|
||||||
|
Repeat passphrase: {{.InputLine "something else"}}
|
||||||
|
Fatal: Passphrases do not match
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountUpdate(t *testing.T) {
|
||||||
|
datadir := tmpDatadirWithKeystore(t)
|
||||||
|
geth := runGeth(t,
|
||||||
|
"--datadir", datadir, "--lightkdf",
|
||||||
|
"account", "update", "f466859ead1932d743d622cb74fc058882e8648a")
|
||||||
|
defer geth.expectExit()
|
||||||
|
geth.expect(`
|
||||||
|
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
||||||
|
!! Unsupported terminal, password will be echoed.
|
||||||
|
Passphrase: {{.InputLine "foobar"}}
|
||||||
|
Please give a new password. Do not forget this password.
|
||||||
|
Passphrase: {{.InputLine "foobar2"}}
|
||||||
|
Repeat passphrase: {{.InputLine "foobar2"}}
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWalletImport(t *testing.T) {
|
||||||
|
geth := runGeth(t, "--lightkdf", "wallet", "import", "testdata/guswallet.json")
|
||||||
|
defer geth.expectExit()
|
||||||
|
geth.expect(`
|
||||||
|
!! Unsupported terminal, password will be echoed.
|
||||||
|
Passphrase: {{.InputLine "foo"}}
|
||||||
|
Address: {d4584b5f6229b7be90727b0fc8c6b91bb427821f}
|
||||||
|
`)
|
||||||
|
|
||||||
|
files, err := ioutil.ReadDir(filepath.Join(geth.Datadir, "keystore"))
|
||||||
|
if len(files) != 1 {
|
||||||
|
t.Errorf("expected one key file in keystore directory, found %d files (error: %v)", len(files), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWalletImportBadPassword(t *testing.T) {
|
||||||
|
geth := runGeth(t, "--lightkdf", "wallet", "import", "testdata/guswallet.json")
|
||||||
|
defer geth.expectExit()
|
||||||
|
geth.expect(`
|
||||||
|
!! Unsupported terminal, password will be echoed.
|
||||||
|
Passphrase: {{.InputLine "wrong"}}
|
||||||
|
Fatal: Could not create the account: Decryption failed: PKCS7Unpad failed after AES decryption
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnlockFlag(t *testing.T) {
|
||||||
|
datadir := tmpDatadirWithKeystore(t)
|
||||||
|
geth := runGeth(t,
|
||||||
|
"--datadir", datadir, "--nat", "none", "--nodiscover", "--dev",
|
||||||
|
"--unlock", "f466859ead1932d743d622cb74fc058882e8648a",
|
||||||
|
"js", "testdata/empty.js")
|
||||||
|
geth.expect(`
|
||||||
|
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
||||||
|
!! Unsupported terminal, password will be echoed.
|
||||||
|
Passphrase: {{.InputLine "foobar"}}
|
||||||
|
`)
|
||||||
|
geth.expectExit()
|
||||||
|
|
||||||
|
wantMessages := []string{
|
||||||
|
"Unlocked account f466859ead1932d743d622cb74fc058882e8648a",
|
||||||
|
}
|
||||||
|
for _, m := range wantMessages {
|
||||||
|
if strings.Index(geth.stderrText(), m) == -1 {
|
||||||
|
t.Errorf("stderr text does not contain %q", m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnlockFlagWrongPassword(t *testing.T) {
|
||||||
|
datadir := tmpDatadirWithKeystore(t)
|
||||||
|
geth := runGeth(t,
|
||||||
|
"--datadir", datadir, "--nat", "none", "--nodiscover", "--dev",
|
||||||
|
"--unlock", "f466859ead1932d743d622cb74fc058882e8648a")
|
||||||
|
defer geth.expectExit()
|
||||||
|
geth.expect(`
|
||||||
|
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
||||||
|
!! Unsupported terminal, password will be echoed.
|
||||||
|
Passphrase: {{.InputLine "wrong1"}}
|
||||||
|
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 2/3
|
||||||
|
Passphrase: {{.InputLine "wrong2"}}
|
||||||
|
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 3/3
|
||||||
|
Passphrase: {{.InputLine "wrong3"}}
|
||||||
|
Fatal: Failed to unlock account: f466859ead1932d743d622cb74fc058882e8648a
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/ethereum/go-ethereum/issues/1785
|
||||||
|
func TestUnlockFlagMultiIndex(t *testing.T) {
|
||||||
|
datadir := tmpDatadirWithKeystore(t)
|
||||||
|
geth := runGeth(t,
|
||||||
|
"--datadir", datadir, "--nat", "none", "--nodiscover", "--dev",
|
||||||
|
"--unlock", "0,2",
|
||||||
|
"js", "testdata/empty.js")
|
||||||
|
geth.expect(`
|
||||||
|
Unlocking account 0 | Attempt 1/3
|
||||||
|
!! Unsupported terminal, password will be echoed.
|
||||||
|
Passphrase: {{.InputLine "foobar"}}
|
||||||
|
Unlocking account 2 | Attempt 1/3
|
||||||
|
Passphrase: {{.InputLine "foobar"}}
|
||||||
|
`)
|
||||||
|
geth.expectExit()
|
||||||
|
|
||||||
|
wantMessages := []string{
|
||||||
|
"Unlocked account 7ef5a6135f1fd6a02593eedc869c6d41d934aef8",
|
||||||
|
"Unlocked account 289d485d9771714cce91d3393d764e1311907acc",
|
||||||
|
}
|
||||||
|
for _, m := range wantMessages {
|
||||||
|
if strings.Index(geth.stderrText(), m) == -1 {
|
||||||
|
t.Errorf("stderr text does not contain %q", m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnlockFlagPasswordFile(t *testing.T) {
|
||||||
|
datadir := tmpDatadirWithKeystore(t)
|
||||||
|
geth := runGeth(t,
|
||||||
|
"--datadir", datadir, "--nat", "none", "--nodiscover", "--dev",
|
||||||
|
"--password", "testdata/passwords.txt", "--unlock", "0,2",
|
||||||
|
"js", "testdata/empty.js")
|
||||||
|
geth.expectExit()
|
||||||
|
|
||||||
|
wantMessages := []string{
|
||||||
|
"Unlocked account 7ef5a6135f1fd6a02593eedc869c6d41d934aef8",
|
||||||
|
"Unlocked account 289d485d9771714cce91d3393d764e1311907acc",
|
||||||
|
}
|
||||||
|
for _, m := range wantMessages {
|
||||||
|
if strings.Index(geth.stderrText(), m) == -1 {
|
||||||
|
t.Errorf("stderr text does not contain %q", m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnlockFlagPasswordFileWrongPassword(t *testing.T) {
|
||||||
|
datadir := tmpDatadirWithKeystore(t)
|
||||||
|
geth := runGeth(t,
|
||||||
|
"--datadir", datadir, "--nat", "none", "--nodiscover", "--dev",
|
||||||
|
"--password", "testdata/wrong-passwords.txt", "--unlock", "0,2")
|
||||||
|
defer geth.expectExit()
|
||||||
|
geth.expect(`
|
||||||
|
Fatal: Failed to unlock account: 0
|
||||||
|
`)
|
||||||
|
}
|
|
@ -0,0 +1,282 @@
|
||||||
|
// Copyright 2016 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tmpdir(t *testing.T) string {
|
||||||
|
dir, err := ioutil.TempDir("", "geth-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
|
||||||
|
type testgeth struct {
|
||||||
|
// For total convenience, all testing methods are available.
|
||||||
|
*testing.T
|
||||||
|
// template variables for expect
|
||||||
|
Datadir string
|
||||||
|
Executable string
|
||||||
|
|
||||||
|
removeDatadir bool
|
||||||
|
cmd *exec.Cmd
|
||||||
|
stdout *bufio.Reader
|
||||||
|
stdin io.WriteCloser
|
||||||
|
stderr *testlogger
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Run the app if we're the child process for runGeth.
|
||||||
|
if os.Getenv("GETH_TEST_CHILD") != "" {
|
||||||
|
app.RunAndExitOnError()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// spawns geth with the given command line args. If the args don't set --datadir, the
|
||||||
|
// child g gets a temporary data directory.
|
||||||
|
func runGeth(t *testing.T, args ...string) *testgeth {
|
||||||
|
tt := &testgeth{T: t, Executable: os.Args[0]}
|
||||||
|
for i, arg := range args {
|
||||||
|
if arg == "-datadir" || arg == "--datadir" {
|
||||||
|
if i < len(args)-1 {
|
||||||
|
tt.Datadir = args[i+1]
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tt.Datadir == "" {
|
||||||
|
tt.Datadir = tmpdir(t)
|
||||||
|
tt.removeDatadir = true
|
||||||
|
args = append([]string{"-datadir", tt.Datadir}, args...)
|
||||||
|
// Remove the temporary datadir if something fails below.
|
||||||
|
defer func() {
|
||||||
|
if t.Failed() {
|
||||||
|
os.RemoveAll(tt.Datadir)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boot "geth". This actually runs the test binary but the init function
|
||||||
|
// will prevent any tests from running.
|
||||||
|
tt.stderr = &testlogger{t: t}
|
||||||
|
tt.cmd = exec.Command(os.Args[0], args...)
|
||||||
|
tt.cmd.Env = append(os.Environ(), "GETH_TEST_CHILD=1")
|
||||||
|
tt.cmd.Stderr = tt.stderr
|
||||||
|
stdout, err := tt.cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tt.stdout = bufio.NewReader(stdout)
|
||||||
|
if tt.stdin, err = tt.cmd.StdinPipe(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tt.cmd.Start(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return tt
|
||||||
|
}
|
||||||
|
|
||||||
|
// InputLine writes the given text to the childs stdin.
|
||||||
|
// This method can also be called from an expect template, e.g.:
|
||||||
|
//
|
||||||
|
// geth.expect(`Passphrase: {{.InputLine "password"}}`)
|
||||||
|
func (tt *testgeth) InputLine(s string) string {
|
||||||
|
io.WriteString(tt.stdin, s+"\n")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// expect runs its argument as a template, then expects the
|
||||||
|
// child process to output the result of the template within 5s.
|
||||||
|
//
|
||||||
|
// If the template starts with a newline, the newline is removed
|
||||||
|
// before matching.
|
||||||
|
func (tt *testgeth) expect(tplsource string) {
|
||||||
|
// Generate the expected output by running the template.
|
||||||
|
tpl := template.Must(template.New("").Parse(tplsource))
|
||||||
|
wantbuf := new(bytes.Buffer)
|
||||||
|
if err := tpl.Execute(wantbuf, tt); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// Trim exactly one newline at the beginning. This makes tests look
|
||||||
|
// much nicer because all expect strings are at column 0.
|
||||||
|
want := bytes.TrimPrefix(wantbuf.Bytes(), []byte("\n"))
|
||||||
|
if err := tt.matchExactOutput(want); err != nil {
|
||||||
|
tt.Fatal(err)
|
||||||
|
}
|
||||||
|
tt.Logf("Matched stdout text:\n%s", want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tt *testgeth) matchExactOutput(want []byte) error {
|
||||||
|
buf := make([]byte, len(want))
|
||||||
|
n := 0
|
||||||
|
tt.withKillTimeout(func() { n, _ = io.ReadFull(tt.stdout, buf) })
|
||||||
|
buf = buf[:n]
|
||||||
|
if n < len(want) || !bytes.Equal(buf, want) {
|
||||||
|
// Grab any additional buffered output in case of mismatch
|
||||||
|
// because it might help with debugging.
|
||||||
|
buf = append(buf, make([]byte, tt.stdout.Buffered())...)
|
||||||
|
tt.stdout.Read(buf[n:])
|
||||||
|
// Find the mismatch position.
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if want[i] != buf[i] {
|
||||||
|
return fmt.Errorf("Output mismatch at ◊:\n---------------- (stdout text)\n%s◊%s\n---------------- (expected text)\n%s",
|
||||||
|
buf[:i], buf[i:n], want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n < len(want) {
|
||||||
|
return fmt.Errorf("Not enough output, got until ◊:\n---------------- (stdout text)\n%s\n---------------- (expected text)\n%s◊%s",
|
||||||
|
buf, want[:n], want[n:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// expectRegexp expects the child process to output text matching the
|
||||||
|
// given regular expression within 5s.
|
||||||
|
//
|
||||||
|
// Note that an arbitrary amount of output may be consumed by the
|
||||||
|
// regular expression. This usually means that expect cannot be used
|
||||||
|
// after expectRegexp.
|
||||||
|
func (tt *testgeth) expectRegexp(resource string) (*regexp.Regexp, []string) {
|
||||||
|
var (
|
||||||
|
re = regexp.MustCompile(resource)
|
||||||
|
rtee = &runeTee{in: tt.stdout}
|
||||||
|
matches []int
|
||||||
|
)
|
||||||
|
tt.withKillTimeout(func() { matches = re.FindReaderSubmatchIndex(rtee) })
|
||||||
|
output := rtee.buf.Bytes()
|
||||||
|
if matches == nil {
|
||||||
|
tt.Fatalf("Output did not match:\n---------------- (stdout text)\n%s\n---------------- (regular expression)\n%s",
|
||||||
|
output, resource)
|
||||||
|
return re, nil
|
||||||
|
}
|
||||||
|
tt.Logf("Matched stdout text:\n%s", output)
|
||||||
|
var submatch []string
|
||||||
|
for i := 0; i < len(matches); i += 2 {
|
||||||
|
submatch = append(submatch, string(output[i:i+1]))
|
||||||
|
}
|
||||||
|
return re, submatch
|
||||||
|
}
|
||||||
|
|
||||||
|
// expectExit expects the child process to exit within 5s without
|
||||||
|
// printing any additional text on stdout.
|
||||||
|
func (tt *testgeth) expectExit() {
|
||||||
|
var output []byte
|
||||||
|
tt.withKillTimeout(func() {
|
||||||
|
output, _ = ioutil.ReadAll(tt.stdout)
|
||||||
|
})
|
||||||
|
tt.cmd.Wait()
|
||||||
|
if tt.removeDatadir {
|
||||||
|
os.RemoveAll(tt.Datadir)
|
||||||
|
}
|
||||||
|
if len(output) > 0 {
|
||||||
|
tt.Errorf("Unmatched stdout text:\n%s", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tt *testgeth) interrupt() {
|
||||||
|
tt.cmd.Process.Signal(os.Interrupt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stderrText returns any stderr output written so far.
|
||||||
|
// The returned text holds all log lines after expectExit has
|
||||||
|
// returned.
|
||||||
|
func (tt *testgeth) stderrText() string {
|
||||||
|
tt.stderr.mu.Lock()
|
||||||
|
defer tt.stderr.mu.Unlock()
|
||||||
|
return tt.stderr.buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tt *testgeth) withKillTimeout(fn func()) {
|
||||||
|
timeout := time.AfterFunc(5*time.Second, func() {
|
||||||
|
tt.Log("killing the child process (timeout)")
|
||||||
|
tt.cmd.Process.Kill()
|
||||||
|
if tt.removeDatadir {
|
||||||
|
os.RemoveAll(tt.Datadir)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer timeout.Stop()
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
// testlogger logs all written lines via t.Log and also
|
||||||
|
// collects them for later inspection.
|
||||||
|
type testlogger struct {
|
||||||
|
t *testing.T
|
||||||
|
mu sync.Mutex
|
||||||
|
buf bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tl *testlogger) Write(b []byte) (n int, err error) {
|
||||||
|
lines := bytes.Split(b, []byte("\n"))
|
||||||
|
for _, line := range lines {
|
||||||
|
if len(line) > 0 {
|
||||||
|
tl.t.Logf("(stderr) %s", line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tl.mu.Lock()
|
||||||
|
tl.buf.Write(b)
|
||||||
|
tl.mu.Unlock()
|
||||||
|
return len(b), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// runeTee collects text read through it into buf.
|
||||||
|
type runeTee struct {
|
||||||
|
in interface {
|
||||||
|
io.Reader
|
||||||
|
io.ByteReader
|
||||||
|
io.RuneReader
|
||||||
|
}
|
||||||
|
buf bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rtee *runeTee) Read(b []byte) (n int, err error) {
|
||||||
|
n, err = rtee.in.Read(b)
|
||||||
|
rtee.buf.Write(b[:n])
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rtee *runeTee) ReadRune() (r rune, size int, err error) {
|
||||||
|
r, size, err = rtee.in.ReadRune()
|
||||||
|
if err == nil {
|
||||||
|
rtee.buf.WriteRune(r)
|
||||||
|
}
|
||||||
|
return r, size, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rtee *runeTee) ReadByte() (b byte, err error) {
|
||||||
|
b, err = rtee.in.ReadByte()
|
||||||
|
if err == nil {
|
||||||
|
rtee.buf.WriteByte(b)
|
||||||
|
}
|
||||||
|
return b, err
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"encseed": "26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba",
|
||||||
|
"ethaddr": "d4584b5f6229b7be90727b0fc8c6b91bb427821f",
|
||||||
|
"email": "gustav.simonsson@gmail.com",
|
||||||
|
"btcaddr": "1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx"
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
foobar
|
||||||
|
foobar
|
||||||
|
foobar
|
|
@ -0,0 +1,3 @@
|
||||||
|
wrong
|
||||||
|
wrong
|
||||||
|
wrong
|
Loading…
Reference in New Issue