diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-06 15:35:24 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-06 15:35:24 +0200 |
| commit | 99b02bf8c389a793df5d5986db05eed7e459f7b1 (patch) | |
| tree | bc4e36cfcd3c9ef9b067beed2eb5b68a75a45aa2 /internal/flamegraph/livehtml_browser_test.go | |
| parent | 4ff17c30120d657b966f8a55188ba167dc875e64 (diff) | |
refactor: remove web flamegrapher and keep TUI-only
Diffstat (limited to 'internal/flamegraph/livehtml_browser_test.go')
| -rw-r--r-- | internal/flamegraph/livehtml_browser_test.go | 314 |
1 files changed, 0 insertions, 314 deletions
diff --git a/internal/flamegraph/livehtml_browser_test.go b/internal/flamegraph/livehtml_browser_test.go deleted file mode 100644 index 10252a9..0000000 --- a/internal/flamegraph/livehtml_browser_test.go +++ /dev/null @@ -1,314 +0,0 @@ -package flamegraph - -import ( - "encoding/json" - "fmt" - "os" - "os/exec" - "strings" - "testing" -) - -type jsFrame struct { - Name string `json:"name"` - X float64 `json:"x"` - Y float64 `json:"y"` - W float64 `json:"w"` - H float64 `json:"h"` - Depth int `json:"depth"` -} - -type liveJSResult struct { - Colors map[string]string `json:"colors"` - KnownFrames []jsFrame `json:"knownFrames"` - SVGHTML string `json:"svgHTML"` - ViewBox string `json:"viewBox"` - TallViewBox string `json:"tallViewBox"` - TallHeight string `json:"tallHeight"` - PrunedMaxEnd float64 `json:"prunedMaxEnd"` - SingleCount int `json:"singleCount"` - DeepMaxDepth int `json:"deepMaxDepth"` - WideFrameCount int `json:"wideFrameCount"` -} - -func TestLiveHTMLJSRenderingParity(t *testing.T) { - if _, err := exec.LookPath("node"); err != nil { - t.Skip("node not available") - } - - out := runLiveHTMLJSHarness(t) - var got liveJSResult - if err := json.Unmarshal([]byte(out), &got); err != nil { - t.Fatalf("unmarshal node output: %v\nraw:\n%s", err, out) - } - - names := []string{"read", "write", "io_uring_enter", "nested/path"} - for _, name := range names { - want := frameColor(name) - if got.Colors[name] != want { - t.Fatalf("fgFrameColor(%q) = %q, want %q", name, got.Colors[name], want) - } - } - - if len(got.KnownFrames) != 3 { - t.Fatalf("known frame count = %d, want 3", len(got.KnownFrames)) - } - assertFrame(t, got.KnownFrames[0], "A", 0, 96, 720, 15, 1) - assertFrame(t, got.KnownFrames[1], "A1", 0, 80, 720, 15, 2) - assertFrame(t, got.KnownFrames[2], "B", 720, 96, 480, 15, 1) - - if !strings.Contains(got.SVGHTML, `<g class="frame"`) { - t.Fatalf("svg markup missing frame group") - } - if !strings.Contains(got.SVGHTML, `data-name="A"`) { - t.Fatalf("svg markup missing data-name for A") - } - if !strings.Contains(got.SVGHTML, `data-x="0.000"`) { - t.Fatalf("svg markup missing data-x") - } - if !strings.Contains(got.SVGHTML, `data-w="720.000"`) { - t.Fatalf("svg markup missing data-w") - } - if !strings.Contains(got.SVGHTML, `data-depth="1"`) { - t.Fatalf("svg markup missing data-depth") - } - if !strings.Contains(got.SVGHTML, `data-base-fill="rgb(`) { - t.Fatalf("svg markup missing data-base-fill") - } - if got.ViewBox != "0 0 1200 128" { - t.Fatalf("viewBox = %q, want %q", got.ViewBox, "0 0 1200 128") - } - if got.TallViewBox != "0 0 1600 844" { - t.Fatalf("tall viewBox = %q, want %q", got.TallViewBox, "0 0 1600 844") - } - if got.TallHeight != "844px" { - t.Fatalf("tall style height = %q, want %q", got.TallHeight, "844px") - } - if diff(got.PrunedMaxEnd, 1600) > 0.01 { - t.Fatalf("pruned max end = %f, want 1600", got.PrunedMaxEnd) - } - - if got.SingleCount != 1 { - t.Fatalf("single-frame case count = %d, want 1", got.SingleCount) - } - if got.DeepMaxDepth < 50 { - t.Fatalf("deep max depth = %d, want at least 50", got.DeepMaxDepth) - } - if got.WideFrameCount != 1000 { - t.Fatalf("wide frame count = %d, want 1000", got.WideFrameCount) - } -} - -func assertFrame(t *testing.T, got jsFrame, name string, x, y, w, h float64, depth int) { - t.Helper() - if got.Name != name { - t.Fatalf("frame name = %q, want %q", got.Name, name) - } - if got.Depth != depth { - t.Fatalf("frame %q depth = %d, want %d", got.Name, got.Depth, depth) - } - const eps = 0.001 - if diff(got.X, x) > eps || diff(got.Y, y) > eps || diff(got.W, w) > eps || diff(got.H, h) > eps { - t.Fatalf("frame %q geometry = {x:%f y:%f w:%f h:%f}, want {x:%f y:%f w:%f h:%f}", - got.Name, got.X, got.Y, got.W, got.H, x, y, w, h) - } -} - -func diff(a, b float64) float64 { - if a > b { - return a - b - } - return b - a -} - -func runLiveHTMLJSHarness(t *testing.T) string { - t.Helper() - - script := extractLiveHTMLScript(t) - harness := fmt.Sprintf(` -const vm = require("vm"); -const liveScript = %q; - -function makeElement(id) { - return { - id, - textContent: "", - innerHTML: "", - style: {}, - dataset: {}, - attrs: {}, - classList: { toggle: function(){}, add: function(){}, remove: function(){} }, - addEventListener: function(){}, - getBoundingClientRect: function() { return { height: id === "controls" ? 56 : 0 }; }, - setAttribute: function(k, v) { this.attrs[k] = String(v); }, - getAttribute: function(k) { return this.attrs[k] || ""; }, - querySelectorAll: function() { return []; }, - querySelector: function() { return null; } - }; -} - -const elements = {}; -["controls", "flamegraph", "status", "btn-pause", "btn-search", "btn-reset-search", "btn-undo-zoom", "btn-reset-zoom", "btn-reset-baseline", "btn-toggle-order"].forEach((id) => { - elements[id] = makeElement(id); -}); -elements["body"] = makeElement("body"); - -global.document = { - body: elements["body"], - getElementById: function(id) { - if (!elements[id]) elements[id] = makeElement(id); - return elements[id]; - }, - addEventListener: function(){}, -}; -global.window = global; -global.prompt = function(){ return ""; }; -global.fetch = function() { - return Promise.resolve({ - ok: true, - json: function() { return Promise.resolve({ fields: ["comm", "tracepoint", "path"], snapshot: { n: "", v: 0, t: 0 } }); }, - text: function() { return Promise.resolve("{\"n\":\"\",\"v\":0,\"t\":0}"); } - }); -}; -global.requestAnimationFrame = function(cb){ cb(); }; -global.EventSource = function() { - this.onmessage = null; - this.onerror = null; -}; -window.addEventListener = function(){}; - -vm.runInThisContext(liveScript); - -const names = ["read", "write", "io_uring_enter", "nested/path"]; -const colors = {}; -for (const n of names) { - colors[n] = fgFrameColor(n); -} - -const knownTree = { - n: "", - v: 0, - t: 10, - c: [ - { n: "A", v: 0, t: 6, c: [{ n: "A1", v: 6, t: 6 }] }, - { n: "B", v: 4, t: 4 } - ] -}; -const maxDepth = fgMaxDepth(knownTree, 0); -const canvasHeight = (liveFlamegraphState.cfg.frameHeight * (maxDepth + 1)) + 80; -const knownFramesRaw = []; -fgBuildFrames(knownTree, knownTree.t, 0, 1200, 0, canvasHeight, true, knownFramesRaw, ""); -const knownFrames = knownFramesRaw.map((f) => ({ - name: f.name, - x: Number(f.x.toFixed(3)), - y: Number(f.y.toFixed(3)), - w: Number(f.w.toFixed(3)), - h: Number(f.h.toFixed(3)), - depth: f.depth, -})); - -fgRender(knownTree); -const svgHTML = elements["flamegraph"].innerHTML; -const viewBox = elements["flamegraph"].attrs["viewBox"] || ""; - -window.innerWidth = 1600; -window.innerHeight = 900; -fgRender(knownTree); -const tallViewBox = elements["flamegraph"].attrs["viewBox"] || ""; -const tallHeight = elements["flamegraph"].style.height || ""; - -const singleTree = { n: "", v: 0, t: 1, c: [{ n: "only", v: 1, t: 1 }] }; -const singleFrames = []; -const singleCanvas = (liveFlamegraphState.cfg.frameHeight * (fgMaxDepth(singleTree, 0) + 1)) + 80; -fgBuildFrames(singleTree, singleTree.t, 0, 1200, 0, singleCanvas, true, singleFrames, ""); - -let deepTree = { n: "", v: 0, t: 1, c: [] }; -let cursor = deepTree; -for (let i = 0; i < 55; i++) { - const child = { n: "d" + i, v: i === 54 ? 1 : 0, t: 1, c: [] }; - cursor.c = [child]; - cursor = child; -} -const deepMaxDepth = fgMaxDepth(deepTree, 0); - -const wideChildren = []; -for (let i = 0; i < 1000; i++) { - wideChildren.push({ n: "w" + i, v: 1, t: 1 }); -} -const wideTree = { n: "", v: 0, t: 1000, c: wideChildren }; -const wideCanvas = (liveFlamegraphState.cfg.frameHeight * (fgMaxDepth(wideTree, 0) + 1)) + 80; -const wideFrames = []; -fgBuildFrames(wideTree, wideTree.t, 0, 1200, 0, wideCanvas, true, wideFrames, ""); - -const prunedTree = { - n: "", - v: 0, - t: 100, - c: [ - { n: "A", v: 0, t: 60 }, - { n: "B", v: 0, t: 20 } - ] -}; -fgRender(prunedTree); -const prunedHTML = elements["flamegraph"].innerHTML; -const prunedMatches = prunedHTML.match(/data-x=\"([0-9.]+)\" data-w=\"([0-9.]+)\"/g) || []; -let prunedMaxEnd = 0; -for (const m of prunedMatches) { - const parts = m.match(/data-x=\"([0-9.]+)\" data-w=\"([0-9.]+)\"/); - if (!parts) continue; - const end = Number(parts[1]) + Number(parts[2]); - if (end > prunedMaxEnd) { - prunedMaxEnd = end; - } -} - -console.log(JSON.stringify({ - colors, - knownFrames, - svgHTML, - viewBox, - tallViewBox, - tallHeight, - prunedMaxEnd, - singleCount: singleFrames.length, - deepMaxDepth, - wideFrameCount: wideFrames.length, -})); -`, script) - - tmp, err := os.CreateTemp("", "livehtml-js-*.cjs") - if err != nil { - t.Fatalf("create temp script: %v", err) - } - defer os.Remove(tmp.Name()) - - if _, err := tmp.WriteString(harness); err != nil { - _ = tmp.Close() - t.Fatalf("write temp script: %v", err) - } - if err := tmp.Close(); err != nil { - t.Fatalf("close temp script: %v", err) - } - - out, err := exec.Command("node", tmp.Name()).CombinedOutput() - if err != nil { - t.Fatalf("node harness failed: %v\n%s", err, string(out)) - } - return strings.TrimSpace(string(out)) -} - -func extractLiveHTMLScript(t *testing.T) string { - t.Helper() - const openTag = "<script>" - const closeTag = "</script>" - start := strings.Index(liveHTML, openTag) - if start < 0 { - t.Fatalf("script tag not found in liveHTML") - } - start += len(openTag) - end := strings.Index(liveHTML[start:], closeTag) - if end < 0 { - t.Fatalf("closing script tag not found in liveHTML") - } - return strings.TrimSpace(liveHTML[start : start+end]) -} |
