<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state_test

import (
	"sort"
	"time"

	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/errors"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/testing"
	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/testing/checkers"
)

type RelationUnitSuite struct {
	ConnSuite
}

var _ = gc.Suite(&RelationUnitSuite{})

func (s *RelationUnitSuite) assertInScope(c *gc.C, ru *state.RelationUnit, inScope bool) {
	ok, err := ru.InScope()
	c.Assert(err, gc.IsNil)
	c.Assert(ok, gc.Equals, inScope)
}

func (s *RelationUnitSuite) TestReadSettingsErrors(c *gc.C) {
	riak, err := s.State.AddService("riak", s.AddTestingCharm(c, "riak"))
	c.Assert(err, gc.IsNil)
	u0, err := riak.AddUnit()
	c.Assert(err, gc.IsNil)
	riakEP, err := riak.Endpoint("ring")
	c.Assert(err, gc.IsNil)
	rel, err := s.State.EndpointsRelation(riakEP)
	c.Assert(err, gc.IsNil)
	ru0, err := rel.Unit(u0)
	c.Assert(err, gc.IsNil)

	_, err = ru0.ReadSettings("nonsense")
	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "nonsense" in relation "riak:ring": "nonsense" is not a valid unit name`)
	_, err = ru0.ReadSettings("unknown/0")
	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "unknown/0" in relation "riak:ring": service "unknown" is not a member of "riak:ring"`)
	_, err = ru0.ReadSettings("riak/pressure")
	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/pressure" in relation "riak:ring": "riak/pressure" is not a valid unit name`)
	_, err = ru0.ReadSettings("riak/1")
	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/1" in relation "riak:ring": settings not found`)
}

func (s *RelationUnitSuite) TestPeerSettings(c *gc.C) {
	pr := NewPeerRelation(c, &s.ConnSuite)
	rus := RUs{pr.ru0, pr.ru1}

	// Check missing settings cannot be read by any RU.
	for _, ru := range rus {
		_, err := ru.ReadSettings("riak/0")
		c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/0" in relation "riak:ring": settings not found`)
	}

	// Add settings for one RU.
	s.assertInScope(c, pr.ru0, false)
	err := pr.ru0.EnterScope(map[string]interface{}{"gene": "kelly"})
	c.Assert(err, gc.IsNil)
	node, err := pr.ru0.Settings()
	c.Assert(err, gc.IsNil)
	node.Set("meme", "socially-awkward-penguin")
	_, err = node.Write()
	c.Assert(err, gc.IsNil)
	normal := map[string]interface{}{
		"gene": "kelly",
		"meme": "socially-awkward-penguin",
	}

	// Check settings can be read by every RU.
	assertSettings := func(u *state.Unit, expect map[string]interface{}) {
		for _, ru := range rus {
			m, err := ru.ReadSettings(u.Name())
			c.Assert(err, gc.IsNil)
			c.Assert(m, gc.DeepEquals, expect)
		}
	}
	assertSettings(pr.u0, normal)
	s.assertInScope(c, pr.ru0, true)

	// Check that EnterScope when scope already entered does not touch
	// settings at all.
	changed := map[string]interface{}{"foo": "bar"}
	err = pr.ru0.EnterScope(changed)
	c.Assert(err, gc.IsNil)
	assertSettings(pr.u0, normal)
	s.assertInScope(c, pr.ru0, true)

	// Leave scope, check settings are still as accessible as before.
	err = pr.ru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	assertSettings(pr.u0, normal)
	s.assertInScope(c, pr.ru0, false)

	// Re-enter scope wih changed settings, and check they completely overwrite
	// the old ones.
	err = pr.ru0.EnterScope(changed)
	c.Assert(err, gc.IsNil)
	assertSettings(pr.u0, changed)
	s.assertInScope(c, pr.ru0, true)

	// Leave and re-enter with nil nettings, and check they overwrite to become
	// an empty map.
	err = pr.ru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru0, false)
	err = pr.ru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	assertSettings(pr.u0, map[string]interface{}{})
	s.assertInScope(c, pr.ru0, true)

	// Check that entering scope for the first time with nil settings works correctly.
	s.assertInScope(c, pr.ru1, false)
	err = pr.ru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	assertSettings(pr.u1, map[string]interface{}{})
	s.assertInScope(c, pr.ru1, true)
}

func (s *RelationUnitSuite) TestProReqSettings(c *gc.C) {
	prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal)
	rus := RUs{prr.pru0, prr.pru1, prr.rru0, prr.rru1}

	// Check missing settings cannot be read by any RU.
	for _, ru := range rus {
		_, err := ru.ReadSettings("mysql/0")
		c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "mysql/0" in relation "wordpress:db mysql:server": settings not found`)
	}

	// Add settings for one RU.
	s.assertInScope(c, prr.pru0, false)
	err := prr.pru0.EnterScope(map[string]interface{}{"gene": "simmons"})
	c.Assert(err, gc.IsNil)
	node, err := prr.pru0.Settings()
	c.Assert(err, gc.IsNil)
	node.Set("meme", "foul-bachelor-frog")
	_, err = node.Write()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, prr.pru0, true)

	// Check settings can be read by every RU.
	for _, ru := range rus {
		m, err := ru.ReadSettings("mysql/0")
		c.Assert(err, gc.IsNil)
		c.Assert(m["gene"], gc.Equals, "simmons")
		c.Assert(m["meme"], gc.Equals, "foul-bachelor-frog")
	}
}

func (s *RelationUnitSuite) TestContainerSettings(c *gc.C) {
	prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeContainer)
	rus := RUs{prr.pru0, prr.pru1, prr.rru0, prr.rru1}

	// Check missing settings cannot be read by any RU.
	for _, ru := range rus {
		_, err := ru.ReadSettings("logging/0")
		c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "logging/0" in relation "logging:info mysql:juju-info": settings not found`)
	}

	// Add settings for one RU.
	s.assertInScope(c, prr.pru0, false)
	err := prr.pru0.EnterScope(map[string]interface{}{"gene": "hackman"})
	c.Assert(err, gc.IsNil)
	node, err := prr.pru0.Settings()
	c.Assert(err, gc.IsNil)
	node.Set("meme", "foul-bachelor-frog")
	_, err = node.Write()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, prr.pru0, true)

	// Check settings can be read by RUs in the same container.
	rus0 := RUs{prr.pru0, prr.rru0}
	for _, ru := range rus0 {
		m, err := ru.ReadSettings("mysql/0")
		c.Assert(err, gc.IsNil)
		c.Assert(m["gene"], gc.Equals, "hackman")
		c.Assert(m["meme"], gc.Equals, "foul-bachelor-frog")
	}

	// Check settings are still inaccessible to RUs outside that container
	rus1 := RUs{prr.pru1, prr.rru1}
	for _, ru := range rus1 {
		_, err := ru.ReadSettings("mysql/0")
		c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "mysql/0" in relation "logging:info mysql:juju-info": settings not found`)
	}
}

func (s *RelationUnitSuite) TestContainerCreateSubordinate(c *gc.C) {
	psvc, err := s.State.AddService("mysql", s.AddTestingCharm(c, "mysql"))
	c.Assert(err, gc.IsNil)
	rsvc, err := s.State.AddService("logging", s.AddTestingCharm(c, "logging"))
	c.Assert(err, gc.IsNil)
	eps, err := s.State.InferEndpoints([]string{"mysql", "logging"})
	c.Assert(err, gc.IsNil)
	rel, err := s.State.AddRelation(eps...)
	c.Assert(err, gc.IsNil)
	punit, err := psvc.AddUnit()
	c.Assert(err, gc.IsNil)
	pru, err := rel.Unit(punit)
	c.Assert(err, gc.IsNil)

	// Check that no units of the subordinate service exist.
	assertSubCount := func(expect int) []*state.Unit {
		runits, err := rsvc.AllUnits()
		c.Assert(err, gc.IsNil)
		c.Assert(runits, gc.HasLen, expect)
		return runits
	}
	assertSubCount(0)

	// Enter principal's scope and check a subordinate was created.
	s.assertInScope(c, pru, false)
	err = pru.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	assertSubCount(1)
	s.assertInScope(c, pru, true)

	// Enter principal scope again and check no more subordinates created.
	err = pru.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	assertSubCount(1)
	s.assertInScope(c, pru, true)

	// Leave principal scope, then re-enter, and check that still no further
	// subordinates are created.
	err = pru.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pru, false)
	err = pru.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	runits := assertSubCount(1)
	s.assertInScope(c, pru, true)

	// Set the subordinate to Dying, and enter scope again; because the scope
	// is already entered, no error is returned.
	runit := runits[0]
	err = runit.Destroy()
	c.Assert(err, gc.IsNil)
	err = pru.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pru, true)

	// Leave scope, then try to enter again with the Dying subordinate.
	err = pru.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pru, false)
	err = pru.EnterScope(nil)
	c.Assert(err, gc.Equals, state.ErrCannotEnterScopeYet)
	s.assertInScope(c, pru, false)

	// Remove the subordinate, and enter scope again; this should work, and
	// create a new subordinate.
	err = runit.EnsureDead()
	c.Assert(err, gc.IsNil)
	err = runit.Remove()
	c.Assert(err, gc.IsNil)
	assertSubCount(0)
	s.assertInScope(c, pru, false)
	err = pru.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	assertSubCount(1)
	s.assertInScope(c, pru, true)
}

func (s *RelationUnitSuite) TestDestroyRelationWithUnitsInScope(c *gc.C) {
	pr := NewPeerRelation(c, &s.ConnSuite)
	rel := pr.ru0.Relation()

	// Enter two units, and check that Destroying the service sets the
	// relation to Dying (rather than removing it directly).
	s.assertInScope(c, pr.ru0, false)
	err := pr.ru0.EnterScope(map[string]interface{}{"some": "settings"})
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru0, true)
	s.assertInScope(c, pr.ru1, false)
	err = pr.ru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru1, true)
	err = pr.svc.Destroy()
	c.Assert(err, gc.IsNil)
	err = rel.Refresh()
	c.Assert(err, gc.IsNil)
	c.Assert(rel.Life(), gc.Equals, state.Dying)

	// Check that we can't add a new unit now.
	s.assertInScope(c, pr.ru2, false)
	err = pr.ru2.EnterScope(nil)
	c.Assert(err, gc.Equals, state.ErrCannotEnterScope)
	s.assertInScope(c, pr.ru2, false)

	// Check that we created no settings for the unit we failed to add.
	_, err = pr.ru0.ReadSettings("riak/2")
	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/2" in relation "riak:ring": settings not found`)

	// ru0 leaves the scope; check that service Destroy is still a no-op.
	s.assertInScope(c, pr.ru0, true)
	err = pr.ru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru0, false)
	err = pr.svc.Destroy()
	c.Assert(err, gc.IsNil)

	// Check that unit settings for the original unit still exist, and have
	// not yet been marked for deletion.
	err = s.State.Cleanup()
	c.Assert(err, gc.IsNil)
	assertSettings := func() {
		settings, err := pr.ru1.ReadSettings("riak/0")
		c.Assert(err, gc.IsNil)
		c.Assert(settings, gc.DeepEquals, map[string]interface{}{"some": "settings"})
	}
	assertSettings()

	// The final unit leaves the scope, and cleans up after itself.
	s.assertInScope(c, pr.ru1, true)
	err = pr.ru1.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru1, false)
	err = rel.Refresh()
	c.Assert(err, checkers.Satisfies, errors.IsNotFoundError)

	// The settings were not themselves actually deleted yet...
	assertSettings()

	// ...but they were scheduled for deletion.
	err = s.State.Cleanup()
	c.Assert(err, gc.IsNil)
	_, err = pr.ru1.ReadSettings("riak/0")
	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/0" in relation "riak:ring": settings not found`)

	// Because this is the only sensible place, check that a further call
	// to Cleanup does not error out.
	err = s.State.Cleanup()
	c.Assert(err, gc.IsNil)
}

