-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
R4R: Fix Non-Deterministic Test Coverage #1850
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,10 +12,19 @@ import ( | |
"strings" | ||
"testing" | ||
|
||
"github.com/cosmos/cosmos-sdk/client" | ||
keys "github.com/cosmos/cosmos-sdk/client/keys" | ||
gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app" | ||
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys" | ||
"github.com/cosmos/cosmos-sdk/server" | ||
"github.com/cosmos/cosmos-sdk/tests" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/wire" | ||
"github.com/cosmos/cosmos-sdk/x/auth" | ||
|
||
"github.com/spf13/viper" | ||
"github.com/stretchr/testify/require" | ||
|
||
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys" | ||
abci "github.com/tendermint/tendermint/abci/types" | ||
tmcfg "github.com/tendermint/tendermint/config" | ||
"github.com/tendermint/tendermint/crypto" | ||
|
@@ -28,29 +37,21 @@ import ( | |
"github.com/tendermint/tendermint/proxy" | ||
tmrpc "github.com/tendermint/tendermint/rpc/lib/server" | ||
tmtypes "github.com/tendermint/tendermint/types" | ||
|
||
"github.com/cosmos/cosmos-sdk/client" | ||
keys "github.com/cosmos/cosmos-sdk/client/keys" | ||
gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app" | ||
"github.com/cosmos/cosmos-sdk/server" | ||
"github.com/cosmos/cosmos-sdk/tests" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/wire" | ||
"github.com/cosmos/cosmos-sdk/x/auth" | ||
) | ||
|
||
// f**ing long, but unique for each test | ||
// makePathname creates a unique pathname for each test. It will panic if it | ||
// cannot get the current working directory. | ||
func makePathname() string { | ||
// get path | ||
p, err := os.Getwd() | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
sep := string(filepath.Separator) | ||
return strings.Replace(p, sep, "_", -1) | ||
} | ||
|
||
// GetConfig returns a config for the test cases as a singleton | ||
// GetConfig returns a Tendermint config for the test cases. | ||
func GetConfig() *tmcfg.Config { | ||
pathname := makePathname() | ||
config := tmcfg.ResetTestRoot(pathname) | ||
|
@@ -59,52 +60,73 @@ func GetConfig() *tmcfg.Config { | |
if err != nil { | ||
panic(err) | ||
} | ||
|
||
rcpAddr, _, err := server.FreeTCPAddr() | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
grpcAddr, _, err := server.FreeTCPAddr() | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
config.P2P.ListenAddress = tmAddr | ||
config.RPC.ListenAddress = rcpAddr | ||
config.RPC.GRPCListenAddress = grpcAddr | ||
|
||
return config | ||
} | ||
|
||
// get the lcd test keybase | ||
// note can't use a memdb because the request is expecting to interact with the default location | ||
func GetKB(t *testing.T) crkeys.Keybase { | ||
// GetKeyBase returns the LCD test keybase. It also requires that a directory | ||
// could be made and a keybase could be fetched. | ||
// | ||
// NOTE: memDB cannot be used because the request is expecting to interact with | ||
// the default location. | ||
func GetKeyBase(t *testing.T) crkeys.Keybase { | ||
dir, err := ioutil.TempDir("", "lcd_test") | ||
require.NoError(t, err) | ||
|
||
viper.Set(cli.HomeFlag, dir) | ||
keybase, err := keys.GetKeyBase() // dbm.NewMemDB()) // :( | ||
|
||
keybase, err := keys.GetKeyBase() | ||
require.NoError(t, err) | ||
|
||
return keybase | ||
} | ||
|
||
// add an address to the store return name and password | ||
func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (addr sdk.AccAddress, seed string) { | ||
var info crkeys.Info | ||
var err error | ||
// CreateAddr adds an address to the key store and returns an address and seed. | ||
// It also requires that the key could be created. | ||
func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (sdk.AccAddress, string) { | ||
var ( | ||
err error | ||
info crkeys.Info | ||
seed string | ||
) | ||
|
||
info, seed, err = kb.CreateMnemonic(name, crkeys.English, password, crkeys.Secp256k1) | ||
require.NoError(t, err) | ||
addr = sdk.AccAddress(info.GetPubKey().Address()) | ||
return | ||
} | ||
|
||
// strt TM and the LCD in process, listening on their respective sockets | ||
// nValidators = number of validators | ||
// initAddrs = accounts to initialize with some steaks | ||
func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress) (cleanup func(), validatorsPKs []crypto.PubKey, port string) { | ||
return sdk.AccAddress(info.GetPubKey().Address()), seed | ||
} | ||
|
||
// InitializeTestLCD starts Tendermint and the LCD in process, listening on | ||
// their respective sockets where nValidators is the total number of validators | ||
// and initAddrs are the accounts to initialize with some steak tokens. It | ||
// returns a cleanup function, a set of validator public keys, and a port. | ||
func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress) (func(), []crypto.PubKey, string) { | ||
config := GetConfig() | ||
config.Consensus.TimeoutCommit = 100 | ||
config.Consensus.SkipTimeoutCommit = false | ||
config.TxIndex.IndexAllTags = true | ||
|
||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) | ||
logger = log.NewFilter(logger, log.AllowDebug()) | ||
|
||
privValidatorFile := config.PrivValidatorFile() | ||
privVal := pvm.LoadOrGenFilePV(privValidatorFile) | ||
privVal.Reset() | ||
|
||
db := dbm.NewMemDB() | ||
app := gapp.NewGaiaApp(logger, db, nil) | ||
cdc = gapp.MakeCodec() | ||
|
@@ -113,10 +135,10 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress | |
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile) | ||
require.NoError(t, err) | ||
|
||
// add more validators | ||
if nValidators < 1 { | ||
panic("InitializeTestLCD must use at least one validator") | ||
} | ||
|
||
for i := 1; i < nValidators; i++ { | ||
genDoc.Validators = append(genDoc.Validators, | ||
tmtypes.GenesisValidator{ | ||
|
@@ -127,14 +149,18 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress | |
) | ||
} | ||
|
||
// NOTE it's bad practice to reuse pk address for the owner address but doing in the | ||
// test for simplicity | ||
var validatorsPKs []crypto.PubKey | ||
|
||
// NOTE: It's bad practice to reuse public key address for the owner | ||
// address but doing in the test for simplicity. | ||
var appGenTxs []json.RawMessage | ||
for _, gdValidator := range genDoc.Validators { | ||
pk := gdValidator.PubKey | ||
validatorsPKs = append(validatorsPKs, pk) // append keys for output | ||
validatorsPKs = append(validatorsPKs, pk) | ||
|
||
appGenTx, _, _, err := gapp.GaiaAppGenTxNF(cdc, pk, sdk.AccAddress(pk.Address()), "test_val1") | ||
require.NoError(t, err) | ||
|
||
appGenTxs = append(appGenTxs, appGenTx) | ||
} | ||
|
||
|
@@ -154,79 +180,87 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress | |
require.NoError(t, err) | ||
genDoc.AppState = appState | ||
|
||
// LCD listen address | ||
var listenAddr string | ||
listenAddr, port, err = server.FreeTCPAddr() | ||
listenAddr, port, err := server.FreeTCPAddr() | ||
require.NoError(t, err) | ||
|
||
// XXX: need to set this so LCD knows the tendermint node address! | ||
// XXX: Need to set this so LCD knows the tendermint node address! | ||
viper.Set(client.FlagNode, config.RPC.ListenAddress) | ||
viper.Set(client.FlagChainID, genDoc.ChainID) | ||
|
||
node, err := startTM(config, logger, genDoc, privVal, app) | ||
require.NoError(t, err) | ||
|
||
lcd, err := startLCD(logger, listenAddr, cdc) | ||
require.NoError(t, err) | ||
|
||
//time.Sleep(time.Second) | ||
//tests.WaitForHeight(2, port) | ||
tests.WaitForLCDStart(port) | ||
tests.WaitForHeight(1, port) | ||
|
||
// for use in defer | ||
cleanup = func() { | ||
cleanup := func() { | ||
logger.Debug("cleaning up LCD initialization") | ||
node.Stop() | ||
node.Wait() | ||
lcd.Close() | ||
} | ||
|
||
return | ||
return cleanup, validatorsPKs, port | ||
} | ||
|
||
// Create & start in-process tendermint node with memdb | ||
// and in-process abci application. | ||
// TODO: need to clean up the WAL dir or enable it to be not persistent | ||
func startTM(tmcfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc, privVal tmtypes.PrivValidator, app abci.Application) (*nm.Node, error) { | ||
// startTM creates and starts an in-process Tendermint node with memDB and | ||
// in-process ABCI application. It returns the new node or any error that | ||
// occurred. | ||
// | ||
// TODO: Clean up the WAL dir or enable it to be not persistent! | ||
func startTM( | ||
tmcfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc, | ||
privVal tmtypes.PrivValidator, app abci.Application, | ||
) (*nm.Node, error) { | ||
genDocProvider := func() (*tmtypes.GenesisDoc, error) { return genDoc, nil } | ||
dbProvider := func(*nm.DBContext) (dbm.DB, error) { return dbm.NewMemDB(), nil } | ||
n, err := nm.NewNode(tmcfg, | ||
node, err := nm.NewNode( | ||
tmcfg, | ||
privVal, | ||
proxy.NewLocalClientCreator(app), | ||
genDocProvider, | ||
dbProvider, | ||
nm.DefaultMetricsProvider, | ||
logger.With("module", "node")) | ||
logger.With("module", "node"), | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
err = n.Start() | ||
err = node.Start() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// wait for rpc | ||
tests.WaitForRPC(tmcfg.RPC.ListenAddress) | ||
|
||
logger.Info("Tendermint running!") | ||
return n, err | ||
|
||
return node, err | ||
} | ||
|
||
// start the LCD. note this blocks! | ||
// startLCD starts the LCD. | ||
// | ||
// NOTE: This causes the thread to block. | ||
func startLCD(logger log.Logger, listenAddr string, cdc *wire.Codec) (net.Listener, error) { | ||
handler := createHandler(cdc) | ||
return tmrpc.StartHTTPServer(listenAddr, handler, logger, tmrpc.Config{}) | ||
return tmrpc.StartHTTPServer(listenAddr, createHandler(cdc), logger, tmrpc.Config{}) | ||
} | ||
|
||
// make a test lcd test request | ||
// Request makes a test LCD test request. It returns a response object and a | ||
// stringified response body. | ||
func Request(t *testing.T, port, method, path string, payload []byte) (*http.Response, string) { | ||
var res *http.Response | ||
var err error | ||
var ( | ||
err error | ||
res *http.Response | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lol I don't like this (fine) but I may switch back at some point - why use 4 lines when you could use 2! - stylistically I'd think it's cleaner to only use var blocks for global variables - or under certain very specific situations where there a whole bunch of variables in a in a function (maybe 5+) - yeah the extra indentation is a bit confusing because in funcs typically means your in an If/switch/for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm, I suppose we have stylistic differences. I see the above as much cleaner and easier to read. Yes, granted, it's two extra lines, but Go is naturally a verbose language. However, I will limit such refactors in future work. |
||
url := fmt.Sprintf("http://localhost:%v%v", port, path) | ||
|
||
req, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) | ||
require.Nil(t, err) | ||
|
||
res, err = http.DefaultClient.Do(req) | ||
// res, err = http.Post(url, "application/json", bytes.NewBuffer(payload)) | ||
require.Nil(t, err) | ||
|
||
output, err := ioutil.ReadAll(res.Body) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not a sentence