flamegraphView.spec.tsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import {vec2} from 'gl-matrix';
  2. import {
  3. makeCanvasMock,
  4. makeContextMock,
  5. makeFlamegraph,
  6. } from 'sentry-test/profiling/utils';
  7. import {Flamegraph} from 'sentry/utils/profiling/flamegraph';
  8. import {LightFlamegraphTheme as theme} from 'sentry/utils/profiling/flamegraph/flamegraphTheme';
  9. import {FlamegraphCanvas} from 'sentry/utils/profiling/flamegraphCanvas';
  10. import {FlamegraphView} from 'sentry/utils/profiling/flamegraphView';
  11. import {Rect} from 'sentry/utils/profiling/gl/utils';
  12. const makeCanvasAndView = (
  13. canvas: HTMLCanvasElement,
  14. flamegraph: Flamegraph,
  15. origin: vec2 = vec2.fromValues(0, 0)
  16. ) => {
  17. const flamegraphCanvas = new FlamegraphCanvas(canvas, origin);
  18. const flamegraphView = new FlamegraphView({
  19. canvas: flamegraphCanvas,
  20. flamegraph,
  21. theme,
  22. });
  23. return {flamegraphCanvas, flamegraphView};
  24. };
  25. describe('flamegraphView', () => {
  26. beforeEach(() => {
  27. // We simulate regular screens unless differently specified
  28. window.devicePixelRatio = 1;
  29. });
  30. describe('initializes', () => {
  31. it('initializes config space', () => {
  32. const canvas = makeCanvasMock();
  33. const flamegraph = makeFlamegraph();
  34. const {flamegraphView} = makeCanvasAndView(canvas, flamegraph);
  35. expect(flamegraphView.configSpace).toEqual(new Rect(0, 0, 10, 50));
  36. });
  37. it('initializes config view', () => {
  38. const canvas = makeCanvasMock();
  39. const flamegraph = makeFlamegraph();
  40. const {flamegraphView} = makeCanvasAndView(canvas, flamegraph);
  41. expect(flamegraphView.configView).toEqual(new Rect(0, 0, 10, 50));
  42. });
  43. it('initializes config view with insufficient height', () => {
  44. const canvas = makeCanvasMock({height: 100});
  45. const flamegraph = makeFlamegraph();
  46. const {flamegraphView} = makeCanvasAndView(canvas, flamegraph);
  47. // 20 pixels tall each, and canvas is 100 pixels tall
  48. expect(flamegraphView.configView).toEqual(new Rect(0, 0, 10, 5));
  49. });
  50. it('resizes config space and config view', () => {
  51. const canvas = makeCanvasMock({width: 200, height: 200});
  52. const flamegraph = makeFlamegraph();
  53. const {flamegraphCanvas, flamegraphView} = makeCanvasAndView(canvas, flamegraph);
  54. expect(flamegraphView.configSpace).toEqual(new Rect(0, 0, 10, 13));
  55. expect(flamegraphView.configView).toEqual(new Rect(0, 0, 10, 10));
  56. // make it smaller
  57. canvas.width = 100;
  58. canvas.height = 100;
  59. flamegraphCanvas.initPhysicalSpace();
  60. flamegraphView.resizeConfigSpace(flamegraphCanvas);
  61. expect(flamegraphView.configSpace).toEqual(new Rect(0, 0, 10, 13));
  62. expect(flamegraphView.configView).toEqual(new Rect(0, 0, 10, 5));
  63. // make it bigger
  64. canvas.width = 1000;
  65. canvas.height = 1000;
  66. flamegraphCanvas.initPhysicalSpace();
  67. flamegraphView.resizeConfigSpace(flamegraphCanvas);
  68. expect(flamegraphView.configSpace).toEqual(new Rect(0, 0, 10, 50));
  69. expect(flamegraphView.configView).toEqual(new Rect(0, 0, 10, 50));
  70. });
  71. });
  72. describe('getConfigSpaceCursor', () => {
  73. it('when view is not zoomed', () => {
  74. const canvas = makeCanvasMock({
  75. getContext: jest
  76. .fn()
  77. // @ts-ignore
  78. .mockReturnValue(makeContextMock({canvas: {width: 1000, height: 2000}})),
  79. });
  80. const flamegraph = makeFlamegraph({startValue: 0, endValue: 100});
  81. const {flamegraphCanvas, flamegraphView} = makeCanvasAndView(canvas, flamegraph);
  82. // x=250 is 1/4 of the width of the viewport, so it should map to flamegraph duration / 4
  83. // y=250 is at 1/8th the height of the viewport, so it should map to view height / 8
  84. const cursor = flamegraphView.getConfigSpaceCursor(
  85. vec2.fromValues(250, 250),
  86. flamegraphCanvas
  87. );
  88. expect(cursor).toEqual(vec2.fromValues(25, 2000 / theme.SIZES.BAR_HEIGHT / 8));
  89. });
  90. });
  91. describe('setConfigView', () => {
  92. const canvas = makeCanvasMock();
  93. const flamegraph = makeFlamegraph(
  94. {
  95. startValue: 0,
  96. endValue: 1000,
  97. events: [
  98. {type: 'O', frame: 0, at: 0},
  99. {type: 'C', frame: 0, at: 500},
  100. ],
  101. },
  102. [{name: 'f0'}]
  103. );
  104. it('does not allow zooming in more than the min width of a frame', () => {
  105. const {flamegraphView} = makeCanvasAndView(canvas, flamegraph);
  106. flamegraphView.setConfigView(new Rect(0, 0, 10, 50));
  107. expect(flamegraphView.configView).toEqual(new Rect(0, 0, 500, 50));
  108. });
  109. it('does not allow zooming out more than the duration of a profile', () => {
  110. const {flamegraphView} = makeCanvasAndView(canvas, flamegraph);
  111. flamegraphView.setConfigView(new Rect(0, 0, 2000, 50));
  112. expect(flamegraphView.configView).toEqual(new Rect(0, 0, 1000, 50));
  113. });
  114. describe('edge detection on X axis', () => {
  115. it('is not zoomed in', () => {
  116. const {flamegraphView} = makeCanvasAndView(canvas, flamegraph);
  117. // Check that we cant go negative X from start of profile
  118. flamegraphView.setConfigView(new Rect(-100, 0, 1000, 50));
  119. expect(flamegraphView.configView).toEqual(new Rect(0, 0, 1000, 50));
  120. // Check that we cant go over X from end of profile
  121. flamegraphView.setConfigView(new Rect(2000, 0, 1000, 50));
  122. expect(flamegraphView.configView).toEqual(new Rect(0, 0, 1000, 50));
  123. });
  124. it('is zoomed in', () => {
  125. const {flamegraphView} = makeCanvasAndView(canvas, flamegraph);
  126. // Duration is is 1000, so we can't go over the end of the profile
  127. flamegraphView.setConfigView(new Rect(600, 0, 500, 50));
  128. expect(flamegraphView.configView).toEqual(new Rect(500, 0, 500, 50));
  129. });
  130. });
  131. describe('edge detection on Y axis', () => {
  132. it('is not zoomed in', () => {
  133. const {flamegraphView} = makeCanvasAndView(canvas, flamegraph);
  134. // Check that we cant go under stack height
  135. flamegraphView.setConfigView(new Rect(0, -50, 1000, 50));
  136. expect(flamegraphView.configView).toEqual(new Rect(0, 0, 1000, 50));
  137. // Check that we cant go over stack height
  138. flamegraphView.setConfigView(new Rect(0, 50, 1000, 50));
  139. expect(flamegraphView.configView).toEqual(new Rect(0, 0, 1000, 50));
  140. });
  141. it('is zoomed in', () => {
  142. const {flamegraphView} = makeCanvasAndView(canvas, flamegraph);
  143. // Check that we cant go over stack height
  144. flamegraphView.setConfigView(new Rect(0, 50, 1000, 25));
  145. expect(flamegraphView.configView).toEqual(new Rect(0, 25, 1000, 25));
  146. });
  147. });
  148. });
  149. });