func (s *RelationUnitSuite) TestAliveRelationScope(c *gc.C) {
	pr := NewPeerRelation(c, &s.ConnSuite)
	rel := pr.ru0.Relation()

	// Two units enter...
	s.assertInScope(c, pr.ru0, false)
	err := pr.ru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru0, true)
	s.assertInScope(c, pr.ru1, false)
	err = pr.ru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru1, true)

	// One unit becomes Dying, then re-enters the scope; this is not an error,
	// because the state is already as requested.
	err = pr.u0.Destroy()
	c.Assert(err, gc.IsNil)
	err = pr.ru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru0, true)

	// Two units leave...
	err = pr.ru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru0, false)
	err = pr.ru1.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru1, false)

	// The relation scope is empty, but the relation is still alive...
	err = rel.Refresh()
	c.Assert(err, gc.IsNil)
	c.Assert(rel.Life(), gc.Equals, state.Alive)

	// ...and new units can still join it...
	s.assertInScope(c, pr.ru2, false)
	err = pr.ru2.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru2, true)

	// ...but Dying units cannot.
	err = pr.u3.Destroy()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru3, false)
	err = pr.ru3.EnterScope(nil)
	c.Assert(err, gc.Equals, state.ErrCannotEnterScope)
	s.assertInScope(c, pr.ru3, false)
}

func (s *RelationUnitSuite) TestPeerWatchScope(c *gc.C) {
	pr := NewPeerRelation(c, &s.ConnSuite)

	// Test empty initial event.
	w0 := pr.ru0.WatchScope()
	defer testing.AssertStop(c, w0)
	s.assertScopeChange(c, w0, nil, nil)
	s.assertNoScopeChange(c, w0)

	// ru0 enters; check no change, but settings written.
	s.assertInScope(c, pr.ru0, false)
	err := pr.ru0.EnterScope(map[string]interface{}{"foo": "bar"})
	c.Assert(err, gc.IsNil)
	s.assertNoScopeChange(c, w0)
	node, err := pr.ru0.Settings()
	c.Assert(err, gc.IsNil)
	c.Assert(node.Map(), gc.DeepEquals, map[string]interface{}{"foo": "bar"})
	s.assertInScope(c, pr.ru0, true)

	// ru1 enters; check change is observed.
	s.assertInScope(c, pr.ru1, false)
	err = pr.ru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertScopeChange(c, w0, []string{"riak/1"}, nil)
	s.assertNoScopeChange(c, w0)
	s.assertInScope(c, pr.ru1, true)

	// ru1 enters again, check no problems and no changes.
	err = pr.ru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertNoScopeChange(c, w0)
	s.assertInScope(c, pr.ru1, true)

	// Stop watching; ru2 enters.
	testing.AssertStop(c, w0)
	s.assertInScope(c, pr.ru2, false)
	err = pr.ru2.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru2, true)

	// Start watch again, check initial event.
	w0 = pr.ru0.WatchScope()
	defer testing.AssertStop(c, w0)
	s.assertScopeChange(c, w0, []string{"riak/1", "riak/2"}, nil)
	s.assertNoScopeChange(c, w0)

	// ru1 leaves; check event.
	s.assertInScope(c, pr.ru1, true)
	err = pr.ru1.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertScopeChange(c, w0, nil, []string{"riak/1"})
	s.assertNoScopeChange(c, w0)
	s.assertInScope(c, pr.ru1, false)

	// ru1 leaves again; check no problems and no changes.
	err = pr.ru1.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertNoScopeChange(c, w0)
	s.assertInScope(c, pr.ru1, false)
}

func (s *RelationUnitSuite) TestProReqWatchScope(c *gc.C) {
	prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal)

	// Test empty initial events for all RUs.
	ws := prr.watches()
	for _, w := range ws {
		defer testing.AssertStop(c, w)
	}
	for _, w := range ws {
		s.assertScopeChange(c, w, nil, nil)
	}
	s.assertNoScopeChange(c, ws...)

	// pru0 enters; check detected only by req RUs.
	s.assertInScope(c, prr.pru0, false)
	err := prr.pru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	rws := func() []*state.RelationScopeWatcher {
		return []*state.RelationScopeWatcher{ws[2], ws[3]}
	}
	for _, w := range rws() {
		s.assertScopeChange(c, w, []string{"mysql/0"}, nil)
	}
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.pru0, true)

	// req0 enters; check detected only by pro RUs.
	s.assertInScope(c, prr.rru0, false)
	err = prr.rru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	pws := func() []*state.RelationScopeWatcher {
		return []*state.RelationScopeWatcher{ws[0], ws[1]}
	}
	for _, w := range pws() {
		s.assertScopeChange(c, w, []string{"wordpress/0"}, nil)
	}
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.rru0, true)

	// Stop watches; remaining RUs enter.
	for _, w := range ws {
		testing.AssertStop(c, w)
	}
	s.assertInScope(c, prr.pru1, false)
	err = prr.pru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, prr.pru1, true)
	s.assertInScope(c, prr.rru1, false)
	err = prr.rru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, prr.rru0, true)

	// Start new watches, check initial events.
	ws = prr.watches()
	for _, w := range ws {
		defer testing.AssertStop(c, w)
	}
	for _, w := range pws() {
		s.assertScopeChange(c, w, []string{"wordpress/0", "wordpress/1"}, nil)
	}
	for _, w := range rws() {
		s.assertScopeChange(c, w, []string{"mysql/0", "mysql/1"}, nil)
	}
	s.assertNoScopeChange(c, ws...)

	// pru0 leaves; check detected only by req RUs.
	s.assertInScope(c, prr.pru0, true)
	err = prr.pru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	for _, w := range rws() {
		s.assertScopeChange(c, w, nil, []string{"mysql/0"})
	}
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.pru0, false)

	// rru0 leaves; check detected only by pro RUs.
	s.assertInScope(c, prr.rru0, true)
	err = prr.rru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	for _, w := range pws() {
		s.assertScopeChange(c, w, nil, []string{"wordpress/0"})
	}
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.rru0, false)
}

