package abcicli_test import ( "errors" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" abcicli "github.com/tendermint/tendermint/abci/client" "github.com/tendermint/tendermint/abci/server" "github.com/tendermint/tendermint/abci/types" tmrand "github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/libs/service" ) type errorStopper interface { StopForError(error) } func TestSocketClientStopForErrorDeadlock(t *testing.T) { c := abcicli.NewSocketClient(":80", false).(errorStopper) err := errors.New("foo-tendermint") // See Issue https://github.com/tendermint/abci/issues/114 doneChan := make(chan bool) go func() { defer close(doneChan) c.StopForError(err) c.StopForError(err) }() select { case <-doneChan: case <-time.After(time.Second * 4): t.Fatalf("Test took too long, potential deadlock still exists") } } func TestProperSyncCalls(t *testing.T) { app := slowApp{} s, c := setupClientServer(t, app) t.Cleanup(func() { if err := s.Stop(); err != nil { t.Error(err) } }) t.Cleanup(func() { if err := c.Stop(); err != nil { t.Error(err) } }) resp := make(chan error, 1) go func() { // This is BeginBlockSync unrolled.... reqres := c.BeginBlockAsync(types.RequestBeginBlock{}) err := c.FlushSync() require.NoError(t, err) res := reqres.Response.GetBeginBlock() require.NotNil(t, res) resp <- c.Error() }() select { case <-time.After(time.Second): require.Fail(t, "No response arrived") case err, ok := <-resp: require.True(t, ok, "Must not close channel") assert.NoError(t, err, "This should return success") } } func TestHangingSyncCalls(t *testing.T) { app := slowApp{} s, c := setupClientServer(t, app) t.Cleanup(func() { if err := s.Stop(); err != nil { t.Log(err) } }) t.Cleanup(func() { if err := c.Stop(); err != nil { t.Log(err) } }) resp := make(chan error, 1) go func() { // Start BeginBlock and flush it reqres := c.BeginBlockAsync(types.RequestBeginBlock{}) flush := c.FlushAsync() // wait 20 ms for all events to travel socket, but // no response yet from server time.Sleep(20 * time.Millisecond) // kill the server, so the connections break err := s.Stop() require.NoError(t, err) // wait for the response from BeginBlock reqres.Wait() flush.Wait() resp <- c.Error() }() select { case <-time.After(time.Second): require.Fail(t, "No response arrived") case err, ok := <-resp: require.True(t, ok, "Must not close channel") assert.Error(t, err, "We should get EOF error") } } func setupClientServer(t *testing.T, app types.Application) ( service.Service, abcicli.Client) { // some port between 20k and 30k port := 20000 + tmrand.Int32()%10000 addr := fmt.Sprintf("localhost:%d", port) s, err := server.NewServer(addr, "socket", app) require.NoError(t, err) err = s.Start() require.NoError(t, err) c := abcicli.NewSocketClient(addr, true) err = c.Start() require.NoError(t, err) return s, c } type slowApp struct { types.BaseApplication } func (slowApp) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock { time.Sleep(200 * time.Millisecond) return types.ResponseBeginBlock{} }