github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/testing/suite.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package testing
     5  
     6  import (
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/juju/clock/testclock"
    11  	jujutesting "github.com/juju/testing"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/juju/names.v2"
    15  	retry "gopkg.in/retry.v1"
    16  
    17  	"github.com/juju/juju/cloud"
    18  	"github.com/juju/juju/environs/config"
    19  	"github.com/juju/juju/state"
    20  	statewatcher "github.com/juju/juju/state/watcher"
    21  	"github.com/juju/juju/testing"
    22  	"github.com/juju/juju/testing/factory"
    23  )
    24  
    25  var _ = gc.Suite(&StateSuite{})
    26  
    27  // StateSuite provides setup and teardown for tests that require a
    28  // state.State.
    29  type StateSuite struct {
    30  	jujutesting.MgoSuite
    31  	testing.BaseSuite
    32  	NewPolicy                 state.NewPolicyFunc
    33  	Controller                *state.Controller
    34  	StatePool                 *state.StatePool
    35  	State                     *state.State
    36  	Model                     *state.Model
    37  	Owner                     names.UserTag
    38  	Factory                   *factory.Factory
    39  	InitialConfig             *config.Config
    40  	InitialTime               time.Time
    41  	ControllerConfig          map[string]interface{}
    42  	ControllerInheritedConfig map[string]interface{}
    43  	RegionConfig              cloud.RegionConfig
    44  	Clock                     *testclock.Clock
    45  	txnSyncNotify             chan struct{}
    46  	modelWatcherIdle          chan string
    47  	modelWatcherMutex         *sync.Mutex
    48  }
    49  
    50  func (s *StateSuite) SetUpSuite(c *gc.C) {
    51  	s.MgoSuite.SetUpSuite(c)
    52  	s.BaseSuite.SetUpSuite(c)
    53  }
    54  
    55  func (s *StateSuite) TearDownSuite(c *gc.C) {
    56  	s.BaseSuite.TearDownSuite(c)
    57  	s.MgoSuite.TearDownSuite(c)
    58  }
    59  
    60  func (s *StateSuite) SetUpTest(c *gc.C) {
    61  	s.MgoSuite.SetUpTest(c)
    62  	s.BaseSuite.SetUpTest(c)
    63  
    64  	s.txnSyncNotify = make(chan struct{})
    65  	s.modelWatcherIdle = nil
    66  	s.modelWatcherMutex = &sync.Mutex{}
    67  	s.PatchValue(&statewatcher.TxnPollNotifyFunc, s.txnNotifyFunc)
    68  	s.PatchValue(&statewatcher.HubWatcherIdleFunc, s.hubWatcherIdleFunc)
    69  
    70  	s.Owner = names.NewLocalUserTag("test-admin")
    71  	initialTime := s.InitialTime
    72  	if initialTime.IsZero() {
    73  		initialTime = testing.NonZeroTime()
    74  	}
    75  	s.Clock = testclock.NewClock(initialTime)
    76  	// Patch the polling policy of the primary txn watcher for the
    77  	// state pool. Since we are using a testing clock the StartSync
    78  	// method on the state object advances the clock one second.
    79  	// Make the txn poller use a standard one second poll interval.
    80  	s.PatchValue(
    81  		&statewatcher.PollStrategy,
    82  		retry.Exponential{
    83  			Initial: time.Second,
    84  			Factor:  1.0,
    85  		})
    86  
    87  	s.Controller = InitializeWithArgs(c, InitializeArgs{
    88  		Owner:                     s.Owner,
    89  		InitialConfig:             s.InitialConfig,
    90  		ControllerConfig:          s.ControllerConfig,
    91  		ControllerInheritedConfig: s.ControllerInheritedConfig,
    92  		RegionConfig:              s.RegionConfig,
    93  		NewPolicy:                 s.NewPolicy,
    94  		Clock:                     s.Clock,
    95  	})
    96  	s.AddCleanup(func(*gc.C) {
    97  		s.Controller.Close()
    98  		close(s.txnSyncNotify)
    99  	})
   100  	s.StatePool = s.Controller.StatePool()
   101  	s.State = s.StatePool.SystemState()
   102  	model, err := s.State.Model()
   103  	c.Assert(err, jc.ErrorIsNil)
   104  	s.Model = model
   105  
   106  	s.Factory = factory.NewFactory(s.State, s.StatePool)
   107  }
   108  
   109  func (s *StateSuite) TearDownTest(c *gc.C) {
   110  	s.BaseSuite.TearDownTest(c)
   111  	s.MgoSuite.TearDownTest(c)
   112  }
   113  
   114  func (s *StateSuite) txnNotifyFunc() {
   115  	select {
   116  	case s.txnSyncNotify <- struct{}{}:
   117  		// Try to send something down the channel.
   118  	default:
   119  		// However don't get stressed if noone is listening.
   120  	}
   121  }
   122  
   123  func (s *StateSuite) hubWatcherIdleFunc(modelUUID string) {
   124  	s.modelWatcherMutex.Lock()
   125  	idleChan := s.modelWatcherIdle
   126  	s.modelWatcherMutex.Unlock()
   127  	if idleChan == nil {
   128  		return
   129  	}
   130  	idleChan <- modelUUID
   131  }
   132  
   133  // WaitForNextSync repeatedly advances the testing clock
   134  // with short waits between until the txn poller doesn't find
   135  // any more changes.
   136  func (s *StateSuite) WaitForNextSync(c *gc.C) {
   137  	done := make(chan struct{})
   138  	go func() {
   139  		<-s.txnSyncNotify
   140  		close(done)
   141  	}()
   142  	timeout := time.After(jujutesting.LongWait)
   143  	for {
   144  		s.Clock.Advance(time.Second)
   145  		loop := time.After(10 * time.Millisecond)
   146  		select {
   147  		case <-done:
   148  			return
   149  		case <-loop:
   150  		case <-timeout:
   151  			c.Fatal("no sync event sent, is the watcher dead?")
   152  		}
   153  	}
   154  }
   155  
   156  // WaitForModelWatchersIdle firstly waits for the txn poller to process
   157  // all pending changes, then waits for the hub watcher on the state object
   158  // to have finished processing all those events.
   159  func (s *StateSuite) WaitForModelWatchersIdle(c *gc.C, modelUUID string) {
   160  	c.Logf("waiting for model %s to be idle", modelUUID)
   161  	s.modelWatcherMutex.Lock()
   162  	idleChan := make(chan string)
   163  	s.modelWatcherIdle = idleChan
   164  	s.modelWatcherMutex.Unlock()
   165  	s.WaitForNextSync(c)
   166  
   167  	defer func() {
   168  		s.modelWatcherMutex.Lock()
   169  		s.modelWatcherIdle = nil
   170  		s.modelWatcherMutex.Unlock()
   171  		// Clear out any pending events.
   172  		for {
   173  			select {
   174  			case <-idleChan:
   175  			default:
   176  				return
   177  			}
   178  		}
   179  	}()
   180  
   181  	timeout := time.After(jujutesting.LongWait)
   182  	for {
   183  		s.Clock.Advance(10 * time.Millisecond)
   184  		loop := time.After(10 * time.Millisecond)
   185  		select {
   186  		case <-loop:
   187  		case uuid := <-idleChan:
   188  			if uuid == modelUUID {
   189  				return
   190  			} else {
   191  				c.Logf("model %s is idle", uuid)
   192  			}
   193  		case <-timeout:
   194  			c.Fatal("no sync event sent, is the watcher dead?")
   195  		}
   196  	}
   197  }