func (s *RelationUnitSuite) TestContainerWatchScope(c *gc.C) {
	prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeContainer)

	// Test empty initial events for all RUs.
	ws := prr.watches()
	for _, w := range ws {
		defer testing.AssertStop(c, w)
	}
	for _, w := range ws {
		s.assertScopeChange(c, w, nil, nil)
	}
	s.assertNoScopeChange(c, ws...)

	// pru0 enters; check detected only by same-container req.
	s.assertInScope(c, prr.pru0, false)
	err := prr.pru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertScopeChange(c, ws[2], []string{"mysql/0"}, nil)
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.pru0, true)

	// req1 enters; check detected only by same-container pro.
	s.assertInScope(c, prr.rru1, false)
	err = prr.rru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertScopeChange(c, ws[1], []string{"logging/1"}, nil)
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.rru1, true)

	// Stop watches; remaining RUs enter scope.
	for _, w := range ws {
		testing.AssertStop(c, w)
	}
	s.assertInScope(c, prr.pru1, false)
	err = prr.pru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, prr.rru0, false)
	err = prr.rru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)

	// Start new watches, check initial events.
	ws = prr.watches()
	for _, w := range ws {
		defer testing.AssertStop(c, w)
	}
	s.assertScopeChange(c, ws[0], []string{"logging/0"}, nil)
	s.assertScopeChange(c, ws[1], []string{"logging/1"}, nil)
	s.assertScopeChange(c, ws[2], []string{"mysql/0"}, nil)
	s.assertScopeChange(c, ws[3], []string{"mysql/1"}, nil)
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.pru1, true)
	s.assertInScope(c, prr.rru0, true)

	// pru0 leaves; check detected only by same-container req.
	s.assertInScope(c, prr.pru0, true)
	err = prr.pru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertScopeChange(c, ws[2], nil, []string{"mysql/0"})
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.pru0, false)

	// rru0 leaves; check detected only by same-container pro.
	s.assertInScope(c, prr.rru0, true)
	err = prr.rru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertScopeChange(c, ws[0], nil, []string{"logging/0"})
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.rru0, false)
}

func (s *RelationUnitSuite) assertScopeChange(c *gc.C, w *state.RelationScopeWatcher, entered, left []string) {
	s.State.StartSync()
	select {
	case ch, ok := <-w.Changes():
		c.Assert(ok, gc.Equals, true)
		sort.Strings(entered)
		sort.Strings(ch.Entered)
		c.Assert(ch.Entered, gc.DeepEquals, entered)
		sort.Strings(left)
		sort.Strings(ch.Left)
		c.Assert(ch.Left, gc.DeepEquals, left)
	case <-time.After(coretesting.LongWait):
		c.Fatalf("no change")
	}
}

func (s *RelationUnitSuite) assertNoScopeChange(c *gc.C, ws ...*state.RelationScopeWatcher) {
	s.State.StartSync()
	for _, w := range ws {
		select {
		case ch, ok := <-w.Changes():
			c.Fatalf("got unwanted change: %#v, %t", ch, ok)
		case <-time.After(coretesting.ShortWait):
		}
	}
}

type PeerRelation struct {
	rel                *state.Relation
	svc                *state.Service
	u0, u1, u2, u3     *state.Unit
	ru0, ru1, ru2, ru3 *state.RelationUnit
}

func NewPeerRelation(c *gc.C, s *ConnSuite) *PeerRelation {
	svc, err := s.State.AddService("riak", s.AddTestingCharm(c, "riak"))
	c.Assert(err, gc.IsNil)
	ep, err := svc.Endpoint("ring")
	c.Assert(err, gc.IsNil)
	rel, err := s.State.EndpointsRelation(ep)
	c.Assert(err, gc.IsNil)
	pr := &PeerRelation{rel: rel, svc: svc}
	pr.u0, pr.ru0 = addRU(c, svc, rel, nil)
	pr.u1, pr.ru1 = addRU(c, svc, rel, nil)
	pr.u2, pr.ru2 = addRU(c, svc, rel, nil)
	pr.u3, pr.ru3 = addRU(c, svc, rel, nil)
	return pr
}

type ProReqRelation struct {
	rel                    *state.Relation
	psvc, rsvc             *state.Service
	pu0, pu1, ru0, ru1     *state.Unit
	pru0, pru1, rru0, rru1 *state.RelationUnit
}

func NewProReqRelation(c *gc.C, s *ConnSuite, scope charm.RelationScope) *ProReqRelation {
	psvc, err := s.State.AddService("mysql", s.AddTestingCharm(c, "mysql"))
	c.Assert(err, gc.IsNil)
	var rsvc *state.Service
	if scope == charm.ScopeGlobal {
		rsvc, err = s.State.AddService("wordpress", s.AddTestingCharm(c, "wordpress"))
	} else {
		rsvc, err = s.State.AddService("logging", s.AddTestingCharm(c, "logging"))
	}
	c.Assert(err, gc.IsNil)
	eps, err := s.State.InferEndpoints([]string{"mysql", rsvc.Name()})
	c.Assert(err, gc.IsNil)
	rel, err := s.State.AddRelation(eps...)
	c.Assert(err, gc.IsNil)
	prr := &ProReqRelation{rel: rel, psvc: psvc, rsvc: rsvc}
	prr.pu0, prr.pru0 = addRU(c, psvc, rel, nil)
	prr.pu1, prr.pru1 = addRU(c, psvc, rel, nil)
	if scope == charm.ScopeGlobal {
		prr.ru0, prr.rru0 = addRU(c, rsvc, rel, nil)
		prr.ru1, prr.rru1 = addRU(c, rsvc, rel, nil)
	} else {
		prr.ru0, prr.rru0 = addRU(c, rsvc, rel, prr.pu0)
		prr.ru1, prr.rru1 = addRU(c, rsvc, rel, prr.pu1)
	}
	return prr
}

func (prr *ProReqRelation) watches() []*state.RelationScopeWatcher {
	return []*state.RelationScopeWatcher{
		prr.pru0.WatchScope(), prr.pru1.WatchScope(),
		prr.rru0.WatchScope(), prr.rru1.WatchScope(),
	}
}

func addRU(c *gc.C, svc *state.Service, rel *state.Relation, principal *state.Unit) (*state.Unit, *state.RelationUnit) {
	// Given the service svc in the relation rel, add a unit of svc and create
	// a RelationUnit with rel. If principal is supplied, svc is assumed to be
	// subordinate and the unit will be created by temporarily entering the
	// relation's scope as the principal.
	var u *state.Unit
	if principal == nil {
		unit, err := svc.AddUnit()
		c.Assert(err, gc.IsNil)
		u = unit
	} else {
		origUnits, err := svc.AllUnits()
		c.Assert(err, gc.IsNil)
		pru, err := rel.Unit(principal)
		c.Assert(err, gc.IsNil)
		err = pru.EnterScope(nil) // to create the subordinate
		c.Assert(err, gc.IsNil)
		err = pru.LeaveScope() // to reset to initial expected state
		c.Assert(err, gc.IsNil)
		newUnits, err := svc.AllUnits()
		c.Assert(err, gc.IsNil)
		for _, unit := range newUnits {
			found := false
			for _, old := range origUnits {
				if unit.Name() == old.Name() {
					found = true
					break
				}
			}
			if !found {
				u = unit
				break
			}
		}
		c.Assert(u, gc.NotNil)
	}
	preventUnitDestroyRemove(c, u)
	ru, err := rel.Unit(u)
	c.Assert(err, gc.IsNil)
	return u, ru
}

type RUs []*state.RelationUnit<commit_msg>[r=fwereade],[bug=1218362] state: reinstate accidentally-dropped tests<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state_test

import (
	"fmt"
	"sort"
	"strconv"
	"time"

	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/errors"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/testing"
	coretesting "launchpad.net/juju-core/testing"
	jc "launchpad.net/juju-core/testing/checkers"
)

type RUs []*state.RelationUnit

