Ejemplo n.º 1
0
func TestAdvanceWithUpdateOffset(t *testing.T) {
	offset := time.Duration(10) * time.Second
	last := time.Unix(0, 0)
	mock := clock.NewMock()
	mock.Add(time.Duration(1) * time.Hour)
	desiredLast := mock.Now().Add(-offset)
	ticker := NewTicker(last, offset, mock)

	last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(10)*time.Millisecond, t)
	assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)

	// lowering offset should see a few more ticks
	offset = time.Duration(5) * time.Second
	ticker.updateOffset(offset)
	desiredLast = mock.Now().Add(-offset)
	last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(9)*time.Millisecond, t)
	assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)

	// advancing clock should see even more ticks
	mock.Add(time.Duration(1) * time.Hour)
	desiredLast = mock.Now().Add(-offset)
	last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(8)*time.Millisecond, t)
	assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)

}
Ejemplo n.º 2
0
func TestTickerNoAdvance(t *testing.T) {

	// it's 00:01:00 now. what are some cases where we don't want the ticker to advance?
	mock := clock.NewMock()
	mock.Add(time.Duration(60) * time.Second)

	type Case struct {
		last   int
		offset int
	}

	// note that some cases add up to now, others go into the future
	cases := []Case{
		{50, 10},
		{50, 30},
		{59, 1},
		{59, 10},
		{59, 30},
		{60, 1},
		{60, 10},
		{60, 30},
		{90, 1},
		{90, 10},
		{90, 30},
	}
	for _, c := range cases {
		last, offset := getCase(c.last, c.offset)
		ticker := NewTicker(last, offset, mock)
		assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
	}
}
Ejemplo n.º 3
0
// Ensure that multiple tickers can be used together.
func TestMock_Ticker_Multi(t *testing.T) {
	var n int32
	clock := clock.NewMock()

	go func() {
		a := clock.Ticker(1 * time.Microsecond)
		b := clock.Ticker(3 * time.Microsecond)

		for {
			select {
			case <-a.C:
				atomic.AddInt32(&n, 1)
			case <-b.C:
				atomic.AddInt32(&n, 100)
			}
		}
	}()
	gosched()

	// Move clock forward.
	clock.Add(10 * time.Microsecond)
	gosched()
	if atomic.LoadInt32(&n) != 310 {
		t.Fatalf("unexpected: %d", n)
	}
}
Ejemplo n.º 4
0
// Ensure that the mock's Ticker can be stopped.
func TestMock_Ticker_Stop(t *testing.T) {
	var n int32
	clock := clock.NewMock()

	// Create a channel to increment every second.
	ticker := clock.Ticker(1 * time.Second)
	go func() {
		for {
			<-ticker.C
			atomic.AddInt32(&n, 1)
		}
	}()
	gosched()

	// Move clock forward.
	clock.Add(5 * time.Second)
	if atomic.LoadInt32(&n) != 5 {
		t.Fatalf("expected 5, got: %d", n)
	}

	ticker.Stop()

	// Move clock forward again.
	clock.Add(5 * time.Second)
	if atomic.LoadInt32(&n) != 5 {
		t.Fatalf("still expected 5, got: %d", n)
	}
}
Ejemplo n.º 5
0
func ExampleMock_Ticker() {
	// Create a new mock clock.
	clock := clock.NewMock()
	count := 0

	// Increment count every mock second.
	go func() {
		ticker := clock.Ticker(1 * time.Second)
		for {
			<-ticker.C
			count++
		}
	}()
	runtime.Gosched()

	// Move the clock forward 10 seconds and print the new value.
	clock.Add(10 * time.Second)
	fmt.Printf("Count is %d after 10 seconds\n", count)

	// Move the clock forward 5 more seconds and print the new value.
	clock.Add(5 * time.Second)
	fmt.Printf("Count is %d after 15 seconds\n", count)

	// Output:
	// Count is 10 after 10 seconds
	// Count is 15 after 15 seconds
}
Ejemplo n.º 6
0
func ExampleMock_After() {
	// Create a new mock clock.
	clock := clock.NewMock()
	count := 0

	// Create a channel to execute after 10 mock seconds.
	go func() {
		<-clock.After(10 * time.Second)
		count = 100
	}()
	runtime.Gosched()

	// Print the starting value.
	fmt.Printf("%s: %d\n", clock.Now().UTC(), count)

	// Move the clock forward 5 seconds and print the value again.
	clock.Add(5 * time.Second)
	fmt.Printf("%s: %d\n", clock.Now().UTC(), count)

	// Move the clock forward 5 seconds to the tick time and check the value.
	clock.Add(5 * time.Second)
	fmt.Printf("%s: %d\n", clock.Now().UTC(), count)

	// Output:
	// 1970-01-01 00:00:00 +0000 UTC: 0
	// 1970-01-01 00:00:05 +0000 UTC: 0
	// 1970-01-01 00:00:10 +0000 UTC: 100
}
Ejemplo n.º 7
0
func TestEtcdKeyMaintainer(t *testing.T) {
	api := new(testEtcdAPI)
	clk := clock.NewMock()
	m := NewKeyMaintainer(api, "/test", "asdf")
	m.clock = clk

	api.On("Set", mock.Anything, "/test", "asdf", mock.Anything).Return(nil, nil)
	go m.Maintain()
	pause()
	api.AssertNumberOfCalls(t, "Set", 1)
	clk.Add(m.Interval - time.Nanosecond)
	api.AssertNumberOfCalls(t, "Set", 1)
	clk.Add(time.Nanosecond + 100)
	api.AssertNumberOfCalls(t, "Set", 2)

	api.On("Delete", mock.Anything, "/test", mock.Anything).Return(nil, nil)
	m.Close()
	pause()

	clk.Add(m.Interval)

	api.AssertNumberOfCalls(t, "Set", 2)
	del := api.Calls[2]
	assert.Equal(t, "Delete", del.Method)
	assert.Equal(t, "/test", del.Arguments.String(1))
}
Ejemplo n.º 8
0
// Ensure we can create a 100 seconds gap in the middle of the time travel
func TestSchedCallsGap(t *testing.T) {
	EnableDebugLogging(testing.Verbose())
	Log.Infoln("TestSchedCallsGap starting")

	const testSecs = 1000
	clk := clock.NewMock()
	schedQueue := NewSchedQueue(clk)
	schedQueue.Start()
	defer schedQueue.Stop()

	c2 := func() time.Time {
		if schedQueue.Count() == testSecs/2 {
			return clk.Now().Add(time.Duration(100) * time.Second)
		}
		return clk.Now().Add(time.Second)
	}

	schedQueue.Add(c2, clk.Now().Add(time.Second))
	schedQueue.Flush()
	for i := 0; i < testSecs; i++ {
		clk.Add(time.Second)
		schedQueue.Flush()
	}

	t.Logf("Now: %s - calls: %d", clk.Now(), schedQueue.Count())
	require.Equal(t, testSecs-100+1, (int)(schedQueue.Count()), "Number of calls")
}
Ejemplo n.º 9
0
func TestSchedCallsStop(t *testing.T) {
	InitDefaultLogging(testing.Verbose())
	Info.Println("TestSchedCallsStop starting")

	const testSecs = 1000
	clk := clock.NewMock()
	schedQueue := NewSchedQueue(clk)
	schedQueue.Start()
	defer schedQueue.Stop()

	c2 := func() time.Time {
		if schedQueue.Count() == testSecs/2 {
			return time.Time{}
		}
		return clk.Now().Add(time.Second)
	}

	schedQueue.Add(c2, clk.Now().Add(time.Second))
	schedQueue.Flush()
	for i := 0; i < testSecs; i++ {
		clk.Add(time.Second)
		schedQueue.Flush()
	}

	t.Logf("Now: %s - calls: %d", clk.Now(), schedQueue.Count())
	require.Equal(t, testSecs/2, (int)(schedQueue.Count()), "Number of calls")
}
Ejemplo n.º 10
0
// Ensure that the mock's Tick channel sends at the correct time.
func TestMock_Tick(t *testing.T) {
	var n int32
	clock := clock.NewMock()

	// Create a channel to increment every 10 seconds.
	go func() {
		tick := clock.Tick(10 * time.Second)
		for {
			<-tick
			atomic.AddInt32(&n, 1)
		}
	}()
	gosched()

	// Move clock forward to just before the first tick.
	clock.Add(9 * time.Second)
	if atomic.LoadInt32(&n) != 0 {
		t.Fatalf("expected 0, got %d", n)
	}

	// Move clock forward to the start of the first tick.
	clock.Add(1 * time.Second)
	if atomic.LoadInt32(&n) != 1 {
		t.Fatalf("expected 1, got %d", n)
	}

	// Move clock forward over several ticks.
	clock.Add(30 * time.Second)
	if atomic.LoadInt32(&n) != 4 {
		t.Fatalf("expected 4, got %d", n)
	}
}
Ejemplo n.º 11
0
func BenchmarkIncomingMetricAmounts(b *testing.B) {
	daemon := New("test", "rates.", "timers.", "gauges.", "counters.", timers.Percentiles{}, 10, 1000, 1000, nil, false, true, true, false)
	daemon.Clock = clock.NewMock()
	daemon.submitFunc = func(c *counters.Counters, g *gauges.Gauges, t *timers.Timers, deadline time.Time) {
	}
	go daemon.RunBare()
	b.ResetTimer()
	counters := make([]*common.Metric, 10)
	for i := 0; i < 10; i++ {
		counters[i] = &common.Metric{
			Bucket:   "test-counter",
			Value:    float64(1),
			Modifier: "c",
			Sampling: float32(1),
		}
	}
	// each operation consists of 100x write (1k * 10 metrics + move clock by 1second)
	// simulating a fake 10k metrics/s load, 1M metrics in total over 100+10s, so 11 flushes
	for n := 0; n < b.N; n++ {
		for j := 0; j < 100; j++ {
			for i := 0; i < 1000; i++ {
				daemon.metricAmounts <- counters
			}
			daemon.Clock.(*clock.Mock).Add(1 * time.Second)
		}
		daemon.Clock.(*clock.Mock).Add(10 * time.Second)
	}

}
Ejemplo n.º 12
0
func init() {
	clock := clock.NewMock()
	backend := memory.NewWithClock(clock)
	check.Suite(&coordinatetest.Suite{
		Coordinate: backend,
		Clock:      clock,
	})
}
Ejemplo n.º 13
0
// newChannelNodeWithHostPort creates a testNode with the address specified by
// the hostport parameter.
func newChannelNodeWithHostPort(t *testing.T, hostport string) *testNode {
	ch, err := tchannel.NewChannel("test", nil)
	require.NoError(t, err, "channel must create successfully")

	node := NewNode("test", hostport, ch.GetSubChannel("test"), &Options{
		Clock: clock.NewMock(),
	})

	return &testNode{node, ch}
}
Ejemplo n.º 14
0
func init() {
	clk := clock.NewMock()
	c, err := postgres.NewWithClock("", clk)
	if err != nil {
		panic(err)
	}
	check.Suite(&coordinatetest.Suite{
		Coordinate: c,
		Clock:      clk,
	})
}
Ejemplo n.º 15
0
// Ensure that the mock's current time can be changed.
func TestMock_Now(t *testing.T) {
	clock := clock.NewMock()
	if now := clock.Now(); !now.Equal(time.Unix(0, 0)) {
		t.Fatalf("expected epoch, got: ", now)
	}

	// Add 10 seconds and check the time.
	clock.Add(10 * time.Second)
	if now := clock.Now(); !now.Equal(time.Unix(10, 0)) {
		t.Fatalf("expected epoch, got: ", now)
	}
}
Ejemplo n.º 16
0
func TestProfileSample(t *testing.T) {

	wantProfile := TimeProfile{
		Total:     10 * time.Second,
		WaitRead:  1 * time.Second,
		WaitWrite: 9 * time.Second,
	}
	clk := clock.NewMock()

	start := clk.Now()

	sleepRead := readFunc(func(p []byte) (int, error) {
		var err error
		if clk.Now().Sub(start) >= wantProfile.Total {
			err = io.EOF
		}
		clk.Sleep(wantProfile.WaitRead / 1000)
		return len(p), err
	})

	sleepWrite := writeFunc(func(p []byte) (int, error) {
		clk.Sleep(wantProfile.WaitWrite / 1000)
		return len(p), nil
	})

	res := make(chan TimeProfile, 1)
	go func() {
		w, r, done := profileSample(clk, sleepWrite, sleepRead, time.Millisecond)
		io.Copy(w, r)
		res <- done().TimeProfile
	}()

	var gotProfile TimeProfile
loop:
	for {
		select {
		case gotProfile = <-res:
			break loop
		default:
			clk.Add(1 * time.Millisecond)
		}
	}

	wantReadRatio := float64(wantProfile.WaitRead) / float64(wantProfile.WaitRead+wantProfile.WaitWrite)
	gotReadRatio := float64(gotProfile.WaitRead) / float64(gotProfile.WaitRead+gotProfile.WaitWrite)

	diff := (math.Max(wantReadRatio, gotReadRatio) - math.Min(wantReadRatio, gotReadRatio)) / math.Max(wantReadRatio, gotReadRatio)
	if diff > 0.05 {
		t.Logf("want=%#v", wantProfile)
		t.Logf(" got=%#v", gotProfile)
		t.Fatalf("profiles are too different: %.2f%% different", 100*diff)
	}
}
Ejemplo n.º 17
0
func TestTickerRetro1Hour(t *testing.T) {
	offset := time.Duration(10) * time.Second
	last := time.Unix(0, 0)
	mock := clock.NewMock()
	mock.Add(time.Duration(1) * time.Hour)
	desiredLast := mock.Now().Add(-offset)
	ticker := NewTicker(last, offset, mock)

	last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(10)*time.Millisecond, t)
	assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)

}
Ejemplo n.º 18
0
// Ensure that the mock's AfterFunc doesn't execute if stopped.
func TestMock_AfterFunc_Stop(t *testing.T) {
	// Execute function after duration.
	clock := clock.NewMock()
	timer := clock.AfterFunc(10*time.Second, func() {
		t.Fatal("unexpected function execution")
	})
	gosched()

	// Stop timer & move clock forward.
	timer.Stop()
	clock.Add(10 * time.Second)
	gosched()
}
Ejemplo n.º 19
0
func (s *RollupTestSuite) SetupTest() {
	s.node = NewNode("test", "127.0.0.1:3001", nil, &Options{
		RollupFlushInterval: 10000 * time.Second,
		RollupMaxUpdates:    10000,
		Clock:               clock.NewMock(),
	})
	s.r = s.node.rollup
	s.incarnation = util.TimeNowMS()
	s.updates = []Change{
		Change{Address: "one"},
		Change{Address: "two"},
	}
}
Ejemplo n.º 20
0
func (s *Suite) SetUpTest(t *testing.T) {
	s.Clock = clock.NewMock()
	backend := memory.NewWithClock(s.Clock)
	var err error
	s.Namespace, err = backend.Namespace("")
	if !assert.NoError(t, err) {
		t.FailNow()
	}
	s.Worker = Worker{
		Namespace: s.Namespace,
	}
	s.Bit = false
	s.GotWork = make(chan bool)
	s.Finished = make(chan string)
	s.Stop = make(chan struct{})

	s.Worker.Tasks = map[string]func(context.Context, []coordinate.Attempt){
		"sanity": func(ctx context.Context, attempts []coordinate.Attempt) {
			if assert.Len(t, attempts, 1) {
				assert.Equal(t, "unit", attempts[0].WorkUnit().Name())
				assert.Equal(t, "spec", attempts[0].WorkUnit().WorkSpec().Name())
				s.Bit = true
				err := attempts[0].Finish(nil)
				assert.NoError(t, err, "finishing attempt in sanity")
			}
		},

		"timeout": func(ctx context.Context, attempts []coordinate.Attempt) {
			if !assert.Len(t, attempts, 1) {
				return
			}
			select {
			case <-ctx.Done():
				s.Bit = false
				status, err := attempts[0].Status()
				if assert.NoError(t, err) && status == coordinate.Pending {
					err = attempts[0].Fail(nil)
					assert.NoError(t, err, "failing attempt in timeout (status=%v)", status)
				}
			case <-s.Stop:
				s.Bit = true
				status, err := attempts[0].Status()
				if assert.NoError(t, err) && status == coordinate.Pending {
					err = attempts[0].Finish(nil)
					assert.NoError(t, err, "finishing attempt in timeout (status=%v)", status)
				}
			}
		},
	}

}
Ejemplo n.º 21
0
func (s *RingpopTestSuite) SetupTest() {
	s.mockClock = clock.NewMock()

	ch, err := tchannel.NewChannel("test", nil)
	s.NoError(err, "channel must create successfully")
	s.channel = ch

	s.ringpop, err = New("test", Identity("127.0.0.1:3001"), Channel(ch), Clock(s.mockClock))
	s.NoError(err, "Ringpop must create successfully")

	s.mockRingpop = &mocks.Ringpop{}
	s.mockSwimNode = &mocks.SwimNode{}

	s.mockSwimNode.On("Destroy").Return()
}
Ejemplo n.º 22
0
// newChannelNode creates a testNode with a listening channel and associated
// SWIM node. The channel listens on a random port assigned by the OS.
func newChannelNode(t *testing.T) *testNode {
	ch, err := tchannel.NewChannel("test", nil)
	require.NoError(t, err, "channel must create successfully")

	// Set the channel listening so it binds to the socket and we get a port
	// allocated by the OS
	err = ch.ListenAndServe("127.0.0.1:0")
	require.NoError(t, err, "channel must listen")

	hostport := ch.PeerInfo().HostPort
	node := NewNode("test", hostport, ch.GetSubChannel("test"), &Options{
		Clock: clock.NewMock(),
	})

	return &testNode{node, ch}
}
Ejemplo n.º 23
0
func TestStat(t *testing.T) {
	mock := clock.NewMock()
	app.Clock = mock
	fixture := Whisper{
		graphPrefix: "bing.bang.",
	}
	fixture.in = make(chan *points.Points)
	go func() {
		output := <-fixture.in
		expected := points.OnePoint(
			"bing.bang.persister.foo.bar",
			1.5,
			0,
		)
		assert.Equal(t, output, expected)
	}()
	fixture.Stat("foo.bar", 1.5)

}
Ejemplo n.º 24
0
func BenchmarkIncomingMetrics(b *testing.B) {
	daemon := New("test", "rates.", "timers.", "gauges.", "counters.", timers.Percentiles{}, 10, 1000, 1000, nil, false, true, true, false)
	daemon.Clock = clock.NewMock()
	total := float64(0)
	totalLock := sync.Mutex{}
	daemon.submitFunc = func(c *counters.Counters, g *gauges.Gauges, t *timers.Timers, deadline time.Time) {
		totalLock.Lock()
		total += c.Values["service_is_statsdaemon.instance_is_test.direction_is_in.statsd_type_is_counter.target_type_is_count.unit_is_Metric"]
		totalLock.Unlock()
	}
	go daemon.RunBare()
	b.ResetTimer()
	counters := make([]*common.Metric, 10)
	for i := 0; i < 10; i++ {
		counters[i] = &common.Metric{
			Bucket:   "test-counter",
			Value:    float64(1),
			Modifier: "c",
			Sampling: float32(1),
		}
	}
	// each operation consists of 100x write (1k * 10 metrics + move clock by 1second)
	// simulating a fake 10k metrics/s load, 1M metrics in total over 100+10s, so 11 flushes
	for n := 0; n < b.N; n++ {
		totalLock.Lock()
		total = 0
		totalLock.Unlock()
		for j := 0; j < 100; j++ {
			for i := 0; i < 1000; i++ {
				daemon.Metrics <- counters
			}
			daemon.Clock.(*clock.Mock).Add(1 * time.Second)
		}
		daemon.Clock.(*clock.Mock).Add(10 * time.Second)
		totalLock.Lock()
		if total != float64(1000000) {
			panic(fmt.Sprintf("didn't see 1M counters. only saw %f", total))
		}
		totalLock.Unlock()
	}

}
Ejemplo n.º 25
0
// Ensure that the mock's Ticker channel sends at the correct time.
func TestMock_Ticker(t *testing.T) {
	var n int32
	clock := clock.NewMock()

	// Create a channel to increment every microsecond.
	go func() {
		ticker := clock.Ticker(1 * time.Microsecond)
		for {
			<-ticker.C
			atomic.AddInt32(&n, 1)
		}
	}()
	gosched()

	// Move clock forward.
	clock.Add(10 * time.Microsecond)
	if atomic.LoadInt32(&n) != 10 {
		t.Fatalf("unexpected: %d", n)
	}
}
Ejemplo n.º 26
0
// Ensure that the mock's AfterFunc executes at the correct time.
func TestMock_AfterFunc(t *testing.T) {
	var ok int32
	clock := clock.NewMock()

	// Execute function after duration.
	clock.AfterFunc(10*time.Second, func() {
		atomic.StoreInt32(&ok, 1)
	})

	// Move clock forward to just before the time.
	clock.Add(9 * time.Second)
	if atomic.LoadInt32(&ok) == 1 {
		t.Fatal("too early")
	}

	// Move clock forward to the after channel's time.
	clock.Add(1 * time.Second)
	if atomic.LoadInt32(&ok) == 0 {
		t.Fatal("too late")
	}
}
Ejemplo n.º 27
0
func ExampleMock_AfterFunc() {
	// Create a new mock clock.
	clock := clock.NewMock()
	count := 0

	// Execute a function after 10 mock seconds.
	clock.AfterFunc(10*time.Second, func() {
		count = 100
	})
	runtime.Gosched()

	// Print the starting value.
	fmt.Printf("%s: %d\n", clock.Now().UTC(), count)

	// Move the clock forward 10 seconds and print the new value.
	clock.Add(10 * time.Second)
	fmt.Printf("%s: %d\n", clock.Now().UTC(), count)

	// Output:
	// 1970-01-01 00:00:00 +0000 UTC: 0
	// 1970-01-01 00:00:10 +0000 UTC: 100
}
Ejemplo n.º 28
0
// Ensure that the mock's After channel sends at the correct time.
func TestMock_After(t *testing.T) {
	var ok int32
	clock := clock.NewMock()

	// Create a channel to execute after 10 mock seconds.
	ch := clock.After(10 * time.Second)
	go func(ch <-chan time.Time) {
		<-ch
		atomic.StoreInt32(&ok, 1)
	}(ch)

	// Move clock forward to just before the time.
	clock.Add(9 * time.Second)
	if atomic.LoadInt32(&ok) == 1 {
		t.Fatal("too early")
	}

	// Move clock forward to the after channel's time.
	clock.Add(1 * time.Second)
	if atomic.LoadInt32(&ok) == 0 {
		t.Fatal("too late")
	}
}
Ejemplo n.º 29
0
// Ensure that the mock can sleep for the correct time.
func TestMock_Sleep(t *testing.T) {
	var ok int32
	clock := clock.NewMock()

	// Create a channel to execute after 10 mock seconds.
	go func() {
		clock.Sleep(10 * time.Second)
		atomic.StoreInt32(&ok, 1)
	}()
	gosched()

	// Move clock forward to just before the sleep duration.
	clock.Add(9 * time.Second)
	if atomic.LoadInt32(&ok) == 1 {
		t.Fatal("too early")
	}

	// Move clock forward to the after the sleep duration.
	clock.Add(1 * time.Second)
	if atomic.LoadInt32(&ok) == 0 {
		t.Fatal("too late")
	}
}
Ejemplo n.º 30
0
func TestEtcdDirMaintainer(t *testing.T) {
	api := new(testEtcdAPI)
	clk := clock.NewMock()
	m := NewDirMaintainer(api, "/test", func(m *Maintainer) error {
		_, err := m.API.Set(m.Context, m.Key+"/asdf", "42", nil)
		return err
	})
	m.clock = clk

	api.On("Set", mock.Anything, "/test", "", &etcd.SetOptions{
		TTL:       time.Second * 9,
		Dir:       true,
		PrevExist: etcd.PrevExist,
	}).Return(nil, nil).Once()

	go m.Maintain()
	pause()
	api.AssertNumberOfCalls(t, "Set", 1)

	api.On("Set", mock.Anything, "/test", "", &etcd.SetOptions{
		TTL:       time.Second * 9,
		Dir:       true,
		PrevExist: etcd.PrevExist,
	}).Return(nil, etcd.Error{Code: etcd.ErrorCodeKeyNotFound}).Once()
	api.On("Set", mock.Anything, "/test", "", &etcd.SetOptions{
		TTL: time.Second * 9,
		Dir: true,
	}).Return(nil, nil).Once()
	var opts *etcd.SetOptions
	api.On("Set", mock.Anything, "/test/asdf", "42", opts).Return(nil, nil).Once()

	clk.Add(m.Interval)
	api.AssertNumberOfCalls(t, "Set", 4)

	api.On("Delete", mock.Anything, "/test", mock.Anything).Return(nil, nil)
	m.Close()
}