github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/pruner/worker_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package pruner_test
     5  
     6  import (
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/juju/clock/testclock"
    11  	"github.com/juju/errors"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/juju/worker.v1"
    15  
    16  	"github.com/juju/juju/core/watcher"
    17  	"github.com/juju/juju/environs/config"
    18  	coretesting "github.com/juju/juju/testing"
    19  	"github.com/juju/juju/worker/pruner"
    20  	"github.com/juju/juju/worker/statushistorypruner"
    21  )
    22  
    23  type PrunerSuite struct {
    24  	coretesting.BaseSuite
    25  }
    26  
    27  var _ = gc.Suite(&PrunerSuite{})
    28  
    29  func (s *PrunerSuite) setupPruner(c *gc.C) (*fakeFacade, *testclock.Clock) {
    30  	facade := newFakeFacade()
    31  	attrs := coretesting.FakeConfig()
    32  	attrs["max-status-history-age"] = "1s"
    33  	attrs["max-status-history-size"] = "3M"
    34  	cfg, err := config.New(config.UseDefaults, attrs)
    35  	c.Assert(err, jc.ErrorIsNil)
    36  	facade.modelConfig = cfg
    37  
    38  	testClock := testclock.NewClock(time.Time{})
    39  	conf := pruner.Config{
    40  		Facade:        facade,
    41  		PruneInterval: coretesting.ShortWait,
    42  		Clock:         testClock,
    43  	}
    44  
    45  	// an example pruner
    46  	pruner, err := statushistorypruner.New(conf)
    47  
    48  	c.Check(err, jc.ErrorIsNil)
    49  	s.AddCleanup(func(*gc.C) {
    50  		c.Assert(worker.Stop(pruner), jc.ErrorIsNil)
    51  	})
    52  
    53  	facade.changesWatcher.changes <- struct{}{}
    54  	select {
    55  	case <-facade.gotConfig:
    56  	case <-time.After(coretesting.LongWait):
    57  		c.Fatal("timed out waiting for model configr")
    58  	}
    59  
    60  	return facade, testClock
    61  }
    62  
    63  func (s *PrunerSuite) assertWorkerCallsPrune(c *gc.C, facade *fakeFacade, testClock *testclock.Clock, collectionSize int) {
    64  	// NewTimer/Reset will have been called with the PruneInterval.
    65  	testClock.WaitAdvance(coretesting.ShortWait-time.Nanosecond, coretesting.LongWait, 1)
    66  	select {
    67  	case <-facade.pruned:
    68  		c.Fatal("unexpected call to Prune")
    69  	case <-time.After(coretesting.ShortWait):
    70  	}
    71  	testClock.Advance(time.Nanosecond)
    72  	select {
    73  	case args := <-facade.pruned:
    74  		c.Assert(args.maxHistoryMB, gc.Equals, collectionSize)
    75  	case <-time.After(coretesting.LongWait):
    76  		c.Fatal("timed out waiting for call to Prune")
    77  	}
    78  }
    79  
    80  func (s *PrunerSuite) TestWorkerCallsPrune(c *gc.C) {
    81  	facade, clock := s.setupPruner(c)
    82  	s.assertWorkerCallsPrune(c, facade, clock, 3)
    83  }
    84  
    85  func (s *PrunerSuite) TestWorkerWontCallPruneBeforeFiringTimer(c *gc.C) {
    86  	facade, _ := s.setupPruner(c)
    87  
    88  	select {
    89  	case <-facade.pruned:
    90  		c.Fatal("called before firing timer.")
    91  	case <-time.After(coretesting.ShortWait):
    92  	}
    93  }
    94  
    95  func (s *PrunerSuite) TestModelConfigChange(c *gc.C) {
    96  	facade, clock := s.setupPruner(c)
    97  	s.assertWorkerCallsPrune(c, facade, clock, 3)
    98  
    99  	var err error
   100  	facade.modelConfig, err = facade.modelConfig.Apply(map[string]interface{}{"max-status-history-size": "4M"})
   101  	c.Assert(err, jc.ErrorIsNil)
   102  	facade.changesWatcher.changes <- struct{}{}
   103  
   104  	s.assertWorkerCallsPrune(c, facade, clock, 4)
   105  }
   106  
   107  type fakeFacade struct {
   108  	pruned         chan pruneParams
   109  	changesWatcher *mockNotifyWatcher
   110  	modelConfig    *config.Config
   111  	gotConfig      chan struct{}
   112  }
   113  
   114  type pruneParams struct {
   115  	maxAge       time.Duration
   116  	maxHistoryMB int
   117  }
   118  
   119  func newFakeFacade() *fakeFacade {
   120  	return &fakeFacade{
   121  		pruned:         make(chan pruneParams, 1),
   122  		gotConfig:      make(chan struct{}, 1),
   123  		changesWatcher: newMockNotifyWatcher(),
   124  	}
   125  }
   126  
   127  // Prune implements Facade
   128  func (f *fakeFacade) Prune(maxAge time.Duration, maxHistoryMB int) error {
   129  	select {
   130  	case f.pruned <- pruneParams{maxAge, maxHistoryMB}:
   131  	case <-time.After(coretesting.LongWait):
   132  		return errors.New("timed out waiting for facade call Prune to run")
   133  	}
   134  	return nil
   135  }
   136  
   137  // WatchForModelConfigChanges implements Facade
   138  func (f *fakeFacade) WatchForModelConfigChanges() (watcher.NotifyWatcher, error) {
   139  	return f.changesWatcher, nil
   140  }
   141  
   142  // ModelConfig implements Facade
   143  func (f *fakeFacade) ModelConfig() (*config.Config, error) {
   144  	f.gotConfig <- struct{}{}
   145  	return f.modelConfig, nil
   146  }
   147  
   148  func newMockWatcher() *mockWatcher {
   149  	return &mockWatcher{
   150  		stopped: make(chan struct{}),
   151  	}
   152  }
   153  
   154  type mockWatcher struct {
   155  	mu      sync.Mutex
   156  	stopped chan struct{}
   157  }
   158  
   159  func (w *mockWatcher) Kill() {
   160  	w.mu.Lock()
   161  	defer w.mu.Unlock()
   162  	if !w.Stopped() {
   163  		close(w.stopped)
   164  	}
   165  }
   166  
   167  func (w *mockWatcher) Wait() error {
   168  	<-w.stopped
   169  	return nil
   170  }
   171  
   172  func (w *mockWatcher) Stopped() bool {
   173  	select {
   174  	case <-w.stopped:
   175  		return true
   176  	default:
   177  		return false
   178  	}
   179  }
   180  
   181  func newMockNotifyWatcher() *mockNotifyWatcher {
   182  	return &mockNotifyWatcher{
   183  		mockWatcher: newMockWatcher(),
   184  		changes:     make(chan struct{}, 1),
   185  	}
   186  }
   187  
   188  type mockNotifyWatcher struct {
   189  	*mockWatcher
   190  	changes chan struct{}
   191  }
   192  
   193  func (w *mockNotifyWatcher) Changes() watcher.NotifyChannel {
   194  	return w.changes
   195  }