dockertop 1.0.0
htop-style view of Docker containers and the processes inside them.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestParseStat(t *testing.T) {
|
||||
// comm with spaces and a ')' must not shift the fields
|
||||
in := "42 (my (odd) proc) S 1 42 42 0 -1 4194560 100 0 0 0 250 50 0 0 20 0 3 0 1000 123456789 1500 18446744073709551615"
|
||||
p, ok := parseStat([]byte(in))
|
||||
if !ok || p.Comm != "my (odd) proc" || p.State != 'S' || p.PPID != 1 || p.Ticks != 300 ||
|
||||
p.Pri != 20 || p.Nice != 0 || p.Threads != 3 || p.Virt != 123456789 {
|
||||
t.Fatalf("bad parse: %+v", p)
|
||||
}
|
||||
if cpuTime(300) != "0:03.00" || cpuTime(100*3725) != "1h02:05" {
|
||||
t.Fatalf("cpuTime: %s %s", cpuTime(300), cpuTime(100*3725))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDemux(t *testing.T) {
|
||||
frame := func(stream byte, s string) string {
|
||||
n := len(s)
|
||||
return string([]byte{stream, 0, 0, 0, byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)}) + s
|
||||
}
|
||||
in := frame(1, "out line\n") + frame(2, "err line\n")
|
||||
if got := string(demux([]byte(in))); got != "out line\nerr line\n" {
|
||||
t.Fatalf("demux: %q", got)
|
||||
}
|
||||
if got := ansi.ReplaceAllString("\x1b[31mred\x1b[0m", ""); got != "red" {
|
||||
t.Fatalf("ansi: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatsRates(t *testing.T) {
|
||||
t0 := time.Unix(1000, 0)
|
||||
var s stats
|
||||
s.Read = t0
|
||||
s.CPU.Usage.Total = 1e9
|
||||
s.Mem.Usage, s.Mem.Limit = 300, 1000
|
||||
s.Mem.Stats = map[string]uint64{"inactive_file": 100}
|
||||
var prev Container
|
||||
applyStats(&prev, s, Container{})
|
||||
if prev.HasRate || prev.Mem != 200 {
|
||||
t.Fatalf("first sample: %+v", prev)
|
||||
}
|
||||
s.Read = t0.Add(2 * time.Second)
|
||||
s.CPU.Usage.Total = 2e9 // 1 CPU-second over 2s = 50%
|
||||
var c Container
|
||||
applyStats(&c, s, prev)
|
||||
if !c.HasRate || c.CPU != 50 || c.MemPct() != 20 {
|
||||
t.Fatalf("rates: %+v", c)
|
||||
}
|
||||
if rate(5, 10, 1) != 0 {
|
||||
t.Fatal("counter reset must give 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupRows(t *testing.T) {
|
||||
cs := []Container{
|
||||
{ID: "a", Name: "web-1", Project: "web", CPU: 30, State: "running"},
|
||||
{ID: "b", Name: "solo", CPU: 20, State: "running"},
|
||||
{ID: "c", Name: "web-db", Project: "web", CPU: 5, State: "exited"},
|
||||
}
|
||||
byCPU := func(x, y Container) bool { return x.CPU > y.CPU }
|
||||
r := groupRows(cs, byCPU)
|
||||
var got []string
|
||||
for _, c := range r {
|
||||
got = append(got, c.Tree+c.Name)
|
||||
}
|
||||
if strings.Join(got, ",") != "web,├─ web-1,└─ web-db,solo" || r[0].CPU != 35 || r[0].Status != "1/2 running" {
|
||||
t.Fatalf("bad grouping: %q %+v", got, r[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
|
||||
c := defaults
|
||||
c.Sort, c.ProcSort, c.Tree, c.All, c.Refresh = "MEM", "TIME", true, false, 0.5
|
||||
if err := c.save(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := loadConfig(); got != c {
|
||||
t.Fatalf("round trip: %+v != %+v", got, c)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user