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  }