From 91b8af4bb75a4ea860d0fbbf145f8e7f155fb242 Mon Sep 17 00:00:00 2001 From: roos Date: Thu, 16 May 2019 14:13:59 +0200 Subject: [PATCH] BS: Add fast recovery unit test The added tests check that the fast recovery mechanism works as expected. fixes #2685 --- .../internal/beaconing/originator_test.go | 65 +++++++++++++++- .../internal/beaconing/propagator_test.go | 78 ++++++++++++++++++- .../internal/beaconing/registrar_test.go | 3 + 3 files changed, 144 insertions(+), 2 deletions(-) diff --git a/go/beacon_srv/internal/beaconing/originator_test.go b/go/beacon_srv/internal/beaconing/originator_test.go index 3a97dfb614..11c1db5002 100644 --- a/go/beacon_srv/internal/beaconing/originator_test.go +++ b/go/beacon_srv/internal/beaconing/originator_test.go @@ -16,9 +16,11 @@ package beaconing import ( "context" + "errors" "fmt" "sync" "testing" + "time" "github.com/golang/mock/gomock" . "github.com/smartystreets/goconvey/convey" @@ -47,13 +49,13 @@ func TestOriginatorRun(t *testing.T) { topoProvider := xtest.TopoProviderFromFile(t, topoCore) mac, err := scrypto.InitMac(make(common.RawBytes, 16)) xtest.FailOnErr(t, err) - intfs := ifstate.NewInterfaces(topoProvider.Get().IFInfoMap, ifstate.Config{}) pub, priv, err := scrypto.GenKeyPair(scrypto.Ed25519) xtest.FailOnErr(t, err) signer := testSigner(t, priv, topoProvider.Get().ISD_AS) Convey("Run originates ifid packets on all active core and child interfaces", t, func() { mctrl := gomock.NewController(t) defer mctrl.Finish() + intfs := ifstate.NewInterfaces(topoProvider.Get().IFInfoMap, ifstate.Config{}) conn := mock_snet.NewMockPacketConn(mctrl) o, err := OriginatorConf{ Config: ExtenderConf{ @@ -62,6 +64,7 @@ func TestOriginatorRun(t *testing.T) { Intfs: intfs, Mac: mac, }, + Period: time.Hour, BeaconSender: &onehop.BeaconSender{ Sender: onehop.Sender{ IA: xtest.MustParseIA("1-ff00:0:110"), @@ -99,6 +102,66 @@ func TestOriginatorRun(t *testing.T) { checkMsg(t, msg, pub, topoProvider.Get().IFInfoMap) }) } + // The second run should not cause any beacons to originate. + o.Run(nil) + }) + Convey("Fast recovery", t, func() { + mctrl := gomock.NewController(t) + defer mctrl.Finish() + intfs := ifstate.NewInterfaces(topoProvider.Get().IFInfoMap, ifstate.Config{}) + conn := mock_snet.NewMockPacketConn(mctrl) + o, err := OriginatorConf{ + Config: ExtenderConf{ + MTU: uint16(topoProvider.Get().MTU), + Signer: signer, + Intfs: intfs, + Mac: mac, + }, + Period: 2 * time.Second, + BeaconSender: &onehop.BeaconSender{ + Sender: onehop.Sender{ + IA: xtest.MustParseIA("1-ff00:0:110"), + Conn: conn, + Addr: &addr.AppAddr{ + L3: addr.HostFromIPStr("127.0.0.1"), + L4: addr.NewL4UDPInfo(4242), + }, + MAC: mac, + }, + }, + }.New() + xtest.FailOnErr(t, err) + // Activate interfaces + intfs.Get(42).Activate(84) + intfs.Get(1129).Activate(82) + + failedMtx := sync.Mutex{} + var failed bool + // 1. Initial run where one beacon fails to send. -> 2 calls + // 2. Second run where the beacon is delivered. -> 1 call + // 3. Run where no beacon is sent. -> no call + // 4. Run where beacons are sent on all interfaces. -> 2 calls + conn.EXPECT().WriteTo(gomock.Any(), gomock.Any()).Times(5).DoAndReturn( + func(ipkt, iov interface{}) error { + failedMtx.Lock() + defer failedMtx.Unlock() + if !failed { + failed = true + return errors.New("fail") + } + return nil + }, + ) + // Initial run. Two writes expected, one write will fail. + o.Run(nil) + time.Sleep(1 * time.Second) + // Second run. One write expected. + o.Run(nil) + // Third run. No write expected + o.Run(nil) + time.Sleep(1 * time.Second) + // Fourth run. Since period has passed, two writes are expected. + o.Run(nil) }) } diff --git a/go/beacon_srv/internal/beaconing/propagator_test.go b/go/beacon_srv/internal/beaconing/propagator_test.go index 8c1a7a6bb4..7cc02a7215 100644 --- a/go/beacon_srv/internal/beaconing/propagator_test.go +++ b/go/beacon_srv/internal/beaconing/propagator_test.go @@ -15,9 +15,11 @@ package beaconing import ( + "errors" "fmt" "sync" "testing" + "time" "github.com/golang/mock/gomock" . "github.com/smartystreets/goconvey/convey" @@ -149,6 +151,7 @@ func TestPropagatorRun(t *testing.T) { Intfs: ifstate.NewInterfaces(topoProvider.Get().IFInfoMap, ifstate.Config{}), MTU: uint16(topoProvider.Get().MTU), }, + Period: time.Hour, BeaconProvider: provider, Core: test.core, BeaconSender: &onehop.BeaconSender{ @@ -172,7 +175,7 @@ func TestPropagatorRun(t *testing.T) { cfg.Config.Intfs.Get(ifid).Activate(remote) } g := graph.NewDefaultGraph(mctrl) - provider.EXPECT().BeaconsToPropagate(gomock.Any()).MaxTimes(1).DoAndReturn( + provider.EXPECT().BeaconsToPropagate(gomock.Any()).MaxTimes(2).DoAndReturn( func(_ interface{}) (<-chan beacon.BeaconOrErr, error) { res := make(chan beacon.BeaconOrErr, len(beacons[test.core])) for _, desc := range beacons[test.core] { @@ -201,6 +204,79 @@ func TestPropagatorRun(t *testing.T) { checkMsg(t, msg, pub, topoProvider.Get().IFInfoMap) }) } + // Check that no beacons are sent, since the period has not passed yet. + p.Run(nil) }) } + Convey("Fast recovery", t, func() { + mctrl := gomock.NewController(t) + defer mctrl.Finish() + topoProvider := xtest.TopoProviderFromFile(t, topoCore) + provider := mock_beaconing.NewMockBeaconProvider(mctrl) + conn := mock_snet.NewMockPacketConn(mctrl) + cfg := PropagatorConf{ + Config: ExtenderConf{ + Signer: testSigner(t, priv, topoProvider.Get().ISD_AS), + Mac: macProp, + Intfs: ifstate.NewInterfaces(topoProvider.Get().IFInfoMap, ifstate.Config{}), + MTU: uint16(topoProvider.Get().MTU), + }, + Period: 2 * time.Second, + BeaconProvider: provider, + Core: true, + BeaconSender: &onehop.BeaconSender{ + Sender: onehop.Sender{ + IA: topoProvider.Get().ISD_AS, + Conn: conn, + Addr: &addr.AppAddr{ + L3: addr.HostFromIPStr("127.0.0.1"), + L4: addr.NewL4UDPInfo(4242), + }, + MAC: macSender, + }, + }, + } + p, err := cfg.New() + SoMsg("err", err, ShouldBeNil) + for ifid, remote := range allIntfs[true] { + cfg.Config.Intfs.Get(ifid).Activate(remote) + } + g := graph.NewDefaultGraph(mctrl) + // We call run 4 times in this test + provider.EXPECT().BeaconsToPropagate(gomock.Any()).Times(4).DoAndReturn( + func(_ interface{}) (<-chan beacon.BeaconOrErr, error) { + res := make(chan beacon.BeaconOrErr, 1) + res <- testBeaconOrErr(g, beacons[true][0]) + close(res) + return res, nil + }, + ) + failedMtx := sync.Mutex{} + var failed bool + // 1. Initial run where one beacon fails to send. -> 2 calls + // 2. Second run where the beacon is delivered. -> 1 call + // 3. Run where no beacon is sent. -> no call + // 4. Run where beacons are sent on all interfaces. -> 2 calls + conn.EXPECT().WriteTo(gomock.Any(), gomock.Any()).Times(5).DoAndReturn( + func(ipkt, iov interface{}) error { + failedMtx.Lock() + defer failedMtx.Unlock() + if !failed { + failed = true + return errors.New("fail") + } + return nil + }, + ) + // Initial run. Two writes expected, one write will fail. + p.Run(nil) + time.Sleep(1 * time.Second) + // Second run. One write expected. + p.Run(nil) + // Third run. No write expected + p.Run(nil) + time.Sleep(1 * time.Second) + // Fourth run. Since period has passed, two writes are expected. + p.Run(nil) + }) } diff --git a/go/beacon_srv/internal/beaconing/registrar_test.go b/go/beacon_srv/internal/beaconing/registrar_test.go index 164e4ea11e..318968b3e2 100644 --- a/go/beacon_srv/internal/beaconing/registrar_test.go +++ b/go/beacon_srv/internal/beaconing/registrar_test.go @@ -102,6 +102,7 @@ func TestRegistrarRun(t *testing.T) { Intfs: ifstate.NewInterfaces(topoProvider.Get().IFInfoMap, ifstate.Config{}), MTU: uint16(topoProvider.Get().MTU), }, + Period: time.Hour, Msgr: msgr, SegProvider: segProvider, TopoProvider: topoProvider, @@ -175,6 +176,8 @@ func TestRegistrarRun(t *testing.T) { SoMsg("Next", s.Addr.NextHop, ShouldResemble, a.PublicOverlay(a.Overlay)) }) } + // The second run should not do anything, since the period has not passed. + r.Run(context.Background()) }) } Convey("Run drains the channel", t, func() {