2023-04-24 23:14:21 +03:00
|
|
|
package avg_sliding_window
|
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestSlidingWindow_AddWithTime_Single(t *testing.T) {
|
|
|
|
now := ts("2023-04-21 15:04:05")
|
|
|
|
clock := NewAdjustableClock(now)
|
|
|
|
|
|
|
|
sw := NewSlidingWindow(
|
|
|
|
WithWindowLength(10*time.Second),
|
|
|
|
WithBucketSize(time.Second),
|
|
|
|
WithClock(clock))
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:05"), 5)
|
|
|
|
require.Equal(t, 5.0, sw.Avg())
|
|
|
|
require.Equal(t, 5.0, sw.Sum())
|
|
|
|
require.Equal(t, 1, int(sw.Count()))
|
|
|
|
require.Equal(t, 1, sw.buckets.Size())
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[0].(*bucket).qty))
|
|
|
|
require.Equal(t, 5.0, sw.buckets.Values()[0].(*bucket).sum)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSlidingWindow_AddWithTime_TwoValues_SameBucket(t *testing.T) {
|
|
|
|
now := ts("2023-04-21 15:04:05")
|
|
|
|
clock := NewAdjustableClock(now)
|
|
|
|
|
|
|
|
sw := NewSlidingWindow(
|
|
|
|
WithWindowLength(10*time.Second),
|
|
|
|
WithBucketSize(time.Second),
|
|
|
|
WithClock(clock))
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:05"), 5)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:05"), 5)
|
|
|
|
require.Equal(t, 5.0, sw.Avg())
|
|
|
|
require.Equal(t, 10.0, sw.Sum())
|
|
|
|
require.Equal(t, 2, int(sw.Count()))
|
|
|
|
require.Equal(t, 1, sw.buckets.Size())
|
|
|
|
require.Equal(t, 2, int(sw.buckets.Values()[0].(*bucket).qty))
|
|
|
|
require.Equal(t, 10.0, sw.buckets.Values()[0].(*bucket).sum)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSlidingWindow_AddWithTime_ThreeValues_SameBucket(t *testing.T) {
|
|
|
|
now := ts("2023-04-21 15:04:05")
|
|
|
|
clock := NewAdjustableClock(now)
|
|
|
|
|
|
|
|
sw := NewSlidingWindow(
|
|
|
|
WithWindowLength(10*time.Second),
|
|
|
|
WithBucketSize(time.Second),
|
|
|
|
WithClock(clock))
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:05"), 4)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:05"), 5)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:05"), 6)
|
|
|
|
require.Equal(t, 5.0, sw.Avg())
|
|
|
|
require.Equal(t, 15.0, sw.Sum())
|
|
|
|
require.Equal(t, 3, int(sw.Count()))
|
|
|
|
require.Equal(t, 1, sw.buckets.Size())
|
|
|
|
require.Equal(t, 15.0, sw.buckets.Values()[0].(*bucket).sum)
|
|
|
|
require.Equal(t, 3, int(sw.buckets.Values()[0].(*bucket).qty))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSlidingWindow_AddWithTime_ThreeValues_ThreeBuckets(t *testing.T) {
|
|
|
|
now := ts("2023-04-21 15:04:05")
|
|
|
|
clock := NewAdjustableClock(now)
|
|
|
|
|
|
|
|
sw := NewSlidingWindow(
|
|
|
|
WithWindowLength(10*time.Second),
|
|
|
|
WithBucketSize(time.Second),
|
|
|
|
WithClock(clock))
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:01"), 4)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:02"), 5)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:05"), 6)
|
|
|
|
require.Equal(t, 5.0, sw.Avg())
|
|
|
|
require.Equal(t, 15.0, sw.Sum())
|
|
|
|
require.Equal(t, 3, int(sw.Count()))
|
|
|
|
require.Equal(t, 3, sw.buckets.Size())
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[0].(*bucket).qty))
|
|
|
|
require.Equal(t, 4.0, sw.buckets.Values()[0].(*bucket).sum)
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[1].(*bucket).qty))
|
|
|
|
require.Equal(t, 5.0, sw.buckets.Values()[1].(*bucket).sum)
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[2].(*bucket).qty))
|
|
|
|
require.Equal(t, 6.0, sw.buckets.Values()[2].(*bucket).sum)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSlidingWindow_AddWithTime_OutWindow(t *testing.T) {
|
|
|
|
now := ts("2023-04-21 15:04:05")
|
|
|
|
clock := NewAdjustableClock(now)
|
|
|
|
|
|
|
|
sw := NewSlidingWindow(
|
|
|
|
WithWindowLength(10*time.Second),
|
|
|
|
WithBucketSize(time.Second),
|
|
|
|
WithClock(clock))
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:03:55"), 1000)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:01"), 4)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:02"), 5)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:05"), 6)
|
|
|
|
require.Equal(t, 5.0, sw.Avg())
|
|
|
|
require.Equal(t, 15.0, sw.Sum())
|
|
|
|
require.Equal(t, 3, int(sw.Count()))
|
|
|
|
require.Equal(t, 3, sw.buckets.Size())
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[0].(*bucket).qty))
|
|
|
|
require.Equal(t, 4.0, sw.buckets.Values()[0].(*bucket).sum)
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[1].(*bucket).qty))
|
|
|
|
require.Equal(t, 5.0, sw.buckets.Values()[1].(*bucket).sum)
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[2].(*bucket).qty))
|
|
|
|
require.Equal(t, 6.0, sw.buckets.Values()[2].(*bucket).sum)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSlidingWindow_AdvanceClock(t *testing.T) {
|
|
|
|
now := ts("2023-04-21 15:04:05")
|
|
|
|
clock := NewAdjustableClock(now)
|
|
|
|
|
|
|
|
sw := NewSlidingWindow(
|
|
|
|
WithWindowLength(10*time.Second),
|
|
|
|
WithBucketSize(time.Second),
|
|
|
|
WithClock(clock))
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:01"), 4)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:02"), 5)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:05"), 6)
|
|
|
|
require.Equal(t, 5.0, sw.Avg())
|
|
|
|
require.Equal(t, 15.0, sw.Sum())
|
|
|
|
require.Equal(t, 3, int(sw.Count()))
|
|
|
|
require.Equal(t, 3, sw.buckets.Size())
|
|
|
|
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[0].(*bucket).qty))
|
|
|
|
require.Equal(t, 4.0, sw.buckets.Values()[0].(*bucket).sum)
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[1].(*bucket).qty))
|
|
|
|
require.Equal(t, 5.0, sw.buckets.Values()[1].(*bucket).sum)
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[2].(*bucket).qty))
|
|
|
|
require.Equal(t, 6.0, sw.buckets.Values()[2].(*bucket).sum)
|
|
|
|
|
|
|
|
// up until 15:04:05 we had 3 buckets
|
|
|
|
// let's advance the clock to 15:04:11 and the first data point should be evicted
|
|
|
|
clock.Set(ts("2023-04-21 15:04:11"))
|
|
|
|
require.Equal(t, 5.5, sw.Avg())
|
|
|
|
require.Equal(t, 11.0, sw.Sum())
|
|
|
|
require.Equal(t, 2, int(sw.Count()))
|
|
|
|
require.Equal(t, 2, sw.buckets.Size())
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[0].(*bucket).qty))
|
|
|
|
require.Equal(t, 5.0, sw.buckets.Values()[0].(*bucket).sum)
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[1].(*bucket).qty))
|
|
|
|
require.Equal(t, 6.0, sw.buckets.Values()[1].(*bucket).sum)
|
|
|
|
|
|
|
|
// let's advance the clock to 15:04:12 and another data point should be evicted
|
|
|
|
clock.Set(ts("2023-04-21 15:04:12"))
|
|
|
|
require.Equal(t, 6.0, sw.Avg())
|
|
|
|
require.Equal(t, 6.0, sw.Sum())
|
|
|
|
require.Equal(t, 1, int(sw.Count()))
|
|
|
|
require.Equal(t, 1, sw.buckets.Size())
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[0].(*bucket).qty))
|
|
|
|
require.Equal(t, 6.0, sw.buckets.Values()[0].(*bucket).sum)
|
|
|
|
|
|
|
|
// let's advance the clock to 15:04:25 and all data point should be evicted
|
|
|
|
clock.Set(ts("2023-04-21 15:04:25"))
|
|
|
|
require.Equal(t, 0.0, sw.Avg())
|
|
|
|
require.Equal(t, 0.0, sw.Sum())
|
|
|
|
require.Equal(t, 0, int(sw.Count()))
|
|
|
|
require.Equal(t, 0, sw.buckets.Size())
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSlidingWindow_MultipleValPerBucket(t *testing.T) {
|
|
|
|
now := ts("2023-04-21 15:04:05")
|
|
|
|
clock := NewAdjustableClock(now)
|
|
|
|
|
|
|
|
sw := NewSlidingWindow(
|
|
|
|
WithWindowLength(10*time.Second),
|
|
|
|
WithBucketSize(time.Second),
|
|
|
|
WithClock(clock))
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:01"), 4)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:01"), 12)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:02"), 5)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:02"), 15)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:05"), 6)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:05"), 3)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:05"), 1)
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:05"), 3)
|
|
|
|
require.Equal(t, 6.125, sw.Avg())
|
|
|
|
require.Equal(t, 49.0, sw.Sum())
|
|
|
|
require.Equal(t, 8, int(sw.Count()))
|
|
|
|
require.Equal(t, 3, sw.buckets.Size())
|
|
|
|
require.Equal(t, 2, int(sw.buckets.Values()[0].(*bucket).qty))
|
|
|
|
require.Equal(t, 16.0, sw.buckets.Values()[0].(*bucket).sum)
|
|
|
|
require.Equal(t, 2, int(sw.buckets.Values()[1].(*bucket).qty))
|
|
|
|
require.Equal(t, 20.0, sw.buckets.Values()[1].(*bucket).sum)
|
|
|
|
require.Equal(t, 4, int(sw.buckets.Values()[2].(*bucket).qty))
|
|
|
|
require.Equal(t, 13.0, sw.buckets.Values()[2].(*bucket).sum)
|
|
|
|
|
|
|
|
// up until 15:04:05 we had 3 buckets
|
|
|
|
// let's advance the clock to 15:04:11 and the first data point should be evicted
|
|
|
|
clock.Set(ts("2023-04-21 15:04:11"))
|
|
|
|
require.Equal(t, 5.5, sw.Avg())
|
|
|
|
require.Equal(t, 33.0, sw.Sum())
|
|
|
|
require.Equal(t, 6, int(sw.Count()))
|
|
|
|
require.Equal(t, 2, sw.buckets.Size())
|
|
|
|
require.Equal(t, 2, int(sw.buckets.Values()[0].(*bucket).qty))
|
|
|
|
require.Equal(t, 20.0, sw.buckets.Values()[0].(*bucket).sum)
|
|
|
|
require.Equal(t, 4, int(sw.buckets.Values()[1].(*bucket).qty))
|
|
|
|
require.Equal(t, 13.0, sw.buckets.Values()[1].(*bucket).sum)
|
|
|
|
|
|
|
|
// let's advance the clock to 15:04:12 and another data point should be evicted
|
|
|
|
clock.Set(ts("2023-04-21 15:04:12"))
|
|
|
|
require.Equal(t, 3.25, sw.Avg())
|
|
|
|
require.Equal(t, 13.0, sw.Sum())
|
|
|
|
require.Equal(t, 4, int(sw.Count()))
|
|
|
|
require.Equal(t, 1, sw.buckets.Size())
|
|
|
|
require.Equal(t, 4, int(sw.buckets.Values()[0].(*bucket).qty))
|
|
|
|
require.Equal(t, 13.0, sw.buckets.Values()[0].(*bucket).sum)
|
|
|
|
|
|
|
|
// let's advance the clock to 15:04:25 and all data point should be evicted
|
|
|
|
clock.Set(ts("2023-04-21 15:04:25"))
|
|
|
|
require.Equal(t, 0.0, sw.Avg())
|
|
|
|
require.Equal(t, 0, sw.buckets.Size())
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSlidingWindow_CustomBucket(t *testing.T) {
|
|
|
|
now := ts("2023-04-21 15:04:05")
|
|
|
|
clock := NewAdjustableClock(now)
|
|
|
|
|
|
|
|
sw := NewSlidingWindow(
|
|
|
|
WithWindowLength(30*time.Second),
|
|
|
|
WithBucketSize(10*time.Second),
|
|
|
|
WithClock(clock))
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:03:49"), 5) // key: 03:50, sum: 5.0
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:02"), 15) // key: 04:00
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:03"), 5) // key: 04:00
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:04"), 1) // key: 04:00, sum: 21.0
|
|
|
|
sw.AddWithTime(ts("2023-04-21 15:04:05"), 3) // key: 04:10, sum: 3.0
|
|
|
|
require.Equal(t, 5.8, sw.Avg())
|
|
|
|
require.Equal(t, 29.0, sw.Sum())
|
|
|
|
require.Equal(t, 5, int(sw.Count()))
|
|
|
|
require.Equal(t, 3, sw.buckets.Size())
|
|
|
|
require.Equal(t, 5.0, sw.buckets.Values()[0].(*bucket).sum)
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[0].(*bucket).qty))
|
|
|
|
require.Equal(t, 21.0, sw.buckets.Values()[1].(*bucket).sum)
|
|
|
|
require.Equal(t, 3, int(sw.buckets.Values()[1].(*bucket).qty))
|
|
|
|
require.Equal(t, 3.0, sw.buckets.Values()[2].(*bucket).sum)
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[2].(*bucket).qty))
|
|
|
|
|
|
|
|
// up until 15:04:05 we had 3 buckets
|
|
|
|
// let's advance the clock to 15:04:21 and the first data point should be evicted
|
|
|
|
clock.Set(ts("2023-04-21 15:04:21"))
|
|
|
|
require.Equal(t, 6.0, sw.Avg())
|
|
|
|
require.Equal(t, 24.0, sw.Sum())
|
|
|
|
require.Equal(t, 4, int(sw.Count()))
|
|
|
|
require.Equal(t, 2, sw.buckets.Size())
|
|
|
|
require.Equal(t, 21.0, sw.buckets.Values()[0].(*bucket).sum)
|
|
|
|
require.Equal(t, 3, int(sw.buckets.Values()[0].(*bucket).qty))
|
|
|
|
require.Equal(t, 3.0, sw.buckets.Values()[1].(*bucket).sum)
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[1].(*bucket).qty))
|
|
|
|
|
|
|
|
// let's advance the clock to 15:04:32 and another data point should be evicted
|
|
|
|
clock.Set(ts("2023-04-21 15:04:32"))
|
|
|
|
require.Equal(t, 3.0, sw.Avg())
|
|
|
|
require.Equal(t, 3.0, sw.Sum())
|
|
|
|
require.Equal(t, 1, sw.buckets.Size())
|
|
|
|
require.Equal(t, 1, int(sw.Count()))
|
|
|
|
require.Equal(t, 3.0, sw.buckets.Values()[0].(*bucket).sum)
|
|
|
|
require.Equal(t, 1, int(sw.buckets.Values()[0].(*bucket).qty))
|
|
|
|
|
|
|
|
// let's advance the clock to 15:04:46 and all data point should be evicted
|
|
|
|
clock.Set(ts("2023-04-21 15:04:46"))
|
|
|
|
require.Equal(t, 0.0, sw.Avg())
|
|
|
|
require.Equal(t, 0.0, sw.Sum())
|
|
|
|
require.Equal(t, 0, int(sw.Count()))
|
|
|
|
require.Equal(t, 0, sw.buckets.Size())
|
|
|
|
}
|
|
|
|
|
|
|
|
// ts is a convenient method that must parse a time.Time from a string in format `"2006-01-02 15:04:05"`
|
|
|
|
func ts(s string) time.Time {
|
2024-04-09 19:54:15 +03:00
|
|
|
t, err := time.Parse(time.DateTime, s)
|
2023-04-24 23:14:21 +03:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return t
|
|
|
|
}
|