type RelationUnitSuite struct {
	ConnSuite
}

var _ = gc.Suite(&RelationUnitSuite{})

func (s *RelationUnitSuite) assertInScope(c *gc.C, ru *state.RelationUnit, inScope bool) {
	ok, err := ru.InScope()
	c.Assert(err, gc.IsNil)
	c.Assert(ok, gc.Equals, inScope)
}

func (s *RelationUnitSuite) TestReadSettingsErrors(c *gc.C) {
	riak, err := s.State.AddService("riak", s.AddTestingCharm(c, "riak"))
	c.Assert(err, gc.IsNil)
	u0, err := riak.AddUnit()
	c.Assert(err, gc.IsNil)
	riakEP, err := riak.Endpoint("ring")
	c.Assert(err, gc.IsNil)
	rel, err := s.State.EndpointsRelation(riakEP)
	c.Assert(err, gc.IsNil)
	ru0, err := rel.Unit(u0)
	c.Assert(err, gc.IsNil)

	_, err = ru0.ReadSettings("nonsense")
	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "nonsense" in relation "riak:ring": "nonsense" is not a valid unit name`)
	_, err = ru0.ReadSettings("unknown/0")
	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "unknown/0" in relation "riak:ring": service "unknown" is not a member of "riak:ring"`)
	_, err = ru0.ReadSettings("riak/pressure")
	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/pressure" in relation "riak:ring": "riak/pressure" is not a valid unit name`)
	_, err = ru0.ReadSettings("riak/1")
	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/1" in relation "riak:ring": settings not found`)
}

func (s *RelationUnitSuite) TestPeerSettings(c *gc.C) {
	pr := NewPeerRelation(c, &s.ConnSuite)
	rus := RUs{pr.ru0, pr.ru1}

	// Check missing settings cannot be read by any RU.
	for _, ru := range rus {
		_, err := ru.ReadSettings("riak/0")
		c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/0" in relation "riak:ring": settings not found`)
	}

	// Add settings for one RU.
	s.assertInScope(c, pr.ru0, false)
	err := pr.ru0.EnterScope(map[string]interface{}{"gene": "kelly"})
	c.Assert(err, gc.IsNil)
	node, err := pr.ru0.Settings()
	c.Assert(err, gc.IsNil)
	node.Set("meme", "socially-awkward-penguin")
	_, err = node.Write()
	c.Assert(err, gc.IsNil)
	normal := map[string]interface{}{
		"gene": "kelly",
		"meme": "socially-awkward-penguin",
	}

	// Check settings can be read by every RU.
	assertSettings := func(u *state.Unit, expect map[string]interface{}) {
		for _, ru := range rus {
			m, err := ru.ReadSettings(u.Name())
			c.Assert(err, gc.IsNil)
			c.Assert(m, gc.DeepEquals, expect)
		}
	}
	assertSettings(pr.u0, normal)
	s.assertInScope(c, pr.ru0, true)

	// Check that EnterScope when scope already entered does not touch
	// settings at all.
	changed := map[string]interface{}{"foo": "bar"}
	err = pr.ru0.EnterScope(changed)
	c.Assert(err, gc.IsNil)
	assertSettings(pr.u0, normal)
	s.assertInScope(c, pr.ru0, true)

	// Leave scope, check settings are still as accessible as before.
	err = pr.ru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	assertSettings(pr.u0, normal)
	s.assertInScope(c, pr.ru0, false)

	// Re-enter scope wih changed settings, and check they completely overwrite
	// the old ones.
	err = pr.ru0.EnterScope(changed)
	c.Assert(err, gc.IsNil)
	assertSettings(pr.u0, changed)
	s.assertInScope(c, pr.ru0, true)

	// Leave and re-enter with nil nettings, and check they overwrite to become
	// an empty map.
	err = pr.ru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru0, false)
	err = pr.ru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	assertSettings(pr.u0, map[string]interface{}{})
	s.assertInScope(c, pr.ru0, true)

	// Check that entering scope for the first time with nil settings works correctly.
	s.assertInScope(c, pr.ru1, false)
	err = pr.ru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	assertSettings(pr.u1, map[string]interface{}{})
	s.assertInScope(c, pr.ru1, true)
}

func (s *RelationUnitSuite) TestProReqSettings(c *gc.C) {
	prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal)
	rus := RUs{prr.pru0, prr.pru1, prr.rru0, prr.rru1}

	// Check missing settings cannot be read by any RU.
	for _, ru := range rus {
		_, err := ru.ReadSettings("mysql/0")
		c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "mysql/0" in relation "wordpress:db mysql:server": settings not found`)
	}

	// Add settings for one RU.
	s.assertInScope(c, prr.pru0, false)
	err := prr.pru0.EnterScope(map[string]interface{}{"gene": "simmons"})
	c.Assert(err, gc.IsNil)
	node, err := prr.pru0.Settings()
	c.Assert(err, gc.IsNil)
	node.Set("meme", "foul-bachelor-frog")
	_, err = node.Write()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, prr.pru0, true)

	// Check settings can be read by every RU.
	for _, ru := range rus {
		m, err := ru.ReadSettings("mysql/0")
		c.Assert(err, gc.IsNil)
		c.Assert(m["gene"], gc.Equals, "simmons")
		c.Assert(m["meme"], gc.Equals, "foul-bachelor-frog")
	}
}

func (s *RelationUnitSuite) TestContainerSettings(c *gc.C) {
	prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeContainer)
	rus := RUs{prr.pru0, prr.pru1, prr.rru0, prr.rru1}

	// Check missing settings cannot be read by any RU.
	for _, ru := range rus {
		_, err := ru.ReadSettings("logging/0")
		c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "logging/0" in relation "logging:info mysql:juju-info": settings not found`)
	}

	// Add settings for one RU.
	s.assertInScope(c, prr.pru0, false)
	err := prr.pru0.EnterScope(map[string]interface{}{"gene": "hackman"})
	c.Assert(err, gc.IsNil)
	node, err := prr.pru0.Settings()
	c.Assert(err, gc.IsNil)
	node.Set("meme", "foul-bachelor-frog")
	_, err = node.Write()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, prr.pru0, true)

	// Check settings can be read by RUs in the same container.
	rus0 := RUs{prr.pru0, prr.rru0}
	for _, ru := range rus0 {
		m, err := ru.ReadSettings("mysql/0")
		c.Assert(err, gc.IsNil)
		c.Assert(m["gene"], gc.Equals, "hackman")
		c.Assert(m["meme"], gc.Equals, "foul-bachelor-frog")
	}

	// Check settings are still inaccessible to RUs outside that container
	rus1 := RUs{prr.pru1, prr.rru1}
	for _, ru := range rus1 {
		_, err := ru.ReadSettings("mysql/0")
		c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "mysql/0" in relation "logging:info mysql:juju-info": settings not found`)
	}
}

func (s *RelationUnitSuite) TestContainerCreateSubordinate(c *gc.C) {
	psvc, err := s.State.AddService("mysql", s.AddTestingCharm(c, "mysql"))
	c.Assert(err, gc.IsNil)
	rsvc, err := s.State.AddService("logging", s.AddTestingCharm(c, "logging"))
	c.Assert(err, gc.IsNil)
	eps, err := s.State.InferEndpoints([]string{"mysql", "logging"})
	c.Assert(err, gc.IsNil)
	rel, err := s.State.AddRelation(eps...)
	c.Assert(err, gc.IsNil)
	punit, err := psvc.AddUnit()
	c.Assert(err, gc.IsNil)
	pru, err := rel.Unit(punit)
	c.Assert(err, gc.IsNil)

	// Check that no units of the subordinate service exist.
	assertSubCount := func(expect int) []*state.Unit {
		runits, err := rsvc.AllUnits()
		c.Assert(err, gc.IsNil)
		c.Assert(runits, gc.HasLen, expect)
		return runits
	}
	assertSubCount(0)

	// Enter principal's scope and check a subordinate was created.
	s.assertInScope(c, pru, false)
	err = pru.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	assertSubCount(1)
	s.assertInScope(c, pru, true)

	// Enter principal scope again and check no more subordinates created.
	err = pru.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	assertSubCount(1)
	s.assertInScope(c, pru, true)

	// Leave principal scope, then re-enter, and check that still no further
	// subordinates are created.
	err = pru.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pru, false)
	err = pru.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	runits := assertSubCount(1)
	s.assertInScope(c, pru, true)

	// Set the subordinate to Dying, and enter scope again; because the scope
	// is already entered, no error is returned.
	runit := runits[0]
	err = runit.Destroy()
	c.Assert(err, gc.IsNil)
	err = pru.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pru, true)

	// Leave scope, then try to enter again with the Dying subordinate.
	err = pru.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pru, false)
	err = pru.EnterScope(nil)
	c.Assert(err, gc.Equals, state.ErrCannotEnterScopeYet)
	s.assertInScope(c, pru, false)

	// Remove the subordinate, and enter scope again; this should work, and
	// create a new subordinate.
	err = runit.EnsureDead()
	c.Assert(err, gc.IsNil)
	err = runit.Remove()
	c.Assert(err, gc.IsNil)
	assertSubCount(0)
	s.assertInScope(c, pru, false)
	err = pru.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	assertSubCount(1)
	s.assertInScope(c, pru, true)
}

func (s *RelationUnitSuite) TestDestroyRelationWithUnitsInScope(c *gc.C) {
	pr := NewPeerRelation(c, &s.ConnSuite)
	rel := pr.ru0.Relation()

	// Enter two units, and check that Destroying the service sets the
	// relation to Dying (rather than removing it directly).
	s.assertInScope(c, pr.ru0, false)
	err := pr.ru0.EnterScope(map[string]interface{}{"some": "settings"})
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru0, true)
	s.assertInScope(c, pr.ru1, false)
	err = pr.ru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru1, true)
	err = pr.svc.Destroy()
	c.Assert(err, gc.IsNil)
	err = rel.Refresh()
	c.Assert(err, gc.IsNil)
	c.Assert(rel.Life(), gc.Equals, state.Dying)

	// Check that we can't add a new unit now.
	s.assertInScope(c, pr.ru2, false)
	err = pr.ru2.EnterScope(nil)
	c.Assert(err, gc.Equals, state.ErrCannotEnterScope)
	s.assertInScope(c, pr.ru2, false)

	// Check that we created no settings for the unit we failed to add.
	_, err = pr.ru0.ReadSettings("riak/2")
	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/2" in relation "riak:ring": settings not found`)

	// ru0 leaves the scope; check that service Destroy is still a no-op.
	s.assertInScope(c, pr.ru0, true)
	err = pr.ru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru0, false)
	err = pr.svc.Destroy()
	c.Assert(err, gc.IsNil)

	// Check that unit settings for the original unit still exist, and have
	// not yet been marked for deletion.
	err = s.State.Cleanup()
	c.Assert(err, gc.IsNil)
	assertSettings := func() {
		settings, err := pr.ru1.ReadSettings("riak/0")
		c.Assert(err, gc.IsNil)
		c.Assert(settings, gc.DeepEquals, map[string]interface{}{"some": "settings"})
	}
	assertSettings()

	// The final unit leaves the scope, and cleans up after itself.
	s.assertInScope(c, pr.ru1, true)
	err = pr.ru1.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru1, false)
	err = rel.Refresh()
	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)

	// The settings were not themselves actually deleted yet...
	assertSettings()

	// ...but they were scheduled for deletion.
	err = s.State.Cleanup()
	c.Assert(err, gc.IsNil)
	_, err = pr.ru1.ReadSettings("riak/0")
	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/0" in relation "riak:ring": settings not found`)

	// Because this is the only sensible place, check that a further call
	// to Cleanup does not error out.
	err = s.State.Cleanup()
	c.Assert(err, gc.IsNil)
}

