From d96fb27ab8adef666c5b0a1d2ce94e5f9bc58922 Mon Sep 17 00:00:00 2001 From: Aleksander Mistewicz Date: Wed, 25 Sep 2019 15:58:19 +0200 Subject: [PATCH 1/6] Add simple connection test --- src/yggdrasil/core_test.go | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/yggdrasil/core_test.go diff --git a/src/yggdrasil/core_test.go b/src/yggdrasil/core_test.go new file mode 100644 index 0000000..4b283d4 --- /dev/null +++ b/src/yggdrasil/core_test.go @@ -0,0 +1,47 @@ +package yggdrasil + +import ( + "os" + "testing" + + "github.com/gologme/log" + + "github.com/yggdrasil-network/yggdrasil-go/src/config" +) + +// GenerateConfig is modification +func GenerateConfig() *config.NodeConfig { + cfg := config.GenerateConfig() + cfg.AdminListen = "none" + cfg.Listen = []string{"tcp://127.0.0.1:0"} + cfg.IfName = "none" + + return cfg +} + +func GetLoggerWithPrefix(prefix string) *log.Logger { + l := log.New(os.Stderr, prefix, log.Flags()) + l.EnableLevel("info") + l.EnableLevel("warn") + l.EnableLevel("error") + return l +} + +func TestCore_Start(t *testing.T) { + nodeA := Core{} + _, err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ")) + if err != nil { + t.Fatal(err) + } + + nodeB := Core{} + _, err = nodeB.Start(GenerateConfig(), GetLoggerWithPrefix("B: ")) + if err != nil { + t.Fatal(err) + } + + err = nodeB.AddPeer("tcp://"+nodeA.link.tcp.getAddr().String(), "") + if err != nil { + t.Fatal(err) + } +} From fffbbbcbd32923dc1219af4f7dfaeb664ca223de Mon Sep 17 00:00:00 2001 From: Aleksander Mistewicz Date: Wed, 25 Sep 2019 20:07:36 +0200 Subject: [PATCH 2/6] Pass message between nodes --- src/yggdrasil/core_test.go | 85 +++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/src/yggdrasil/core_test.go b/src/yggdrasil/core_test.go index 4b283d4..5bca94f 100644 --- a/src/yggdrasil/core_test.go +++ b/src/yggdrasil/core_test.go @@ -1,8 +1,11 @@ package yggdrasil import ( + "bytes" + "math/rand" "os" "testing" + "time" "github.com/gologme/log" @@ -27,7 +30,7 @@ func GetLoggerWithPrefix(prefix string) *log.Logger { return l } -func TestCore_Start(t *testing.T) { +func CreateAndConnectTwo(t *testing.T) (*Core, *Core) { nodeA := Core{} _, err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ")) if err != nil { @@ -44,4 +47,84 @@ func TestCore_Start(t *testing.T) { if err != nil { t.Fatal(err) } + + if l := len(nodeA.GetPeers()); l != 1 { + t.Fatal("unexpected number of peers", l) + } + if l := len(nodeB.GetPeers()); l != 1 { + t.Fatal("unexpected number of peers", l) + } + + return &nodeA, &nodeB +} + +func TestCore_Start_Connect(t *testing.T) { + CreateAndConnectTwo(t) +} + +func TestCore_Start_Transfer(t *testing.T) { + nodeA, nodeB := CreateAndConnectTwo(t) + + // Listen + listener, err := nodeA.ConnListen() + if err != nil { + t.Fatal(err) + } + defer listener.Close() + + done := make(chan struct{}) + go func() { + conn, err := listener.Accept() + if err != nil { + t.Error(err) + return + } + defer conn.Close() + buf := make([]byte, 64) + n, err := conn.Read(buf) + if err != nil { + t.Error(err) + return + } + if n != 64 { + t.Error("missing data") + return + } + _, err = conn.Write(buf) + if err != nil { + t.Error(err) + } + done <- struct{}{} + }() + + time.Sleep(3 * time.Second) // FIXME + // Dial + dialer, err := nodeB.ConnDialer() + if err != nil { + t.Fatal(err) + } + t.Log(nodeA.GetSwitchPeers()) + t.Log(nodeB.GetSwitchPeers()) + t.Log(nodeA.GetSessions()) + t.Log(nodeB.GetSessions()) + conn, err := dialer.Dial("nodeid", nodeA.NodeID().String()) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + msg := make([]byte, 64) + rand.Read(msg) + conn.Write(msg) + if err != nil { + t.Fatal(err) + } + buf := make([]byte, 64) + _, err = conn.Read(buf) + if err != nil { + t.Fatal(err) + } + if bytes.Compare(msg, buf) != 0 { + t.Fatal("expected echo") + } + <-done } From 8677a042cf7d079a41c25f2d85e610c3b76f14cc Mon Sep 17 00:00:00 2001 From: Aleksander Mistewicz Date: Sat, 28 Sep 2019 14:10:17 +0200 Subject: [PATCH 3/6] Wait for nodes to negotiate --- src/yggdrasil/core_test.go | 44 +++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/yggdrasil/core_test.go b/src/yggdrasil/core_test.go index 5bca94f..0c70aaf 100644 --- a/src/yggdrasil/core_test.go +++ b/src/yggdrasil/core_test.go @@ -58,35 +58,45 @@ func CreateAndConnectTwo(t *testing.T) (*Core, *Core) { return &nodeA, &nodeB } +// WaitConnected blocks until either nodes negotiated DHT or 5 seconds passed. +func WaitConnected(nodeA, nodeB *Core) bool { + // It may take up to 3 seconds, but let's wait 5. + for i := 0; i < 50; i++ { + time.Sleep(100 * time.Millisecond) + if len(nodeA.GetSwitchPeers()) > 0 && len(nodeB.GetSwitchPeers()) > 0 { + return true + } + } + return false +} + func TestCore_Start_Connect(t *testing.T) { CreateAndConnectTwo(t) } -func TestCore_Start_Transfer(t *testing.T) { - nodeA, nodeB := CreateAndConnectTwo(t) - +func CreateEchoListener(t *testing.T, nodeA *Core, bufLen int) chan struct{} { // Listen listener, err := nodeA.ConnListen() if err != nil { t.Fatal(err) } - defer listener.Close() done := make(chan struct{}) go func() { + defer listener.Close() conn, err := listener.Accept() if err != nil { t.Error(err) return } defer conn.Close() - buf := make([]byte, 64) + buf := make([]byte, bufLen) n, err := conn.Read(buf) if err != nil { t.Error(err) return } - if n != 64 { + if n != bufLen { t.Error("missing data") return } @@ -97,28 +107,36 @@ func TestCore_Start_Transfer(t *testing.T) { done <- struct{}{} }() - time.Sleep(3 * time.Second) // FIXME + return done +} + +func TestCore_Start_Transfer(t *testing.T) { + nodeA, nodeB := CreateAndConnectTwo(t) + + msgLen := 1500 + done := CreateEchoListener(t, nodeA, msgLen) + + if !WaitConnected(nodeA, nodeB) { + t.Fatal("nodes did not connect") + } + // Dial dialer, err := nodeB.ConnDialer() if err != nil { t.Fatal(err) } - t.Log(nodeA.GetSwitchPeers()) - t.Log(nodeB.GetSwitchPeers()) - t.Log(nodeA.GetSessions()) - t.Log(nodeB.GetSessions()) conn, err := dialer.Dial("nodeid", nodeA.NodeID().String()) if err != nil { t.Fatal(err) } defer conn.Close() - msg := make([]byte, 64) + msg := make([]byte, msgLen) rand.Read(msg) conn.Write(msg) if err != nil { t.Fatal(err) } - buf := make([]byte, 64) + buf := make([]byte, msgLen) _, err = conn.Read(buf) if err != nil { t.Fatal(err) From 21b236771b31499590ed704b93902e87b88b33a0 Mon Sep 17 00:00:00 2001 From: Aleksander Mistewicz Date: Sat, 28 Sep 2019 14:20:57 +0200 Subject: [PATCH 4/6] Add a simple transfer benchmark --- src/yggdrasil/core_test.go | 73 ++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/src/yggdrasil/core_test.go b/src/yggdrasil/core_test.go index 0c70aaf..aaf600a 100644 --- a/src/yggdrasil/core_test.go +++ b/src/yggdrasil/core_test.go @@ -30,7 +30,7 @@ func GetLoggerWithPrefix(prefix string) *log.Logger { return l } -func CreateAndConnectTwo(t *testing.T) (*Core, *Core) { +func CreateAndConnectTwo(t testing.TB) (*Core, *Core) { nodeA := Core{} _, err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ")) if err != nil { @@ -74,7 +74,7 @@ func TestCore_Start_Connect(t *testing.T) { CreateAndConnectTwo(t) } -func CreateEchoListener(t *testing.T, nodeA *Core, bufLen int) chan struct{} { +func CreateEchoListener(t testing.TB, nodeA *Core, bufLen int, repeats int) chan struct{} { // Listen listener, err := nodeA.ConnListen() if err != nil { @@ -91,18 +91,21 @@ func CreateEchoListener(t *testing.T, nodeA *Core, bufLen int) chan struct{} { } defer conn.Close() buf := make([]byte, bufLen) - n, err := conn.Read(buf) - if err != nil { - t.Error(err) - return - } - if n != bufLen { - t.Error("missing data") - return - } - _, err = conn.Write(buf) - if err != nil { - t.Error(err) + + for i := 0; i < repeats; i++ { + n, err := conn.Read(buf) + if err != nil { + t.Error(err) + return + } + if n != bufLen { + t.Error("missing data") + return + } + _, err = conn.Write(buf) + if err != nil { + t.Error(err) + } } done <- struct{}{} }() @@ -114,7 +117,7 @@ func TestCore_Start_Transfer(t *testing.T) { nodeA, nodeB := CreateAndConnectTwo(t) msgLen := 1500 - done := CreateEchoListener(t, nodeA, msgLen) + done := CreateEchoListener(t, nodeA, msgLen, 1) if !WaitConnected(nodeA, nodeB) { t.Fatal("nodes did not connect") @@ -146,3 +149,43 @@ func TestCore_Start_Transfer(t *testing.T) { } <-done } + +func BenchmarkCore_Start_Transfer(b *testing.B) { + nodeA, nodeB := CreateAndConnectTwo(b) + + msgLen := 1500 // typical MTU + done := CreateEchoListener(b, nodeA, msgLen, b.N) + + if !WaitConnected(nodeA, nodeB) { + b.Fatal("nodes did not connect") + } + + // Dial + dialer, err := nodeB.ConnDialer() + if err != nil { + b.Fatal(err) + } + conn, err := dialer.Dial("nodeid", nodeA.NodeID().String()) + if err != nil { + b.Fatal(err) + } + defer conn.Close() + msg := make([]byte, msgLen) + rand.Read(msg) + buf := make([]byte, msgLen) + + b.SetBytes(int64(b.N * msgLen)) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + conn.Write(msg) + if err != nil { + b.Fatal(err) + } + _, err = conn.Read(buf) + if err != nil { + b.Fatal(err) + } + } + <-done +} From 805376609209d543f92b2bf165f5f668f230c16e Mon Sep 17 00:00:00 2001 From: Aleksander Mistewicz Date: Sat, 28 Sep 2019 14:24:54 +0200 Subject: [PATCH 5/6] Add verbosity setting --- src/yggdrasil/core_test.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/yggdrasil/core_test.go b/src/yggdrasil/core_test.go index aaf600a..2823495 100644 --- a/src/yggdrasil/core_test.go +++ b/src/yggdrasil/core_test.go @@ -22,23 +22,26 @@ func GenerateConfig() *config.NodeConfig { return cfg } -func GetLoggerWithPrefix(prefix string) *log.Logger { +func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger { l := log.New(os.Stderr, prefix, log.Flags()) + if !verbose { + return l + } l.EnableLevel("info") l.EnableLevel("warn") l.EnableLevel("error") return l } -func CreateAndConnectTwo(t testing.TB) (*Core, *Core) { +func CreateAndConnectTwo(t testing.TB, verbose bool) (*Core, *Core) { nodeA := Core{} - _, err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ")) + _, err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ", verbose)) if err != nil { t.Fatal(err) } nodeB := Core{} - _, err = nodeB.Start(GenerateConfig(), GetLoggerWithPrefix("B: ")) + _, err = nodeB.Start(GenerateConfig(), GetLoggerWithPrefix("B: ", verbose)) if err != nil { t.Fatal(err) } @@ -71,7 +74,7 @@ func WaitConnected(nodeA, nodeB *Core) bool { } func TestCore_Start_Connect(t *testing.T) { - CreateAndConnectTwo(t) + CreateAndConnectTwo(t, true) } func CreateEchoListener(t testing.TB, nodeA *Core, bufLen int, repeats int) chan struct{} { @@ -114,7 +117,7 @@ func CreateEchoListener(t testing.TB, nodeA *Core, bufLen int, repeats int) chan } func TestCore_Start_Transfer(t *testing.T) { - nodeA, nodeB := CreateAndConnectTwo(t) + nodeA, nodeB := CreateAndConnectTwo(t, true) msgLen := 1500 done := CreateEchoListener(t, nodeA, msgLen, 1) @@ -151,7 +154,7 @@ func TestCore_Start_Transfer(t *testing.T) { } func BenchmarkCore_Start_Transfer(b *testing.B) { - nodeA, nodeB := CreateAndConnectTwo(b) + nodeA, nodeB := CreateAndConnectTwo(b, false) msgLen := 1500 // typical MTU done := CreateEchoListener(b, nodeA, msgLen, b.N) From 783959208cf879a1ab0f5cf8c3566aeecc911954 Mon Sep 17 00:00:00 2001 From: Aleksander Mistewicz Date: Sat, 28 Sep 2019 14:41:53 +0200 Subject: [PATCH 6/6] Add more comments to explain helper functions --- src/yggdrasil/core_test.go | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/yggdrasil/core_test.go b/src/yggdrasil/core_test.go index 2823495..364ed0b 100644 --- a/src/yggdrasil/core_test.go +++ b/src/yggdrasil/core_test.go @@ -12,7 +12,7 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/config" ) -// GenerateConfig is modification +// GenerateConfig produces default configuration with suitable modifications for tests. func GenerateConfig() *config.NodeConfig { cfg := config.GenerateConfig() cfg.AdminListen = "none" @@ -22,6 +22,8 @@ func GenerateConfig() *config.NodeConfig { return cfg } +// GetLoggerWithPrefix creates a new logger instance wih prefix. +// If verbose is set to true, three log levels are enabled: "info", "warn", "error". func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger { l := log.New(os.Stderr, prefix, log.Flags()) if !verbose { @@ -33,14 +35,16 @@ func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger { return l } -func CreateAndConnectTwo(t testing.TB, verbose bool) (*Core, *Core) { - nodeA := Core{} +// CreateAndConnectTwo creates two nodes. nodeB connects to nodeA. +// Verbosity flag is passed to logger. +func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) { + nodeA = new(Core) _, err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ", verbose)) if err != nil { t.Fatal(err) } - nodeB := Core{} + nodeB = new(Core) _, err = nodeB.Start(GenerateConfig(), GetLoggerWithPrefix("B: ", verbose)) if err != nil { t.Fatal(err) @@ -58,7 +62,7 @@ func CreateAndConnectTwo(t testing.TB, verbose bool) (*Core, *Core) { t.Fatal("unexpected number of peers", l) } - return &nodeA, &nodeB + return nodeA, nodeB } // WaitConnected blocks until either nodes negotiated DHT or 5 seconds passed. @@ -73,17 +77,16 @@ func WaitConnected(nodeA, nodeB *Core) bool { return false } -func TestCore_Start_Connect(t *testing.T) { - CreateAndConnectTwo(t, true) -} - +// CreateEchoListener creates a routine listening on nodeA. It expects repeats messages of length bufLen. +// It returns a channel used to synchronize the routine with caller. func CreateEchoListener(t testing.TB, nodeA *Core, bufLen int, repeats int) chan struct{} { - // Listen + // Listen. Doing it here guarantees that there will be something to try to connect when it returns. listener, err := nodeA.ConnListen() if err != nil { t.Fatal(err) } + // Start routine done := make(chan struct{}) go func() { defer listener.Close() @@ -116,6 +119,12 @@ func CreateEchoListener(t testing.TB, nodeA *Core, bufLen int, repeats int) chan return done } +// TestCore_Start_Connect checks if two nodes can connect together. +func TestCore_Start_Connect(t *testing.T) { + CreateAndConnectTwo(t, true) +} + +// TestCore_Start_Transfer checks that messages can be passed between nodes (in both directions). func TestCore_Start_Transfer(t *testing.T) { nodeA, nodeB := CreateAndConnectTwo(t, true) @@ -153,6 +162,7 @@ func TestCore_Start_Transfer(t *testing.T) { <-done } +// BenchmarkCore_Start_Transfer estimates the possible transfer between nodes (in MB/s). func BenchmarkCore_Start_Transfer(b *testing.B) { nodeA, nodeB := CreateAndConnectTwo(b, false)