github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/renter/hostdb/scan_test.go (about)

     1  package hostdb
     2  
     3  import (
     4  	"net"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/NebulousLabs/Sia/crypto"
     9  	"github.com/NebulousLabs/Sia/encoding"
    10  	"github.com/NebulousLabs/Sia/modules"
    11  	"github.com/NebulousLabs/Sia/types"
    12  )
    13  
    14  // TestDecrementReliability tests the decrementReliability method.
    15  func TestDecrementReliability(t *testing.T) {
    16  	hdb := bareHostDB()
    17  
    18  	// Decrementing a non-existent host should be a no-op.
    19  	// NOTE: can't check any post-conditions here; only indication of correct
    20  	// behavior is that the test doesn't panic.
    21  	hdb.decrementReliability("foo", types.NewCurrency64(0))
    22  
    23  	// Add a host to allHosts and activeHosts. Decrementing it should remove it
    24  	// from activeHosts.
    25  	h := new(hostEntry)
    26  	h.NetAddress = "foo"
    27  	h.Reliability = types.NewCurrency64(1)
    28  	hdb.allHosts[h.NetAddress] = h
    29  	hdb.activeHosts[h.NetAddress] = &hostNode{hostEntry: h}
    30  	hdb.decrementReliability(h.NetAddress, types.NewCurrency64(0))
    31  	if len(hdb.ActiveHosts()) != 0 {
    32  		t.Error("decrementing did not remove host from activeHosts")
    33  	}
    34  
    35  	// Decrement reliability to 0. This should remove the host from allHosts.
    36  	hdb.decrementReliability(h.NetAddress, h.Reliability)
    37  	if len(hdb.AllHosts()) != 0 {
    38  		t.Error("decrementing did not remove host from allHosts")
    39  	}
    40  }
    41  
    42  // probeDialer is used to test the threadedProbeHosts method. A simple type
    43  // alias is used so that it can easily be redefined during testing, allowing
    44  // multiple behaviors to be tested.
    45  type probeDialer func(modules.NetAddress, time.Duration) (net.Conn, error)
    46  
    47  func (dial probeDialer) DialTimeout(addr modules.NetAddress, timeout time.Duration) (net.Conn, error) {
    48  	return dial(addr, timeout)
    49  }
    50  
    51  // TestThreadedProbeHosts tests the threadedProbeHosts method.
    52  func TestThreadedProbeHosts(t *testing.T) {
    53  	hdb := bareHostDB()
    54  	hdb.persist = &memPersist{}
    55  
    56  	// create a host to send to threadedProbeHosts
    57  	sk, pk, err := crypto.GenerateKeyPair()
    58  	if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  	h := new(hostEntry)
    62  	h.NetAddress = "foo"
    63  	h.PublicKey = types.SiaPublicKey{
    64  		Algorithm: types.SignatureEd25519,
    65  		Key:       pk[:],
    66  	}
    67  	h.Reliability = baseWeight // enough to withstand a few failures
    68  
    69  	// define a helper function for running threadedProbeHosts. We send the
    70  	// hostEntry, close the channel, and then call threadedProbeHosts.
    71  	// threadedProbeHosts will receive the host, loop once, and return after
    72  	// seeing the channel has closed.
    73  	//
    74  	// NOTE: since threadedProbeHosts decrements hdb.threadGroup, we Add(100)
    75  	// to prevent it from going negative. This is acceptable because we don't
    76  	// call hdb.Close in this test.
    77  	hdb.threadGroup.Add(100)
    78  	runProbe := func(h *hostEntry) {
    79  		hdb.scanPool <- h
    80  		close(hdb.scanPool)
    81  		hdb.threadedProbeHosts()
    82  		// reset hdb.scanPool
    83  		hdb.scanPool = make(chan *hostEntry, 1)
    84  	}
    85  
    86  	// make the dial fail
    87  	hdb.dialer = probeDialer(func(modules.NetAddress, time.Duration) (net.Conn, error) {
    88  		return nil, net.UnknownNetworkError("fail")
    89  	})
    90  	runProbe(h)
    91  	if len(hdb.ActiveHosts()) != 0 {
    92  		t.Error("unresponsive host was added")
    93  	}
    94  
    95  	// make the RPC fail
    96  	hdb.dialer = probeDialer(func(modules.NetAddress, time.Duration) (net.Conn, error) {
    97  		ourPipe, theirPipe := net.Pipe()
    98  		ourPipe.Close()
    99  		return theirPipe, nil
   100  	})
   101  	runProbe(h)
   102  	if len(hdb.ActiveHosts()) != 0 {
   103  		t.Error("unresponsive host was added")
   104  	}
   105  
   106  	// normal host
   107  	hdb.dialer = probeDialer(func(modules.NetAddress, time.Duration) (net.Conn, error) {
   108  		// create an in-memory conn and spawn a goroutine to handle our half
   109  		ourConn, theirConn := net.Pipe()
   110  		go func() {
   111  			// read the RPC
   112  			encoding.ReadObject(ourConn, new(types.Specifier), types.SpecifierLen)
   113  			// write host settings
   114  			crypto.WriteSignedObject(ourConn, modules.HostExternalSettings{
   115  				NetAddress: "probed",
   116  			}, sk)
   117  			ourConn.Close()
   118  		}()
   119  		return theirConn, nil
   120  	})
   121  	runProbe(h)
   122  	if len(hdb.ActiveHosts()) != 1 {
   123  		t.Error("host was not added")
   124  	}
   125  }
   126  
   127  // TestThreadedScan tests the threadedScan method.
   128  func TestThreadedScan(t *testing.T) {
   129  	hdb := bareHostDB()
   130  	hdb.persist = &memPersist{}
   131  
   132  	// use a real sleeper; this will prevent threadedScan from looping too
   133  	// quickly.
   134  	hdb.sleeper = stdSleeper{}
   135  	// use a dummy dialer that always fails
   136  	hdb.dialer = probeDialer(func(modules.NetAddress, time.Duration) (net.Conn, error) {
   137  		return nil, net.UnknownNetworkError("fail")
   138  	})
   139  
   140  	// create a host to be scanned
   141  	h := new(hostEntry)
   142  	h.NetAddress = "foo"
   143  	h.Reliability = types.NewCurrency64(1)
   144  	hdb.activeHosts[h.NetAddress] = &hostNode{hostEntry: h}
   145  
   146  	// perform one scan
   147  	go hdb.threadedScan()
   148  
   149  	// host should be sent down scanPool
   150  	select {
   151  	case <-hdb.scanPool:
   152  	case <-time.After(time.Second):
   153  		t.Error("host was not scanned")
   154  	}
   155  
   156  	// remove the host from activeHosts and add it to allHosts
   157  	hdb.mu.Lock()
   158  	delete(hdb.activeHosts, h.NetAddress)
   159  	hdb.allHosts[h.NetAddress] = h
   160  	hdb.mu.Unlock()
   161  
   162  	// perform one scan
   163  	go hdb.threadedScan()
   164  
   165  	// host should be sent down scanPool
   166  	select {
   167  	case <-hdb.scanPool:
   168  	case <-time.After(time.Second):
   169  		t.Error("host was not scanned")
   170  	}
   171  }