func (s *RelationUnitSuite) TestAliveRelationScope(c *gc.C) {
	pr := NewPeerRelation(c, &s.ConnSuite)
	rel := pr.ru0.Relation()

	// Two units enter...
	s.assertInScope(c, pr.ru0, false)
	err := pr.ru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru0, true)
	s.assertInScope(c, pr.ru1, false)
	err = pr.ru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru1, true)

	// One unit becomes Dying, then re-enters the scope; this is not an error,
	// because the state is already as requested.
	err = pr.u0.Destroy()
	c.Assert(err, gc.IsNil)
	err = pr.ru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru0, true)

	// Two units leave...
	err = pr.ru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru0, false)
	err = pr.ru1.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru1, false)

	// The relation scope is empty, but the relation is still alive...
	err = rel.Refresh()
	c.Assert(err, gc.IsNil)
	c.Assert(rel.Life(), gc.Equals, state.Alive)

	// ...and new units can still join it...
	s.assertInScope(c, pr.ru2, false)
	err = pr.ru2.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru2, true)

	// ...but Dying units cannot.
	err = pr.u3.Destroy()
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru3, false)
	err = pr.ru3.EnterScope(nil)
	c.Assert(err, gc.Equals, state.ErrCannotEnterScope)
	s.assertInScope(c, pr.ru3, false)
}

func (s *RelationUnitSuite) TestPeerWatchScope(c *gc.C) {
	pr := NewPeerRelation(c, &s.ConnSuite)

	// Test empty initial event.
	w0 := pr.ru0.WatchScope()
	defer testing.AssertStop(c, w0)
	s.assertScopeChange(c, w0, nil, nil)
	s.assertNoScopeChange(c, w0)

	// ru0 enters; check no change, but settings written.
	s.assertInScope(c, pr.ru0, false)
	err := pr.ru0.EnterScope(map[string]interface{}{"foo": "bar"})
	c.Assert(err, gc.IsNil)
	s.assertNoScopeChange(c, w0)
	node, err := pr.ru0.Settings()
	c.Assert(err, gc.IsNil)
	c.Assert(node.Map(), gc.DeepEquals, map[string]interface{}{"foo": "bar"})
	s.assertInScope(c, pr.ru0, true)

	// ru1 enters; check change is observed.
	s.assertInScope(c, pr.ru1, false)
	err = pr.ru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertScopeChange(c, w0, []string{"riak/1"}, nil)
	s.assertNoScopeChange(c, w0)
	s.assertInScope(c, pr.ru1, true)

	// ru1 enters again, check no problems and no changes.
	err = pr.ru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertNoScopeChange(c, w0)
	s.assertInScope(c, pr.ru1, true)

	// Stop watching; ru2 enters.
	testing.AssertStop(c, w0)
	s.assertInScope(c, pr.ru2, false)
	err = pr.ru2.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, pr.ru2, true)

	// Start watch again, check initial event.
	w0 = pr.ru0.WatchScope()
	defer testing.AssertStop(c, w0)
	s.assertScopeChange(c, w0, []string{"riak/1", "riak/2"}, nil)
	s.assertNoScopeChange(c, w0)

	// ru1 leaves; check event.
	s.assertInScope(c, pr.ru1, true)
	err = pr.ru1.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertScopeChange(c, w0, nil, []string{"riak/1"})
	s.assertNoScopeChange(c, w0)
	s.assertInScope(c, pr.ru1, false)

	// ru1 leaves again; check no problems and no changes.
	err = pr.ru1.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertNoScopeChange(c, w0)
	s.assertInScope(c, pr.ru1, false)
}

func (s *RelationUnitSuite) TestProReqWatchScope(c *gc.C) {
	prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal)

	// Test empty initial events for all RUs.
	ws := prr.watches()
	for _, w := range ws {
		defer testing.AssertStop(c, w)
	}
	for _, w := range ws {
		s.assertScopeChange(c, w, nil, nil)
	}
	s.assertNoScopeChange(c, ws...)

	// pru0 enters; check detected only by req RUs.
	s.assertInScope(c, prr.pru0, false)
	err := prr.pru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	rws := func() []*state.RelationScopeWatcher {
		return []*state.RelationScopeWatcher{ws[2], ws[3]}
	}
	for _, w := range rws() {
		s.assertScopeChange(c, w, []string{"mysql/0"}, nil)
	}
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.pru0, true)

	// req0 enters; check detected only by pro RUs.
	s.assertInScope(c, prr.rru0, false)
	err = prr.rru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	pws := func() []*state.RelationScopeWatcher {
		return []*state.RelationScopeWatcher{ws[0], ws[1]}
	}
	for _, w := range pws() {
		s.assertScopeChange(c, w, []string{"wordpress/0"}, nil)
	}
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.rru0, true)

	// Stop watches; remaining RUs enter.
	for _, w := range ws {
		testing.AssertStop(c, w)
	}
	s.assertInScope(c, prr.pru1, false)
	err = prr.pru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, prr.pru1, true)
	s.assertInScope(c, prr.rru1, false)
	err = prr.rru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, prr.rru0, true)

	// Start new watches, check initial events.
	ws = prr.watches()
	for _, w := range ws {
		defer testing.AssertStop(c, w)
	}
	for _, w := range pws() {
		s.assertScopeChange(c, w, []string{"wordpress/0", "wordpress/1"}, nil)
	}
	for _, w := range rws() {
		s.assertScopeChange(c, w, []string{"mysql/0", "mysql/1"}, nil)
	}
	s.assertNoScopeChange(c, ws...)

	// pru0 leaves; check detected only by req RUs.
	s.assertInScope(c, prr.pru0, true)
	err = prr.pru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	for _, w := range rws() {
		s.assertScopeChange(c, w, nil, []string{"mysql/0"})
	}
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.pru0, false)

	// rru0 leaves; check detected only by pro RUs.
	s.assertInScope(c, prr.rru0, true)
	err = prr.rru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	for _, w := range pws() {
		s.assertScopeChange(c, w, nil, []string{"wordpress/0"})
	}
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.rru0, false)
}

