package timer_test import ( "errors" "testing" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/timer" "github.com/stretchr/testify/require" ) func tickN(t *timer.BlockTimer, n uint32) { for range n { t.Tick(0) } } // This test emulates inner ring handling of a new epoch and a new block. // "resetting" consists of ticking the current height as well and invoking `Reset`. func TestIRBlockTimer_Reset(t *testing.T) { var baseCounter [2]int const blockDur = uint32(3) bt1 := timer.NewBlockTimer( func() (uint32, error) { return blockDur, nil }, func() { baseCounter[0]++ }) bt2 := timer.NewBlockTimer( func() (uint32, error) { return blockDur, nil }, func() { baseCounter[1]++ }) require.NoError(t, bt1.Reset()) require.NoError(t, bt2.Reset()) run := func(bt *timer.BlockTimer, direct bool) { if direct { bt.Tick(1) require.NoError(t, bt.Reset()) bt.Tick(1) } else { bt.Tick(1) bt.Tick(1) require.NoError(t, bt.Reset()) } bt.Tick(2) bt.Tick(3) } run(bt1, true) run(bt2, false) require.Equal(t, baseCounter[0], baseCounter[1]) } func TestBlockTimer_ResetChangeDuration(t *testing.T) { var dur uint32 = 2 var err error var counter int bt := timer.NewBlockTimer( func() (uint32, error) { return dur, err }, func() { counter++ }) require.NoError(t, bt.Reset()) tickN(bt, 2) require.Equal(t, 1, counter) t.Run("return error", func(t *testing.T) { dur = 5 err = errors.New("my awesome error") require.ErrorIs(t, bt.Reset(), err) tickN(bt, 2) require.Equal(t, 2, counter) }) t.Run("change duration", func(t *testing.T) { dur = 5 err = nil require.NoError(t, bt.Reset()) tickN(bt, 5) require.Equal(t, 3, counter) }) } func TestBlockTimer(t *testing.T) { const blockDur = uint32(10) baseCallCounter := uint32(0) bt := timer.NewBlockTimer(timer.StaticBlockMeter(blockDur), func() { baseCallCounter++ }) require.NoError(t, bt.Reset()) intervalNum := uint32(7) tickN(bt, intervalNum*blockDur) require.Equal(t, intervalNum, uint32(baseCallCounter)) } func TestNewOneTickTimer(t *testing.T) { blockDur := uint32(1) baseCallCounter := 0 bt := timer.NewOneTickTimer(timer.StaticBlockMeter(blockDur), func() { baseCallCounter++ }) require.NoError(t, bt.Reset()) tickN(bt, 10) require.Equal(t, 1, baseCallCounter) // happens once no matter what t.Run("zero duration", func(t *testing.T) { blockDur = uint32(0) baseCallCounter = 0 bt = timer.NewOneTickTimer(timer.StaticBlockMeter(blockDur), func() { baseCallCounter++ }) require.NoError(t, bt.Reset()) tickN(bt, 10) require.Equal(t, 1, baseCallCounter) }) } func TestBlockTimer_TickSameHeight(t *testing.T) { var baseCounter int blockDur := uint32(2) bt := timer.NewBlockTimer( func() (uint32, error) { return blockDur, nil }, func() { baseCounter++ }) require.NoError(t, bt.Reset()) check := func(t *testing.T, h uint32, base int) { for range 2 * int(blockDur) { bt.Tick(h) require.Equal(t, base, baseCounter) } } check(t, 1, 0) check(t, 2, 1) check(t, 3, 1) check(t, 4, 2) t.Run("works the same way after `Reset()`", func(t *testing.T) { t.Run("same block duration", func(t *testing.T) { require.NoError(t, bt.Reset()) baseCounter = 0 check(t, 1, 0) check(t, 2, 1) check(t, 3, 1) check(t, 4, 2) }) t.Run("different block duration", func(t *testing.T) { blockDur = 3 require.NoError(t, bt.Reset()) baseCounter = 0 check(t, 1, 0) check(t, 2, 0) check(t, 3, 1) check(t, 4, 1) check(t, 5, 1) check(t, 6, 2) }) }) }