123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286 |
- import {vec2} from 'gl-matrix';
- import {
- makeCanvasMock,
- makeContextMock,
- makeFlamegraph,
- } from 'sentry-test/profiling/utils';
- import {FlamegraphSearch} from 'sentry/utils/profiling/flamegraph/flamegraphStateProvider/flamegraphSearch';
- import {
- LightFlamegraphTheme,
- LightFlamegraphTheme as theme,
- } from 'sentry/utils/profiling/flamegraph/flamegraphTheme';
- import {FlamegraphCanvas} from 'sentry/utils/profiling/flamegraphCanvas';
- import {FlamegraphView} from 'sentry/utils/profiling/flamegraphView';
- import {Rect} from 'sentry/utils/profiling/gl/utils';
- import {FlamegraphRenderer} from 'sentry/utils/profiling/renderers/flamegraphRenderer';
- const originalDpr = window.devicePixelRatio;
- describe('flamegraphRenderer', () => {
- beforeEach(() => {
- // We simulate regular screens unless differently specified
- window.devicePixelRatio = 1;
- });
- afterEach(() => {
- window.devicePixelRatio = originalDpr;
- });
- describe('colors', () => {
- it('generates new colors if none are set on the flamegraph', () => {
- const canvas = makeCanvasMock({
- getContext: jest.fn().mockReturnValue(makeContextMock()),
- });
- const flamegraph = makeFlamegraph();
- const renderer = new FlamegraphRenderer(
- canvas as HTMLCanvasElement,
- flamegraph,
- {
- ...theme,
- COLORS: {
- ...theme.COLORS,
- // @ts-ignore overridee the colors implementation
- STACK_TO_COLOR: () => {
- const colorMap = new Map<string, number[]>([['f0', [1, 0, 0, 1]]]);
- return {colorBuffer: [1, 0, 0, 1], colorMap};
- },
- },
- },
- vec2.fromValues(0, 0)
- );
- expect(renderer.colors).toEqual([1, 0, 0, 1]);
- });
- });
- it('inits vertices', () => {
- const canvas = makeCanvasMock({
- getContext: jest.fn().mockReturnValue(makeContextMock()),
- });
- const flamegraph = makeFlamegraph();
- const renderer = new FlamegraphRenderer(
- canvas as HTMLCanvasElement,
- flamegraph,
- theme
- );
- // Helper rect for the only frame in our flamegraph
- const rect = new Rect(0, 0, 10, 1);
- // To draw a rect, we need to draw 2 triangles, each with 3 vertices
- // First triangle: top left -> top right -> bottom left
- // Second triangle: bottom left -> top right -> bottom right
- expect(renderer.positions.slice(0, 2)).toEqual([rect.left, rect.top]);
- expect(renderer.positions.slice(2, 4)).toEqual([rect.right, rect.top]);
- expect(renderer.positions.slice(4, 6)).toEqual([rect.left, rect.bottom]);
- expect(renderer.positions.slice(6, 8)).toEqual([rect.left, rect.bottom]);
- expect(renderer.positions.slice(8, 10)).toEqual([rect.right, rect.top]);
- expect(renderer.positions.slice(10, 12)).toEqual([rect.right, rect.bottom]);
- });
- it('inits shaders', () => {
- const VERTEX = `void main() { gl_Position = vec4(pos, 0.0, 1.0); }`;
- const FRAGMENT = `void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }`;
- const context = makeContextMock({
- createShader: jest.fn().mockReturnValueOnce(VERTEX).mockReturnValueOnce(FRAGMENT),
- });
- const canvas = makeCanvasMock({
- getContext: jest.fn().mockReturnValue(context),
- });
- const flamegraph = makeFlamegraph();
- // @ts-ignore shaders are init from the constructor
- const _renderer = new FlamegraphRenderer(
- canvas as HTMLCanvasElement,
- flamegraph,
- theme
- );
- expect(context.createShader).toHaveBeenCalledTimes(2);
- expect(context.getShaderParameter).toHaveBeenCalledTimes(2);
- // @ts-ignore this is a mock
- expect(context.getShaderParameter.mock.calls[0][0]).toEqual(VERTEX);
- // @ts-ignore this is a mock
- expect(context.getShaderParameter.mock.calls[1][0]).toEqual(FRAGMENT);
- });
- it('getColorForFrame', () => {
- const canvas = makeCanvasMock({
- getContext: jest.fn().mockReturnValue(makeContextMock()),
- });
- const flamegraph = makeFlamegraph();
- const renderer = new FlamegraphRenderer(
- canvas as HTMLCanvasElement,
- flamegraph,
- theme
- );
- expect(renderer.getColorForFrame(flamegraph.frames[0])).toEqual([
- 0.9750000000000001, 0.7250000000000001, 0.7250000000000001,
- ]);
- expect(
- renderer.getColorForFrame({
- key: 20,
- frame: flamegraph.frames[0].frame,
- node: flamegraph.frames[0].node,
- parent: null,
- children: [],
- depth: 0,
- start: 0,
- end: 0,
- })
- ).toEqual(LightFlamegraphTheme.COLORS.FRAME_FALLBACK_COLOR);
- });
- it('getHoveredNode', () => {
- const flamegraph = makeFlamegraph(
- {
- events: [
- {type: 'O', at: 0, frame: 0},
- {type: 'O', at: 1, frame: 1},
- {type: 'C', at: 2, frame: 1},
- {type: 'C', at: 3, frame: 0},
- {type: 'O', at: 4, frame: 2},
- {type: 'O', at: 5, frame: 3},
- {type: 'C', at: 7, frame: 3},
- {type: 'C', at: 8, frame: 2},
- {type: 'O', at: 9, frame: 4},
- {type: 'O', at: 10, frame: 5},
- {type: 'C', at: 11, frame: 5},
- {type: 'C', at: 12, frame: 4},
- ],
- },
- [{name: 'f0'}, {name: 'f1'}, {name: 'f2'}, {name: 'f3'}, {name: 'f4'}, {name: 'f5'}]
- );
- const renderer = new FlamegraphRenderer(
- makeCanvasMock() as HTMLCanvasElement,
- flamegraph,
- theme
- );
- expect(renderer.getHoveredNode(vec2.fromValues(-1, 0))).toBeNull();
- expect(renderer.getHoveredNode(vec2.fromValues(-1, 0))).toBeNull();
- expect(renderer.getHoveredNode(vec2.fromValues(0, 0))?.frame?.name).toBe('f0');
- expect(renderer.getHoveredNode(vec2.fromValues(5, 2))?.frame?.name).toBe('f3');
- });
- describe('draw', () => {
- it('sets uniform1f for search results', () => {
- const context = makeContextMock();
- const canvas = makeCanvasMock({
- getContext: jest.fn().mockReturnValue(context),
- }) as HTMLCanvasElement;
- const flamegraph = makeFlamegraph(
- {
- startValue: 0,
- endValue: 100,
- events: [
- {
- type: 'O',
- frame: 0,
- at: 0,
- },
- {
- type: 'C',
- frame: 0,
- at: 1,
- },
- {
- type: 'O',
- frame: 1,
- at: 1,
- },
- {
- type: 'C',
- frame: 1,
- at: 2,
- },
- ],
- },
- [{name: 'f0'}, {name: 'f1'}]
- );
- const results: FlamegraphSearch['results'] = new Map();
- // @ts-ignore we just need a partial frame
- results.set('f00', {});
- const flamegraphCanvas = new FlamegraphCanvas(canvas, vec2.fromValues(0, 0));
- const flamegraphView = new FlamegraphView({
- canvas: flamegraphCanvas,
- flamegraph,
- theme,
- });
- const renderer = new FlamegraphRenderer(canvas, flamegraph, theme);
- renderer.draw(
- flamegraphView.fromConfigView(flamegraphCanvas.physicalSpace),
- results
- );
- expect(context.uniform1i).toHaveBeenCalledTimes(3);
- expect(context.drawArrays).toHaveBeenCalledTimes(2);
- });
- it('draws all frames', () => {
- const context = makeContextMock();
- const canvas = makeCanvasMock({
- getContext: jest.fn().mockReturnValue(context),
- }) as HTMLCanvasElement;
- const flamegraph = makeFlamegraph(
- {
- startValue: 0,
- endValue: 100,
- events: [
- {
- type: 'O',
- frame: 0,
- at: 0,
- },
- {
- type: 'C',
- frame: 0,
- at: 1,
- },
- {
- type: 'O',
- frame: 1,
- at: 1,
- },
- {
- type: 'C',
- frame: 1,
- at: 2,
- },
- ],
- },
- [{name: 'f0'}, {name: 'f1'}]
- );
- const flamegraphCanvas = new FlamegraphCanvas(canvas, vec2.fromValues(0, 0));
- const flamegraphView = new FlamegraphView({
- canvas: flamegraphCanvas,
- flamegraph,
- theme,
- });
- const renderer = new FlamegraphRenderer(canvas, flamegraph, theme);
- renderer.draw(
- flamegraphView.fromConfigView(flamegraphCanvas.physicalSpace),
- new Map()
- );
- expect(context.drawArrays).toHaveBeenCalledTimes(2);
- });
- });
- });
|