| 1 | package metrics |
| 2 | |
| 3 | import ( |
| 4 | "math" |
| 5 | "testing" |
| 6 | "time" |
| 7 | ) |
| 8 | |
| 9 | func TestParseProcStat(t *testing.T) { |
| 10 | // user nice system idle iowait irq softirq steal |
| 11 | c := ParseProcStat("cpu 100 20 30 800 50 5 5 0 0 0\ncpu0 1 2 3 4 5 6 7 8 9 10\n") |
| 12 | if c == nil { |
| 13 | t.Fatal("nil for valid input") |
| 14 | } |
| 15 | // Busy = everything except idle(800) and iowait(50) = 160; Total = 1010. |
| 16 | if c.Busy != 160 || c.Total != 1010 { |
| 17 | t.Fatalf("got busy=%d total=%d, want 160/1010", c.Busy, c.Total) |
| 18 | } |
| 19 | if ParseProcStat("") != nil || ParseProcStat("cpu x y z") != nil { |
| 20 | t.Fatal("garbage must parse to nil") |
| 21 | } |
| 22 | } |
| 23 | |
| 24 | func TestCPUFrac(t *testing.T) { |
| 25 | prev := &CPUCounters{Busy: 100, Total: 1000} |
| 26 | cur := &CPUCounters{Busy: 160, Total: 1100} |
| 27 | f := CPUFrac(prev, cur) |
| 28 | if f == nil || math.Abs(*f-0.6) > 1e-9 { // 60 busy / 100 total |
| 29 | t.Fatalf("got %v, want 0.6", f) |
| 30 | } |
| 31 | if CPUFrac(nil, cur) != nil || CPUFrac(prev, nil) != nil { |
| 32 | t.Fatal("nil input must be nil") |
| 33 | } |
| 34 | if CPUFrac(cur, prev) != nil { // counters went backwards (reboot) — no guess |
| 35 | t.Fatal("backwards counters must be nil") |
| 36 | } |
| 37 | |
| 38 | // Total advances but Busy alone regresses: uint64 subtraction of |
| 39 | // cur.Busy-prev.Busy would wrap into a huge garbage value instead of |
| 40 | // going negative, so this must be caught explicitly, not just inferred |
| 41 | // from the Total check. |
| 42 | if got := CPUFrac(&CPUCounters{Busy: 500, Total: 1000}, &CPUCounters{Busy: 100, Total: 1100}); got != nil { |
| 43 | t.Fatalf("Busy regressing while Total advances must be nil, got %v", *got) |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | func TestParseLoadAvg(t *testing.T) { |
| 48 | l1, l5, l15 := ParseLoadAvg("0.52 0.41 0.30 1/234 5678") |
| 49 | if l1 == nil || l5 == nil || l15 == nil { |
| 50 | t.Fatal("nil for valid input") |
| 51 | } |
| 52 | if math.Abs(*l1-0.52) > 1e-9 || math.Abs(*l5-0.41) > 1e-9 || math.Abs(*l15-0.30) > 1e-9 { |
| 53 | t.Fatalf("got %v/%v/%v, want 0.52/0.41/0.30", *l1, *l5, *l15) |
| 54 | } |
| 55 | |
| 56 | l1, l5, l15 = ParseLoadAvg("garbage") |
| 57 | if l1 != nil || l5 != nil || l15 != nil { |
| 58 | t.Fatal("garbage must be all-nil") |
| 59 | } |
| 60 | |
| 61 | l1, l5, l15 = ParseLoadAvg("") |
| 62 | if l1 != nil || l5 != nil || l15 != nil { |
| 63 | t.Fatal("empty must be all-nil") |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | func TestParseCgroupCPUUsec(t *testing.T) { |
| 68 | v := ParseCgroupCPUUsec("usage_usec 4500000\nuser_usec 3000000\nsystem_usec 1500000\n") |
| 69 | if v == nil || *v != 4500000 { |
| 70 | t.Fatalf("got %v, want 4500000", v) |
| 71 | } |
| 72 | if ParseCgroupCPUUsec("") != nil { |
| 73 | t.Fatal("empty must be nil") |
| 74 | } |
| 75 | if ParseCgroupCPUUsec("user_usec 3000000\nsystem_usec 1500000\n") != nil { |
| 76 | t.Fatal("missing usage_usec must be nil") |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | func TestParseCgroupInt(t *testing.T) { |
| 81 | v := ParseCgroupInt("123456\n") |
| 82 | if v == nil || *v != 123456 { |
| 83 | t.Fatalf("got %v, want 123456", v) |
| 84 | } |
| 85 | if ParseCgroupInt("max\n") != nil { |
| 86 | t.Fatal(`"max" must be nil`) |
| 87 | } |
| 88 | if ParseCgroupInt("") != nil { |
| 89 | t.Fatal("empty must be nil") |
| 90 | } |
| 91 | if ParseCgroupInt("not-a-number\n") != nil { |
| 92 | t.Fatal("garbage must be nil") |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | func TestParseCgroupIO(t *testing.T) { |
| 97 | r, w := ParseCgroupIO("259:0 rbytes=1000 wbytes=2000 rios=10 wios=20 dbytes=0 dios=0\n259:1 rbytes=500 wbytes=100 rios=1 wios=1 dbytes=0 dios=0\n") |
| 98 | if r == nil || w == nil || *r != 1500 || *w != 2100 { |
| 99 | t.Fatalf("got r=%v w=%v, want 1500/2100", r, w) |
| 100 | } |
| 101 | |
| 102 | r, w = ParseCgroupIO("") |
| 103 | if r != nil || w != nil { |
| 104 | t.Fatal("empty must be both-nil") |
| 105 | } |
| 106 | |
| 107 | r, w = ParseCgroupIO("garbage line with no equals signs\n") |
| 108 | if r != nil || w != nil { |
| 109 | t.Fatal("garbage must be both-nil") |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | func TestUnitCPUFrac(t *testing.T) { |
| 114 | prev := int64(1_000_000) |
| 115 | cur := int64(4_000_000) |
| 116 | f := UnitCPUFrac(&prev, &cur, 60*time.Second) |
| 117 | if f == nil || math.Abs(*f-0.05) > 1e-9 { |
| 118 | t.Fatalf("got %v, want 0.05", f) |
| 119 | } |
| 120 | |
| 121 | if UnitCPUFrac(nil, &cur, 60*time.Second) != nil { |
| 122 | t.Fatal("nil prev must be nil") |
| 123 | } |
| 124 | if UnitCPUFrac(&prev, nil, 60*time.Second) != nil { |
| 125 | t.Fatal("nil cur must be nil") |
| 126 | } |
| 127 | if UnitCPUFrac(&cur, &prev, 60*time.Second) != nil { |
| 128 | t.Fatal("backwards counters must be nil") |
| 129 | } |
| 130 | if UnitCPUFrac(&prev, &cur, 0) != nil { |
| 131 | t.Fatal("zero elapsed must be nil") |
| 132 | } |
| 133 | if UnitCPUFrac(&prev, &cur, -time.Second) != nil { |
| 134 | t.Fatal("negative elapsed must be nil") |
| 135 | } |
| 136 | } |
| 137 | |