Skip to content

Commit

Permalink
BS: Add fast recovery unit test
Browse files Browse the repository at this point in the history
The added tests check that the fast recovery mechanism works as
expected.

fixes #2685
  • Loading branch information
oncilla committed May 16, 2019
1 parent f937af6 commit 91b8af4
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 2 deletions.
65 changes: 64 additions & 1 deletion go/beacon_srv/internal/beaconing/originator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ package beaconing

import (
"context"
"errors"
"fmt"
"sync"
"testing"
"time"

"github.com/golang/mock/gomock"
. "github.com/smartystreets/goconvey/convey"
Expand Down Expand Up @@ -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{
Expand All @@ -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"),
Expand Down Expand Up @@ -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)
})
}

Expand Down
78 changes: 77 additions & 1 deletion go/beacon_srv/internal/beaconing/propagator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
package beaconing

import (
"errors"
"fmt"
"sync"
"testing"
"time"

"github.com/golang/mock/gomock"
. "github.com/smartystreets/goconvey/convey"
Expand Down Expand Up @@ -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{
Expand All @@ -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] {
Expand Down Expand Up @@ -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)
})
}
3 changes: 3 additions & 0 deletions go/beacon_srv/internal/beaconing/registrar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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() {
Expand Down

0 comments on commit 91b8af4

Please sign in to comment.