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 }