func (s *RelationUnitSuite) TestContainerWatchScope(c *gc.C) {
	prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeContainer)

	// Test empty initial events for all RUs.
	ws := prr.watches()
	for _, w := range ws {
		defer testing.AssertStop(c, w)
	}
	for _, w := range ws {
		s.assertScopeChange(c, w, nil, nil)
	}
	s.assertNoScopeChange(c, ws...)

	// pru0 enters; check detected only by same-container req.
	s.assertInScope(c, prr.pru0, false)
	err := prr.pru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertScopeChange(c, ws[2], []string{"mysql/0"}, nil)
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.pru0, true)

	// req1 enters; check detected only by same-container pro.
	s.assertInScope(c, prr.rru1, false)
	err = prr.rru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertScopeChange(c, ws[1], []string{"logging/1"}, nil)
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.rru1, true)

	// Stop watches; remaining RUs enter scope.
	for _, w := range ws {
		testing.AssertStop(c, w)
	}
	s.assertInScope(c, prr.pru1, false)
	err = prr.pru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertInScope(c, prr.rru0, false)
	err = prr.rru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)

	// Start new watches, check initial events.
	ws = prr.watches()
	for _, w := range ws {
		defer testing.AssertStop(c, w)
	}
	s.assertScopeChange(c, ws[0], []string{"logging/0"}, nil)
	s.assertScopeChange(c, ws[1], []string{"logging/1"}, nil)
	s.assertScopeChange(c, ws[2], []string{"mysql/0"}, nil)
	s.assertScopeChange(c, ws[3], []string{"mysql/1"}, nil)
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.pru1, true)
	s.assertInScope(c, prr.rru0, true)

	// pru0 leaves; check detected only by same-container req.
	s.assertInScope(c, prr.pru0, true)
	err = prr.pru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertScopeChange(c, ws[2], nil, []string{"mysql/0"})
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.pru0, false)

	// rru0 leaves; check detected only by same-container pro.
	s.assertInScope(c, prr.rru0, true)
	err = prr.rru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	s.assertScopeChange(c, ws[0], nil, []string{"logging/0"})
	s.assertNoScopeChange(c, ws...)
	s.assertInScope(c, prr.rru0, false)
}

func (s *RelationUnitSuite) assertScopeChange(c *gc.C, w *state.RelationScopeWatcher, entered, left []string) {
	s.State.StartSync()
	select {
	case ch, ok := <-w.Changes():
		c.Assert(ok, gc.Equals, true)
		sort.Strings(entered)
		sort.Strings(ch.Entered)
		c.Assert(ch.Entered, gc.DeepEquals, entered)
		sort.Strings(left)
		sort.Strings(ch.Left)
		c.Assert(ch.Left, gc.DeepEquals, left)
	case <-time.After(coretesting.LongWait):
		c.Fatalf("no change")
	}
}

func (s *RelationUnitSuite) assertNoScopeChange(c *gc.C, ws ...*state.RelationScopeWatcher) {
	s.State.StartSync()
	for _, w := range ws {
		select {
		case ch, ok := <-w.Changes():
			c.Fatalf("got unwanted change: %#v, %t", ch, ok)
		case <-time.After(coretesting.ShortWait):
		}
	}
}

type PeerRelation struct {
	rel                *state.Relation
	svc                *state.Service
	u0, u1, u2, u3     *state.Unit
	ru0, ru1, ru2, ru3 *state.RelationUnit
}

func NewPeerRelation(c *gc.C, s *ConnSuite) *PeerRelation {
	svc, err := s.State.AddService("riak", s.AddTestingCharm(c, "riak"))
	c.Assert(err, gc.IsNil)
	ep, err := svc.Endpoint("ring")
	c.Assert(err, gc.IsNil)
	rel, err := s.State.EndpointsRelation(ep)
	c.Assert(err, gc.IsNil)
	pr := &PeerRelation{rel: rel, svc: svc}
	pr.u0, pr.ru0 = addRU(c, svc, rel, nil)
	pr.u1, pr.ru1 = addRU(c, svc, rel, nil)
	pr.u2, pr.ru2 = addRU(c, svc, rel, nil)
	pr.u3, pr.ru3 = addRU(c, svc, rel, nil)
	return pr
}

type ProReqRelation struct {
	rel                    *state.Relation
	psvc, rsvc             *state.Service
	pu0, pu1, ru0, ru1     *state.Unit
	pru0, pru1, rru0, rru1 *state.RelationUnit
}

func NewProReqRelation(c *gc.C, s *ConnSuite, scope charm.RelationScope) *ProReqRelation {
	psvc, err := s.State.AddService("mysql", s.AddTestingCharm(c, "mysql"))
	c.Assert(err, gc.IsNil)
	var rsvc *state.Service
	if scope == charm.ScopeGlobal {
		rsvc, err = s.State.AddService("wordpress", s.AddTestingCharm(c, "wordpress"))
	} else {
		rsvc, err = s.State.AddService("logging", s.AddTestingCharm(c, "logging"))
	}
	c.Assert(err, gc.IsNil)
	eps, err := s.State.InferEndpoints([]string{"mysql", rsvc.Name()})
	c.Assert(err, gc.IsNil)
	rel, err := s.State.AddRelation(eps...)
	c.Assert(err, gc.IsNil)
	prr := &ProReqRelation{rel: rel, psvc: psvc, rsvc: rsvc}
	prr.pu0, prr.pru0 = addRU(c, psvc, rel, nil)
	prr.pu1, prr.pru1 = addRU(c, psvc, rel, nil)
	if scope == charm.ScopeGlobal {
		prr.ru0, prr.rru0 = addRU(c, rsvc, rel, nil)
		prr.ru1, prr.rru1 = addRU(c, rsvc, rel, nil)
	} else {
		prr.ru0, prr.rru0 = addRU(c, rsvc, rel, prr.pu0)
		prr.ru1, prr.rru1 = addRU(c, rsvc, rel, prr.pu1)
	}
	return prr
}

func (prr *ProReqRelation) watches() []*state.RelationScopeWatcher {
	return []*state.RelationScopeWatcher{
		prr.pru0.WatchScope(), prr.pru1.WatchScope(),
		prr.rru0.WatchScope(), prr.rru1.WatchScope(),
	}
}

func addRU(c *gc.C, svc *state.Service, rel *state.Relation, principal *state.Unit) (*state.Unit, *state.RelationUnit) {
	// Given the service svc in the relation rel, add a unit of svc and create
	// a RelationUnit with rel. If principal is supplied, svc is assumed to be
	// subordinate and the unit will be created by temporarily entering the
	// relation's scope as the principal.
	var u *state.Unit
	if principal == nil {
		unit, err := svc.AddUnit()
		c.Assert(err, gc.IsNil)
		u = unit
	} else {
		origUnits, err := svc.AllUnits()
		c.Assert(err, gc.IsNil)
		pru, err := rel.Unit(principal)
		c.Assert(err, gc.IsNil)
		err = pru.EnterScope(nil) // to create the subordinate
		c.Assert(err, gc.IsNil)
		err = pru.LeaveScope() // to reset to initial expected state
		c.Assert(err, gc.IsNil)
		newUnits, err := svc.AllUnits()
		c.Assert(err, gc.IsNil)
		for _, unit := range newUnits {
			found := false
			for _, old := range origUnits {
				if unit.Name() == old.Name() {
					found = true
					break
				}
			}
			if !found {
				u = unit
				break
			}
		}
		c.Assert(u, gc.NotNil)
	}
	preventUnitDestroyRemove(c, u)
	ru, err := rel.Unit(u)
	c.Assert(err, gc.IsNil)
	return u, ru
}

type WatchScopeSuite struct {
	ConnSuite
}

var _ = gc.Suite(&WatchScopeSuite{})

