Files
2024-02-15 17:01:50 +00:00

209 lines
6.6 KiB
TypeScript

import {LightFlamegraphTheme} from 'sentry/utils/profiling/flamegraph/flamegraphTheme';
import {transformMatrixBetweenRect} from 'sentry/utils/profiling/gl/utils';
import {
getIntervalTimeAtX,
GridRenderer,
} from 'sentry/utils/profiling/renderers/gridRenderer';
import {computeInterval, Rect} from 'sentry/utils/profiling/speedscope';
describe('getIntervalTimeAtX', () => {
beforeEach(() => {
window.devicePixelRatio = 1;
});
it('when origin is at 0', () => {
const configView = new Rect(0, 0, 10, 10);
const physicalSpace = new Rect(0, 0, 1000, 1000);
const logicalSpace = physicalSpace.scale(
1 / window.devicePixelRatio,
1 / window.devicePixelRatio
);
const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
expect(getIntervalTimeAtX(logicalToConfig, 500)).toBe(5);
});
it('when origin is offset', () => {
const configView = new Rect(5, 0, 10, 10);
const physicalSpace = new Rect(0, 0, 1000, 1000);
const logicalSpace = physicalSpace.scale(
1 / window.devicePixelRatio,
1 / window.devicePixelRatio
);
const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
expect(getIntervalTimeAtX(logicalToConfig, 500)).toBe(10);
});
it('high dpr - when origin is at 0', () => {
window.devicePixelRatio = 2;
const configView = new Rect(0, 0, 10, 10);
const physicalSpace = new Rect(0, 0, 1000, 1000);
const logicalSpace = physicalSpace.scale(
1 / window.devicePixelRatio,
1 / window.devicePixelRatio
);
const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
expect(getIntervalTimeAtX(logicalToConfig, 500)).toBe(10);
});
it('high dpr - when origin is offset', () => {
window.devicePixelRatio = 2;
const configView = new Rect(5, 0, 10, 10);
const physicalSpace = new Rect(0, 0, 1000, 1000);
const logicalSpace = physicalSpace.scale(
1 / window.devicePixelRatio,
1 / window.devicePixelRatio
);
const logicalToPhysical = transformMatrixBetweenRect(logicalSpace, configView);
expect(getIntervalTimeAtX(logicalToPhysical, 500)).toBe(15);
});
});
describe('computeInterval', () => {
beforeEach(() => {
window.devicePixelRatio = 1;
});
it('computes intervals when origin is 0', () => {
const configView = new Rect(0, 0, 100, 100);
const physicalSpace = new Rect(0, 0, 1000, 1000);
const logicalSpace = physicalSpace.scale(
1 / window.devicePixelRatio,
1 / window.devicePixelRatio
);
const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
expect(computeInterval(configView, logicalToConfig, getIntervalTimeAtX)).toEqual([
0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100,
]);
});
it('computes intervals when origin is offset', () => {
const configView = new Rect(50, 0, 50, 100);
const physicalSpace = new Rect(0, 0, 1000, 1000);
const logicalSpace = physicalSpace.scale(
1 / window.devicePixelRatio,
1 / window.devicePixelRatio
);
const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
expect(computeInterval(configView, logicalToConfig, getIntervalTimeAtX)).toEqual([
50, 60, 70, 80, 90, 100,
]);
});
it('readjust intervals *5 when they are too small', () => {
const configView = new Rect(60, 0, 40, 100);
const physicalSpace = new Rect(0, 0, 1000, 1000);
const logicalSpace = physicalSpace.scale(
1 / window.devicePixelRatio,
1 / window.devicePixelRatio
);
const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
expect(computeInterval(configView, logicalToConfig, getIntervalTimeAtX)).toEqual([
60, 65, 70, 75, 80, 85, 90, 95, 100,
]);
});
it('readjust intervals *2 when they are too small', () => {
const configView = new Rect(80, 0, 20, 100);
const physicalSpace = new Rect(0, 0, 1000, 1000);
const logicalSpace = physicalSpace.scale(
1 / window.devicePixelRatio,
1 / window.devicePixelRatio
);
const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
expect(computeInterval(configView, logicalToConfig, getIntervalTimeAtX)).toEqual([
80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100,
]);
});
it('supports fractions', () => {
const configView = new Rect(0, 0, 3, 100);
const physicalSpace = new Rect(0, 0, 1000, 1000);
const logicalSpace = physicalSpace.scale(
1 / window.devicePixelRatio,
1 / window.devicePixelRatio
);
const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
expect(computeInterval(configView, logicalToConfig, getIntervalTimeAtX)).toEqual([
0, 0.5, 1, 1.5, 2, 2.5, 3,
]);
});
});
describe('gridRenderer', () => {
it('draws each interval line', () => {
// Mock the width of the measured text, we dont actually care if this is accurate or not
const WIDTH = 20;
const context: Partial<CanvasRenderingContext2D> = {
fillText: jest.fn(),
fillRect: jest.fn(),
strokeRect: jest.fn(),
measureText: jest.fn().mockReturnValue({width: WIDTH}),
};
const canvas: Partial<HTMLCanvasElement> = {
getContext: jest.fn().mockReturnValue(context),
};
const renderer = new GridRenderer(
canvas as HTMLCanvasElement,
LightFlamegraphTheme,
jest.fn().mockImplementation(n => n + 'ms')
);
const configView = new Rect(0, 0, 10, 100);
const physicalSpace = new Rect(0, 0, 1000, 1000);
const logicalSpace = physicalSpace.scale(
1 / window.devicePixelRatio,
1 / window.devicePixelRatio
);
const configViewToPhysicalSpace = transformMatrixBetweenRect(
configView,
physicalSpace
);
const logicalSpaceToConfigView = transformMatrixBetweenRect(logicalSpace, configView);
renderer.draw(
configView,
physicalSpace,
configViewToPhysicalSpace,
logicalSpaceToConfigView
);
// Labels should be 0 - 10
expect(context.fillRect).toHaveBeenCalledTimes(3);
// @ts-expect-error this is a mock
for (let i = 0; i < context.fillText.mock.calls.length; i++) {
// @ts-expect-error this is a mock
expect(context.fillText.mock.calls[i][0]).toEqual(i + 'ms');
// @ts-expect-error this is a mock
expect(context.fillText.mock.calls[i][1]).toEqual(
i * 100 - LightFlamegraphTheme.SIZES.LABEL_FONT_PADDING - WIDTH
);
// @ts-expect-error this is a mock
// First 3 draw calls are for the horizontal line, the rest are verticals
expect(context.strokeRect.mock.calls[i][0]).toEqual(i * 100 - 1);
}
});
});