2014-08-18 21:42:08 +00:00
|
|
|
/*
|
2015-05-01 16:19:44 +00:00
|
|
|
Copyright 2014 The Kubernetes Authors All rights reserved.
|
2014-08-18 21:42:08 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package wait
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2015-10-02 00:11:51 +00:00
|
|
|
"sync/atomic"
|
2014-08-18 21:42:08 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
2015-09-16 18:52:07 +00:00
|
|
|
|
|
|
|
"k8s.io/kubernetes/pkg/util"
|
2014-08-18 21:42:08 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestPoller(t *testing.T) {
|
2015-10-02 20:48:50 +00:00
|
|
|
done := make(chan struct{})
|
|
|
|
defer close(done)
|
2014-08-22 15:36:01 +00:00
|
|
|
w := poller(time.Millisecond, 2*time.Millisecond)
|
2015-10-02 20:48:50 +00:00
|
|
|
ch := w(done)
|
2014-08-18 21:42:08 +00:00
|
|
|
count := 0
|
|
|
|
DRAIN:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case _, open := <-ch:
|
|
|
|
if !open {
|
|
|
|
break DRAIN
|
|
|
|
}
|
|
|
|
count++
|
2015-09-16 18:52:07 +00:00
|
|
|
case <-time.After(util.ForeverTestTimeout):
|
2014-08-18 21:42:08 +00:00
|
|
|
t.Errorf("unexpected timeout after poll")
|
|
|
|
}
|
|
|
|
}
|
2015-09-23 23:32:05 +00:00
|
|
|
if count > 3 {
|
|
|
|
t.Errorf("expected up to three values, got %d", count)
|
2014-08-18 21:42:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-02 00:11:51 +00:00
|
|
|
func fakeTicker(max int, used *int32) WaitFunc {
|
2015-10-02 20:48:50 +00:00
|
|
|
return func(done <-chan struct{}) <-chan struct{} {
|
2014-08-18 21:42:08 +00:00
|
|
|
ch := make(chan struct{})
|
|
|
|
go func() {
|
2015-10-02 20:48:50 +00:00
|
|
|
defer close(ch)
|
2015-10-02 00:11:51 +00:00
|
|
|
for i := 0; i < max; i++ {
|
2015-10-02 20:48:50 +00:00
|
|
|
select {
|
|
|
|
case ch <- struct{}{}:
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
}
|
2015-10-02 00:11:51 +00:00
|
|
|
if used != nil {
|
|
|
|
atomic.AddInt32(used, 1)
|
|
|
|
}
|
2014-08-18 21:42:08 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
return ch
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-02 00:11:51 +00:00
|
|
|
type fakePoller struct {
|
|
|
|
max int
|
|
|
|
used int32 // accessed with atomics
|
|
|
|
}
|
|
|
|
|
|
|
|
func (fp *fakePoller) GetWaitFunc(interval, timeout time.Duration) WaitFunc {
|
|
|
|
return fakeTicker(fp.max, &fp.used)
|
|
|
|
}
|
|
|
|
|
2014-08-18 21:42:08 +00:00
|
|
|
func TestPoll(t *testing.T) {
|
|
|
|
invocations := 0
|
|
|
|
f := ConditionFunc(func() (bool, error) {
|
|
|
|
invocations++
|
|
|
|
return true, nil
|
|
|
|
})
|
2015-10-02 00:11:51 +00:00
|
|
|
fp := fakePoller{max: 1}
|
|
|
|
if err := pollInternal(fp.GetWaitFunc(time.Microsecond, time.Microsecond), f); err != nil {
|
|
|
|
t.Fatalf("unexpected error %v", err)
|
|
|
|
}
|
|
|
|
if invocations != 1 {
|
|
|
|
t.Errorf("Expected exactly one invocation, got %d", invocations)
|
|
|
|
}
|
|
|
|
used := atomic.LoadInt32(&fp.used)
|
|
|
|
if used != 1 {
|
|
|
|
t.Errorf("Expected exactly one tick, got %d", used)
|
|
|
|
}
|
|
|
|
|
|
|
|
expectedError := errors.New("Expected error")
|
|
|
|
f = ConditionFunc(func() (bool, error) {
|
|
|
|
return false, expectedError
|
|
|
|
})
|
|
|
|
fp = fakePoller{max: 1}
|
|
|
|
if err := pollInternal(fp.GetWaitFunc(time.Microsecond, time.Microsecond), f); err == nil || err != expectedError {
|
|
|
|
t.Fatalf("Expected error %v, got none %v", expectedError, err)
|
|
|
|
}
|
|
|
|
if invocations != 1 {
|
|
|
|
t.Errorf("Expected exactly one invocation, got %d", invocations)
|
|
|
|
}
|
|
|
|
used = atomic.LoadInt32(&fp.used)
|
|
|
|
if used != 1 {
|
|
|
|
t.Errorf("Expected exactly one tick, got %d", used)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPollImmediate(t *testing.T) {
|
|
|
|
invocations := 0
|
|
|
|
f := ConditionFunc(func() (bool, error) {
|
|
|
|
invocations++
|
|
|
|
return true, nil
|
|
|
|
})
|
|
|
|
fp := fakePoller{max: 0}
|
|
|
|
if err := pollImmediateInternal(fp.GetWaitFunc(time.Microsecond, time.Microsecond), f); err != nil {
|
2014-08-18 21:42:08 +00:00
|
|
|
t.Fatalf("unexpected error %v", err)
|
|
|
|
}
|
2015-10-02 00:11:51 +00:00
|
|
|
if invocations != 1 {
|
|
|
|
t.Errorf("Expected exactly one invocation, got %d", invocations)
|
|
|
|
}
|
|
|
|
used := atomic.LoadInt32(&fp.used)
|
|
|
|
if used != 0 {
|
|
|
|
t.Errorf("Expected exactly zero ticks, got %d", used)
|
2014-08-18 21:42:08 +00:00
|
|
|
}
|
2015-10-02 00:11:51 +00:00
|
|
|
|
2015-03-01 02:40:57 +00:00
|
|
|
expectedError := errors.New("Expected error")
|
|
|
|
f = ConditionFunc(func() (bool, error) {
|
|
|
|
return false, expectedError
|
|
|
|
})
|
2015-10-02 00:11:51 +00:00
|
|
|
fp = fakePoller{max: 0}
|
|
|
|
if err := pollImmediateInternal(fp.GetWaitFunc(time.Microsecond, time.Microsecond), f); err == nil || err != expectedError {
|
2015-03-01 02:40:57 +00:00
|
|
|
t.Fatalf("Expected error %v, got none %v", expectedError, err)
|
|
|
|
}
|
2015-10-02 00:11:51 +00:00
|
|
|
if invocations != 1 {
|
|
|
|
t.Errorf("Expected exactly one invocation, got %d", invocations)
|
|
|
|
}
|
|
|
|
used = atomic.LoadInt32(&fp.used)
|
|
|
|
if used != 0 {
|
|
|
|
t.Errorf("Expected exactly zero ticks, got %d", used)
|
|
|
|
}
|
2014-08-18 21:42:08 +00:00
|
|
|
}
|
|
|
|
|
2014-08-22 15:36:01 +00:00
|
|
|
func TestPollForever(t *testing.T) {
|
|
|
|
ch := make(chan struct{})
|
|
|
|
done := make(chan struct{}, 1)
|
|
|
|
complete := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
f := ConditionFunc(func() (bool, error) {
|
|
|
|
ch <- struct{}{}
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
return true, nil
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
})
|
2015-10-02 20:48:50 +00:00
|
|
|
|
2015-07-29 21:14:18 +00:00
|
|
|
if err := PollInfinite(time.Microsecond, f); err != nil {
|
2014-08-22 15:36:01 +00:00
|
|
|
t.Fatalf("unexpected error %v", err)
|
|
|
|
}
|
2015-07-29 21:14:18 +00:00
|
|
|
|
2014-08-22 15:36:01 +00:00
|
|
|
close(ch)
|
|
|
|
complete <- struct{}{}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// ensure the condition is opened
|
|
|
|
<-ch
|
|
|
|
|
|
|
|
// ensure channel sends events
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
select {
|
|
|
|
case _, open := <-ch:
|
|
|
|
if !open {
|
|
|
|
t.Fatalf("did not expect channel to be closed")
|
|
|
|
}
|
2015-09-16 18:52:07 +00:00
|
|
|
case <-time.After(util.ForeverTestTimeout):
|
2014-08-22 15:36:01 +00:00
|
|
|
t.Fatalf("channel did not return at least once within the poll interval")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-30 13:03:38 +00:00
|
|
|
// at most one poll notification should be sent once we return from the condition
|
2014-08-22 15:36:01 +00:00
|
|
|
done <- struct{}{}
|
|
|
|
go func() {
|
|
|
|
for i := 0; i < 2; i++ {
|
|
|
|
_, open := <-ch
|
2015-12-30 13:03:38 +00:00
|
|
|
if !open {
|
2014-08-22 15:36:01 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
t.Fatalf("expected closed channel after two iterations")
|
|
|
|
}()
|
|
|
|
<-complete
|
|
|
|
}
|
|
|
|
|
2014-08-18 21:42:08 +00:00
|
|
|
func TestWaitFor(t *testing.T) {
|
|
|
|
var invocations int
|
|
|
|
testCases := map[string]struct {
|
|
|
|
F ConditionFunc
|
|
|
|
Ticks int
|
|
|
|
Invoked int
|
|
|
|
Err bool
|
|
|
|
}{
|
|
|
|
"invoked once": {
|
|
|
|
ConditionFunc(func() (bool, error) {
|
|
|
|
invocations++
|
|
|
|
return true, nil
|
|
|
|
}),
|
|
|
|
2,
|
|
|
|
1,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
"invoked and returns a timeout": {
|
|
|
|
ConditionFunc(func() (bool, error) {
|
|
|
|
invocations++
|
|
|
|
return false, nil
|
|
|
|
}),
|
|
|
|
2,
|
2015-10-02 00:11:51 +00:00
|
|
|
3, // the contract of WaitFor() says the func is called once more at the end of the wait
|
2014-08-18 21:42:08 +00:00
|
|
|
true,
|
|
|
|
},
|
|
|
|
"returns immediately on error": {
|
|
|
|
ConditionFunc(func() (bool, error) {
|
|
|
|
invocations++
|
|
|
|
return false, errors.New("test")
|
|
|
|
}),
|
|
|
|
2,
|
|
|
|
1,
|
|
|
|
true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for k, c := range testCases {
|
|
|
|
invocations = 0
|
2015-10-02 00:11:51 +00:00
|
|
|
ticker := fakeTicker(c.Ticks, nil)
|
2015-10-02 20:48:50 +00:00
|
|
|
err := func() error {
|
|
|
|
done := make(chan struct{})
|
|
|
|
defer close(done)
|
|
|
|
return WaitFor(ticker, c.F, done)
|
|
|
|
}()
|
2014-08-18 21:42:08 +00:00
|
|
|
switch {
|
|
|
|
case c.Err && err == nil:
|
|
|
|
t.Errorf("%s: Expected error, got nil", k)
|
|
|
|
continue
|
|
|
|
case !c.Err && err != nil:
|
|
|
|
t.Errorf("%s: Expected no error, got: %#v", k, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if invocations != c.Invoked {
|
2015-10-02 00:11:51 +00:00
|
|
|
t.Errorf("%s: Expected %d invocations, got %d", k, c.Invoked, invocations)
|
2014-08-18 21:42:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-11-16 00:15:32 +00:00
|
|
|
|
|
|
|
func TestWaitForWithDelay(t *testing.T) {
|
|
|
|
done := make(chan struct{})
|
|
|
|
defer close(done)
|
|
|
|
WaitFor(poller(time.Millisecond, util.ForeverTestTimeout), func() (bool, error) {
|
|
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
return true, nil
|
|
|
|
}, done)
|
|
|
|
// If polling goroutine doesn't see the done signal it will leak timers.
|
|
|
|
select {
|
|
|
|
case done <- struct{}{}:
|
|
|
|
case <-time.After(util.ForeverTestTimeout):
|
|
|
|
t.Errorf("expected an ack of the done signal.")
|
|
|
|
}
|
|
|
|
}
|