github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/presence/presence_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package presence_test
     5  
     6  import (
     7  	"strconv"
     8  	stdtesting "testing"
     9  	"time"
    10  
    11  	"labix.org/v2/mgo"
    12  	gc "launchpad.net/gocheck"
    13  	"launchpad.net/tomb"
    14  
    15  	"github.com/juju/juju/state/presence"
    16  	"github.com/juju/juju/testing"
    17  )
    18  
    19  func TestPackage(t *stdtesting.T) {
    20  	testing.MgoTestPackage(t)
    21  }
    22  
    23  type PresenceSuite struct {
    24  	testing.MgoSuite
    25  	testing.BaseSuite
    26  	presence *mgo.Collection
    27  	pings    *mgo.Collection
    28  }
    29  
    30  var _ = gc.Suite(&PresenceSuite{})
    31  
    32  func (s *PresenceSuite) SetUpSuite(c *gc.C) {
    33  	s.BaseSuite.SetUpSuite(c)
    34  	s.MgoSuite.SetUpSuite(c)
    35  }
    36  
    37  func (s *PresenceSuite) TearDownSuite(c *gc.C) {
    38  	s.MgoSuite.TearDownSuite(c)
    39  	s.BaseSuite.TearDownSuite(c)
    40  }
    41  
    42  func (s *PresenceSuite) SetUpTest(c *gc.C) {
    43  	s.BaseSuite.SetUpTest(c)
    44  	s.MgoSuite.SetUpTest(c)
    45  
    46  	db := s.MgoSuite.Session.DB("presence")
    47  	s.presence = db.C("presence")
    48  	s.pings = db.C("presence.pings")
    49  
    50  	presence.FakeTimeSlot(0)
    51  }
    52  
    53  func (s *PresenceSuite) TearDownTest(c *gc.C) {
    54  	s.MgoSuite.TearDownTest(c)
    55  	s.BaseSuite.TearDownTest(c)
    56  
    57  	presence.RealTimeSlot()
    58  	presence.RealPeriod()
    59  }
    60  
    61  func assertChange(c *gc.C, watch <-chan presence.Change, want presence.Change) {
    62  	select {
    63  	case got := <-watch:
    64  		if got != want {
    65  			c.Fatalf("watch reported %v, want %v", got, want)
    66  		}
    67  	case <-time.After(testing.LongWait):
    68  		c.Fatalf("watch reported nothing, want %v", want)
    69  	}
    70  }
    71  
    72  func assertNoChange(c *gc.C, watch <-chan presence.Change) {
    73  	select {
    74  	case got := <-watch:
    75  		c.Fatalf("watch reported %v, want nothing", got)
    76  	case <-time.After(testing.ShortWait):
    77  	}
    78  }
    79  
    80  func assertAlive(c *gc.C, w *presence.Watcher, key string, alive bool) {
    81  	alive, err := w.Alive("a")
    82  	c.Assert(err, gc.IsNil)
    83  	c.Assert(alive, gc.Equals, alive)
    84  }
    85  
    86  func (s *PresenceSuite) TestErrAndDead(c *gc.C) {
    87  	w := presence.NewWatcher(s.presence)
    88  	defer w.Stop()
    89  
    90  	c.Assert(w.Err(), gc.Equals, tomb.ErrStillAlive)
    91  	select {
    92  	case <-w.Dead():
    93  		c.Fatalf("Dead channel fired unexpectedly")
    94  	default:
    95  	}
    96  	c.Assert(w.Stop(), gc.IsNil)
    97  	c.Assert(w.Err(), gc.IsNil)
    98  	select {
    99  	case <-w.Dead():
   100  	default:
   101  		c.Fatalf("Dead channel should have fired")
   102  	}
   103  }
   104  
   105  func (s *PresenceSuite) TestAliveError(c *gc.C) {
   106  	w := presence.NewWatcher(s.presence)
   107  	c.Assert(w.Stop(), gc.IsNil)
   108  
   109  	alive, err := w.Alive("a")
   110  	c.Assert(err, gc.ErrorMatches, ".*: watcher is dying")
   111  	c.Assert(alive, gc.Equals, false)
   112  }
   113  
   114  func (s *PresenceSuite) TestWorkflow(c *gc.C) {
   115  	w := presence.NewWatcher(s.presence)
   116  	pa := presence.NewPinger(s.presence, "a")
   117  	pb := presence.NewPinger(s.presence, "b")
   118  	defer w.Stop()
   119  	defer pa.Stop()
   120  	defer pb.Stop()
   121  
   122  	assertAlive(c, w, "a", false)
   123  	assertAlive(c, w, "b", false)
   124  
   125  	// Buffer one entry to avoid blocking the watcher here.
   126  	cha := make(chan presence.Change, 1)
   127  	chb := make(chan presence.Change, 1)
   128  	w.Watch("a", cha)
   129  	w.Watch("b", chb)
   130  
   131  	// Initial events with current status.
   132  	assertChange(c, cha, presence.Change{"a", false})
   133  	assertChange(c, chb, presence.Change{"b", false})
   134  
   135  	w.StartSync()
   136  	assertNoChange(c, cha)
   137  	assertNoChange(c, chb)
   138  
   139  	c.Assert(pa.Start(), gc.IsNil)
   140  
   141  	w.StartSync()
   142  	assertChange(c, cha, presence.Change{"a", true})
   143  	assertNoChange(c, cha)
   144  	assertNoChange(c, chb)
   145  
   146  	assertAlive(c, w, "a", true)
   147  	assertAlive(c, w, "b", false)
   148  
   149  	// Changes while the channel is out are not observed.
   150  	w.Unwatch("a", cha)
   151  	assertNoChange(c, cha)
   152  	pa.Kill()
   153  	w.Sync()
   154  	pa = presence.NewPinger(s.presence, "a")
   155  	pa.Start()
   156  	w.StartSync()
   157  	assertNoChange(c, cha)
   158  
   159  	// We can still query it manually, though.
   160  	assertAlive(c, w, "a", true)
   161  	assertAlive(c, w, "b", false)
   162  
   163  	// Initial positive event. No refresh needed.
   164  	w.Watch("a", cha)
   165  	assertChange(c, cha, presence.Change{"a", true})
   166  
   167  	c.Assert(pb.Start(), gc.IsNil)
   168  
   169  	w.StartSync()
   170  	assertChange(c, chb, presence.Change{"b", true})
   171  	assertNoChange(c, cha)
   172  	assertNoChange(c, chb)
   173  
   174  	c.Assert(pa.Stop(), gc.IsNil)
   175  
   176  	w.StartSync()
   177  	assertNoChange(c, cha)
   178  	assertNoChange(c, chb)
   179  
   180  	// pb is running, pa isn't.
   181  	c.Assert(pa.Kill(), gc.IsNil)
   182  	c.Assert(pb.Kill(), gc.IsNil)
   183  
   184  	w.StartSync()
   185  	assertChange(c, cha, presence.Change{"a", false})
   186  	assertChange(c, chb, presence.Change{"b", false})
   187  
   188  	c.Assert(w.Stop(), gc.IsNil)
   189  }
   190  
   191  func (s *PresenceSuite) TestScale(c *gc.C) {
   192  	const N = 1000
   193  	var ps []*presence.Pinger
   194  	defer func() {
   195  		for _, p := range ps {
   196  			p.Stop()
   197  		}
   198  	}()
   199  
   200  	c.Logf("Starting %d pingers...", N)
   201  	for i := 0; i < N; i++ {
   202  		p := presence.NewPinger(s.presence, strconv.Itoa(i))
   203  		c.Assert(p.Start(), gc.IsNil)
   204  		ps = append(ps, p)
   205  	}
   206  
   207  	c.Logf("Killing odd ones...")
   208  	for i := 1; i < N; i += 2 {
   209  		c.Assert(ps[i].Kill(), gc.IsNil)
   210  	}
   211  
   212  	c.Logf("Checking who's still alive...")
   213  	w := presence.NewWatcher(s.presence)
   214  	defer w.Stop()
   215  	w.Sync()
   216  	ch := make(chan presence.Change)
   217  	for i := 0; i < N; i++ {
   218  		k := strconv.Itoa(i)
   219  		w.Watch(k, ch)
   220  		if i%2 == 0 {
   221  			assertChange(c, ch, presence.Change{k, true})
   222  		} else {
   223  			assertChange(c, ch, presence.Change{k, false})
   224  		}
   225  	}
   226  }
   227  
   228  func (s *PresenceSuite) TestExpiry(c *gc.C) {
   229  	w := presence.NewWatcher(s.presence)
   230  	p := presence.NewPinger(s.presence, "a")
   231  	defer w.Stop()
   232  	defer p.Stop()
   233  
   234  	ch := make(chan presence.Change)
   235  	w.Watch("a", ch)
   236  	assertChange(c, ch, presence.Change{"a", false})
   237  
   238  	c.Assert(p.Start(), gc.IsNil)
   239  	w.StartSync()
   240  	assertChange(c, ch, presence.Change{"a", true})
   241  
   242  	// Still alive in previous slot.
   243  	presence.FakeTimeSlot(1)
   244  	w.StartSync()
   245  	assertNoChange(c, ch)
   246  
   247  	// Two last slots are empty.
   248  	presence.FakeTimeSlot(2)
   249  	w.StartSync()
   250  	assertChange(c, ch, presence.Change{"a", false})
   251  
   252  	// Already dead so killing isn't noticed.
   253  	p.Kill()
   254  	w.StartSync()
   255  	assertNoChange(c, ch)
   256  }
   257  
   258  func (s *PresenceSuite) TestWatchPeriod(c *gc.C) {
   259  	presence.FakePeriod(1)
   260  	presence.RealTimeSlot()
   261  
   262  	w := presence.NewWatcher(s.presence)
   263  	p := presence.NewPinger(s.presence, "a")
   264  	defer w.Stop()
   265  	defer p.Stop()
   266  
   267  	ch := make(chan presence.Change)
   268  	w.Watch("a", ch)
   269  	assertChange(c, ch, presence.Change{"a", false})
   270  
   271  	// A single ping.
   272  	c.Assert(p.Start(), gc.IsNil)
   273  	c.Assert(p.Stop(), gc.IsNil)
   274  
   275  	// Wait for next periodic refresh.
   276  	time.Sleep(1 * time.Second)
   277  	assertChange(c, ch, presence.Change{"a", true})
   278  }
   279  
   280  func (s *PresenceSuite) TestWatchUnwatchOnQueue(c *gc.C) {
   281  	w := presence.NewWatcher(s.presence)
   282  	ch := make(chan presence.Change)
   283  	for i := 0; i < 100; i++ {
   284  		key := strconv.Itoa(i)
   285  		c.Logf("Adding %q", key)
   286  		w.Watch(key, ch)
   287  	}
   288  	for i := 1; i < 100; i += 2 {
   289  		key := strconv.Itoa(i)
   290  		c.Logf("Removing %q", key)
   291  		w.Unwatch(key, ch)
   292  	}
   293  	alive := make(map[string]bool)
   294  	for i := 0; i < 50; i++ {
   295  		change := <-ch
   296  		c.Logf("Got change for %q: %v", change.Key, change.Alive)
   297  		alive[change.Key] = change.Alive
   298  	}
   299  	for i := 0; i < 100; i += 2 {
   300  		key := strconv.Itoa(i)
   301  		c.Logf("Checking %q...", key)
   302  		c.Assert(alive[key], gc.Equals, false)
   303  	}
   304  }
   305  
   306  func (s *PresenceSuite) TestRestartWithoutGaps(c *gc.C) {
   307  	p := presence.NewPinger(s.presence, "a")
   308  	c.Assert(p.Start(), gc.IsNil)
   309  	defer p.Stop()
   310  
   311  	done := make(chan bool)
   312  	go func() {
   313  		stop := false
   314  		for !stop {
   315  			if !c.Check(p.Stop(), gc.IsNil) {
   316  				break
   317  			}
   318  			if !c.Check(p.Start(), gc.IsNil) {
   319  				break
   320  			}
   321  			select {
   322  			case stop = <-done:
   323  			default:
   324  			}
   325  		}
   326  	}()
   327  	go func() {
   328  		stop := false
   329  		for !stop {
   330  			w := presence.NewWatcher(s.presence)
   331  			w.Sync()
   332  			alive, err := w.Alive("a")
   333  			c.Check(w.Stop(), gc.IsNil)
   334  			if !c.Check(err, gc.IsNil) || !c.Check(alive, gc.Equals, true) {
   335  				break
   336  			}
   337  			select {
   338  			case stop = <-done:
   339  			default:
   340  			}
   341  		}
   342  	}()
   343  	// TODO(jam): This forceful delay of 500ms sounds like a bad test,
   344  	//  since we always sleep for the full timeout
   345  	time.Sleep(500 * time.Millisecond)
   346  	done <- true
   347  	done <- true
   348  }
   349  
   350  func (s *PresenceSuite) TestPingerPeriodAndResilience(c *gc.C) {
   351  	// This test verifies both the periodic pinging,
   352  	// and also a great property of the design: deaths
   353  	// also expire, which means erroneous scenarios are
   354  	// automatically recovered from.
   355  
   356  	const period = 1
   357  	presence.FakePeriod(period)
   358  	presence.RealTimeSlot()
   359  
   360  	w := presence.NewWatcher(s.presence)
   361  	p1 := presence.NewPinger(s.presence, "a")
   362  	p2 := presence.NewPinger(s.presence, "a")
   363  	defer w.Stop()
   364  	defer p1.Stop()
   365  	defer p2.Stop()
   366  
   367  	// Start p1 and let it go on.
   368  	c.Assert(p1.Start(), gc.IsNil)
   369  
   370  	w.Sync()
   371  	assertAlive(c, w, "a", true)
   372  
   373  	// Start and kill p2, which will temporarily
   374  	// invalidate p1 and set the key as dead.
   375  	c.Assert(p2.Start(), gc.IsNil)
   376  	c.Assert(p2.Kill(), gc.IsNil)
   377  
   378  	w.Sync()
   379  	assertAlive(c, w, "a", false)
   380  
   381  	// Wait for two periods, and check again. Since
   382  	// p1 is still alive, p2's death will expire and
   383  	// the key will come back.
   384  	time.Sleep(period * 2 * time.Second)
   385  
   386  	w.Sync()
   387  	assertAlive(c, w, "a", true)
   388  }
   389  
   390  func (s *PresenceSuite) TestStartSync(c *gc.C) {
   391  	w := presence.NewWatcher(s.presence)
   392  	p := presence.NewPinger(s.presence, "a")
   393  	defer w.Stop()
   394  	defer p.Stop()
   395  
   396  	ch := make(chan presence.Change)
   397  	w.Watch("a", ch)
   398  	assertChange(c, ch, presence.Change{"a", false})
   399  
   400  	c.Assert(p.Start(), gc.IsNil)
   401  
   402  	done := make(chan bool)
   403  	go func() {
   404  		w.StartSync()
   405  		w.StartSync()
   406  		w.StartSync()
   407  		done <- true
   408  	}()
   409  
   410  	select {
   411  	case <-done:
   412  	case <-time.After(testing.LongWait):
   413  		c.Fatalf("StartSync failed to return")
   414  	}
   415  
   416  	assertChange(c, ch, presence.Change{"a", true})
   417  }
   418  
   419  func (s *PresenceSuite) TestSync(c *gc.C) {
   420  	w := presence.NewWatcher(s.presence)
   421  	p := presence.NewPinger(s.presence, "a")
   422  	defer w.Stop()
   423  	defer p.Stop()
   424  
   425  	ch := make(chan presence.Change)
   426  	w.Watch("a", ch)
   427  	assertChange(c, ch, presence.Change{"a", false})
   428  
   429  	// Nothing to do here.
   430  	w.Sync()
   431  
   432  	c.Assert(p.Start(), gc.IsNil)
   433  
   434  	done := make(chan bool)
   435  	go func() {
   436  		w.Sync()
   437  		done <- true
   438  	}()
   439  
   440  	select {
   441  	case <-done:
   442  		c.Fatalf("Sync returned too early")
   443  		// Note(jam): This used to wait 200ms to ensure that
   444  		// Sync was actually blocked waiting for a presence
   445  		// change. Is ShortWait long enough for this assurance?
   446  	case <-time.After(testing.ShortWait):
   447  	}
   448  
   449  	assertChange(c, ch, presence.Change{"a", true})
   450  
   451  	select {
   452  	case <-done:
   453  	case <-time.After(testing.LongWait):
   454  		c.Fatalf("Sync failed to returned")
   455  	}
   456  }
   457  
   458  func (s *PresenceSuite) TestFindAllBeings(c *gc.C) {
   459  	w := presence.NewWatcher(s.presence)
   460  	p := presence.NewPinger(s.presence, "a")
   461  	defer w.Stop()
   462  	defer p.Stop()
   463  
   464  	ch := make(chan presence.Change)
   465  	w.Watch("a", ch)
   466  	assertChange(c, ch, presence.Change{"a", false})
   467  	c.Assert(p.Start(), gc.IsNil)
   468  	done := make(chan bool)
   469  	go func() {
   470  		w.Sync()
   471  		done <- true
   472  	}()
   473  	assertChange(c, ch, presence.Change{"a", true})
   474  	results, err := presence.FindAllBeings(w)
   475  	c.Assert(err, gc.IsNil)
   476  	c.Assert(results, gc.HasLen, 1)
   477  	select {
   478  	case <-done:
   479  	case <-time.After(testing.LongWait):
   480  		c.Fatalf("Sync failed to returned")
   481  	}
   482  }