package astikit import ( "context" "reflect" "sync" "sync/atomic" "testing" "time" ) func TestStater(t *testing.T) { // Update the now function so that it increments by 5s every time stats are computed var c int64 mc := &sync.Mutex{} // Locks c nowPrevious := now defer func() { now = nowPrevious }() mn := &sync.Mutex{} // Locks nowV nowV := time.Unix(c*5, 0) now = func() time.Time { mn.Lock() defer mn.Unlock() return nowV } // Add stats var u1 uint64 v1 := NewAtomicUint64RateStat(&u1) m1 := &StatMetadata{Description: "1"} o1 := StatOptions{Metadata: m1, Valuer: v1} d2 := NewAtomicDuration(0) v2 := NewAtomicDurationPercentageStat(d2) m2 := &StatMetadata{Description: "2"} o2 := StatOptions{Metadata: m2, Valuer: v2} d3 := NewAtomicDuration(0) v3 := NewAtomicDurationAvgStat(d3, &u1) m3 := &StatMetadata{Description: "3"} o3 := StatOptions{Metadata: m3, Valuer: v3} v4 := StatValuerFunc(func(d time.Duration) any { return 42 }) m4 := &StatMetadata{Description: "4"} o4 := StatOptions{Metadata: m4, Valuer: v4} // First time stats are computed, it actually acts as if stats were being updated // Second time stats are computed, results are stored and context is cancelled var ss []StatValue ctx, cancel := context.WithCancel(context.Background()) s := NewStater(StaterOptions{ HandleFunc: func(stats []StatValue) { mc.Lock() defer mc.Unlock() c++ switch c { case 1: atomic.AddUint64(&u1, 10) d2.Add(4 * time.Second) d3.Add(10 * time.Second) mn.Lock() nowV = time.Unix(5, 0) mn.Unlock() case 2: ss = stats cancel() } }, Period: time.Millisecond, }) s.AddStats(o1, o2, o3, o4) s.Start(ctx) defer s.Stop() for _, e := range []StatValue{ {StatMetadata: m1, Value: 2.0}, {StatMetadata: m2, Value: 80.0}, {StatMetadata: m3, Value: time.Second}, {StatMetadata: m4, Value: 42}, } { found := false for _, s := range ss { if reflect.DeepEqual(s, e) { found = true break } } if !found { t.Fatalf("expected %+v, not found", e) } } }