blob: 67f0be851ebb2bbb612590573da2a172546bb9f9 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package conn
import (
"bytes"
"fmt"
"testing"
"time"
"v.io/v23"
"v.io/v23/flow"
"v.io/x/ref/runtime/internal/flow/flowtest"
"v.io/x/ref/test/goroutines"
)
func TestLameDuck(t *testing.T) {
defer goroutines.NoLeaks(t, leakWaitTime)()
ctx, shutdown := v23.Init()
defer shutdown()
events := make(chan StatusUpdate, 2)
dflows, aflows := make(chan flow.Flow, 3), make(chan flow.Flow, 3)
dc, ac, _ := setupConnsWithEvents(t, ctx, ctx, dflows, aflows, events)
go func() {
for {
select {
case f := <-aflows:
if got, err := f.ReadMsg(); err != nil {
panic(fmt.Sprintf("got %v wanted nil", err))
} else if !bytes.Equal(got, []byte("hello")) {
panic(fmt.Sprintf("got %q, wanted 'hello'", string(got)))
}
case <-ac.Closed():
return
}
}
}()
// Dial a flow and write it (which causes it to open).
f1, err := dc.Dial(ctx, flowtest.AllowAllPeersAuthorizer{})
if err != nil {
t.Fatal(err)
}
if _, err := f1.WriteMsg([]byte("hello")); err != nil {
t.Fatal(err)
}
// Dial more flows, but don't write to them yet.
f2, err := dc.Dial(ctx, flowtest.AllowAllPeersAuthorizer{})
if err != nil {
t.Fatal(err)
}
f3, err := dc.Dial(ctx, flowtest.AllowAllPeersAuthorizer{})
if err != nil {
t.Fatal(err)
}
// Now put the accepted conn into lame duck mode.
ac.EnterLameDuck(ctx)
if e := <-events; e.Conn != dc || e.Status != (Status{false, false, true}) {
t.Errorf("Expected RemoteLameDuck on dialer, got %#v (a %p, d %p)", e, ac, dc)
}
// Now we shouldn't be able to dial from dc because it's in lame duck mode.
if _, err := dc.Dial(ctx, flowtest.AllowAllPeersAuthorizer{}); err == nil {
t.Fatalf("expected an error, got nil")
}
// I can't think of a non-flaky way to test for it, but it should
// be the case that we don't send the AckLameDuck message until
// we write to or close the other flows. This should catch it sometimes.
select {
case e := <-events:
t.Errorf("Didn't expect any additional events yet, got %#v", e)
case <-time.After(time.Millisecond * 100):
}
// Now write or close the other flows.
if _, err := f2.WriteMsg([]byte("hello")); err != nil {
t.Fatal(err)
}
f3.Close()
if e := <-events; e.Conn != ac || e.Status != (Status{false, true, false}) {
t.Errorf("Expected LocalLameDuck on acceptor, got %#v (a %p, d %p)", e, ac, dc)
}
// Now put the dialer side into lame duck.
dc.EnterLameDuck(ctx)
if e := <-events; e.Conn != ac || e.Status != (Status{false, true, true}) {
t.Errorf("Expected RemoteLameDuck on acceptor, got %#v (a %p, d %p)", e, ac, dc)
}
if e := <-events; e.Conn != dc || e.Status != (Status{false, true, true}) {
t.Errorf("Expected LocalLameDuck on dialer, got %#v (a %p, d %p)", e, ac, dc)
}
// Now close the accept side.
ac.Close(ctx, nil)
if e := <-events; e.Status != (Status{true, true, true}) {
t.Errorf("Expected Closed got %#v (a %p, d %p)", e, ac, dc)
}
if e := <-events; e.Status != (Status{true, true, true}) {
t.Errorf("Expected Closed got %#v (a %p, d %p)", e, ac, dc)
}
<-dc.Closed()
<-ac.Closed()
}