@kylincloud/flamegraph 0.35.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1211 -0
- package/LICENSE +202 -0
- package/README.md +251 -0
- package/dist/FlameGraph/FlameGraphComponent/CheckIcon.d.ts +2 -0
- package/dist/FlameGraph/FlameGraphComponent/CheckIcon.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/ContextMenu.d.ts +17 -0
- package/dist/FlameGraph/FlameGraphComponent/ContextMenu.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/ContextMenuHighlight.d.ts +14 -0
- package/dist/FlameGraph/FlameGraphComponent/ContextMenuHighlight.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/DiffLegend.d.ts +9 -0
- package/dist/FlameGraph/FlameGraphComponent/DiffLegend.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.d.ts +8 -0
- package/dist/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/Flamegraph.d.ts +96 -0
- package/dist/FlameGraph/FlameGraphComponent/Flamegraph.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/Flamegraph_render.d.ts +27 -0
- package/dist/FlameGraph/FlameGraphComponent/Flamegraph_render.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/GraphVizPane.d.ts +7 -0
- package/dist/FlameGraph/FlameGraphComponent/GraphVizPane.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/Header.d.ts +12 -0
- package/dist/FlameGraph/FlameGraphComponent/Header.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/Highlight.d.ts +18 -0
- package/dist/FlameGraph/FlameGraphComponent/Highlight.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/LogoLink.d.ts +2 -0
- package/dist/FlameGraph/FlameGraphComponent/LogoLink.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/color.d.ts +20 -0
- package/dist/FlameGraph/FlameGraphComponent/color.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/colorPalette.d.ts +11 -0
- package/dist/FlameGraph/FlameGraphComponent/colorPalette.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/constants.d.ts +6 -0
- package/dist/FlameGraph/FlameGraphComponent/constants.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/index.d.ts +37 -0
- package/dist/FlameGraph/FlameGraphComponent/index.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/murmur3.d.ts +2 -0
- package/dist/FlameGraph/FlameGraphComponent/murmur3.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/testData.d.ts +53 -0
- package/dist/FlameGraph/FlameGraphComponent/testData.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/utils.d.ts +6 -0
- package/dist/FlameGraph/FlameGraphComponent/utils.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphComponent/viewTypes.d.ts +2 -0
- package/dist/FlameGraph/FlameGraphComponent/viewTypes.d.ts.map +1 -0
- package/dist/FlameGraph/FlameGraphRenderer.d.ts +86 -0
- package/dist/FlameGraph/FlameGraphRenderer.d.ts.map +1 -0
- package/dist/FlameGraph/decode.d.ts +27 -0
- package/dist/FlameGraph/decode.d.ts.map +1 -0
- package/dist/FlameGraph/normalize.d.ts +6 -0
- package/dist/FlameGraph/normalize.d.ts.map +1 -0
- package/dist/FlameGraph/uniqueness.d.ts +3 -0
- package/dist/FlameGraph/uniqueness.d.ts.map +1 -0
- package/dist/FlamegraphRenderer.d.ts +19 -0
- package/dist/FlamegraphRenderer.d.ts.map +1 -0
- package/dist/Icons.d.ts +9 -0
- package/dist/Icons.d.ts.map +1 -0
- package/dist/ProfilerTable.d.ts +21 -0
- package/dist/ProfilerTable.d.ts.map +1 -0
- package/dist/SharedQueryInput.d.ts +10 -0
- package/dist/SharedQueryInput.d.ts.map +1 -0
- package/dist/Toolbar.d.ts +31 -0
- package/dist/Toolbar.d.ts.map +1 -0
- package/dist/Tooltip/FlamegraphTooltip.d.ts +59 -0
- package/dist/Tooltip/FlamegraphTooltip.d.ts.map +1 -0
- package/dist/Tooltip/LeftClickIcon.d.ts +2 -0
- package/dist/Tooltip/LeftClickIcon.d.ts.map +1 -0
- package/dist/Tooltip/RightClickIcon.d.ts +2 -0
- package/dist/Tooltip/RightClickIcon.d.ts.map +1 -0
- package/dist/Tooltip/TableTooltip.d.ts +12 -0
- package/dist/Tooltip/TableTooltip.d.ts.map +1 -0
- package/dist/Tooltip/Tooltip.d.ts +29 -0
- package/dist/Tooltip/Tooltip.d.ts.map +1 -0
- package/dist/convert/convertJaegerTraceToProfile.d.ts +3 -0
- package/dist/convert/convertJaegerTraceToProfile.d.ts.map +1 -0
- package/dist/convert/diffTwoProfiles.d.ts +3 -0
- package/dist/convert/diffTwoProfiles.d.ts.map +1 -0
- package/dist/convert/flamebearersToTree.d.ts +11 -0
- package/dist/convert/flamebearersToTree.d.ts.map +1 -0
- package/dist/convert/sandwichViewProfiles.d.ts +14 -0
- package/dist/convert/sandwichViewProfiles.d.ts.map +1 -0
- package/dist/convert/subtract.d.ts +3 -0
- package/dist/convert/subtract.d.ts.map +1 -0
- package/dist/convert/testData.d.ts +50 -0
- package/dist/convert/testData.d.ts.map +1 -0
- package/dist/convert/toGraphviz.d.ts +3 -0
- package/dist/convert/toGraphviz.d.ts.map +1 -0
- package/dist/fitMode/fitMode.d.ts +42 -0
- package/dist/fitMode/fitMode.d.ts.map +1 -0
- package/dist/format/format.d.ts +42 -0
- package/dist/format/format.d.ts.map +1 -0
- package/dist/i18n.d.ts +55 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/index.cjs.css +792 -0
- package/dist/index.cjs.js +5087 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.css +792 -0
- package/dist/index.esm.js +5079 -0
- package/dist/index.node.d.ts +9 -0
- package/dist/index.node.d.ts.map +1 -0
- package/dist/logo-v3-small-T5VXIMRR.svg +32 -0
- package/dist/models/decode.d.ts +3 -0
- package/dist/models/decode.d.ts.map +1 -0
- package/dist/models/flamebearer.d.ts +63 -0
- package/dist/models/flamebearer.d.ts.map +1 -0
- package/dist/models/groups.d.ts +37 -0
- package/dist/models/groups.d.ts.map +1 -0
- package/dist/models/index.d.ts +8 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/profile.d.ts +152 -0
- package/dist/models/profile.d.ts.map +1 -0
- package/dist/models/spyName.d.ts +8 -0
- package/dist/models/spyName.d.ts.map +1 -0
- package/dist/models/trace.d.ts +357 -0
- package/dist/models/trace.d.ts.map +1 -0
- package/dist/models/units.d.ts +6 -0
- package/dist/models/units.d.ts.map +1 -0
- package/dist/search.d.ts +2 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/shims/Box.d.ts +38 -0
- package/dist/shims/Box.d.ts.map +1 -0
- package/dist/shims/Button.d.ts +26 -0
- package/dist/shims/Button.d.ts.map +1 -0
- package/dist/shims/Dropdown.d.ts +30 -0
- package/dist/shims/Dropdown.d.ts.map +1 -0
- package/dist/shims/Input.d.ts +19 -0
- package/dist/shims/Input.d.ts.map +1 -0
- package/dist/shims/LoadingSpinner.d.ts +7 -0
- package/dist/shims/LoadingSpinner.d.ts.map +1 -0
- package/dist/shims/Menu.d.ts +4 -0
- package/dist/shims/Menu.d.ts.map +1 -0
- package/dist/shims/NoData.d.ts +2 -0
- package/dist/shims/NoData.d.ts.map +1 -0
- package/dist/shims/Table.d.ts +52 -0
- package/dist/shims/Table.d.ts.map +1 -0
- package/dist/shims/Tooltip.d.ts +9 -0
- package/dist/shims/Tooltip.d.ts.map +1 -0
- package/package.json +84 -0
- package/src/FlameGraph/FlameGraphComponent/CheckIcon.tsx +27 -0
- package/src/FlameGraph/FlameGraphComponent/ContextMenu.module.scss +10 -0
- package/src/FlameGraph/FlameGraphComponent/ContextMenu.spec.tsx +84 -0
- package/src/FlameGraph/FlameGraphComponent/ContextMenu.tsx +86 -0
- package/src/FlameGraph/FlameGraphComponent/ContextMenuHighlight.module.css +8 -0
- package/src/FlameGraph/FlameGraphComponent/ContextMenuHighlight.tsx +47 -0
- package/src/FlameGraph/FlameGraphComponent/DiffLegend.module.css +21 -0
- package/src/FlameGraph/FlameGraphComponent/DiffLegend.tsx +52 -0
- package/src/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.module.css +40 -0
- package/src/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.tsx +129 -0
- package/src/FlameGraph/FlameGraphComponent/Flamegraph.spec.ts +552 -0
- package/src/FlameGraph/FlameGraphComponent/Flamegraph.ts +446 -0
- package/src/FlameGraph/FlameGraphComponent/Flamegraph_render.spec.tsx +233 -0
- package/src/FlameGraph/FlameGraphComponent/Flamegraph_render.ts +478 -0
- package/src/FlameGraph/FlameGraphComponent/GraphVizPane.tsx +56 -0
- package/src/FlameGraph/FlameGraphComponent/GraphVizPanel.module.scss +55 -0
- package/src/FlameGraph/FlameGraphComponent/Header.module.css +27 -0
- package/src/FlameGraph/FlameGraphComponent/Header.tsx +71 -0
- package/src/FlameGraph/FlameGraphComponent/Highlight.module.css +7 -0
- package/src/FlameGraph/FlameGraphComponent/Highlight.spec.tsx +53 -0
- package/src/FlameGraph/FlameGraphComponent/Highlight.tsx +94 -0
- package/src/FlameGraph/FlameGraphComponent/LogoLink.module.scss +10 -0
- package/src/FlameGraph/FlameGraphComponent/LogoLink.tsx +101 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-collapses-small-blocks-into-one-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-diff-mode-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-highlighted-flamegraph-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-normal-flamegraph-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-selected-node-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-also-zooms-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-in-the-beginning-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-when-node-is-not-in-the-beginning-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-zoom-top-level-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-complex-flamegraph-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-double-diff-flamegraph-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-highlighted-double-flamegraph-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-highlighted-flamegraph-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-simple-flamegraph-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-simple-tree-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-zoomed-flamegraph-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-zoomed-with-fit-mode-tail-1-snap.png +0 -0
- package/src/FlameGraph/FlameGraphComponent/canvas.module.css +6 -0
- package/src/FlameGraph/FlameGraphComponent/color.spec.ts +308 -0
- package/src/FlameGraph/FlameGraphComponent/color.ts +167 -0
- package/src/FlameGraph/FlameGraphComponent/colorPalette.ts +58 -0
- package/src/FlameGraph/FlameGraphComponent/constants.ts +5 -0
- package/src/FlameGraph/FlameGraphComponent/index.spec.tsx +291 -0
- package/src/FlameGraph/FlameGraphComponent/index.tsx +411 -0
- package/src/FlameGraph/FlameGraphComponent/murmur3.ts +97 -0
- package/src/FlameGraph/FlameGraphComponent/styles.module.scss +10 -0
- package/src/FlameGraph/FlameGraphComponent/testData.ts +427 -0
- package/src/FlameGraph/FlameGraphComponent/utils.ts +31 -0
- package/src/FlameGraph/FlameGraphComponent/viewTypes.ts +6 -0
- package/src/FlameGraph/FlameGraphRenderer.tsx +603 -0
- package/src/FlameGraph/FlamegraphRenderer.module.scss +93 -0
- package/src/FlameGraph/decode.ts +78 -0
- package/src/FlameGraph/normalize.spec.ts +76 -0
- package/src/FlameGraph/normalize.ts +60 -0
- package/src/FlameGraph/testData.json +423 -0
- package/src/FlameGraph/uniqueness.spec.ts +16 -0
- package/src/FlameGraph/uniqueness.ts +84 -0
- package/src/FlamegraphRenderer.tsx +61 -0
- package/src/Icons.tsx +74 -0
- package/src/ProfilerTable.tsx +527 -0
- package/src/SharedQueryInput.module.scss +82 -0
- package/src/SharedQueryInput.tsx +127 -0
- package/src/Toolbar.module.scss +117 -0
- package/src/Toolbar.spec.tsx +217 -0
- package/src/Toolbar.tsx +471 -0
- package/src/Tooltip/FlamegraphTooltip.spec.tsx +81 -0
- package/src/Tooltip/FlamegraphTooltip.tsx +257 -0
- package/src/Tooltip/LeftClickIcon.tsx +18 -0
- package/src/Tooltip/RightClickIcon.tsx +18 -0
- package/src/Tooltip/TableTooltip.spec.tsx +44 -0
- package/src/Tooltip/TableTooltip.tsx +145 -0
- package/src/Tooltip/Tooltip.module.scss +71 -0
- package/src/Tooltip/Tooltip.spec.tsx +395 -0
- package/src/Tooltip/Tooltip.tsx +336 -0
- package/src/__snapshots__/Toolbar.spec.tsx.snap +297 -0
- package/src/convert/convertJaegerTraceToProfile.ts +97 -0
- package/src/convert/diffTwoProfiles.ts +81 -0
- package/src/convert/flamebearersToTree.ts +78 -0
- package/src/convert/sandwichViewProfiles.spec.ts +65 -0
- package/src/convert/sandwichViewProfiles.ts +191 -0
- package/src/convert/subtract.ts +87 -0
- package/src/convert/testData.ts +145 -0
- package/src/convert/toGraphviz.ts +485 -0
- package/src/fitMode/fitMode.spec.ts +93 -0
- package/src/fitMode/fitMode.ts +122 -0
- package/src/format/format.spec.ts +291 -0
- package/src/format/format.ts +303 -0
- package/src/globals.d.ts +13 -0
- package/src/i18n.tsx +293 -0
- package/src/index.node.ts +19 -0
- package/src/index.spec.tsx +383 -0
- package/src/index.tsx +10 -0
- package/src/logo-v3-small.svg +32 -0
- package/src/models/decode.ts +45 -0
- package/src/models/flamebearer.ts +86 -0
- package/src/models/groups.ts +14 -0
- package/src/models/index.ts +7 -0
- package/src/models/profile.spec.ts +32 -0
- package/src/models/profile.ts +48 -0
- package/src/models/spyName.spec.ts +18 -0
- package/src/models/spyName.ts +32 -0
- package/src/models/trace.ts +45 -0
- package/src/models/units.spec.ts +21 -0
- package/src/models/units.ts +24 -0
- package/src/sass/_common.scss +206 -0
- package/src/sass/_css-variables.scss +201 -0
- package/src/sass/_mixins.scss +15 -0
- package/src/sass/_sanitize.scss +407 -0
- package/src/sass/_variables.scss +53 -0
- package/src/sass/flamegraph.scss +18 -0
- package/src/search.spec.ts +11 -0
- package/src/search.ts +4 -0
- package/src/shameful-any.d.ts +2 -0
- package/src/shims/Box.module.scss +57 -0
- package/src/shims/Box.tsx +105 -0
- package/src/shims/Button.module.scss +129 -0
- package/src/shims/Button.tsx +128 -0
- package/src/shims/Dropdown.module.scss +63 -0
- package/src/shims/Dropdown.tsx +96 -0
- package/src/shims/Input.module.scss +15 -0
- package/src/shims/Input.tsx +55 -0
- package/src/shims/LoadingSpinner.tsx +19 -0
- package/src/shims/Menu.tsx +9 -0
- package/src/shims/NoData.module.scss +6 -0
- package/src/shims/NoData.tsx +11 -0
- package/src/shims/Table.module.scss +82 -0
- package/src/shims/Table.spec.tsx +121 -0
- package/src/shims/Table.tsx +252 -0
- package/src/shims/Tooltip.tsx +51 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import { DeepReadonly } from 'ts-essentials';
|
|
2
|
+
import { Maybe } from 'true-myth';
|
|
3
|
+
import {
|
|
4
|
+
createFF,
|
|
5
|
+
Flamebearer,
|
|
6
|
+
singleFF,
|
|
7
|
+
doubleFF,
|
|
8
|
+
SpyName,
|
|
9
|
+
} from '../../models';
|
|
10
|
+
import type { Units } from '../../models';
|
|
11
|
+
import { PX_PER_LEVEL, BAR_HEIGHT, COLLAPSE_THRESHOLD } from './constants';
|
|
12
|
+
import type { FlamegraphPalette } from './colorPalette';
|
|
13
|
+
// there's a dependency cycle here but it should be fine
|
|
14
|
+
/* eslint-disable-next-line import/no-cycle */
|
|
15
|
+
import RenderCanvas from './Flamegraph_render';
|
|
16
|
+
|
|
17
|
+
/* eslint-disable no-useless-constructor */
|
|
18
|
+
|
|
19
|
+
/*
|
|
20
|
+
* Branded Type to distinguish between x,y that were validated to be within bounds or not.
|
|
21
|
+
*/
|
|
22
|
+
type XYWithinBounds = { x: number; y: number } & { __brand: 'XYWithinBounds' };
|
|
23
|
+
|
|
24
|
+
export default class Flamegraph {
|
|
25
|
+
private ff: ReturnType<typeof createFF>;
|
|
26
|
+
|
|
27
|
+
constructor(
|
|
28
|
+
private readonly flamebearer: Flamebearer,
|
|
29
|
+
private canvas: HTMLCanvasElement,
|
|
30
|
+
/**
|
|
31
|
+
* What node to be 'focused'
|
|
32
|
+
* ie what node to start the tree
|
|
33
|
+
*/
|
|
34
|
+
private focusedNode: Maybe<DeepReadonly<{ i: number; j: number }>>,
|
|
35
|
+
/**
|
|
36
|
+
* What level has been "selected"
|
|
37
|
+
* All nodes above will be dimmed out
|
|
38
|
+
*/
|
|
39
|
+
// private selectedLevel: number,
|
|
40
|
+
private readonly fitMode: 'HEAD' | 'TAIL',
|
|
41
|
+
/**
|
|
42
|
+
* The query used to match against the node name.
|
|
43
|
+
* For each node,
|
|
44
|
+
* if it matches it will be highlighted,
|
|
45
|
+
* otherwise it will be greyish.
|
|
46
|
+
*/
|
|
47
|
+
private readonly highlightQuery: string,
|
|
48
|
+
private zoom: Maybe<DeepReadonly<{ i: number; j: number }>>,
|
|
49
|
+
|
|
50
|
+
private palette: FlamegraphPalette
|
|
51
|
+
) {
|
|
52
|
+
// TODO
|
|
53
|
+
// these were only added because storybook is not setting
|
|
54
|
+
// the property to the component
|
|
55
|
+
this.zoom = zoom;
|
|
56
|
+
this.focusedNode = focusedNode;
|
|
57
|
+
this.flamebearer = flamebearer;
|
|
58
|
+
this.canvas = canvas;
|
|
59
|
+
this.highlightQuery = highlightQuery;
|
|
60
|
+
this.ff = createFF(flamebearer.format);
|
|
61
|
+
this.palette = palette;
|
|
62
|
+
|
|
63
|
+
// don't allow to have a zoom smaller than the focus
|
|
64
|
+
// since it does not make sense
|
|
65
|
+
if (focusedNode.isJust && zoom.isJust) {
|
|
66
|
+
if (zoom.value.i < focusedNode.value.i) {
|
|
67
|
+
throw new Error('Zoom i level should be bigger than Focus');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public render() {
|
|
73
|
+
const { rangeMin, rangeMax } = this.getRange();
|
|
74
|
+
|
|
75
|
+
const props = {
|
|
76
|
+
canvas: this.canvas,
|
|
77
|
+
|
|
78
|
+
format: this.flamebearer.format,
|
|
79
|
+
numTicks: this.flamebearer.numTicks,
|
|
80
|
+
sampleRate: this.flamebearer.sampleRate,
|
|
81
|
+
names: this.flamebearer.names,
|
|
82
|
+
levels: this.flamebearer.levels,
|
|
83
|
+
// keep type narrow https://stackoverflow.com/q/54333982
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
85
|
+
spyName: this.flamebearer.spyName as SpyName,
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
87
|
+
units: this.flamebearer.units as Units,
|
|
88
|
+
maxSelf: this.flamebearer.maxSelf,
|
|
89
|
+
|
|
90
|
+
rangeMin,
|
|
91
|
+
rangeMax,
|
|
92
|
+
fitMode: this.fitMode,
|
|
93
|
+
highlightQuery: this.highlightQuery,
|
|
94
|
+
zoom: this.zoom,
|
|
95
|
+
focusedNode: this.focusedNode,
|
|
96
|
+
pxPerTick: this.pxPerTick(),
|
|
97
|
+
tickToX: this.tickToX,
|
|
98
|
+
palette: this.palette,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const { format: viewType } = this.flamebearer;
|
|
102
|
+
|
|
103
|
+
switch (viewType) {
|
|
104
|
+
case 'single': {
|
|
105
|
+
RenderCanvas({ ...props, format: 'single' });
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
case 'double': {
|
|
109
|
+
RenderCanvas({
|
|
110
|
+
...props,
|
|
111
|
+
leftTicks: this.flamebearer.leftTicks,
|
|
112
|
+
rightTicks: this.flamebearer.rightTicks,
|
|
113
|
+
});
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
default: {
|
|
117
|
+
throw new Error(`Invalid format: '${viewType}'`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private pxPerTick() {
|
|
123
|
+
const { rangeMin, rangeMax } = this.getRange();
|
|
124
|
+
// const graphWidth = this.canvas.width;
|
|
125
|
+
const graphWidth = this.getCanvasWidth();
|
|
126
|
+
|
|
127
|
+
return graphWidth / this.flamebearer.numTicks / (rangeMax - rangeMin);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private tickToX = (i: number) => {
|
|
131
|
+
const { rangeMin } = this.getRange();
|
|
132
|
+
return (i - this.flamebearer.numTicks * rangeMin) * this.pxPerTick();
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
private getRange() {
|
|
136
|
+
const { ff } = this;
|
|
137
|
+
|
|
138
|
+
// delay calculation since they may not be set
|
|
139
|
+
const calculatedZoomRange = (zoom: { i: number; j: number }) => {
|
|
140
|
+
const level = this.flamebearer.levels[zoom.i];
|
|
141
|
+
if (!level) {
|
|
142
|
+
throw new Error(`Could not find level: '${zoom.i}'`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const zoomMin =
|
|
146
|
+
ff.getBarOffset(level, zoom.j) / this.flamebearer.numTicks;
|
|
147
|
+
const zoomMax =
|
|
148
|
+
(ff.getBarOffset(level, zoom.j) + ff.getBarTotal(level, zoom.j)) /
|
|
149
|
+
this.flamebearer.numTicks;
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
rangeMin: zoomMin,
|
|
153
|
+
rangeMax: zoomMax,
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const calculatedFocusRange = (focusedNode: { i: number; j: number }) => {
|
|
158
|
+
const level = this.flamebearer.levels[focusedNode.i];
|
|
159
|
+
|
|
160
|
+
if (!level) {
|
|
161
|
+
throw new Error(`Could not find level: '${focusedNode.i}'`);
|
|
162
|
+
}
|
|
163
|
+
const focusMin =
|
|
164
|
+
ff.getBarOffset(level, focusedNode.j) / this.flamebearer.numTicks;
|
|
165
|
+
|
|
166
|
+
const focusMax =
|
|
167
|
+
(ff.getBarOffset(level, focusedNode.j) +
|
|
168
|
+
ff.getBarTotal(level, focusedNode.j)) /
|
|
169
|
+
this.flamebearer.numTicks;
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
rangeMin: focusMin,
|
|
173
|
+
rangeMax: focusMax,
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const { zoom, focusedNode } = this;
|
|
178
|
+
|
|
179
|
+
return zoom.match({
|
|
180
|
+
Just: (z) => {
|
|
181
|
+
return focusedNode.match({
|
|
182
|
+
// both are set
|
|
183
|
+
Just: (f) => {
|
|
184
|
+
const fRange = calculatedFocusRange(f);
|
|
185
|
+
const zRange = calculatedZoomRange(z);
|
|
186
|
+
|
|
187
|
+
// focus is smaller, let's use it
|
|
188
|
+
if (
|
|
189
|
+
fRange.rangeMax - fRange.rangeMin <
|
|
190
|
+
zRange.rangeMax - zRange.rangeMin
|
|
191
|
+
) {
|
|
192
|
+
console.warn(
|
|
193
|
+
'Focus is smaller than range, this shouldnt happen. Verify that the zoom is always bigger than the focus.'
|
|
194
|
+
);
|
|
195
|
+
return calculatedFocusRange(f);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return calculatedZoomRange(z);
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// only zoom is set
|
|
202
|
+
Nothing: () => {
|
|
203
|
+
return calculatedZoomRange(z);
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
Nothing: () => {
|
|
209
|
+
return focusedNode.match({
|
|
210
|
+
Just: (f) => {
|
|
211
|
+
// only focus is set
|
|
212
|
+
return calculatedFocusRange(f);
|
|
213
|
+
},
|
|
214
|
+
Nothing: () => {
|
|
215
|
+
// neither are set
|
|
216
|
+
return {
|
|
217
|
+
rangeMin: 0,
|
|
218
|
+
rangeMax: 1,
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private getCanvasWidth() {
|
|
227
|
+
// bit of a hack, but clientWidth is not available in node-canvas
|
|
228
|
+
return this.canvas.clientWidth || this.canvas.width;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private isFocused() {
|
|
232
|
+
return this.focusedNode.isJust;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// binary search of a block in a stack level
|
|
236
|
+
// TODO(eh-am): calculations seem wrong when x is 0 and y != 0,
|
|
237
|
+
// also on the border
|
|
238
|
+
private binarySearchLevel(x: number, level: number[]) {
|
|
239
|
+
const { ff } = this;
|
|
240
|
+
|
|
241
|
+
let i = 0;
|
|
242
|
+
let j = level.length - ff.jStep;
|
|
243
|
+
|
|
244
|
+
while (i <= j) {
|
|
245
|
+
/* eslint-disable-next-line no-bitwise */
|
|
246
|
+
const m = ff.jStep * ((i / ff.jStep + j / ff.jStep) >> 1);
|
|
247
|
+
const x0 = this.tickToX(ff.getBarOffset(level, m));
|
|
248
|
+
const x1 = this.tickToX(
|
|
249
|
+
ff.getBarOffset(level, m) + ff.getBarTotal(level, m)
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
if (x0 <= x && x1 >= x) {
|
|
253
|
+
return x1 - x0 > COLLAPSE_THRESHOLD ? m : -1;
|
|
254
|
+
}
|
|
255
|
+
if (x0 > x) {
|
|
256
|
+
j = m - ff.jStep;
|
|
257
|
+
} else {
|
|
258
|
+
i = m + ff.jStep;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return -1;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private xyToBarIndex(x: number, y: number) {
|
|
265
|
+
if (x < 0 || y < 0) {
|
|
266
|
+
throw new Error(`x and y must be bigger than 0. x = ${x}, y = ${y}`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// clicked on the top bar and it's focused
|
|
270
|
+
if (this.isFocused() && y <= BAR_HEIGHT) {
|
|
271
|
+
return { i: 0, j: 0 };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// in focused mode there's a "fake" bar at the top
|
|
275
|
+
// so we must discount for it
|
|
276
|
+
const computedY = this.isFocused() ? y - BAR_HEIGHT : y;
|
|
277
|
+
|
|
278
|
+
const compensatedFocusedY = this.focusedNode.mapOrElse(
|
|
279
|
+
() => 0,
|
|
280
|
+
(node) => {
|
|
281
|
+
return node.i <= 0 ? 0 : node.i;
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const compensation = this.zoom.match({
|
|
286
|
+
Just: () => {
|
|
287
|
+
return this.focusedNode.match({
|
|
288
|
+
Just: () => {
|
|
289
|
+
// both are set, prefer focus
|
|
290
|
+
return compensatedFocusedY;
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
Nothing: () => {
|
|
294
|
+
// only zoom is set
|
|
295
|
+
return 0;
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
Nothing: () => {
|
|
301
|
+
return this.focusedNode.match({
|
|
302
|
+
Just: () => {
|
|
303
|
+
// only focus is set
|
|
304
|
+
return compensatedFocusedY;
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
Nothing: () => {
|
|
308
|
+
// none of them are set
|
|
309
|
+
return 0;
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const i = Math.floor(computedY / PX_PER_LEVEL) + compensation;
|
|
316
|
+
|
|
317
|
+
if (i >= 0 && i < this.flamebearer.levels.length) {
|
|
318
|
+
const level = this.flamebearer.levels[i];
|
|
319
|
+
if (!level) {
|
|
320
|
+
throw new Error(`Could not find level: '${i}'`);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const j = this.binarySearchLevel(x, level);
|
|
324
|
+
|
|
325
|
+
return { i, j };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return { i: 0, j: 0 };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private parseXY(x: number, y: number) {
|
|
332
|
+
const withinBounds = this.isWithinBounds(x, y);
|
|
333
|
+
|
|
334
|
+
const v = { x, y } as XYWithinBounds;
|
|
335
|
+
|
|
336
|
+
if (withinBounds) {
|
|
337
|
+
return Maybe.of(v);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return Maybe.nothing<typeof v>();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private xyToBarPosition = (xy: XYWithinBounds) => {
|
|
344
|
+
const { ff } = this;
|
|
345
|
+
const { i, j } = this.xyToBarIndex(xy.x, xy.y);
|
|
346
|
+
|
|
347
|
+
const topLevel = this.focusedNode.mapOrElse(
|
|
348
|
+
() => 0,
|
|
349
|
+
(node) => (node.i < 0 ? 0 : node.i - 1)
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
const level = this.flamebearer.levels[i];
|
|
353
|
+
if (!level) {
|
|
354
|
+
throw new Error(`Could not find level: '${i}'`);
|
|
355
|
+
}
|
|
356
|
+
const posX = Math.max(this.tickToX(ff.getBarOffset(level, j)), 0);
|
|
357
|
+
|
|
358
|
+
// lower bound is 0
|
|
359
|
+
const posY = Math.max((i - topLevel) * PX_PER_LEVEL, 0);
|
|
360
|
+
|
|
361
|
+
const sw = Math.min(
|
|
362
|
+
this.tickToX(ff.getBarOffset(level, j) + ff.getBarTotal(level, j)) - posX,
|
|
363
|
+
this.getCanvasWidth()
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
x: posX,
|
|
368
|
+
y: posY,
|
|
369
|
+
width: sw,
|
|
370
|
+
};
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
private xyToBarData = (xy: XYWithinBounds) => {
|
|
374
|
+
const { i, j } = this.xyToBarIndex(xy.x, xy.y);
|
|
375
|
+
const level = this.flamebearer.levels[i];
|
|
376
|
+
if (!level) {
|
|
377
|
+
throw new Error(`Could not find level: '${i}'`);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
switch (this.flamebearer.format) {
|
|
381
|
+
case 'single': {
|
|
382
|
+
const ff = singleFF;
|
|
383
|
+
|
|
384
|
+
return {
|
|
385
|
+
format: 'single' as const,
|
|
386
|
+
name: this.flamebearer.names[ff.getBarName(level, j)],
|
|
387
|
+
self: ff.getBarSelf(level, j),
|
|
388
|
+
offset: ff.getBarOffset(level, j),
|
|
389
|
+
total: ff.getBarTotal(level, j),
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
case 'double': {
|
|
393
|
+
const ff = doubleFF;
|
|
394
|
+
|
|
395
|
+
return {
|
|
396
|
+
format: 'double' as const,
|
|
397
|
+
barTotal: ff.getBarTotal(level, j),
|
|
398
|
+
totalLeft: ff.getBarTotalLeft(level, j),
|
|
399
|
+
totalRight: ff.getBarTotalRght(level, j),
|
|
400
|
+
totalDiff: ff.getBarTotalDiff(level, j),
|
|
401
|
+
name: this.flamebearer.names[ff.getBarName(level, j)],
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
default: {
|
|
406
|
+
throw new Error(`Unsupported type`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
public isWithinBounds = (x: number, y: number) => {
|
|
412
|
+
if (x < 0 || x > this.getCanvasWidth()) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
try {
|
|
417
|
+
const { i, j } = this.xyToBarIndex(x, y);
|
|
418
|
+
if (j === -1 || i === -1) {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
} catch (e) {
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return true;
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
/*
|
|
429
|
+
* Given x and y coordinates
|
|
430
|
+
* return all information about the bar under those coordinates
|
|
431
|
+
*/
|
|
432
|
+
public xyToBar(x: number, y: number) {
|
|
433
|
+
return this.parseXY(x, y).map((xyWithinBounds) => {
|
|
434
|
+
const { i, j } = this.xyToBarIndex(x, y);
|
|
435
|
+
const position = this.xyToBarPosition(xyWithinBounds);
|
|
436
|
+
const data = this.xyToBarData(xyWithinBounds);
|
|
437
|
+
|
|
438
|
+
return {
|
|
439
|
+
i,
|
|
440
|
+
j,
|
|
441
|
+
...position,
|
|
442
|
+
...data,
|
|
443
|
+
};
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
2
|
+
// @ts-ignore
|
|
3
|
+
import CanvasConverter from 'canvas-to-buffer';
|
|
4
|
+
import { createCanvas } from 'canvas';
|
|
5
|
+
import { Maybe } from 'true-myth';
|
|
6
|
+
import TestData from './testData';
|
|
7
|
+
import Flamegraph from './Flamegraph';
|
|
8
|
+
import { DefaultPalette } from './colorPalette';
|
|
9
|
+
import { configureToMatchImageSnapshot } from 'jest-image-snapshot';
|
|
10
|
+
import type { MatchImageSnapshotOptions } from 'jest-image-snapshot';
|
|
11
|
+
|
|
12
|
+
type focusedNodeType = ConstructorParameters<typeof Flamegraph>[2];
|
|
13
|
+
type zoomType = ConstructorParameters<typeof Flamegraph>[5];
|
|
14
|
+
|
|
15
|
+
expect.extend({
|
|
16
|
+
toMatchImageSnapshot(received: string, options: MatchImageSnapshotOptions) {
|
|
17
|
+
// If these checks pass, assume we're in a JSDOM environment with the 'canvas' package.
|
|
18
|
+
if (process.env.RUN_SNAPSHOTS) {
|
|
19
|
+
const toMatchImageSnapshot = configureToMatchImageSnapshot({
|
|
20
|
+
// Big enough threshold to account for different font rendering
|
|
21
|
+
// TODO: fix it
|
|
22
|
+
failureThreshold: 0.1,
|
|
23
|
+
failureThresholdType: 'percent',
|
|
24
|
+
}) as any;
|
|
25
|
+
|
|
26
|
+
// TODO
|
|
27
|
+
// for some reason it fails with
|
|
28
|
+
// Expected 1 arguments, but got 3.
|
|
29
|
+
// hence the any
|
|
30
|
+
return toMatchImageSnapshot.call(this, received, options);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
pass: true,
|
|
35
|
+
message: () =>
|
|
36
|
+
`Skipping 'toMatchImageSnapshot' assertion since env var 'RUN_SNAPSHOTS' is not set.`,
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
// All tests here refer strictly to the rendering bit of "Flamegraph"
|
|
41
|
+
describe("render group:snapshot'", () => {
|
|
42
|
+
// TODO i'm thinking here if we can simply reuse this?
|
|
43
|
+
const canvas = createCanvas(800, 0) as unknown as HTMLCanvasElement;
|
|
44
|
+
const fitMode = 'HEAD';
|
|
45
|
+
const highlightQuery = '';
|
|
46
|
+
const zoom: zoomType = Maybe.nothing();
|
|
47
|
+
const focusedNode: focusedNodeType = Maybe.nothing();
|
|
48
|
+
|
|
49
|
+
it('renders a simple flamegraph', () => {
|
|
50
|
+
const flame = new Flamegraph(
|
|
51
|
+
TestData.SimpleTree,
|
|
52
|
+
canvas,
|
|
53
|
+
focusedNode,
|
|
54
|
+
fitMode,
|
|
55
|
+
highlightQuery,
|
|
56
|
+
zoom,
|
|
57
|
+
DefaultPalette
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
flame.render();
|
|
61
|
+
expect(canvasToBuffer(canvas)).toMatchImageSnapshot();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// this test servers to validate funcionality like collapsing nodes
|
|
65
|
+
it('renders a complex flamegraph', () => {
|
|
66
|
+
const flame = new Flamegraph(
|
|
67
|
+
TestData.ComplexTree,
|
|
68
|
+
canvas,
|
|
69
|
+
focusedNode,
|
|
70
|
+
fitMode,
|
|
71
|
+
highlightQuery,
|
|
72
|
+
zoom,
|
|
73
|
+
DefaultPalette
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
flame.render();
|
|
77
|
+
expect(canvasToBuffer(canvas)).toMatchImageSnapshot();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('renders a double(diff) flamegraph', () => {
|
|
81
|
+
const flame = new Flamegraph(
|
|
82
|
+
TestData.DiffTree,
|
|
83
|
+
canvas,
|
|
84
|
+
focusedNode,
|
|
85
|
+
fitMode,
|
|
86
|
+
highlightQuery,
|
|
87
|
+
zoom,
|
|
88
|
+
DefaultPalette
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
flame.render();
|
|
92
|
+
expect(canvasToBuffer(canvas)).toMatchImageSnapshot();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('renders a highlighted flamegraph', () => {
|
|
96
|
+
const highlightQuery = 'main';
|
|
97
|
+
const focusedNode: focusedNodeType = Maybe.nothing();
|
|
98
|
+
|
|
99
|
+
const flame = new Flamegraph(
|
|
100
|
+
TestData.SimpleTree,
|
|
101
|
+
canvas,
|
|
102
|
+
focusedNode,
|
|
103
|
+
fitMode,
|
|
104
|
+
highlightQuery,
|
|
105
|
+
zoom,
|
|
106
|
+
DefaultPalette
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
flame.render();
|
|
110
|
+
expect(canvasToBuffer(canvas)).toMatchImageSnapshot();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('renders a highlighted double flamegraph', () => {
|
|
114
|
+
const highlightQuery = 'main';
|
|
115
|
+
const focusedNode: focusedNodeType = Maybe.nothing();
|
|
116
|
+
|
|
117
|
+
const flame = new Flamegraph(
|
|
118
|
+
TestData.DiffTree,
|
|
119
|
+
canvas,
|
|
120
|
+
focusedNode,
|
|
121
|
+
fitMode,
|
|
122
|
+
highlightQuery,
|
|
123
|
+
zoom,
|
|
124
|
+
DefaultPalette
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
flame.render();
|
|
128
|
+
expect(canvasToBuffer(canvas)).toMatchImageSnapshot();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('renders a zoomed flamegraph', () => {
|
|
132
|
+
const zoom = Maybe.just({ i: 2, j: 8 });
|
|
133
|
+
const focusedNode: focusedNodeType = Maybe.nothing();
|
|
134
|
+
|
|
135
|
+
const flame = new Flamegraph(
|
|
136
|
+
TestData.SimpleTree,
|
|
137
|
+
canvas,
|
|
138
|
+
focusedNode,
|
|
139
|
+
fitMode,
|
|
140
|
+
highlightQuery,
|
|
141
|
+
zoom,
|
|
142
|
+
DefaultPalette
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
flame.render();
|
|
146
|
+
expect(canvasToBuffer(canvas)).toMatchImageSnapshot();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('renders a zoomed with fitMode="TAIL"', () => {
|
|
150
|
+
// we need a smaller canvas
|
|
151
|
+
// so that the function names don't fit
|
|
152
|
+
const canvas = createCanvas(300, 0) as unknown as HTMLCanvasElement;
|
|
153
|
+
const fitMode = 'TAIL';
|
|
154
|
+
const focusedNode: focusedNodeType = Maybe.nothing();
|
|
155
|
+
|
|
156
|
+
const flame = new Flamegraph(
|
|
157
|
+
TestData.SimpleTree,
|
|
158
|
+
canvas,
|
|
159
|
+
focusedNode,
|
|
160
|
+
fitMode,
|
|
161
|
+
highlightQuery,
|
|
162
|
+
zoom,
|
|
163
|
+
DefaultPalette
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
flame.render();
|
|
167
|
+
expect(canvasToBuffer(canvas)).toMatchImageSnapshot();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('focused', () => {
|
|
171
|
+
it('renders a focused node in the beginning', () => {
|
|
172
|
+
const zoom: zoomType = Maybe.nothing();
|
|
173
|
+
const focusedNode = Maybe.just({ i: 2, j: 0 });
|
|
174
|
+
|
|
175
|
+
const flame = new Flamegraph(
|
|
176
|
+
TestData.SimpleTree,
|
|
177
|
+
canvas,
|
|
178
|
+
focusedNode,
|
|
179
|
+
fitMode,
|
|
180
|
+
highlightQuery,
|
|
181
|
+
zoom,
|
|
182
|
+
DefaultPalette
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
flame.render();
|
|
186
|
+
expect(canvasToBuffer(canvas)).toMatchImageSnapshot();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('renders a focused node (when node is not in the beginning)', () => {
|
|
190
|
+
const zoom: zoomType = Maybe.nothing();
|
|
191
|
+
const focusedNode = Maybe.just({ i: 2, j: 8 });
|
|
192
|
+
|
|
193
|
+
const flame = new Flamegraph(
|
|
194
|
+
TestData.SimpleTree,
|
|
195
|
+
canvas,
|
|
196
|
+
focusedNode,
|
|
197
|
+
fitMode,
|
|
198
|
+
highlightQuery,
|
|
199
|
+
zoom,
|
|
200
|
+
DefaultPalette
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
flame.render();
|
|
204
|
+
expect(canvasToBuffer(canvas)).toMatchImageSnapshot();
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('also zooms', () => {
|
|
208
|
+
const focusedNode = Maybe.just({ i: 1, j: 0 });
|
|
209
|
+
const zoom = Maybe.just({ i: 2, j: 0 }); // main.fastFunction
|
|
210
|
+
|
|
211
|
+
const flame = new Flamegraph(
|
|
212
|
+
TestData.SimpleTree,
|
|
213
|
+
canvas,
|
|
214
|
+
focusedNode,
|
|
215
|
+
fitMode,
|
|
216
|
+
highlightQuery,
|
|
217
|
+
zoom,
|
|
218
|
+
DefaultPalette
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
flame.render();
|
|
222
|
+
expect(canvasToBuffer(canvas)).toMatchImageSnapshot();
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
function canvasToBuffer(canvas: HTMLCanvasElement) {
|
|
228
|
+
const converter = new CanvasConverter(canvas, {
|
|
229
|
+
image: { types: ['png'] },
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return converter.toBuffer();
|
|
233
|
+
}
|