Skip to content
quarry Artifact [782b8cfd96]
Login

Artifact [782b8cfd96]

Artifact 782b8cfd967ec4a4dcc2177059038c57cea8f934f0da07d5ee05da91e5508c47:


package web

import (
	"strings"
	"testing"
	"time"
)

// The panel lists each live process with what started it, its age, its
// usual and its memory; a slow one is marked, and an empty forge says
// nothing is running rather than drawing an empty table.
func TestRunningNowPanelNamesEachProcess(t *testing.T) {
	var v opsView
	remarshal(map[string]any{"budgetMs": 500, "running": []map[string]any{
		{"verb": "timeline", "repo": "fossil", "cause": "GET /r/fossil/timeline", "ageMs": 9000, "usualMs": 1100, "rss": 22 << 20, "slow": true},
		{"verb": "ls", "repo": "tcl", "cause": "ingest", "ageMs": 300, "usualMs": 60},
	}}, &v)
	body := renderOps(t, v)
	if !strings.Contains(body, `data-hook="ops-running" data-count="2"`) {
		t.Fatalf("FAIL: no running panel with two rows:\n%s", body)
	}
	if !strings.Contains(body, `data-verb="timeline" data-repo="fossil" data-slow="true"`) ||
		!strings.Contains(body, `data-verb="ls" data-repo="tcl" data-slow="false"`) {
		t.Errorf("FAIL: rows are not marked by slowness:\n%s", body)
	}
	for _, want := range []string{"<code>GET /<wbr>r/<wbr>fossil/<wbr>timeline</code>", ">ingest<", "9.00 s", "1.10 s", "22 MB"} {
		if !strings.Contains(body, want) {
			t.Errorf("FAIL: the panel lacks %q", want)
		}
	}
	empty := renderOps(t, opsView{BudgetMs: 500})
	if !strings.Contains(empty, `data-hook="ops-running" data-count="0"`) || strings.Contains(empty, `class="ops-t ops-running"`) {
		t.Errorf("FAIL: an idle forge draws a table or no panel")
	}
}

// With process memory reported the panel leads with it, the heap is a
// part of it, the cap gets a bar, and the chart plots both series. All
// of it is data attributes; without /proc the panel reads heap-first and
// prints none of it.
func TestMemoryPanelLeadsWithProcess(t *testing.T) {
	now := time.Now()
	var samples []map[string]any
	for i := 0; i < 30; i++ {
		samples = append(samples, map[string]any{"at": now.Add(time.Duration(i-30) * time.Minute),
			"heap": 800 << 20, "anon": (5 << 30) + uint64(i)<<26, "file": 200 << 20})
	}
	var v opsView
	remarshal(map[string]any{"budgetMs": 500, "memory": map[string]any{
		"heap": 800 << 20, "cap": 11 << 30, "anon": 6 << 30, "file": 200 << 20, "current": 8 << 30,
		"available": 1 << 30, "swapFree": 100 << 20, "swapTotal": 4 << 30,
		"unaccounted": 800 << 20, "samples": samples,
		"warnings": []map[string]any{{"kind": "anon-high"}},
	}}, &v)
	body := renderOps(t, v)
	for _, want := range []string{
		`data-hook="mem-process" data-bytes="6652166144"`,
		`data-hook="mem-resident" data-anon="6442450944" data-file="209715200" data-warnings="1"`,
		`data-hook="mem-heap" data-bytes="838860800"`,
		`data-hook="mem-available" data-bytes="1073741824"`,
		`data-hook="mem-swap" data-free="104857600" data-total="4294967296"`,
		`data-hook="mem-cap" data-process="6652166144" data-cap="11811160064" data-near="false"`,
		`data-part="outside"`,
		`data-series="heap"`, `data-series="process"`,
	} {
		if !strings.Contains(body, want) {
			t.Errorf("FAIL: the panel lacks %q", want)
		}
	}
	var hot opsView
	remarshal(map[string]any{"budgetMs": 500, "memory": map[string]any{
		"heap": 800 << 20, "cap": 10 << 30, "anon": 8 << 30, "file": 200 << 20}}, &hot)
	if hb := renderOps(t, hot); !strings.Contains(hb, `data-near="true"`) || !strings.Contains(hb, "ops-bar-bad") {
		t.Errorf("FAIL: 82%% of the cap is not marked near")
	}
	var none opsView
	remarshal(map[string]any{"budgetMs": 500, "memory": map[string]any{"heap": 1 << 30}}, &none)
	nb := renderOps(t, none)
	for _, bad := range []string{`data-hook="mem-process"`, `data-hook="mem-resident"`, `data-hook="mem-cap"`, `data-series=`} {
		if strings.Contains(nb, bad) {
			t.Errorf("FAIL: a host without /proc still draws %q", bad)
		}
	}
	if !strings.Contains(nb, `data-hook="mem-part"`) {
		t.Errorf("FAIL: the heap-first panel lost its parts")
	}
	if !t.Failed() {
		t.Log("ok")
	}
}