func (s *WatchScopeSuite) TestPeer(c *gc.C) {
	// Create a service and get a peer relation.
	riak, err := s.State.AddService("riak", s.AddTestingCharm(c, "riak"))
	c.Assert(err, gc.IsNil)
	riakEP, err := riak.Endpoint("ring")
	c.Assert(err, gc.IsNil)
	rels, err := riak.Relations()
	c.Assert(err, gc.IsNil)
	c.Assert(rels, gc.HasLen, 1)
	rel := rels[0]

	// Add some units to the service and set their private addresses; get
	// the relevant RelationUnits.
	// (Private addresses should be set by their unit agents on
	// startup; this test does not include that, but Join expects
	// the information to be available, and uses it to populate the
	// relation settings node.)
	addUnit := func(i int) *state.RelationUnit {
		unit, err := riak.AddUnit()
		c.Assert(err, gc.IsNil)
		err = unit.SetPrivateAddress(fmt.Sprintf("riak%d.example.com", i))
		c.Assert(err, gc.IsNil)
		ru, err := rel.Unit(unit)
		c.Assert(err, gc.IsNil)
		c.Assert(ru.Endpoint(), gc.Equals, riakEP)
		return ru
	}
	ru0 := addUnit(0)
	ru1 := addUnit(1)
	ru2 := addUnit(2)

	// ---------- Single unit ----------

	// Start watching the relation from the perspective of the first unit.
	w0 := ru0.Watch()
	defer testing.AssertStop(c, w0)
	s.assertChange(c, w0, nil, nil)
	s.assertNoChange(c, w0)

	// Join the first unit to the relation, and change the settings, and
	// check that nothing apparently happens.
	err = ru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s0 := changeSettings(c, ru0)
	s.assertNoChange(c, w0)

	// ---------- Two units ----------

	// Now join another unit to the relation...
	err = ru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)

	// ...and check that the first relation unit sees the change.
	s1, err := ru1.Settings()
	c.Assert(err, gc.IsNil)
	expectChanged := map[string]map[string]interface{}{
		"riak/1": s1.Map(),
	}
	s.assertChange(c, w0, expectChanged, nil)
	s.assertNoChange(c, w0)

	// Join again, check it's a no-op.
	err = ru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertNoChange(c, w0)

	// Start watching the relation from the perspective of the second unit,
	// and check that it sees the right state.
	w1 := ru1.Watch()
	defer testing.AssertStop(c, w1)
	expectChanged = map[string]map[string]interface{}{
		"riak/0": s0.Map(),
	}
	s.assertChange(c, w1, expectChanged, nil)
	s.assertNoChange(c, w1)

	// ---------- Three units ----------

	// Whoa, it works. Ok, check the third unit's opinion of the state.
	w2 := ru2.Watch()
	defer testing.AssertStop(c, w2)
	expectChanged = map[string]map[string]interface{}{
		"riak/0": s0.Map(),
		"riak/1": s1.Map(),
	}
	s.assertChange(c, w2, expectChanged, nil)
	s.assertNoChange(c, w2)

	// Join the third unit, and check the first and second units see it.
	err = ru2.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s2, err := ru2.Settings()
	c.Assert(err, gc.IsNil)
	expectChanged = map[string]map[string]interface{}{
		"riak/2": s2.Map(),
	}
	s.assertChange(c, w0, expectChanged, nil)
	s.assertNoChange(c, w0)
	s.assertChange(c, w1, expectChanged, nil)
	s.assertNoChange(c, w1)

	// Change the second unit's settings, and check that only
	// the first and third see changes.
	s1 = changeSettings(c, ru1)
	s.assertNoChange(c, w1)
	expectChanged = map[string]map[string]interface{}{
		"riak/1": s1.Map(),
	}
	s.assertChange(c, w0, expectChanged, nil)
	s.assertNoChange(c, w0)
	s.assertChange(c, w2, expectChanged, nil)
	s.assertNoChange(c, w2)

	// ---------- Two units again ----------

	// Depart the second unit, and check that the first and third detect it.
	err = ru1.LeaveScope()
	c.Assert(err, gc.IsNil)
	expectDeparted := []string{"riak/1"}
	s.assertChange(c, w0, nil, expectDeparted)
	s.assertNoChange(c, w0)
	s.assertChange(c, w2, nil, expectDeparted)
	s.assertNoChange(c, w2)

	// Change its settings, and check the others don't observe anything.
	s1 = changeSettings(c, ru1)
	s.assertNoChange(c, w0)
	s.assertNoChange(c, w2)

	// Check no spurious events showed up on the second unit's watch, and check
	// it closes cleanly.
	s.assertNoChange(c, w1)
	testing.AssertStop(c, w1)

	// OK, we're done here. Cleanup, and error detection during same,
	// will be handled by the deferred kill/stop calls. Phew.
}

func (s *WatchScopeSuite) TestProviderRequirerGlobal(c *gc.C) {
	// Create a pair of services and a relation between them.
	mysql, err := s.State.AddService("mysql", s.AddTestingCharm(c, "mysql"))
	c.Assert(err, gc.IsNil)
	mysqlEP, err := mysql.Endpoint("server")
	c.Assert(err, gc.IsNil)
	wordpress, err := s.State.AddService("wordpress", s.AddTestingCharm(c, "wordpress"))
	c.Assert(err, gc.IsNil)
	wordpressEP, err := wordpress.Endpoint("db")
	c.Assert(err, gc.IsNil)
	rel, err := s.State.AddRelation(mysqlEP, wordpressEP)
	c.Assert(err, gc.IsNil)

	// Add some units to the services and set their private addresses.
	addUnit := func(srv *state.Service, sub string, ep state.Endpoint) *state.RelationUnit {
		unit, err := srv.AddUnit()
		c.Assert(err, gc.IsNil)
		ru, err := rel.Unit(unit)
		c.Assert(err, gc.IsNil)
		c.Assert(ru.Endpoint(), gc.Equals, ep)
		return ru
	}
	msru0 := addUnit(mysql, "ms0", mysqlEP)
	msru1 := addUnit(mysql, "ms1", mysqlEP)
	wpru0 := addUnit(wordpress, "wp0", wordpressEP)
	wpru1 := addUnit(wordpress, "wp1", wordpressEP)

	// ---------- Single role active ----------

	// Watch the relation from the perspective of the first provider unit and
	// check initial event.
	msw0 := msru0.Watch()
	defer testing.AssertStop(c, msw0)
	s.assertChange(c, msw0, nil, nil)
	s.assertNoChange(c, msw0)

	// Join the unit to the relation, change its settings, and check that
	// nothing apparently happens.
	err = msru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	mss0 := changeSettings(c, msru0)
	s.assertNoChange(c, msw0)

	// Join the second provider unit, start its watch, and check what it thinks the
	// state of the relation is.
	err = msru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	msw1 := msru1.Watch()
	defer testing.AssertStop(c, msw1)
	s.assertChange(c, msw1, nil, nil)
	s.assertNoChange(c, msw1)

	// Change the unit's settings, and check that neither provider unit
	// observes any change.
	mss1 := changeSettings(c, msru1)
	s.assertNoChange(c, msw1)
	s.assertNoChange(c, msw0)

	// ---------- Two roles active ----------

	// Start watches from both requirer units' perspectives, and check that
	// they see the provider units.
	expectChanged := map[string]map[string]interface{}{
		"mysql/0": mss0.Map(),
		"mysql/1": mss1.Map(),
	}
	wpw0 := wpru0.Watch()
	defer testing.AssertStop(c, wpw0)
	s.assertChange(c, wpw0, expectChanged, nil)
	s.assertNoChange(c, wpw0)
	wpw1 := wpru1.Watch()
	defer testing.AssertStop(c, wpw1)
	s.assertChange(c, wpw1, expectChanged, nil)
	s.assertNoChange(c, wpw1)

	// Join the first requirer unit, and check the provider units see it.
	err = wpru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	wps0, err := wpru0.Settings()
	c.Assert(err, gc.IsNil)
	expectChanged = map[string]map[string]interface{}{
		"wordpress/0": wps0.Map(),
	}
	s.assertChange(c, msw0, expectChanged, nil)
	s.assertNoChange(c, msw0)
	s.assertChange(c, msw1, expectChanged, nil)
	s.assertNoChange(c, msw1)

	// Join again, check no-op.
	err = wpru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertNoChange(c, msw0)
	s.assertNoChange(c, msw1)

	// Join the second requirer, and check the provider units see the change.
	err = wpru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	wps1, err := wpru1.Settings()
	c.Assert(err, gc.IsNil)
	expectChanged = map[string]map[string]interface{}{
		"wordpress/1": wps1.Map(),
	}
	s.assertChange(c, msw0, expectChanged, nil)
	s.assertNoChange(c, msw0)
	s.assertChange(c, msw1, expectChanged, nil)
	s.assertNoChange(c, msw1)

	// Verify that neither requirer has observed any change to the relation.
	s.assertNoChange(c, wpw0)
	s.assertNoChange(c, wpw1)

	// Change settings for the first requirer, check providers see it...
	wps0 = changeSettings(c, wpru0)
	expectChanged = map[string]map[string]interface{}{
		"wordpress/0": wps0.Map(),
	}
	s.assertChange(c, msw0, expectChanged, nil)
	s.assertNoChange(c, msw0)
	s.assertChange(c, msw1, expectChanged, nil)
	s.assertNoChange(c, msw1)

	// ...and requirers don't.
	s.assertNoChange(c, wpw0)
	s.assertNoChange(c, wpw1)

	// Depart the second requirer and check the providers see it...
	err = wpru1.LeaveScope()
	c.Assert(err, gc.IsNil)
	expectDeparted := []string{"wordpress/1"}
	s.assertChange(c, msw0, nil, expectDeparted)
	s.assertNoChange(c, msw0)
	s.assertChange(c, msw1, nil, expectDeparted)
	s.assertNoChange(c, msw1)

	// ...and the requirers don't.
	s.assertNoChange(c, wpw0)
	s.assertNoChange(c, wpw1)

	// Cleanup handled by defers as before.
}

