gridRenderer.spec.tsx 6.7 KB

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