github.com/Cloud-Foundations/Dominator@v0.3.4/lib/slavedriver/impl_test.go (about)

     1  package slavedriver
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/Cloud-Foundations/Dominator/lib/format"
    11  	"github.com/Cloud-Foundations/Dominator/lib/log/testlogger"
    12  	"github.com/Cloud-Foundations/Dominator/lib/srpc"
    13  )
    14  
    15  type fakeDatabase struct {
    16  }
    17  
    18  func (db *fakeDatabase) load() (*slaveRoll, error) {
    19  	return nil, nil
    20  }
    21  
    22  func (db *fakeDatabase) save(slaves slaveRoll) error {
    23  	return nil
    24  }
    25  
    26  type failingTrader struct{}
    27  
    28  func (trader *failingTrader) Close() error {
    29  	return nil
    30  }
    31  
    32  func (trader *failingTrader) CreateSlave() (SlaveInfo, error) {
    33  	return SlaveInfo{}, errors.New("cannot create slave")
    34  }
    35  
    36  func (trader *failingTrader) DestroySlave(identifier string) error {
    37  	return errors.New("cannot destroy slave that cannot have been created")
    38  }
    39  
    40  type fakeTrader struct {
    41  	createDelay  time.Duration
    42  	destroyDelay time.Duration
    43  	mutex        sync.RWMutex
    44  	numExisting  int
    45  	sequence     int
    46  }
    47  
    48  func fakeDialer(string, string, time.Duration) (*srpc.Client, error) {
    49  	return srpc.NewFakeClient(srpc.FakeClientOptions{}), nil
    50  }
    51  
    52  func (trader *fakeTrader) Close() error {
    53  	return nil
    54  }
    55  
    56  func (trader *fakeTrader) CreateSlave() (SlaveInfo, error) {
    57  	trader.mutex.Lock()
    58  	defer trader.mutex.Unlock()
    59  	time.Sleep(trader.createDelay)
    60  	slave := SlaveInfo{
    61  		Identifier: fmt.Sprintf("%d", trader.sequence),
    62  	}
    63  	trader.numExisting++
    64  	trader.sequence++
    65  	return slave, nil
    66  }
    67  
    68  func (trader *fakeTrader) DestroySlave(identifier string) error {
    69  	trader.mutex.Lock()
    70  	defer trader.mutex.Unlock()
    71  	time.Sleep(trader.destroyDelay)
    72  	trader.numExisting--
    73  	return nil
    74  }
    75  
    76  func (trader *fakeTrader) getSequence() int {
    77  	trader.mutex.RLock()
    78  	defer trader.mutex.RUnlock()
    79  	return trader.sequence
    80  }
    81  
    82  func TestOneGet(t *testing.T) {
    83  	logger := testlogger.NewWithTimestamps(t)
    84  	trader := &fakeTrader{
    85  		createDelay:  100 * time.Microsecond,
    86  		destroyDelay: 10 * time.Microsecond,
    87  	}
    88  	slaveDriver, err := newSlaveDriver(
    89  		SlaveDriverOptions{
    90  			MaximumIdleSlaves: 2,
    91  			MinimumIdleSlaves: 1,
    92  		},
    93  		trader,
    94  		fakeDialer,
    95  		&fakeDatabase{},
    96  		logger)
    97  	if err != nil {
    98  		logger.Fatal(err)
    99  	}
   100  	startTime := time.Now()
   101  	slave, err := slaveDriver.GetSlave()
   102  	if err != nil {
   103  		logger.Fatal(err)
   104  	}
   105  	logger.Printf("got slave: %s after: %s",
   106  		slave, format.Duration(time.Since(startTime)))
   107  	time.Sleep(time.Millisecond)
   108  	slave.Destroy()
   109  	logger.Print("slave.Destroy() returned")
   110  	time.Sleep(10 * time.Millisecond)
   111  	if sequence := trader.getSequence(); sequence != 2 {
   112  		logger.Fatalf("sequence: %d != 2", sequence)
   113  	} else {
   114  		logger.Printf("sequence: %d, as expected", sequence)
   115  	}
   116  }
   117  
   118  func TestTwoGets(t *testing.T) {
   119  	logger := testlogger.NewWithTimestamps(t)
   120  	trader := &fakeTrader{
   121  		createDelay:  100 * time.Millisecond,
   122  		destroyDelay: 10 * time.Millisecond,
   123  	}
   124  	slaveDriver, err := newSlaveDriver(
   125  		SlaveDriverOptions{
   126  			MaximumIdleSlaves: 2,
   127  			MinimumIdleSlaves: 1,
   128  		},
   129  		trader,
   130  		fakeDialer,
   131  		&fakeDatabase{},
   132  		logger)
   133  	if err != nil {
   134  		logger.Fatal(err)
   135  	}
   136  	startTime := time.Now()
   137  	slave, err := slaveDriver.GetSlave()
   138  	if err != nil {
   139  		logger.Fatal(err)
   140  	}
   141  	timeTaken := time.Since(startTime)
   142  	if timeTaken > 150*time.Millisecond {
   143  		logger.Fatalf("got slave: %s after: %s",
   144  			slave, format.Duration(timeTaken))
   145  	}
   146  	logger.Printf("got slave: %s after: %s", slave, format.Duration(timeTaken))
   147  	slave.Destroy()
   148  	logger.Print("destroyed slave")
   149  	startTime = time.Now()
   150  	slave, err = slaveDriver.GetSlave()
   151  	if err != nil {
   152  		logger.Fatal(err)
   153  	}
   154  	timeTaken = time.Since(startTime)
   155  	if timeTaken > 150*time.Millisecond {
   156  		logger.Fatalf("got slave: %s after: %s",
   157  			slave, format.Duration(timeTaken))
   158  	}
   159  	logger.Printf("got slave: %s after: %s", slave, format.Duration(timeTaken))
   160  	slave.Destroy()
   161  	logger.Print("destroyed slave")
   162  }
   163  
   164  func TestStarvedGet(t *testing.T) {
   165  	logger := testlogger.NewWithTimestamps(t)
   166  	trader := &failingTrader{}
   167  	slaveDriver, err := newSlaveDriver(
   168  		SlaveDriverOptions{
   169  			MaximumIdleSlaves: 2,
   170  			MinimumIdleSlaves: 1,
   171  		},
   172  		trader,
   173  		fakeDialer,
   174  		&fakeDatabase{},
   175  		logger)
   176  	if err != nil {
   177  		logger.Fatal(err)
   178  	}
   179  	errorChannel := make(chan error, 1)
   180  	timer := time.NewTimer(100 * time.Millisecond)
   181  	go func() {
   182  		_, err := slaveDriver.GetSlaveWithTimeout(time.Millisecond)
   183  		errorChannel <- err
   184  	}()
   185  	select {
   186  	case err := <-errorChannel:
   187  		if err == nil {
   188  			logger.Fatal("GetSlaveWithTimeout() succeeded")
   189  		}
   190  	case <-timer.C:
   191  		logger.Fatal("GetSlaveWithTimeout() did not time out")
   192  	}
   193  }