diff --git a/.travis-bazelrc b/.travis-bazelrc index 58d0445a06f2..2be706c5c17c 100644 --- a/.travis-bazelrc +++ b/.travis-bazelrc @@ -12,3 +12,4 @@ build --verbose_failures build --sandbox_debug build --test_output=errors build --flaky_test_attempts=5 +build --features=race diff --git a/beacon-chain/sync/service_test.go b/beacon-chain/sync/service_test.go index d0d45e150b41..8078ae2ac979 100644 --- a/beacon-chain/sync/service_test.go +++ b/beacon-chain/sync/service_test.go @@ -785,22 +785,26 @@ func TestSavingBlocksInSync(t *testing.T) { t.Fatalf("unable to get hash of crystallized state: %v", err) } - block := &pb.BeaconBlock{ - MainChainRef: []byte{1, 2, 3}, - ParentHash: generichash, - SlotNumber: uint64(20), - CrystallizedStateHash: crystallizedStateHash[:], - } + getBlockResponseMsg := func(slotNumber uint64) p2p.Message { + block := &pb.BeaconBlock{ + MainChainRef: []byte{1, 2, 3}, + ParentHash: generichash, + SlotNumber: slotNumber, + CrystallizedStateHash: crystallizedStateHash[:], + } - blockResponse := &pb.BeaconBlockResponse{ - Block: block, - } + blockResponse := &pb.BeaconBlockResponse{ + Block: block, + } - msg1 := p2p.Message{ - Peer: p2p.Peer{}, - Data: blockResponse, + return p2p.Message{ + Peer: p2p.Peer{}, + Data: blockResponse, + } } + msg1 := getBlockResponseMsg(0) + msg2 := p2p.Message{ Peer: p2p.Peer{}, Data: incorrectStateResponse, @@ -818,24 +822,25 @@ func TestSavingBlocksInSync(t *testing.T) { ss.crystallizedStateBuf <- msg2 if crystallizedStateHash != ss.initialCrystallizedStateHash { - t.Fatalf("Crystallized state hash not updated: %x", blockResponse.Block.CrystallizedStateHash) + br := msg1.Data.(*pb.BeaconBlockResponse) + t.Fatalf("Crystallized state hash not updated: %x", br.Block.CrystallizedStateHash) } - blockResponse.Block.SlotNumber = 30 - msg1.Data = blockResponse + msg1 = getBlockResponseMsg(30) ss.blockBuf <- msg1 if stateResponse.CrystallizedState.GetLastFinalizedEpoch() != ss.currentSlotNumber { t.Fatalf("slotnumber saved when it was not supposed too: %v", stateResponse.CrystallizedState.GetLastFinalizedEpoch()) } - blockResponse.Block.SlotNumber = 100 + msg1 = getBlockResponseMsg(100) ss.blockBuf <- msg1 ss.cancel() <-exitRoutine - if blockResponse.Block.GetSlotNumber() != ss.currentSlotNumber { + br := msg1.Data.(*pb.BeaconBlockResponse) + if br.Block.GetSlotNumber() != ss.currentSlotNumber { t.Fatalf("slotnumber not updated despite receiving a valid block: %v", ss.currentSlotNumber) } diff --git a/client/contracts/BUILD.bazel b/client/contracts/BUILD.bazel index 3395f48c598e..f4054365b51f 100644 --- a/client/contracts/BUILD.bazel +++ b/client/contracts/BUILD.bazel @@ -23,6 +23,7 @@ go_test( name = "go_default_test", srcs = ["sharding_manager_test.go"], embed = [":go_default_library"], + race = "off", deps = [ "//client/params:go_default_library", "@com_github_ethereum_go_ethereum//accounts/abi/bind:go_default_library", diff --git a/client/syncer/BUILD.bazel b/client/syncer/BUILD.bazel index aeb2f6494384..320651cc823e 100644 --- a/client/syncer/BUILD.bazel +++ b/client/syncer/BUILD.bazel @@ -45,6 +45,29 @@ go_test( "@com_github_ethereum_go_ethereum//core:go_default_library", "@com_github_ethereum_go_ethereum//core/types:go_default_library", "@com_github_ethereum_go_ethereum//crypto:go_default_library", + "@com_github_sirupsen_logrus//hooks/test:go_default_library", + ], +) + +# by default gazelle tries to add all the test files to the first +# go_test; we need to exclude the tests that we want to configure +# in a particular way +# gazelle:exclude service_norace_test.go + +go_test( + name = "go_norace_test", + srcs = ["service_norace_test.go"], + embed = [":go_default_library"], + race = "off", # TODO(#377): fix issues with race detection testing. + deps = [ + "//client/mainchain:go_default_library", + "//client/params:go_default_library", + "//client/types:go_default_library", + "//proto/sharding/p2p/v1:go_default_library", + "//shared/database:go_default_library", + "//shared/p2p:go_default_library", + "@com_github_ethereum_go_ethereum//common:go_default_library", + "@com_github_ethereum_go_ethereum//core/types:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@com_github_sirupsen_logrus//hooks/test:go_default_library", ], diff --git a/client/syncer/service_norace_test.go b/client/syncer/service_norace_test.go new file mode 100644 index 000000000000..d066f586f5a1 --- /dev/null +++ b/client/syncer/service_norace_test.go @@ -0,0 +1,98 @@ +package syncer + +import ( + "fmt" + "io/ioutil" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + gethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/prysmaticlabs/prysm/client/mainchain" + "github.com/prysmaticlabs/prysm/client/params" + "github.com/prysmaticlabs/prysm/client/types" + pb "github.com/prysmaticlabs/prysm/proto/sharding/p2p/v1" + "github.com/prysmaticlabs/prysm/shared/database" + "github.com/prysmaticlabs/prysm/shared/p2p" + "github.com/sirupsen/logrus" + logTest "github.com/sirupsen/logrus/hooks/test" +) + +func init() { + logrus.SetLevel(logrus.DebugLevel) + logrus.SetOutput(ioutil.Discard) +} + +// This test checks the proper functioning of the handleCollationBodyRequests goroutine +// by listening to the responseSent channel which occurs after successful +// construction and sending of a response via p2p. +func TestHandleCollationBodyRequests(t *testing.T) { + hook := logTest.NewGlobal() + + config := &database.DBConfig{Name: "", DataDir: "", InMemory: true} + shardChainDB, err := database.NewDB(config) + if err != nil { + t.Fatalf("unable to setup db: %v", err) + } + server, err := p2p.NewServer() + if err != nil { + t.Fatalf("Unable to setup p2p server: %v", err) + } + + body := []byte{1, 2, 3, 4, 5} + shardID := big.NewInt(0) + chunkRoot := gethTypes.DeriveSha(types.Chunks(body)) + period := big.NewInt(0) + proposerAddress := common.BytesToAddress([]byte{}) + + header := types.NewCollationHeader(shardID, &chunkRoot, period, &proposerAddress, [32]byte{}) + // Stores the collation into the inmemory kv store shardChainDB. + collation := types.NewCollation(header, body, nil) + + shard := types.NewShard(shardID, shardChainDB.DB()) + + if err := shard.SaveCollation(collation); err != nil { + t.Fatalf("Could not store collation in shardChainDB: %v", err) + } + + syncer, err := NewSyncer(params.DefaultConfig(), &mainchain.SMCClient{}, server, shardChainDB, 0) + if err != nil { + t.Fatalf("Unable to setup syncer service: %v", err) + } + syncer.Start() + syncer.collationBodyBuf = make(chan p2p.Message) + + doneChan := make(chan struct{}) + exitRoutine := make(chan bool) + + go func() { + syncer.run(doneChan) + <-exitRoutine + }() + + msg := p2p.Message{ + Peer: p2p.Peer{}, + Data: &pb.CollationBodyRequest{ + ChunkRoot: chunkRoot.Bytes(), + ShardId: shardID.Uint64(), + Period: period.Uint64(), + ProposerAddress: proposerAddress.Bytes(), + }, + } + syncer.collationBodyBuf <- msg + doneChan <- struct{}{} + exitRoutine <- true + + logMsg := hook.Entries[1].Message + want := fmt.Sprintf("Received p2p request of type: %T", &pb.CollationBodyRequest{}) + if logMsg != want { + t.Errorf("incorrect log, expected %s, got %s", want, logMsg) + } + + logMsg = hook.Entries[4].Message + want = fmt.Sprintf("Responding to p2p collation request") + if logMsg != want { + t.Errorf("incorrect log, expected %s, got %s", want, logMsg) + } + hook.Reset() +} diff --git a/client/syncer/service_test.go b/client/syncer/service_test.go index 6e1619443e23..ccbc54594031 100644 --- a/client/syncer/service_test.go +++ b/client/syncer/service_test.go @@ -1,31 +1,18 @@ package syncer import ( - "fmt" - "io/ioutil" - "math/big" "testing" - "github.com/ethereum/go-ethereum/common" - gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/prysmaticlabs/prysm/client/mainchain" "github.com/prysmaticlabs/prysm/client/params" - "github.com/prysmaticlabs/prysm/client/types" - pb "github.com/prysmaticlabs/prysm/proto/sharding/p2p/v1" "github.com/prysmaticlabs/prysm/shared" "github.com/prysmaticlabs/prysm/shared/database" "github.com/prysmaticlabs/prysm/shared/p2p" - "github.com/sirupsen/logrus" logTest "github.com/sirupsen/logrus/hooks/test" ) var _ = shared.Service(&Syncer{}) -func init() { - logrus.SetLevel(logrus.DebugLevel) - logrus.SetOutput(ioutil.Discard) -} - func TestStop(t *testing.T) { hook := logTest.NewGlobal() @@ -63,77 +50,3 @@ func TestStop(t *testing.T) { } hook.Reset() } - -// This test checks the proper functioning of the handleCollationBodyRequests goroutine -// by listening to the responseSent channel which occurs after successful -// construction and sending of a response via p2p. -func TestHandleCollationBodyRequests(t *testing.T) { - hook := logTest.NewGlobal() - - config := &database.DBConfig{Name: "", DataDir: "", InMemory: true} - shardChainDB, err := database.NewDB(config) - if err != nil { - t.Fatalf("unable to setup db: %v", err) - } - server, err := p2p.NewServer() - if err != nil { - t.Fatalf("Unable to setup p2p server: %v", err) - } - - body := []byte{1, 2, 3, 4, 5} - shardID := big.NewInt(0) - chunkRoot := gethTypes.DeriveSha(types.Chunks(body)) - period := big.NewInt(0) - proposerAddress := common.BytesToAddress([]byte{}) - - header := types.NewCollationHeader(shardID, &chunkRoot, period, &proposerAddress, [32]byte{}) - // Stores the collation into the inmemory kv store shardChainDB. - collation := types.NewCollation(header, body, nil) - - shard := types.NewShard(shardID, shardChainDB.DB()) - - if err := shard.SaveCollation(collation); err != nil { - t.Fatalf("Could not store collation in shardChainDB: %v", err) - } - - syncer, err := NewSyncer(params.DefaultConfig(), &mainchain.SMCClient{}, server, shardChainDB, 0) - if err != nil { - t.Fatalf("Unable to setup syncer service: %v", err) - } - syncer.Start() - syncer.collationBodyBuf = make(chan p2p.Message) - - doneChan := make(chan struct{}) - exitRoutine := make(chan bool) - - go func() { - syncer.run(doneChan) - <-exitRoutine - }() - - msg := p2p.Message{ - Peer: p2p.Peer{}, - Data: &pb.CollationBodyRequest{ - ChunkRoot: chunkRoot.Bytes(), - ShardId: shardID.Uint64(), - Period: period.Uint64(), - ProposerAddress: proposerAddress.Bytes(), - }, - } - syncer.collationBodyBuf <- msg - doneChan <- struct{}{} - exitRoutine <- true - - logMsg := hook.Entries[1].Message - want := fmt.Sprintf("Received p2p request of type: %T", &pb.CollationBodyRequest{}) - if logMsg != want { - t.Errorf("incorrect log, expected %s, got %s", want, logMsg) - } - - logMsg = hook.Entries[4].Message - want = fmt.Sprintf("Responding to p2p collation request") - if logMsg != want { - t.Errorf("incorrect log, expected %s, got %s", want, logMsg) - } - hook.Reset() -} diff --git a/shared/p2p/BUILD.bazel b/shared/p2p/BUILD.bazel index de87fb0e7a31..35a294e8fb13 100644 --- a/shared/p2p/BUILD.bazel +++ b/shared/p2p/BUILD.bazel @@ -33,7 +33,6 @@ go_library( go_test( name = "go_default_test", srcs = [ - "discovery_test.go", "feed_example_test.go", "feed_test.go", "options_test.go", @@ -47,22 +46,29 @@ go_test( "@com_github_ethereum_go_ethereum//event:go_default_library", "@com_github_golang_protobuf//proto:go_default_library", "@com_github_libp2p_go_floodsub//:go_default_library", - "@com_github_libp2p_go_libp2p//p2p/discovery:go_default_library", "@com_github_libp2p_go_libp2p//p2p/host/basic:go_default_library", - "@com_github_libp2p_go_libp2p_peer//:go_default_library", "@com_github_libp2p_go_libp2p_swarm//testing:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", - "@com_github_sirupsen_logrus//hooks/test:go_default_library", ], ) # by default gazelle tries to add all the test files to the first -# go_test; we want to treat feed_concurrent_test.go differently -# gazelle:exclude feed_concurrent_test.go +# go_test; we need to exclude the tests that we want to configure +# in a particular way +# gazelle:exclude discovery_norace_test.go +# gazelle:exclude service_norace_test.go go_test( - name = "go_feed_concurrent_write_test", - srcs = ["feed_concurrent_test.go"], + name = "go_norace_test", + srcs = [ + "discovery_norace_test.go", + "service_norace_test.go", + ], embed = [":go_default_library"], - race = "on", + race = "off", # TODO(#377): fix issues with race detection testing. + deps = [ + "@com_github_libp2p_go_libp2p//p2p/host/basic:go_default_library", + "@com_github_libp2p_go_libp2p_swarm//testing:go_default_library", + "@com_github_sirupsen_logrus//hooks/test:go_default_library", + ], ) diff --git a/shared/p2p/discovery_test.go b/shared/p2p/discovery_norace_test.go similarity index 100% rename from shared/p2p/discovery_test.go rename to shared/p2p/discovery_norace_test.go index cb9b1b594b6a..2d42402f111b 100644 --- a/shared/p2p/discovery_test.go +++ b/shared/p2p/discovery_norace_test.go @@ -24,6 +24,18 @@ func (f *fakeTopicPeerLister) ListPeers(topic string) []peer.ID { return nil } +func expectPeers(t *testing.T, h *bhost.BasicHost, n int) { + if len(h.Peerstore().Peers()) != n { + t.Errorf( + "Expected %d peer for host %v, but has %d peers. They are: %v.", + n, + h.ID(), + len(h.Peerstore().Peers()), + h.Peerstore().Peers(), + ) + } +} + func TestStartDiscovery_HandlePeerFound(t *testing.T) { discoveryInterval = 50 * time.Millisecond // Short interval for testing. @@ -50,15 +62,3 @@ func TestStartDiscovery_HandlePeerFound(t *testing.T) { expectPeers(t, a, 2) expectPeers(t, b, 2) } - -func expectPeers(t *testing.T, h *bhost.BasicHost, n int) { - if len(h.Peerstore().Peers()) != n { - t.Errorf( - "Expected %d peer for host %v, but has %d peers. They are: %v.", - n, - h.ID(), - len(h.Peerstore().Peers()), - h.Peerstore().Peers(), - ) - } -} diff --git a/shared/p2p/feed_concurrent_test.go b/shared/p2p/feed_concurrent_test.go deleted file mode 100644 index fb0c62f8fc3e..000000000000 --- a/shared/p2p/feed_concurrent_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package p2p - -import "testing" - -func TestFeed_ConcurrentWrite(t *testing.T) { - s, err := NewServer() - if err != nil { - t.Fatalf("could not create server %v", err) - } - - for i := 0; i < 5; i++ { - go s.Feed("a") - } -} diff --git a/shared/p2p/feed_test.go b/shared/p2p/feed_test.go index 96241fc5166b..0f3fb38ab323 100644 --- a/shared/p2p/feed_test.go +++ b/shared/p2p/feed_test.go @@ -35,3 +35,14 @@ func TestFeed_ReturnsSameFeed(t *testing.T) { } } } + +func TestFeed_ConcurrentWrite(t *testing.T) { + s, err := NewServer() + if err != nil { + t.Fatalf("could not create server %v", err) + } + + for i := 0; i < 5; i++ { + go s.Feed("a") + } +} diff --git a/shared/p2p/service_norace_test.go b/shared/p2p/service_norace_test.go new file mode 100644 index 000000000000..c183db51431c --- /dev/null +++ b/shared/p2p/service_norace_test.go @@ -0,0 +1,35 @@ +package p2p + +import ( + "testing" + + logTest "github.com/sirupsen/logrus/hooks/test" +) + +func TestLifecycle(t *testing.T) { + hook := logTest.NewGlobal() + + s, err := NewServer() + if err != nil { + t.Fatalf("Could not start a new server: %v", err) + } + + s.Start() + msg := hook.Entries[0].Message + want := "Starting service" + if msg != want { + t.Errorf("incorrect log. wanted: %s. got: %v", want, msg) + } + + s.Stop() + msg = hook.LastEntry().Message + want = "Stopping service" + if msg != want { + t.Errorf("incorrect log. wanted: %s. got: %v", want, msg) + } + + // The context should have been cancelled. + if s.ctx.Err() == nil { + t.Error("Context was not cancelled") + } +} diff --git a/shared/p2p/service_test.go b/shared/p2p/service_test.go index 0c0ce5682b98..cf7dbc84832d 100644 --- a/shared/p2p/service_test.go +++ b/shared/p2p/service_test.go @@ -17,7 +17,6 @@ import ( bhost "github.com/libp2p/go-libp2p/p2p/host/basic" shardpb "github.com/prysmaticlabs/prysm/proto/sharding/p2p/v1" "github.com/sirupsen/logrus" - logTest "github.com/sirupsen/logrus/hooks/test" ) // Ensure that server implements service. @@ -28,34 +27,6 @@ func init() { logrus.SetOutput(ioutil.Discard) } -func TestLifecycle(t *testing.T) { - hook := logTest.NewGlobal() - - s, err := NewServer() - if err != nil { - t.Fatalf("Could not start a new server: %v", err) - } - - s.Start() - msg := hook.Entries[0].Message - want := "Starting service" - if msg != want { - t.Errorf("incorrect log. wanted: %s. got: %v", want, msg) - } - - s.Stop() - msg = hook.LastEntry().Message - want = "Stopping service" - if msg != want { - t.Errorf("incorrect log. wanted: %s. got: %v", want, msg) - } - - // The context should have been cancelled. - if s.ctx.Err() == nil { - t.Error("Context was not cancelled") - } -} - func TestBroadcast(t *testing.T) { s, err := NewServer() if err != nil {