github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/watcher/watchertest/strings.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package watchertest
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/collections/set"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  	tomb "gopkg.in/tomb.v2"
    13  
    14  	"github.com/juju/juju/core/watcher"
    15  	"github.com/juju/juju/testing"
    16  )
    17  
    18  type MockStringsWatcher struct {
    19  	tomb tomb.Tomb
    20  	ch   <-chan []string
    21  }
    22  
    23  func NewMockStringsWatcher(ch <-chan []string) *MockStringsWatcher {
    24  	w := &MockStringsWatcher{ch: ch}
    25  	w.tomb.Go(func() error {
    26  		<-w.tomb.Dying()
    27  		return tomb.ErrDying
    28  	})
    29  	return w
    30  }
    31  
    32  func (w *MockStringsWatcher) Changes() watcher.StringsChannel {
    33  	return w.ch
    34  }
    35  
    36  func (w *MockStringsWatcher) Stop() error {
    37  	w.Kill()
    38  	return w.Wait()
    39  }
    40  
    41  func (w *MockStringsWatcher) Kill() {
    42  	w.tomb.Kill(nil)
    43  }
    44  
    45  // KillErr can be used to kill the worker with
    46  // an error, to simulate a failing watcher.
    47  func (w *MockStringsWatcher) KillErr(err error) {
    48  	w.tomb.Kill(err)
    49  }
    50  
    51  func (w *MockStringsWatcher) Err() error {
    52  	return w.tomb.Err()
    53  }
    54  
    55  func (w *MockStringsWatcher) Wait() error {
    56  	return w.tomb.Wait()
    57  }
    58  
    59  func NewStringsWatcherC(c *gc.C, watcher watcher.StringsWatcher) StringsWatcherC {
    60  	return StringsWatcherC{
    61  		C:       c,
    62  		Watcher: watcher,
    63  	}
    64  }
    65  
    66  type StringsWatcherC struct {
    67  	*gc.C
    68  	Watcher watcher.StringsWatcher
    69  }
    70  
    71  // AssertChanges fails if it cannot read a value from Changes despite waiting a
    72  // long time. It logs, but does not check, the received changes; but will fail
    73  // if the Changes chan is closed.
    74  func (c StringsWatcherC) AssertChanges() {
    75  	select {
    76  	case change, ok := <-c.Watcher.Changes():
    77  		c.Logf("received change: %#v", change)
    78  		c.Assert(ok, jc.IsTrue)
    79  	case <-time.After(testing.LongWait):
    80  		c.Fatalf("watcher did not send change")
    81  	}
    82  	c.AssertNoChange()
    83  }
    84  
    85  // AssertNoChange fails if it manages to read a value from Changes before a
    86  // short time has passed.
    87  func (c StringsWatcherC) AssertNoChange() {
    88  	select {
    89  	case change, ok := <-c.Watcher.Changes():
    90  		if !ok {
    91  			c.Fatalf("watcher closed Changes channel")
    92  		} else {
    93  			c.Fatalf("watcher sent unexpected change: %#v", change)
    94  		}
    95  	case <-time.After(testing.ShortWait):
    96  	}
    97  }
    98  
    99  // AssertStops Kills the watcher and asserts (1) that Wait completes without
   100  // error before a long time has passed; and (2) that Changes remains open but
   101  // no values are being sent.
   102  func (c StringsWatcherC) AssertStops() {
   103  	c.Watcher.Kill()
   104  	wait := make(chan error)
   105  	go func() {
   106  		wait <- c.Watcher.Wait()
   107  	}()
   108  	select {
   109  	case <-time.After(testing.LongWait):
   110  		c.Fatalf("watcher never stopped")
   111  	case err := <-wait:
   112  		c.Assert(err, jc.ErrorIsNil)
   113  	}
   114  
   115  	select {
   116  	case change, ok := <-c.Watcher.Changes():
   117  		c.Fatalf("watcher sent unexpected change: (%#v, %v)", change, ok)
   118  	default:
   119  	}
   120  }
   121  
   122  func (c StringsWatcherC) AssertChange(expect ...string) {
   123  	c.assertChange(false, expect...)
   124  }
   125  
   126  func (c StringsWatcherC) AssertChangeInSingleEvent(expect ...string) {
   127  	c.assertChange(true, expect...)
   128  }
   129  
   130  // AssertChangeMaybeIncluding verifies that there is a change that may
   131  // contain zero to all of the passed in strings, and no other changes.
   132  func (c StringsWatcherC) AssertChangeMaybeIncluding(expect ...string) {
   133  	maxCount := len(expect)
   134  	actual := c.collectChanges(true, maxCount)
   135  
   136  	if maxCount == 0 {
   137  		c.Assert(actual, gc.HasLen, 0)
   138  	} else {
   139  		actualCount := len(actual)
   140  		c.Assert(actualCount <= maxCount, jc.IsTrue, gc.Commentf("expected at most %d, got %d", maxCount, actualCount))
   141  		unexpected := set.NewStrings(actual...).Difference(set.NewStrings(expect...))
   142  		c.Assert(unexpected.Values(), gc.HasLen, 0)
   143  	}
   144  }
   145  
   146  // assertChange asserts the given list of changes was reported by
   147  // the watcher, but does not assume there are no following changes.
   148  func (c StringsWatcherC) assertChange(single bool, expect ...string) {
   149  	actual := c.collectChanges(single, len(expect))
   150  	if len(expect) == 0 {
   151  		c.Assert(actual, gc.HasLen, 0)
   152  	} else {
   153  		c.Assert(actual, jc.SameContents, expect)
   154  	}
   155  }
   156  
   157  // collectChanges gets up to the max number of changes within the
   158  // testing.LongWait period.
   159  func (c StringsWatcherC) collectChanges(single bool, max int) []string {
   160  	timeout := time.After(testing.LongWait)
   161  	var actual []string
   162  	gotOneChange := false
   163  loop:
   164  	for {
   165  		select {
   166  		case changes, ok := <-c.Watcher.Changes():
   167  			c.Assert(ok, jc.IsTrue)
   168  			gotOneChange = true
   169  			actual = append(actual, changes...)
   170  			if single || len(actual) >= max {
   171  				break loop
   172  			}
   173  		case <-timeout:
   174  			if !gotOneChange {
   175  				c.Fatalf("watcher did not send change")
   176  			}
   177  			break loop
   178  		}
   179  	}
   180  	return actual
   181  }