func (s *WatchScopeSuite) TestProviderRequirerContainer(c *gc.C) {
	// Create a pair of services and a relation between them.
	mysql, err := s.State.AddService("mysql", s.AddTestingCharm(c, "mysql"))
	c.Assert(err, gc.IsNil)
	mysqlEP, err := mysql.Endpoint("juju-info")
	c.Assert(err, gc.IsNil)
	logging, err := s.State.AddService("logging", s.AddTestingCharm(c, "logging"))
	c.Assert(err, gc.IsNil)
	loggingEP, err := logging.Endpoint("info")
	c.Assert(err, gc.IsNil)
	rel, err := s.State.AddRelation(mysqlEP, loggingEP)
	c.Assert(err, gc.IsNil)

	// Change mysqlEP to match the endpoint that will actually be used by the relation.
	mysqlEP.Scope = charm.ScopeContainer

	// Add some units to the services and set their private addresses.
	addUnits := func(i int) (*state.RelationUnit, *state.RelationUnit) {
		msu, err := mysql.AddUnit()
		c.Assert(err, gc.IsNil)
		msru, err := rel.Unit(msu)
		c.Assert(err, gc.IsNil)
		c.Assert(msru.Endpoint(), gc.Equals, mysqlEP)
		err = msru.EnterScope(nil)
		c.Assert(err, gc.IsNil)
		err = msru.LeaveScope()
		c.Assert(err, gc.IsNil)
		lgu, err := s.State.Unit("logging/" + strconv.Itoa(i))
		c.Assert(err, gc.IsNil)
		lgru, err := rel.Unit(lgu)
		c.Assert(err, gc.IsNil)
		c.Assert(lgru.Endpoint(), gc.Equals, loggingEP)
		return msru, lgru
	}
	msru0, lgru0 := addUnits(0)
	msru1, lgru1 := addUnits(1)

	// ---------- Single role active ----------

	// Start watching the relation from the perspective of the first unit, and
	// check the initial event.
	msw0 := msru0.Watch()
	defer testing.AssertStop(c, msw0)
	s.assertChange(c, msw0, nil, nil)
	s.assertNoChange(c, msw0)

	// Join the unit to the relation, change its settings, and check that
	// nothing apparently happens.
	err = msru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	mss0 := changeSettings(c, msru0)
	s.assertNoChange(c, msw0)

	// Watch the relation from the perspective of the second provider, and
	// check initial event.
	msw1 := msru1.Watch()
	defer testing.AssertStop(c, msw1)
	s.assertChange(c, msw1, nil, nil)
	s.assertNoChange(c, msw1)

	// Join the second provider unit to the relation, and check that neither
	// watching unit observes any change.
	err = msru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	s.assertNoChange(c, msw1)
	s.assertNoChange(c, msw0)

	// Change the unit's settings, and check that nothing apparently happens.
	mss1 := changeSettings(c, msru1)
	s.assertNoChange(c, msw1)
	s.assertNoChange(c, msw0)

	// ---------- Two roles active ----------

	// Start a watch from the first requirer unit's perspective, and check it
	// only sees the first provider (with which it shares a container).
	lgw0 := lgru0.Watch()
	defer testing.AssertStop(c, lgw0)
	expectChanged := map[string]map[string]interface{}{
		"mysql/0": mss0.Map(),
	}
	s.assertChange(c, lgw0, expectChanged, nil)
	s.assertNoChange(c, lgw0)

	// Join the first requirer unit, and check that only the first provider
	// observes the change.
	err = lgru0.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	lgs0, err := lgru0.Settings()
	c.Assert(err, gc.IsNil)
	expectChanged = map[string]map[string]interface{}{
		"logging/0": lgs0.Map(),
	}
	s.assertChange(c, msw0, expectChanged, nil)
	s.assertNoChange(c, msw0)
	s.assertNoChange(c, msw1)
	s.assertNoChange(c, lgw0)

	// Watch from the second requirer's perspective, and check it only sees the
	// second provider.
	lgw1 := lgru1.Watch()
	defer testing.AssertStop(c, lgw1)
	expectChanged = map[string]map[string]interface{}{
		"mysql/1": mss1.Map(),
	}
	s.assertChange(c, lgw1, expectChanged, nil)
	s.assertNoChange(c, lgw1)

	// Join the second requirer, and check that the first provider observes it...
	err = lgru1.EnterScope(nil)
	c.Assert(err, gc.IsNil)
	lgs1, err := lgru1.Settings()
	c.Assert(err, gc.IsNil)
	expectChanged = map[string]map[string]interface{}{
		"logging/1": lgs1.Map(),
	}
	s.assertChange(c, msw1, expectChanged, nil)
	s.assertNoChange(c, msw1)

	// ...and that nothing else sees anything.
	s.assertNoChange(c, msw0)
	s.assertNoChange(c, lgw0)
	s.assertNoChange(c, lgw1)

	// Change the second provider's settings and check that the second
	// requirer notices...
	mss1 = changeSettings(c, msru1)
	expectChanged = map[string]map[string]interface{}{
		"mysql/1": mss1.Map(),
	}
	s.assertChange(c, lgw1, expectChanged, nil)
	s.assertNoChange(c, lgw1)

	// ...but that nothing else does.
	s.assertNoChange(c, msw0)
	s.assertNoChange(c, msw1)
	s.assertNoChange(c, msw0)

	// Finally, depart the first provider, and check that only the first
	// requirer observes any change.
	err = msru0.LeaveScope()
	c.Assert(err, gc.IsNil)
	expectDeparted := []string{"mysql/0"}
	s.assertChange(c, lgw0, nil, expectDeparted)
	s.assertNoChange(c, lgw0)
	s.assertNoChange(c, lgw1)
	s.assertNoChange(c, msw0)
	s.assertNoChange(c, msw1)

	// Again, I think we're done, and can be comfortable that the appropriate
	// connections are in place.
}

func changeSettings(c *gc.C, ru *state.RelationUnit) *state.Settings {
	node, err := ru.Settings()
	c.Assert(err, gc.IsNil)
	value, _ := node.Get("value")
	v, _ := value.(int)
	node.Set("value", v+1)
	_, err = node.Write()
	c.Assert(err, gc.IsNil)
	return node
}

func (s *WatchScopeSuite) assertChange(
	c *gc.C, w *state.RelationUnitsWatcher,
	changed map[string]map[string]interface{},
	departed []string,
) {
	s.State.StartSync()
	select {
	case ch, ok := <-w.Changes():
		if !ok {
			c.Fatalf("channel closed; watcher error: %#v", w.Err())
		}
		c.Assert(ch.Changed, gc.HasLen, len(changed))
		for name, m := range changed {
			c.Assert(ch.Changed[name].Settings, gc.DeepEquals, m)
		}
		c.Assert(departed, jc.SameContents, ch.Departed)
	case <-time.After(5 * time.Second):
		c.Fatalf("expected changed %#v, departed %#v; got nothing", changed, departed)
	}
}

func (s *WatchScopeSuite) assertNoChange(c *gc.C, w *state.RelationUnitsWatcher) {
	s.State.StartSync()
	select {
	case ch := <-w.Changes():
		c.Fatalf("got %#v, expected nothing", ch)
	case <-time.After(50 * time.Millisecond):
	}
}
{'fraction_non_alphanumeric': 0.15928715185353504, 'fraction_numerical': 0.013227942608582744, 'mean_word_length': 3.996188777165099, 'pattern_counts': {'":': 56, '<': 15, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5739718', 'n_tokens_mistral': 25580, 'n_tokens_neox': 24751, 'n_words': 5883}