summaryrefslogtreecommitdiff
path: root/internal/statsengine/process_test.go
blob: add5ae1f25d591c3e819fdd581846fdabf4be45b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package statsengine

import (
	"ior/internal/event"
	"ior/internal/types"
	"math"
	"testing"
	"time"
)

func TestProcessAccumulatorBasicStats(t *testing.T) {
	acc := newProcessAccumulator()

	acc.Add(newProcessPair(2000, "proc-a", 10, 100))
	acc.Add(newProcessPair(2000, "proc-a", 30, 50))
	acc.Add(newProcessPair(3000, "proc-b", 20, 25))

	snap := acc.Snapshot(2 * time.Second)
	if len(snap) != 2 {
		t.Fatalf("expected 2 process snapshots, got %d", len(snap))
	}

	p0 := snap[0]
	if p0.PID != 2000 {
		t.Fatalf("expected pid 2000 first, got %d", p0.PID)
	}
	if p0.Comm != "proc-a" {
		t.Fatalf("unexpected comm: got %q", p0.Comm)
	}
	if p0.Syscalls != 2 || p0.Bytes != 150 {
		t.Fatalf("unexpected count/bytes: %+v", p0)
	}
	if p0.AvgLatencyNs != 20 {
		t.Fatalf("unexpected avg latency: got %v", p0.AvgLatencyNs)
	}
	if math.Abs(p0.RatePerSec-1.0) > 1e-9 {
		t.Fatalf("unexpected rate: got %v", p0.RatePerSec)
	}

	p1 := snap[1]
	if p1.PID != 3000 || p1.Comm != "proc-b" || p1.Syscalls != 1 {
		t.Fatalf("unexpected second row: %+v", p1)
	}
}

func TestProcessAccumulatorSortsBySyscallsBytesPid(t *testing.T) {
	acc := newProcessAccumulator()

	acc.Add(newProcessPair(20, "b", 10, 2))
	acc.Add(newProcessPair(10, "a", 10, 3))
	acc.Add(newProcessPair(10, "a", 10, 3))
	acc.Add(newProcessPair(20, "b", 10, 2))

	snap := acc.Snapshot(1 * time.Second)
	if len(snap) != 2 {
		t.Fatalf("expected 2 process snapshots, got %d", len(snap))
	}

	if snap[0].PID != 10 {
		t.Fatalf("expected pid 10 first by bytes tie-breaker, got %d", snap[0].PID)
	}
	if snap[1].PID != 20 {
		t.Fatalf("expected pid 20 second, got %d", snap[1].PID)
	}
}

func TestProcessAccumulatorCommUpdateAndZeroRate(t *testing.T) {
	acc := newProcessAccumulator()

	acc.Add(newProcessPair(7, "", 10, 1))
	acc.Add(newProcessPair(7, "worker", 20, 2))
	snap := acc.Snapshot(0)

	if len(snap) != 1 {
		t.Fatalf("expected 1 snapshot row, got %d", len(snap))
	}
	if snap[0].Comm != "worker" {
		t.Fatalf("expected comm to be updated, got %q", snap[0].Comm)
	}
	if snap[0].RatePerSec != 0 {
		t.Fatalf("expected zero rate on zero elapsed, got %v", snap[0].RatePerSec)
	}
}

func TestProcessAccumulatorResetsOnCommChangeForSamePID(t *testing.T) {
	acc := newProcessAccumulator()

	acc.Add(newProcessPair(42, "old", 100, 10))
	acc.Add(newProcessPair(42, "new", 200, 20))

	snap := acc.Snapshot(time.Second)
	if len(snap) != 1 {
		t.Fatalf("expected 1 snapshot row, got %d", len(snap))
	}
	if snap[0].Comm != "new" {
		t.Fatalf("expected new comm after reset, got %q", snap[0].Comm)
	}
	if snap[0].Syscalls != 1 || snap[0].Bytes != 20 || snap[0].AvgLatencyNs != 200 {
		t.Fatalf("expected counters to reset on comm change, got %+v", snap[0])
	}
}

func TestProcessAccumulatorNilInputs(t *testing.T) {
	var acc *processAccumulator
	acc.Add(nil)
	if got := acc.Snapshot(time.Second); got != nil {
		t.Fatalf("expected nil snapshot from nil accumulator, got %#v", got)
	}

	acc = newProcessAccumulator()
	acc.Add(nil)
	acc.Add(&event.Pair{})
	if got := acc.Snapshot(time.Second); len(got) != 0 {
		t.Fatalf("expected empty snapshot, got %#v", got)
	}
}

func newProcessPair(pid uint32, comm string, duration uint64, bytes uint64) *event.Pair {
	return &event.Pair{
		EnterEv:  &types.RetEvent{Pid: pid},
		Comm:     comm,
		Duration: duration,
		Bytes:    bytes,
	}
}