From 2912defb978b6c6eedf24a3934ef2767e4f18650 Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Thu, 27 Nov 2014 23:14:41 +0000 Subject: [PATCH] Add tests for new discovery --- internal/discover/client_test.go | 227 +++++++++++++++++++++++++++++ internal/discover/discover_test.go | 134 ++++++++++++++++- 2 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 internal/discover/client_test.go diff --git a/internal/discover/client_test.go b/internal/discover/client_test.go new file mode 100644 index 00000000..f57085c3 --- /dev/null +++ b/internal/discover/client_test.go @@ -0,0 +1,227 @@ +// Copyright (C) 2014 The Syncthing Authors. +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see . + +package discover + +import ( + "fmt" + "net" + "sync" + "time" + + "testing" + + "github.com/syncthing/syncthing/internal/protocol" +) + +var device protocol.DeviceID + +func init() { + device, _ = protocol.DeviceIDFromString("P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2") +} + +func TestUDP4Success(t *testing.T) { + conn, err := net.ListenUDP("udp4", nil) + if err != nil { + t.Fatal(err) + } + + port := conn.LocalAddr().(*net.UDPAddr).Port + + address := fmt.Sprintf("udp4://127.0.0.1:%d", port) + pkt := &Announce{ + Magic: AnnouncementMagic, + This: Device{ + device[:], + []Address{{ + IP: net.IPv4(123, 123, 123, 123), + Port: 1234, + }}, + }, + } + + client, err := New(address, pkt) + if err != nil { + t.Fatal(err) + } + + udpclient := client.(*UDPClient) + if udpclient.errorRetryInterval != DefaultErrorRetryInternval { + t.Fatal("Incorrect retry interval") + } + + if udpclient.listenAddress.IP != nil || udpclient.listenAddress.Port != 0 { + t.Fatal("Wrong listen IP or port", udpclient.listenAddress) + } + + if client.Address() != address { + t.Fatal("Incorrect address") + } + + buf := make([]byte, 2048) + + // First announcement + conn.SetDeadline(time.Now().Add(time.Millisecond * 100)) + _, err = conn.Read(buf) + if err != nil { + t.Fatal(err) + } + + // Announcement verification + conn.SetDeadline(time.Now().Add(time.Millisecond * 1100)) + _, addr, err := conn.ReadFromUDP(buf) + if err != nil { + t.Fatal(err) + } + + // Reply to it. + _, err = conn.WriteToUDP(pkt.MustMarshalXDR(), addr) + if err != nil { + t.Fatal(err) + } + + // We should get nothing else + conn.SetDeadline(time.Now().Add(time.Millisecond * 100)) + _, err = conn.Read(buf) + if err == nil { + t.Fatal("Expected error") + } + + // Status should be ok + if !client.StatusOK() { + t.Fatal("Wrong status") + } + + // Do a lookup in a separate routine + addrs := []string{} + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + addrs = client.Lookup(device) + wg.Done() + }() + + // Receive the lookup and reply + conn.SetDeadline(time.Now().Add(time.Millisecond * 100)) + _, addr, err = conn.ReadFromUDP(buf) + if err != nil { + t.Fatal(err) + } + + conn.WriteToUDP(pkt.MustMarshalXDR(), addr) + + // Wait for the lookup to arrive, verify that the number of answers is correct + wg.Wait() + + if len(addrs) != 1 || addrs[0] != "123.123.123.123:1234" { + t.Fatal("Wrong number of answers") + } + + client.Stop() +} + +func TestUDP4Failure(t *testing.T) { + conn, err := net.ListenUDP("udp4", nil) + if err != nil { + t.Fatal(err) + } + + port := conn.LocalAddr().(*net.UDPAddr).Port + + address := fmt.Sprintf("udp4://127.0.0.1:%d/?listenaddress=127.0.0.1&retry=5", port) + + pkt := &Announce{ + Magic: AnnouncementMagic, + This: Device{ + device[:], + []Address{{ + IP: net.IPv4(123, 123, 123, 123), + Port: 1234, + }}, + }, + } + + client, err := New(address, pkt) + if err != nil { + t.Fatal(err) + } + + udpclient := client.(*UDPClient) + if udpclient.errorRetryInterval != time.Second*5 { + t.Fatal("Incorrect retry interval") + } + + if !udpclient.listenAddress.IP.Equal(net.IPv4(127, 0, 0, 1)) || udpclient.listenAddress.Port != 0 { + t.Fatal("Wrong listen IP or port", udpclient.listenAddress) + } + + if client.Address() != address { + t.Fatal("Incorrect address") + } + + buf := make([]byte, 2048) + + // First announcement + conn.SetDeadline(time.Now().Add(time.Millisecond * 100)) + _, err = conn.Read(buf) + if err != nil { + t.Fatal(err) + } + + // Announcement verification + conn.SetDeadline(time.Now().Add(time.Millisecond * 1100)) + _, _, err = conn.ReadFromUDP(buf) + if err != nil { + t.Fatal(err) + } + + // Don't reply + // We should get nothing else + conn.SetDeadline(time.Now().Add(time.Millisecond * 100)) + _, err = conn.Read(buf) + if err == nil { + t.Fatal("Expected error") + } + + // Status should be failure + if client.StatusOK() { + t.Fatal("Wrong status") + } + + // Do a lookup in a separate routine + addrs := []string{} + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + addrs = client.Lookup(device) + wg.Done() + }() + + // Receive the lookup and don't reply + conn.SetDeadline(time.Now().Add(time.Millisecond * 100)) + _, _, err = conn.ReadFromUDP(buf) + if err != nil { + t.Fatal(err) + } + + // Wait for the lookup to timeout, verify that the number of answers is none + wg.Wait() + + if len(addrs) != 0 { + t.Fatal("Wrong number of answers") + } + + client.Stop() +} diff --git a/internal/discover/discover_test.go b/internal/discover/discover_test.go index f2b69f58..fce201fb 100644 --- a/internal/discover/discover_test.go +++ b/internal/discover/discover_test.go @@ -13,6 +13,136 @@ // You should have received a copy of the GNU General Public License along // with this program. If not, see . -package discover_test +package discover -// Empty test file to generate 0% coverage rather than no coverage +import ( + "net/url" + "time" + + "testing" + + "github.com/syncthing/syncthing/internal/protocol" +) + +type DummyClient struct { + url *url.URL + lookups []protocol.DeviceID + lookupRet []string + stops int + statusRet bool + statusChecks int +} + +func (c *DummyClient) Lookup(device protocol.DeviceID) []string { + c.lookups = append(c.lookups, device) + return c.lookupRet +} + +func (c *DummyClient) StatusOK() bool { + c.statusChecks++ + return c.statusRet +} + +func (c *DummyClient) Stop() { + c.stops++ +} + +func (c *DummyClient) Address() string { + return c.url.String() +} + +func TestGlobalDiscovery(t *testing.T) { + c1 := &DummyClient{ + statusRet: false, + lookupRet: []string{"test.com:1234"}, + } + + c2 := &DummyClient{ + statusRet: true, + lookupRet: []string{}, + } + + c3 := &DummyClient{ + statusRet: true, + lookupRet: []string{"best.com:2345"}, + } + + clients := []*DummyClient{c1, c2} + + Register("test1", func(uri *url.URL, pkt *Announce) (Client, error) { + c := clients[0] + clients = clients[1:] + c.url = uri + return c, nil + }) + + Register("test2", func(uri *url.URL, pkt *Announce) (Client, error) { + c3.url = uri + return c3, nil + }) + + d := NewDiscoverer(device, []string{}) + d.localBcastStart = time.Time{} + servers := []string{ + "test1://123.123.123.123:1234", + "test1://23.23.23.23:234", + "test2://234.234.234.234.2345", + } + d.StartGlobal(servers, 1234) + + if len(d.clients) != 3 { + t.Fatal("Wrong number of clients") + } + + status := d.ExtAnnounceOK() + + for _, c := range []*DummyClient{c1, c2, c3} { + if status[c.url.String()] != c.statusRet || c.statusChecks != 1 { + t.Fatal("Wrong status") + } + } + + addrs := d.Lookup(device) + if len(addrs) != 2 { + t.Fatal("Wrong numer of addresses", addrs) + } + + for _, addr := range []string{"test.com:1234", "best.com:2345"} { + found := false + for _, laddr := range addrs { + if laddr == addr { + found = true + break + } + } + if !found { + t.Fatal("Couldn't find", addr) + } + } + + for _, c := range []*DummyClient{c1, c2, c3} { + if len(c.lookups) != 1 || c.lookups[0] != device { + t.Fatal("Wrong lookups") + } + } + + addrs = d.Lookup(device) + if len(addrs) != 2 { + t.Fatal("Wrong numer of addresses", addrs) + } + + // Answer should be cached, so number of lookups should have not incresed + for _, c := range []*DummyClient{c1, c2, c3} { + if len(c.lookups) != 1 || c.lookups[0] != device { + t.Fatal("Wrong lookups") + } + } + + d.StopGlobal() + + for _, c := range []*DummyClient{c1, c2, c3} { + if c.stops != 1 { + t.Fatal("Wrong number of stops") + } + } +}