diff --git a/helper/shadow/keyed_value.go b/helper/shadow/keyed_value.go index ed26bb72b..e685e6c3d 100644 --- a/helper/shadow/keyed_value.go +++ b/helper/shadow/keyed_value.go @@ -42,6 +42,26 @@ func (w *KeyedValue) Value(k string) interface{} { return val.Value() } +// WaitForChange waits for the value with the given key to be set again. +// If the key isn't set, it'll wait for an initial value. Note that while +// it is called "WaitForChange", the value isn't guaranteed to _change_; +// this will return when a SetValue is called for the given k. +func (w *KeyedValue) WaitForChange(k string) interface{} { + w.lock.Lock() + w.once.Do(w.init) + + // Check for an active waiter. If there isn't one, make it + val := w.waiters[k] + if val == nil { + val = new(Value) + w.waiters[k] = val + } + w.lock.Unlock() + + // And wait + return val.Value() +} + // ValueOk gets the value for the given key, returning immediately if the // value doesn't exist. The second return argument is true if the value exists. func (w *KeyedValue) ValueOk(k string) (interface{}, bool) { diff --git a/helper/shadow/keyed_value_test.go b/helper/shadow/keyed_value_test.go index 92b26917e..56d368190 100644 --- a/helper/shadow/keyed_value_test.go +++ b/helper/shadow/keyed_value_test.go @@ -168,3 +168,58 @@ func TestKeyedValueClose_existingBlocked(t *testing.T) { t.Fatalf("bad: %#v", val) } } + +func TestKeyedValueWaitForChange(t *testing.T) { + var v KeyedValue + + // Set a value + v.SetValue("foo", 42) + + // Start reading this should be blocking + valueCh := make(chan interface{}) + go func() { + valueCh <- v.WaitForChange("foo") + }() + + // We should not get the value + select { + case <-valueCh: + t.Fatal("shouldn't receive value") + case <-time.After(10 * time.Millisecond): + } + + // Set a new value + v.SetValue("foo", 84) + + // Verify + val := <-valueCh + if val != 84 { + t.Fatalf("bad: %#v", val) + } +} + +func TestKeyedValueWaitForChange_initial(t *testing.T) { + var v KeyedValue + + // Start reading this should be blocking + valueCh := make(chan interface{}) + go func() { + valueCh <- v.WaitForChange("foo") + }() + + // We should not get the value + select { + case <-valueCh: + t.Fatal("shouldn't receive value") + case <-time.After(10 * time.Millisecond): + } + + // Set a new value + v.SetValue("foo", 84) + + // Verify + val := <-valueCh + if val != 84 { + t.Fatalf("bad: %#v", val) + } +}