gridRenderer.spec.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import {LightFlamegraphTheme} from 'sentry/utils/profiling/flamegraph/flamegraphTheme';
  2. import {transformMatrixBetweenRect} from 'sentry/utils/profiling/gl/utils';
  3. import {
  4. getIntervalTimeAtX,
  5. GridRenderer,
  6. } from 'sentry/utils/profiling/renderers/gridRenderer';
  7. import {computeInterval, Rect} from 'sentry/utils/profiling/speedscope';
  8. describe('getIntervalTimeAtX', () => {
  9. beforeEach(() => {
  10. window.devicePixelRatio = 1;
  11. });
  12. it('when origin is at 0', () => {
  13. const configView = new Rect(0, 0, 10, 10);
  14. const physicalSpace = new Rect(0, 0, 1000, 1000);
  15. const logicalSpace = physicalSpace.scale(
  16. 1 / window.devicePixelRatio,
  17. 1 / window.devicePixelRatio
  18. );
  19. const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
  20. expect(getIntervalTimeAtX(logicalToConfig, 500)).toBe(5);
  21. });
  22. it('when origin is offset', () => {
  23. const configView = new Rect(5, 0, 10, 10);
  24. const physicalSpace = new Rect(0, 0, 1000, 1000);
  25. const logicalSpace = physicalSpace.scale(
  26. 1 / window.devicePixelRatio,
  27. 1 / window.devicePixelRatio
  28. );
  29. const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
  30. expect(getIntervalTimeAtX(logicalToConfig, 500)).toBe(10);
  31. });
  32. it('high dpr - when origin is at 0', () => {
  33. window.devicePixelRatio = 2;
  34. const configView = new Rect(0, 0, 10, 10);
  35. const physicalSpace = new Rect(0, 0, 1000, 1000);
  36. const logicalSpace = physicalSpace.scale(
  37. 1 / window.devicePixelRatio,
  38. 1 / window.devicePixelRatio
  39. );
  40. const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
  41. expect(getIntervalTimeAtX(logicalToConfig, 500)).toBe(10);
  42. });
  43. it('high dpr - when origin is offset', () => {
  44. window.devicePixelRatio = 2;
  45. const configView = new Rect(5, 0, 10, 10);
  46. const physicalSpace = new Rect(0, 0, 1000, 1000);
  47. const logicalSpace = physicalSpace.scale(
  48. 1 / window.devicePixelRatio,
  49. 1 / window.devicePixelRatio
  50. );
  51. const logicalToPhysical = transformMatrixBetweenRect(logicalSpace, configView);
  52. expect(getIntervalTimeAtX(logicalToPhysical, 500)).toBe(15);
  53. });
  54. });
  55. describe('computeInterval', () => {
  56. beforeEach(() => {
  57. window.devicePixelRatio = 1;
  58. });
  59. it('computes intervals when origin is 0', () => {
  60. const configView = new Rect(0, 0, 100, 100);
  61. const physicalSpace = new Rect(0, 0, 1000, 1000);
  62. const logicalSpace = physicalSpace.scale(
  63. 1 / window.devicePixelRatio,
  64. 1 / window.devicePixelRatio
  65. );
  66. const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
  67. expect(computeInterval(configView, logicalToConfig, getIntervalTimeAtX)).toEqual([
  68. 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100,
  69. ]);
  70. });
  71. it('computes intervals when origin is offset', () => {
  72. const configView = new Rect(50, 0, 50, 100);
  73. const physicalSpace = new Rect(0, 0, 1000, 1000);
  74. const logicalSpace = physicalSpace.scale(
  75. 1 / window.devicePixelRatio,
  76. 1 / window.devicePixelRatio
  77. );
  78. const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
  79. expect(computeInterval(configView, logicalToConfig, getIntervalTimeAtX)).toEqual([
  80. 50, 60, 70, 80, 90, 100,
  81. ]);
  82. });
  83. it('readjust intervals *5 when they are too small', () => {
  84. const configView = new Rect(60, 0, 40, 100);
  85. const physicalSpace = new Rect(0, 0, 1000, 1000);
  86. const logicalSpace = physicalSpace.scale(
  87. 1 / window.devicePixelRatio,
  88. 1 / window.devicePixelRatio
  89. );
  90. const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
  91. expect(computeInterval(configView, logicalToConfig, getIntervalTimeAtX)).toEqual([
  92. 60, 65, 70, 75, 80, 85, 90, 95, 100,
  93. ]);
  94. });
  95. it('readjust intervals *2 when they are too small', () => {
  96. const configView = new Rect(80, 0, 20, 100);
  97. const physicalSpace = new Rect(0, 0, 1000, 1000);
  98. const logicalSpace = physicalSpace.scale(
  99. 1 / window.devicePixelRatio,
  100. 1 / window.devicePixelRatio
  101. );
  102. const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
  103. expect(computeInterval(configView, logicalToConfig, getIntervalTimeAtX)).toEqual([
  104. 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100,
  105. ]);
  106. });
  107. it('supports fractions', () => {
  108. const configView = new Rect(0, 0, 3, 100);
  109. const physicalSpace = new Rect(0, 0, 1000, 1000);
  110. const logicalSpace = physicalSpace.scale(
  111. 1 / window.devicePixelRatio,
  112. 1 / window.devicePixelRatio
  113. );
  114. const logicalToConfig = transformMatrixBetweenRect(logicalSpace, configView);
  115. expect(computeInterval(configView, logicalToConfig, getIntervalTimeAtX)).toEqual([
  116. 0, 0.5, 1, 1.5, 2, 2.5, 3,
  117. ]);
  118. });
  119. });
  120. describe('gridRenderer', () => {
  121. it('draws each interval line', () => {
  122. // Mock the width of the measured text, we dont actually care if this is accurate or not
  123. const WIDTH = 20;
  124. const context: Partial<CanvasRenderingContext2D> = {
  125. fillText: jest.fn(),
  126. fillRect: jest.fn(),
  127. strokeRect: jest.fn(),
  128. measureText: jest.fn().mockReturnValue({width: WIDTH}),
  129. };
  130. const canvas: Partial<HTMLCanvasElement> = {
  131. getContext: jest.fn().mockReturnValue(context),
  132. };
  133. const renderer = new GridRenderer(
  134. canvas as HTMLCanvasElement,
  135. LightFlamegraphTheme,
  136. jest.fn().mockImplementation(n => n + 'ms')
  137. );
  138. const configView = new Rect(0, 0, 10, 100);
  139. const physicalSpace = new Rect(0, 0, 1000, 1000);
  140. const logicalSpace = physicalSpace.scale(
  141. 1 / window.devicePixelRatio,
  142. 1 / window.devicePixelRatio
  143. );
  144. const configViewToPhysicalSpace = transformMatrixBetweenRect(
  145. configView,
  146. physicalSpace
  147. );
  148. const logicalSpaceToConfigView = transformMatrixBetweenRect(logicalSpace, configView);
  149. renderer.draw(
  150. configView,
  151. physicalSpace,
  152. configViewToPhysicalSpace,
  153. logicalSpaceToConfigView
  154. );
  155. // Labels should be 0 - 10
  156. expect(context.fillRect).toHaveBeenCalledTimes(3);
  157. // @ts-expect-error this is a mock
  158. for (let i = 0; i < context.fillText.mock.calls.length; i++) {
  159. // @ts-expect-error this is a mock
  160. expect(context.fillText.mock.calls[i][0]).toEqual(i + 'ms');
  161. // @ts-expect-error this is a mock
  162. expect(context.fillText.mock.calls[i][1]).toEqual(
  163. i * 100 - LightFlamegraphTheme.SIZES.LABEL_FONT_PADDING - WIDTH
  164. );
  165. // @ts-expect-error this is a mock
  166. // First 3 draw calls are for the horizontal line, the rest are verticals
  167. expect(context.strokeRect.mock.calls[i][0]).toEqual(i * 100 - 1);
  168. }
  169. });
  170. });