sentrySampledProfile.spec.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import merge from 'lodash/merge';
  2. import {DeepPartial} from 'sentry/types/utils';
  3. import {makeTestingBoilerplate} from './profile.spec';
  4. import {SentrySampledProfile} from './sentrySampledProfile';
  5. import {createSentrySampleProfileFrameIndex} from './utils';
  6. export const makeSentrySampledProfile = (
  7. profile?: DeepPartial<Profiling.SentrySampledProfile>
  8. ) => {
  9. return merge(
  10. {
  11. event_id: '1',
  12. version: '1',
  13. os: {
  14. name: 'iOS',
  15. version: '16.0',
  16. build_number: '19H253',
  17. },
  18. device: {
  19. architecture: 'arm64e',
  20. is_emulator: false,
  21. locale: 'en_US',
  22. manufacturer: 'Apple',
  23. model: 'iPhone14,3',
  24. },
  25. timestamp: '2022-09-01T09:45:00.000Z',
  26. release: '0.1 (199)',
  27. platform: 'cocoa',
  28. profile: {
  29. samples: [
  30. {
  31. stack_id: 0,
  32. thread_id: '0',
  33. elapsed_since_start_ns: '0',
  34. },
  35. {
  36. stack_id: 1,
  37. thread_id: '0',
  38. elapsed_since_start_ns: '1000',
  39. },
  40. ],
  41. frames: [
  42. {
  43. function: 'foo',
  44. instruction_addr: '',
  45. lineno: 2,
  46. colno: 2,
  47. file: 'main.c',
  48. },
  49. {
  50. function: 'main',
  51. instruction_addr: '',
  52. lineno: 1,
  53. colno: 1,
  54. file: 'main.c',
  55. },
  56. ],
  57. stacks: [[0], [0, 1]],
  58. },
  59. },
  60. profile
  61. ) as Profiling.SentrySampledProfile;
  62. };
  63. describe('SentrySampledProfile', () => {
  64. it('constructs a profile', () => {
  65. const sampledProfile: Profiling.SentrySampledProfile = makeSentrySampledProfile();
  66. const profile = SentrySampledProfile.FromProfile(
  67. sampledProfile,
  68. createSentrySampleProfileFrameIndex(sampledProfile.profile.frames)
  69. );
  70. const {open, close, timings} = makeTestingBoilerplate();
  71. profile.forEach(open, close);
  72. expect(profile.duration).toBe(1000);
  73. expect(timings).toEqual([
  74. ['main', 'open'],
  75. ['foo', 'open'],
  76. ['foo', 'close'],
  77. ['main', 'close'],
  78. ]);
  79. expect(profile.startedAt).toEqual(0);
  80. expect(profile.endedAt).toEqual(1000);
  81. });
  82. it('derives a profile name from the transaction.name and thread_id', () => {
  83. const sampledProfile = makeSentrySampledProfile({
  84. transactions: [
  85. {
  86. id: '',
  87. name: 'foo',
  88. active_thread_id: '1',
  89. relative_start_ns: '0',
  90. relative_end_ns: '1000000',
  91. trace_id: '1',
  92. },
  93. ],
  94. profile: {
  95. samples: [
  96. {
  97. stack_id: 0,
  98. elapsed_since_start_ns: '1000',
  99. thread_id: '0',
  100. },
  101. ],
  102. thread_metadata: {
  103. '0': {
  104. name: 'bar',
  105. },
  106. },
  107. },
  108. });
  109. const profile = SentrySampledProfile.FromProfile(
  110. sampledProfile,
  111. createSentrySampleProfileFrameIndex(sampledProfile.profile.frames)
  112. );
  113. expect(profile.name).toBe('foo (thread: bar)');
  114. });
  115. it('derives a profile name from just thread_id', () => {
  116. const sampledProfile = makeSentrySampledProfile({
  117. profile: {
  118. samples: [
  119. {
  120. stack_id: 0,
  121. elapsed_since_start_ns: '1000',
  122. thread_id: '0',
  123. },
  124. ],
  125. thread_metadata: {},
  126. },
  127. });
  128. const profile = SentrySampledProfile.FromProfile(
  129. sampledProfile,
  130. createSentrySampleProfileFrameIndex(sampledProfile.profile.frames)
  131. );
  132. expect(profile.name).toBe('thread: 0');
  133. });
  134. it('derives a profile name from just thread name', () => {
  135. const sampledProfile = makeSentrySampledProfile({
  136. profile: {
  137. samples: [
  138. {
  139. stack_id: 0,
  140. elapsed_since_start_ns: '1000',
  141. thread_id: '0',
  142. },
  143. ],
  144. thread_metadata: {
  145. '0': {
  146. name: 'foo',
  147. },
  148. },
  149. },
  150. });
  151. const profile = SentrySampledProfile.FromProfile(
  152. sampledProfile,
  153. createSentrySampleProfileFrameIndex(sampledProfile.profile.frames)
  154. );
  155. expect(profile.name).toBe('thread: foo');
  156. });
  157. it('throws a TypeError when it cannot parse startedAt or endedAt', () => {
  158. const sampledProfile = makeSentrySampledProfile({
  159. profile: {
  160. samples: [
  161. {
  162. stack_id: 0,
  163. elapsed_since_start_ns: 'a1000',
  164. thread_id: '0',
  165. },
  166. ],
  167. thread_metadata: {
  168. '0': {
  169. name: 'foo',
  170. },
  171. },
  172. },
  173. });
  174. expect(() =>
  175. SentrySampledProfile.FromProfile(
  176. sampledProfile,
  177. createSentrySampleProfileFrameIndex(sampledProfile.profile.frames)
  178. )
  179. ).toThrow(new TypeError('startedAt or endedAt is NaN'));
  180. });
  181. });