func TestBasicController(test *testing.T) { filePath := "/tmp/test_basic_controller.log" simpleLog := log.SimpleFileLog{} if err := simpleLog.Initialize(filePath); err != nil { test.Fatalf("could not initialize log backend: %v", err) return } logger := simpleLog.NewLogger("test-basic-controller") logger.Infof("starting new controller test") controller := &BasicController{} controller.Initialize(logger) defer func() { if err := controller.Close(); err != nil { test.Errorf("could not close the controller: %v", err) return } lock, errLock := controller.LockAll() if !errs.IsClosed(errLock) { test.Errorf("controller issued lock %v after it is closed", lock) return } // Lock and Unlock work even after a Close. Safety is not expected. foobar := controller.ReadLock("foo", "bar") foobar.Unlock() }() lock1, errLock1 := controller.LockAll() if errLock1 != nil { test.Errorf("could not acquire lock1: %v", errLock1) return } lock2, errLock2 := controller.TimedLockAll(time.Millisecond) if !errs.IsTimeout(errLock2) { test.Errorf("second lock %v is issued while lock1 %v is active", lock2, lock1) return } lock1.Unlock() lock3, errLock3 := controller.TimedLock(time.Millisecond, "a") if errLock3 != nil { test.Errorf("could not acquire lock3: %v", errLock3) return } lock4, errLock4 := controller.TimedLock(time.Millisecond, "b") if errLock4 != nil { test.Errorf("could not acquire lock4: %v", errLock4) return } lock5, errLock5 := controller.TimedLockAll(time.Millisecond) if errLock5 == nil { test.Errorf("lock all lock %v issue while locks %v and %v are active", lock5, lock3, lock4) return } lock3.Unlock() lock4.Unlock() foo := controller.ReadLock("foo") bar := controller.ReadLock("bar") bar.Unlock("bar") foo.Unlock("foo") baz := controller.ReadLock("baz") baz.Unlock() test.Logf("returning") }
func TestCondition(test *testing.T) { mutex := sync.Mutex{} cond := &Condition{} cond.Initialize(&mutex) // A signal must wake up waiting condition. mutex.Lock() go func() { mutex.Lock() cond.Signal() mutex.Unlock() }() if err := cond.Wait(); err != nil { test.Errorf("condition woke up with a non-nil error: %v", err) return } mutex.Unlock() // A signal must wake up timed wait. mutex.Lock() go func() { mutex.Lock() cond.Signal() mutex.Unlock() }() if err := cond.WaitTimeout(time.After(time.Second)); err != nil { test.Errorf("timed wait woke up with a non-nil error on signal: %v", err) return } mutex.Unlock() // A broadcast must wake up all waiters. wg := sync.WaitGroup{} for ii := 0; ii < 10; ii++ { doneCh := make(chan bool) wg.Add(1) go func() { mutex.Lock() doneCh <- true if err := cond.Wait(); err != nil { test.Errorf("wait returned with unexpected status: %v", err) } mutex.Unlock() wg.Done() }() <-doneCh } mutex.Lock() cond.Broadcast() mutex.Unlock() wg.Wait() // A broadcast must wake up all timed waiters. for ii := 0; ii < 10; ii++ { doneCh := make(chan bool) wg.Add(1) go func() { mutex.Lock() doneCh <- true if err := cond.WaitTimeout(time.After(time.Minute)); err != nil { test.Errorf("timed wait returned with unexpected status: %v", err) } mutex.Unlock() wg.Done() }() <-doneCh } mutex.Lock() cond.Broadcast() mutex.Unlock() wg.Wait() // A timeout must wake up the condition. mutex.Lock() if err := cond.WaitTimeout(time.After(time.Second)); !errs.IsTimeout(err) { test.Errorf("timed wait woke up with unexpected status: %v", err) return } mutex.Unlock() // Closing a condition must wake up all timed waiters and normal waiters. for ii := 0; ii < 10; ii++ { doneCh := make(chan bool) wg.Add(1) go func() { mutex.Lock() doneCh <- true if err := cond.Wait(); !errs.IsClosed(err) { test.Errorf("wait returned with unexpected status: %v", err) } mutex.Unlock() wg.Done() }() <-doneCh } for ii := 0; ii < 10; ii++ { doneCh := make(chan bool) wg.Add(1) go func() { mutex.Lock() doneCh <- true errWait := cond.WaitTimeout(time.After(time.Minute)) if !errs.IsClosed(errWait) { test.Errorf("timed wait returned with unexpected status: %v", errWait) } mutex.Unlock() wg.Done() }() <-doneCh } cond.Close() wg.Wait() }
func TestResourceController(test *testing.T) { filePath := "/tmp/test_resource_controller.log" simpleLog := log.SimpleFileLog{} if err := simpleLog.Initialize(filePath); err != nil { test.Fatalf("could not initialize log backend: %v", err) return } logger := simpleLog.NewLogger("test-resource-controller") logger.Infof("starting new test") controller := &ResourceController{} controller.Initialize(logger) foo := controller.LockResources("foo") bar := controller.LockResources("bar") timeoutCh := time.After(time.Millisecond) if _, err := controller.TimeLockResources(timeoutCh, "foo"); err == nil { test.Errorf("resource foo is issued multiple times") return } timeoutCh = time.After(time.Millisecond) if _, err := controller.TimeLockAll(timeoutCh); err == nil { test.Errorf("lock all issued when foo and bar are busy") return } bar.Unlock("bar") foo.Unlock("foo") all := controller.LockAll() all.Unlock() baz := controller.LockResources("baz") baz.Unlock() baz2 := controller.LockResources("baz") baz2.Unlock() // Allow multiple readers on a resource. a1 := controller.LockResources("", "aa") a2 := controller.LockResources("", "aa") timeoutCh = time.After(time.Millisecond) if _, err := controller.TimeLockAll(timeoutCh); err == nil { test.Errorf("lock all issued when two readers are sharing aa") return } timeoutCh = time.After(time.Millisecond) if _, err := controller.TimeLockResources(timeoutCh, "aa"); err == nil { test.Errorf("exclusive lock is issued when two readers have aa") return } a1.Unlock() a2.Unlock() // A close on the timeout channel must unlock the waiters. b1 := controller.LockAll() timeoutCh2 := make(chan time.Time) close(timeoutCh2) if _, err := controller.TimeLockAll(timeoutCh2); !errs.IsTimeout(err) { test.Errorf("closing timeout channel did not unblock the lock") return } b1.Unlock() }