github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/uniter/remotestate/watcher_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package remotestate_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/names"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  	"gopkg.in/juju/charm.v6-unstable"
    13  
    14  	"github.com/juju/juju/apiserver/params"
    15  	"github.com/juju/juju/testing"
    16  	"github.com/juju/juju/watcher"
    17  	"github.com/juju/juju/worker/uniter/remotestate"
    18  )
    19  
    20  type WatcherSuite struct {
    21  	testing.BaseSuite
    22  
    23  	st         *mockState
    24  	leadership *mockLeadershipTracker
    25  	watcher    *remotestate.RemoteStateWatcher
    26  	clock      *testing.Clock
    27  }
    28  
    29  // Duration is arbitrary, we'll trigger the ticker
    30  // by advancing the clock past the duration.
    31  var statusTickDuration = 10 * time.Second
    32  
    33  var _ = gc.Suite(&WatcherSuite{})
    34  
    35  func (s *WatcherSuite) SetUpTest(c *gc.C) {
    36  	s.BaseSuite.SetUpTest(c)
    37  	s.st = &mockState{
    38  		unit: mockUnit{
    39  			tag:  names.NewUnitTag("mysql/0"),
    40  			life: params.Alive,
    41  			service: mockService{
    42  				tag:                   names.NewServiceTag("mysql"),
    43  				life:                  params.Alive,
    44  				curl:                  charm.MustParseURL("cs:trusty/mysql"),
    45  				charmModifiedVersion:  5,
    46  				serviceWatcher:        newMockNotifyWatcher(),
    47  				leaderSettingsWatcher: newMockNotifyWatcher(),
    48  				relationsWatcher:      newMockStringsWatcher(),
    49  			},
    50  			unitWatcher:           newMockNotifyWatcher(),
    51  			addressesWatcher:      newMockNotifyWatcher(),
    52  			configSettingsWatcher: newMockNotifyWatcher(),
    53  			storageWatcher:        newMockStringsWatcher(),
    54  			actionWatcher:         newMockStringsWatcher(),
    55  		},
    56  		relations:                 make(map[names.RelationTag]*mockRelation),
    57  		storageAttachment:         make(map[params.StorageAttachmentId]params.StorageAttachment),
    58  		relationUnitsWatchers:     make(map[names.RelationTag]*mockRelationUnitsWatcher),
    59  		storageAttachmentWatchers: make(map[names.StorageTag]*mockNotifyWatcher),
    60  	}
    61  
    62  	s.leadership = &mockLeadershipTracker{
    63  		claimTicket:  mockTicket{make(chan struct{}, 1), true},
    64  		leaderTicket: mockTicket{make(chan struct{}, 1), true},
    65  		minionTicket: mockTicket{make(chan struct{}, 1), true},
    66  	}
    67  
    68  	s.clock = testing.NewClock(time.Now())
    69  	statusTicker := func() <-chan time.Time {
    70  		return s.clock.After(statusTickDuration)
    71  	}
    72  
    73  	w, err := remotestate.NewWatcher(remotestate.WatcherConfig{
    74  		State:               s.st,
    75  		LeadershipTracker:   s.leadership,
    76  		UnitTag:             s.st.unit.tag,
    77  		UpdateStatusChannel: statusTicker,
    78  	})
    79  	c.Assert(err, jc.ErrorIsNil)
    80  	s.watcher = w
    81  }
    82  
    83  func (s *WatcherSuite) TearDownTest(c *gc.C) {
    84  	if s.watcher != nil {
    85  		s.watcher.Kill()
    86  		err := s.watcher.Wait()
    87  		c.Assert(err, jc.ErrorIsNil)
    88  	}
    89  }
    90  
    91  func (s *WatcherSuite) TestInitialSnapshot(c *gc.C) {
    92  	snap := s.watcher.Snapshot()
    93  	c.Assert(snap, jc.DeepEquals, remotestate.Snapshot{
    94  		Relations: map[int]remotestate.RelationSnapshot{},
    95  		Storage:   map[names.StorageTag]remotestate.StorageSnapshot{},
    96  	})
    97  }
    98  
    99  func (s *WatcherSuite) TestInitialSignal(c *gc.C) {
   100  	// There should not be a remote state change until
   101  	// we've seen all of the top-level notifications.
   102  	s.st.unit.unitWatcher.changes <- struct{}{}
   103  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   104  
   105  	s.st.unit.addressesWatcher.changes <- struct{}{}
   106  	s.st.unit.configSettingsWatcher.changes <- struct{}{}
   107  	s.st.unit.storageWatcher.changes <- []string{}
   108  	s.st.unit.actionWatcher.changes <- []string{}
   109  	s.st.unit.service.serviceWatcher.changes <- struct{}{}
   110  	s.st.unit.service.leaderSettingsWatcher.changes <- struct{}{}
   111  	s.st.unit.service.relationsWatcher.changes <- []string{}
   112  	s.leadership.claimTicket.ch <- struct{}{}
   113  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   114  }
   115  
   116  func signalAll(st *mockState, l *mockLeadershipTracker) {
   117  	st.unit.unitWatcher.changes <- struct{}{}
   118  	st.unit.addressesWatcher.changes <- struct{}{}
   119  	st.unit.configSettingsWatcher.changes <- struct{}{}
   120  	st.unit.storageWatcher.changes <- []string{}
   121  	st.unit.actionWatcher.changes <- []string{}
   122  	st.unit.service.serviceWatcher.changes <- struct{}{}
   123  	st.unit.service.leaderSettingsWatcher.changes <- struct{}{}
   124  	st.unit.service.relationsWatcher.changes <- []string{}
   125  	l.claimTicket.ch <- struct{}{}
   126  }
   127  
   128  func (s *WatcherSuite) TestSnapshot(c *gc.C) {
   129  	signalAll(s.st, s.leadership)
   130  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   131  
   132  	snap := s.watcher.Snapshot()
   133  	c.Assert(snap, jc.DeepEquals, remotestate.Snapshot{
   134  		Life:                  s.st.unit.life,
   135  		Relations:             map[int]remotestate.RelationSnapshot{},
   136  		Storage:               map[names.StorageTag]remotestate.StorageSnapshot{},
   137  		CharmModifiedVersion:  s.st.unit.service.charmModifiedVersion,
   138  		CharmURL:              s.st.unit.service.curl,
   139  		ForceCharmUpgrade:     s.st.unit.service.forceUpgrade,
   140  		ResolvedMode:          s.st.unit.resolved,
   141  		ConfigVersion:         2, // config settings and addresses
   142  		LeaderSettingsVersion: 1,
   143  		Leader:                true,
   144  	})
   145  }
   146  
   147  func (s *WatcherSuite) TestRemoteStateChanged(c *gc.C) {
   148  	assertOneChange := func() {
   149  		assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   150  		assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   151  	}
   152  
   153  	signalAll(s.st, s.leadership)
   154  	assertOneChange()
   155  	initial := s.watcher.Snapshot()
   156  
   157  	s.st.unit.life = params.Dying
   158  	s.st.unit.unitWatcher.changes <- struct{}{}
   159  	assertOneChange()
   160  	c.Assert(s.watcher.Snapshot().Life, gc.Equals, params.Dying)
   161  
   162  	s.st.unit.addressesWatcher.changes <- struct{}{}
   163  	assertOneChange()
   164  	c.Assert(s.watcher.Snapshot().ConfigVersion, gc.Equals, initial.ConfigVersion+1)
   165  
   166  	s.st.unit.configSettingsWatcher.changes <- struct{}{}
   167  	assertOneChange()
   168  	c.Assert(s.watcher.Snapshot().ConfigVersion, gc.Equals, initial.ConfigVersion+2)
   169  
   170  	s.st.unit.storageWatcher.changes <- []string{}
   171  	assertOneChange()
   172  
   173  	s.st.unit.service.forceUpgrade = true
   174  	s.st.unit.service.serviceWatcher.changes <- struct{}{}
   175  	assertOneChange()
   176  	c.Assert(s.watcher.Snapshot().ForceCharmUpgrade, jc.IsTrue)
   177  
   178  	s.st.unit.service.leaderSettingsWatcher.changes <- struct{}{}
   179  	assertOneChange()
   180  	c.Assert(s.watcher.Snapshot().LeaderSettingsVersion, gc.Equals, initial.LeaderSettingsVersion+1)
   181  
   182  	s.st.unit.service.relationsWatcher.changes <- []string{}
   183  	assertOneChange()
   184  
   185  	s.clock.Advance(statusTickDuration + 1)
   186  	assertOneChange()
   187  }
   188  
   189  func (s *WatcherSuite) TestActionsReceived(c *gc.C) {
   190  	signalAll(s.st, s.leadership)
   191  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   192  
   193  	s.st.unit.actionWatcher.changes <- []string{"an-action"}
   194  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   195  	c.Assert(s.watcher.Snapshot().Actions, gc.DeepEquals, []string{"an-action"})
   196  }
   197  
   198  func (s *WatcherSuite) TestClearResolvedMode(c *gc.C) {
   199  	s.st.unit.resolved = params.ResolvedRetryHooks
   200  	signalAll(s.st, s.leadership)
   201  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   202  
   203  	snap := s.watcher.Snapshot()
   204  	c.Assert(snap.ResolvedMode, gc.Equals, params.ResolvedRetryHooks)
   205  
   206  	s.watcher.ClearResolvedMode()
   207  	snap = s.watcher.Snapshot()
   208  	c.Assert(snap.ResolvedMode, gc.Equals, params.ResolvedNone)
   209  }
   210  
   211  func (s *WatcherSuite) TestLeadershipChanged(c *gc.C) {
   212  	s.leadership.claimTicket.result = false
   213  	signalAll(s.st, s.leadership)
   214  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   215  	c.Assert(s.watcher.Snapshot().Leader, jc.IsFalse)
   216  
   217  	s.leadership.leaderTicket.ch <- struct{}{}
   218  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   219  	c.Assert(s.watcher.Snapshot().Leader, jc.IsTrue)
   220  
   221  	s.leadership.minionTicket.ch <- struct{}{}
   222  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   223  	c.Assert(s.watcher.Snapshot().Leader, jc.IsFalse)
   224  }
   225  
   226  func (s *WatcherSuite) TestLeadershipMinionUnchanged(c *gc.C) {
   227  	s.leadership.claimTicket.result = false
   228  	signalAll(s.st, s.leadership)
   229  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   230  
   231  	// Initially minion, so triggering minion should have no effect.
   232  	s.leadership.minionTicket.ch <- struct{}{}
   233  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   234  }
   235  
   236  func (s *WatcherSuite) TestLeadershipLeaderUnchanged(c *gc.C) {
   237  	signalAll(s.st, s.leadership)
   238  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   239  
   240  	// Initially leader, so triggering leader should have no effect.
   241  	s.leadership.leaderTicket.ch <- struct{}{}
   242  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   243  }
   244  
   245  func (s *WatcherSuite) TestStorageChanged(c *gc.C) {
   246  	signalAll(s.st, s.leadership)
   247  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   248  
   249  	storageTag0 := names.NewStorageTag("blob/0")
   250  	storageAttachmentId0 := params.StorageAttachmentId{
   251  		UnitTag:    s.st.unit.tag.String(),
   252  		StorageTag: storageTag0.String(),
   253  	}
   254  	storageTag0Watcher := newMockNotifyWatcher()
   255  	s.st.storageAttachmentWatchers[storageTag0] = storageTag0Watcher
   256  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   257  		UnitTag:    storageAttachmentId0.UnitTag,
   258  		StorageTag: storageAttachmentId0.StorageTag,
   259  		Life:       params.Alive,
   260  		Kind:       params.StorageKindUnknown, // unprovisioned
   261  		Location:   "nowhere",
   262  	}
   263  
   264  	storageTag1 := names.NewStorageTag("blob/1")
   265  	storageAttachmentId1 := params.StorageAttachmentId{
   266  		UnitTag:    s.st.unit.tag.String(),
   267  		StorageTag: storageTag1.String(),
   268  	}
   269  	storageTag1Watcher := newMockNotifyWatcher()
   270  	s.st.storageAttachmentWatchers[storageTag1] = storageTag1Watcher
   271  	s.st.storageAttachment[storageAttachmentId1] = params.StorageAttachment{
   272  		UnitTag:    storageAttachmentId1.UnitTag,
   273  		StorageTag: storageAttachmentId1.StorageTag,
   274  		Life:       params.Dying,
   275  		Kind:       params.StorageKindBlock,
   276  		Location:   "malta",
   277  	}
   278  
   279  	// We should not see any event until the storage attachment watchers
   280  	// return their initial events.
   281  	s.st.unit.storageWatcher.changes <- []string{"blob/0", "blob/1"}
   282  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   283  	storageTag0Watcher.changes <- struct{}{}
   284  	storageTag1Watcher.changes <- struct{}{}
   285  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   286  
   287  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   288  		storageTag0: remotestate.StorageSnapshot{
   289  			Life: params.Alive,
   290  		},
   291  		storageTag1: remotestate.StorageSnapshot{
   292  			Life:     params.Dying,
   293  			Kind:     params.StorageKindBlock,
   294  			Attached: true,
   295  			Location: "malta",
   296  		},
   297  	})
   298  
   299  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   300  		UnitTag:    storageAttachmentId0.UnitTag,
   301  		StorageTag: storageAttachmentId0.StorageTag,
   302  		Life:       params.Dying,
   303  		Kind:       params.StorageKindFilesystem,
   304  		Location:   "somewhere",
   305  	}
   306  	delete(s.st.storageAttachment, storageAttachmentId1)
   307  	storageTag0Watcher.changes <- struct{}{}
   308  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   309  	s.st.unit.storageWatcher.changes <- []string{"blob/1"}
   310  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   311  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   312  		storageTag0: remotestate.StorageSnapshot{
   313  			Life:     params.Dying,
   314  			Attached: true,
   315  			Kind:     params.StorageKindFilesystem,
   316  			Location: "somewhere",
   317  		},
   318  	})
   319  }
   320  
   321  func (s *WatcherSuite) TestStorageUnattachedChanged(c *gc.C) {
   322  	signalAll(s.st, s.leadership)
   323  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   324  
   325  	storageTag0 := names.NewStorageTag("blob/0")
   326  	storageAttachmentId0 := params.StorageAttachmentId{
   327  		UnitTag:    s.st.unit.tag.String(),
   328  		StorageTag: storageTag0.String(),
   329  	}
   330  	storageTag0Watcher := newMockNotifyWatcher()
   331  	s.st.storageAttachmentWatchers[storageTag0] = storageTag0Watcher
   332  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   333  		UnitTag:    storageAttachmentId0.UnitTag,
   334  		StorageTag: storageAttachmentId0.StorageTag,
   335  		Life:       params.Alive,
   336  		Kind:       params.StorageKindUnknown, // unprovisioned
   337  	}
   338  
   339  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   340  	storageTag0Watcher.changes <- struct{}{}
   341  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   342  
   343  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   344  		storageTag0: remotestate.StorageSnapshot{
   345  			Life: params.Alive,
   346  		},
   347  	})
   348  
   349  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   350  		UnitTag:    storageAttachmentId0.UnitTag,
   351  		StorageTag: storageAttachmentId0.StorageTag,
   352  		Life:       params.Dying,
   353  	}
   354  	// The storage is still unattached; triggering the storage-specific
   355  	// watcher should not cause any event to be emitted.
   356  	storageTag0Watcher.changes <- struct{}{}
   357  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   358  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   359  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   360  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   361  		storageTag0: remotestate.StorageSnapshot{
   362  			Life: params.Dying,
   363  		},
   364  	})
   365  }
   366  
   367  func (s *WatcherSuite) TestStorageAttachmentRemoved(c *gc.C) {
   368  	signalAll(s.st, s.leadership)
   369  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   370  
   371  	storageTag0 := names.NewStorageTag("blob/0")
   372  	storageAttachmentId0 := params.StorageAttachmentId{
   373  		UnitTag:    s.st.unit.tag.String(),
   374  		StorageTag: storageTag0.String(),
   375  	}
   376  	storageTag0Watcher := newMockNotifyWatcher()
   377  	s.st.storageAttachmentWatchers[storageTag0] = storageTag0Watcher
   378  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   379  		UnitTag:    storageAttachmentId0.UnitTag,
   380  		StorageTag: storageAttachmentId0.StorageTag,
   381  		Life:       params.Dying,
   382  		Kind:       params.StorageKindUnknown, // unprovisioned
   383  	}
   384  
   385  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   386  	storageTag0Watcher.changes <- struct{}{}
   387  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   388  
   389  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   390  		storageTag0: remotestate.StorageSnapshot{
   391  			Life: params.Dying,
   392  		},
   393  	})
   394  
   395  	// Removing the storage attachment and then triggering the storage-
   396  	// specific watcher should not cause an event to be emitted, but it
   397  	// will cause that watcher to stop running. Triggering the top-level
   398  	// storage watcher will remove it and update the snapshot.
   399  	delete(s.st.storageAttachment, storageAttachmentId0)
   400  	storageTag0Watcher.changes <- struct{}{}
   401  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   402  	c.Assert(storageTag0Watcher.Stopped(), jc.IsTrue)
   403  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   404  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   405  	c.Assert(s.watcher.Snapshot().Storage, gc.HasLen, 0)
   406  }
   407  
   408  func (s *WatcherSuite) TestStorageChangedNotFoundInitially(c *gc.C) {
   409  	signalAll(s.st, s.leadership)
   410  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   411  
   412  	// blob/0 is initially in state, but is removed between the
   413  	// watcher signal and the uniter querying it. This should
   414  	// not cause the watcher to raise an error.
   415  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   416  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   417  	c.Assert(s.watcher.Snapshot().Storage, gc.HasLen, 0)
   418  }
   419  
   420  func (s *WatcherSuite) TestRelationsChanged(c *gc.C) {
   421  	signalAll(s.st, s.leadership)
   422  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   423  
   424  	relationTag := names.NewRelationTag("mysql:peer")
   425  	s.st.relations[relationTag] = &mockRelation{
   426  		id: 123, life: params.Alive,
   427  	}
   428  	s.st.relationUnitsWatchers[relationTag] = newMockRelationUnitsWatcher()
   429  	s.st.unit.service.relationsWatcher.changes <- []string{relationTag.Id()}
   430  
   431  	// There should not be any signal until the relation units watcher has
   432  	// returned its initial event also.
   433  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   434  	s.st.relationUnitsWatchers[relationTag].changes <- watcher.RelationUnitsChange{
   435  		Changed: map[string]watcher.UnitSettings{"mysql/1": {1}, "mysql/2": {2}},
   436  	}
   437  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   438  	c.Assert(
   439  		s.watcher.Snapshot().Relations,
   440  		jc.DeepEquals,
   441  		map[int]remotestate.RelationSnapshot{
   442  			123: remotestate.RelationSnapshot{
   443  				Life:    params.Alive,
   444  				Members: map[string]int64{"mysql/1": 1, "mysql/2": 2},
   445  			},
   446  		},
   447  	)
   448  
   449  	// If a relation is known, then updating it does not require any input
   450  	// from the relation units watcher.
   451  	s.st.relations[relationTag].life = params.Dying
   452  	s.st.unit.service.relationsWatcher.changes <- []string{relationTag.Id()}
   453  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   454  	c.Assert(s.watcher.Snapshot().Relations[123].Life, gc.Equals, params.Dying)
   455  
   456  	// If a relation is not found, then it should be removed from the
   457  	// snapshot and its relation units watcher stopped.
   458  	delete(s.st.relations, relationTag)
   459  	s.st.unit.service.relationsWatcher.changes <- []string{relationTag.Id()}
   460  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   461  	c.Assert(s.watcher.Snapshot().Relations, gc.HasLen, 0)
   462  	c.Assert(s.st.relationUnitsWatchers[relationTag].Stopped(), jc.IsTrue)
   463  }
   464  
   465  func (s *WatcherSuite) TestRelationUnitsChanged(c *gc.C) {
   466  	signalAll(s.st, s.leadership)
   467  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   468  
   469  	relationTag := names.NewRelationTag("mysql:peer")
   470  	s.st.relations[relationTag] = &mockRelation{
   471  		id: 123, life: params.Alive,
   472  	}
   473  	s.st.relationUnitsWatchers[relationTag] = newMockRelationUnitsWatcher()
   474  
   475  	s.st.unit.service.relationsWatcher.changes <- []string{relationTag.Id()}
   476  	s.st.relationUnitsWatchers[relationTag].changes <- watcher.RelationUnitsChange{
   477  		Changed: map[string]watcher.UnitSettings{"mysql/1": {1}},
   478  	}
   479  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   480  
   481  	s.st.relationUnitsWatchers[relationTag].changes <- watcher.RelationUnitsChange{
   482  		Changed: map[string]watcher.UnitSettings{"mysql/1": {2}, "mysql/2": {1}},
   483  	}
   484  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   485  	c.Assert(
   486  		s.watcher.Snapshot().Relations[123].Members,
   487  		jc.DeepEquals,
   488  		map[string]int64{"mysql/1": 2, "mysql/2": 1},
   489  	)
   490  
   491  	s.st.relationUnitsWatchers[relationTag].changes <- watcher.RelationUnitsChange{
   492  		Departed: []string{"mysql/1", "mysql/42"},
   493  	}
   494  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   495  	c.Assert(
   496  		s.watcher.Snapshot().Relations[123].Members,
   497  		jc.DeepEquals,
   498  		map[string]int64{"mysql/2": 1},
   499  	)
   500  }
   501  
   502  func (s *WatcherSuite) TestUpdateStatusTicker(c *gc.C) {
   503  	signalAll(s.st, s.leadership)
   504  	initial := s.watcher.Snapshot()
   505  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   506  
   507  	// Advance the clock past the trigger time.
   508  	s.clock.Advance(11 * time.Second)
   509  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   510  	c.Assert(s.watcher.Snapshot().UpdateStatusVersion, gc.Equals, initial.UpdateStatusVersion+1)
   511  
   512  	// Advance again but not past the trigger time.
   513  	s.clock.Advance(6 * time.Second)
   514  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "unexpected remote state change")
   515  	c.Assert(s.watcher.Snapshot().UpdateStatusVersion, gc.Equals, initial.UpdateStatusVersion+1)
   516  
   517  	// And we hit the trigger time.
   518  	s.clock.Advance(5 * time.Second)
   519  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   520  	c.Assert(s.watcher.Snapshot().UpdateStatusVersion, gc.Equals, initial.UpdateStatusVersion+2)
   521  }