github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/x/watch/value_test.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package watch 22 23 import ( 24 "errors" 25 "sync/atomic" 26 "testing" 27 "time" 28 29 "github.com/m3db/m3/src/x/instrument" 30 "github.com/stretchr/testify/require" 31 ) 32 33 func TestValueWatchAlreadyWatching(t *testing.T) { 34 rv := NewValue(testValueOptions()).(*value) 35 rv.status = valueWatching 36 require.NoError(t, rv.Watch()) 37 } 38 39 func TestValueWatchCreateWatchError(t *testing.T) { 40 errWatch := errors.New("error creating watch") 41 updatableFn := func() (Updatable, error) { 42 return nil, errWatch 43 } 44 rv := NewValue( 45 testValueOptions(). 46 SetNewUpdatableFn(updatableFn). 47 SetKey("foobar"), 48 ).(*value) 49 50 err := rv.Watch() 51 require.Equal(t, CreateWatchError{innerError: errWatch, key: "foobar"}, err) 52 require.Equal(t, valueNotWatching, rv.status) 53 54 rv.Unwatch() 55 require.Equal(t, valueNotWatching, rv.status) 56 } 57 58 func TestValueWatchWatchTimeout(t *testing.T) { 59 _, rv := testWatchableAndValue() 60 err := rv.Watch() 61 require.Equal(t, InitValueError{innerError: errInitWatchTimeout, key: "foobar"}, err) 62 require.Equal(t, valueWatching, rv.status) 63 64 rv.Unwatch() 65 require.Equal(t, valueNotWatching, rv.status) 66 } 67 68 func TestValueWatchUpdateError(t *testing.T) { 69 errUpdate := errors.New("error updating") 70 wa, rv := testWatchableAndValue() 71 require.NoError(t, wa.Update(1)) 72 rv.processWithLockFn = func(interface{}) error { return errUpdate } 73 74 require.Equal(t, InitValueError{innerError: errUpdate, key: "foobar"}, rv.Watch()) 75 require.Equal(t, valueWatching, rv.status) 76 77 rv.Unwatch() 78 require.Equal(t, valueNotWatching, rv.status) 79 } 80 81 func TestValueWatchSuccess(t *testing.T) { 82 wa, rv := testWatchableAndValue() 83 rv.processWithLockFn = func(interface{}) error { 84 return nil 85 } 86 require.NoError(t, wa.Update(1)) 87 88 require.NoError(t, rv.Watch()) 89 require.Equal(t, valueWatching, rv.status) 90 91 rv.Unwatch() 92 require.Equal(t, valueNotWatching, rv.status) 93 } 94 95 func TestValueWatchInterrupt(t *testing.T) { 96 interruptedCh := make(chan struct{}) 97 close(interruptedCh) 98 99 opts := testValueOptions(). 100 SetInterruptedCh(interruptedCh). 101 SetNewUpdatableFn(testUpdatableFn(NewWatchable())) 102 103 val := NewValue(opts).(*value) 104 err := val.Watch() 105 106 require.Error(t, err) 107 require.Equal(t, err.Error(), "interrupted") 108 } 109 110 func TestValueUnwatchNotWatching(t *testing.T) { 111 _, rv := testWatchableAndValue() 112 rv.status = valueNotWatching 113 rv.Unwatch() 114 require.Equal(t, valueNotWatching, rv.status) 115 } 116 117 func TestValueWatchUnWatchMultipleTimes(t *testing.T) { 118 wa, rv := testWatchableAndValue() 119 rv.processWithLockFn = func(interface{}) error { 120 return nil 121 } 122 require.NoError(t, wa.Update(1)) 123 124 iter := 10 125 for i := 0; i < iter; i++ { 126 require.NoError(t, rv.Watch()) 127 rv.Unwatch() 128 } 129 } 130 131 func TestValueWatchUpdatesError(t *testing.T) { 132 wa, rv := testWatchableAndValue() 133 wa.Update(1) 134 135 doneCh := make(chan struct{}) 136 errUpdate := errors.New("error updating") 137 rv.processWithLockFn = func(interface{}) error { 138 close(doneCh) 139 return errUpdate 140 } 141 _, w, err := wa.Watch() 142 require.NoError(t, err) 143 144 rv.updatable = w 145 rv.getUpdateFn = func(Updatable) (interface{}, error) { return 1, nil } 146 rv.status = valueWatching 147 go rv.watchUpdates(w) 148 <-doneCh 149 rv.Unwatch() 150 require.Equal(t, valueNotWatching, rv.status) 151 } 152 153 func TestValueGetFnError(t *testing.T) { 154 wa, rv := testWatchableAndValue() 155 wa.Update(1) 156 157 doneCh := make(chan struct{}) 158 errGet := errors.New("error get") 159 160 rv.getUpdateFn = func(Updatable) (interface{}, error) { close(doneCh); return nil, errGet } 161 require.Error(t, rv.Watch()) 162 <-doneCh 163 rv.Unwatch() 164 require.Equal(t, valueNotWatching, rv.status) 165 } 166 167 func TestValueWatchValueUnwatched(t *testing.T) { 168 wa, rv := testWatchableAndValue() 169 wa.Update(1) 170 171 require.False(t, wa.IsClosed()) 172 var updated int32 173 rv.processWithLockFn = func(interface{}) error { atomic.AddInt32(&updated, 1); return nil } 174 _, w, err := wa.Watch() 175 require.NoError(t, err) 176 require.Equal(t, 1, len(w.C())) 177 178 rv.updatable = w 179 rv.status = valueNotWatching 180 go rv.watchUpdates(w) 181 182 for { 183 if 0 == len(w.C()) { 184 break 185 } 186 time.Sleep(100 * time.Millisecond) 187 } 188 require.Equal(t, int32(0), atomic.LoadInt32(&updated)) 189 } 190 191 func TestValueWatchValueDifferentWatch(t *testing.T) { 192 wa, rv := testWatchableAndValue() 193 wa.Update(1) 194 var updated int32 195 rv.processWithLockFn = func(interface{}) error { atomic.AddInt32(&updated, 1); return nil } 196 _, w1, err := wa.Watch() 197 require.NoError(t, err) 198 199 rv.updatable = w1 200 rv.status = valueWatching 201 _, w2, err := wa.Watch() 202 require.NoError(t, err) 203 go rv.watchUpdates(w2) 204 205 for { 206 if 0 == len(w2.C()) { 207 break 208 } 209 time.Sleep(100 * time.Millisecond) 210 } 211 require.Equal(t, int32(0), atomic.LoadInt32(&updated)) 212 } 213 214 func TestValueUpdateNilValueError(t *testing.T) { 215 rv := NewValue(testValueOptions()).(*value) 216 require.Equal(t, errNilValue, rv.processWithLockFn(nil)) 217 } 218 219 func TestValueUpdateProcessError(t *testing.T) { 220 rv := NewValue(testValueOptions()).(*value) 221 errProcess := errors.New("error processing") 222 rv.processFn = func(interface{}) error { return errProcess } 223 224 require.Error(t, rv.processWithLockFn(3)) 225 } 226 227 func TestValueUpdateSuccess(t *testing.T) { 228 var outputs []interface{} 229 rv := NewValue(testValueOptions()).(*value) 230 rv.processFn = func(v interface{}) error { 231 outputs = append(outputs, v) 232 return nil 233 } 234 235 require.NoError(t, rv.processWithLock(3)) 236 require.Equal(t, []interface{}{3}, outputs) 237 } 238 239 func testValueOptions() Options { 240 return NewOptions(). 241 SetInstrumentOptions(instrument.NewOptions()). 242 SetInitWatchTimeout(100 * time.Millisecond). 243 SetProcessFn(nil) 244 } 245 246 func testWatchableAndValue() (Watchable, *value) { 247 wa := NewWatchable() 248 opts := testValueOptions(). 249 SetNewUpdatableFn(testUpdatableFn(wa)). 250 SetGetUpdateFn(func(updatable Updatable) (interface{}, error) { 251 return updatable.(Watch).Get(), nil 252 }). 253 SetKey("foobar") 254 255 return wa, NewValue(opts).(*value) 256 } 257 258 func testUpdatableFn(wa Watchable) NewUpdatableFn { 259 return func() (Updatable, error) { 260 _, w, err := wa.Watch() 261 return w, err 262 } 263 }