importProfile.spec.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. import {ChromeTraceProfile} from 'sentry/utils/profiling/profile/chromeTraceProfile';
  2. import {EventedProfile} from 'sentry/utils/profiling/profile/eventedProfile';
  3. import {
  4. importDroppedProfile,
  5. importProfile,
  6. } from 'sentry/utils/profiling/profile/importProfile';
  7. import {JSSelfProfile} from 'sentry/utils/profiling/profile/jsSelfProfile';
  8. import {SampledProfile} from 'sentry/utils/profiling/profile/sampledProfile';
  9. describe('importProfile', () => {
  10. it('imports evented profile', () => {
  11. const eventedProfile: Profiling.EventedProfile = {
  12. name: 'profile',
  13. startValue: 0,
  14. endValue: 1000,
  15. threadID: 0,
  16. unit: 'milliseconds',
  17. type: 'evented',
  18. events: [],
  19. };
  20. const imported = importProfile(
  21. {
  22. activeProfileIndex: 0,
  23. durationNS: 0,
  24. platform: 'android',
  25. profileID: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
  26. profiles: [eventedProfile],
  27. projectID: 1,
  28. shared: {
  29. frames: [],
  30. },
  31. transactionName: 'profile',
  32. version: '1.1.0 (build 2)',
  33. },
  34. ''
  35. );
  36. expect(imported.profiles[0]).toBeInstanceOf(EventedProfile);
  37. });
  38. it('imports sampled profile', () => {
  39. const sampledProfile: Profiling.SampledProfile = {
  40. name: 'profile',
  41. startValue: 0,
  42. endValue: 1000,
  43. threadID: 0,
  44. unit: 'milliseconds',
  45. type: 'sampled',
  46. weights: [],
  47. samples: [],
  48. };
  49. const imported = importProfile(
  50. {
  51. activeProfileIndex: 0,
  52. durationNS: 0,
  53. platform: 'cocoa',
  54. profileID: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
  55. profiles: [sampledProfile],
  56. projectID: 1,
  57. shared: {
  58. frames: [],
  59. },
  60. transactionName: 'profile',
  61. version: '7.14.0 (build 1)',
  62. },
  63. ''
  64. );
  65. expect(imported.profiles[0]).toBeInstanceOf(SampledProfile);
  66. });
  67. it('imports typescript profile', () => {
  68. const typescriptProfile: ChromeTrace.ArrayFormat = [
  69. {
  70. ph: 'B',
  71. ts: 1000,
  72. cat: 'program',
  73. pid: 0,
  74. tid: 0,
  75. name: 'createProgram',
  76. args: {configFilePath: '/Users/jonasbadalic/Work/sentry/tsconfig.json'},
  77. },
  78. {
  79. ph: 'E',
  80. ts: 2000,
  81. cat: 'program',
  82. pid: 0,
  83. tid: 0,
  84. name: 'createProgram',
  85. args: {configFilePath: '/Users/jonasbadalic/Work/sentry/tsconfig.json'},
  86. },
  87. ];
  88. const imported = importProfile(typescriptProfile, '');
  89. expect(imported.profiles[0]).toBeInstanceOf(ChromeTraceProfile);
  90. });
  91. it('imports JS self profile from schema', () => {
  92. const jsSelfProfile: JSSelfProfiling.Trace = {
  93. resources: ['app.js', 'vendor.js'],
  94. frames: [{name: 'ReactDOM.render', line: 1, column: 1, resourceId: 0}],
  95. samples: [
  96. {
  97. timestamp: 0,
  98. },
  99. {
  100. timestamp: 1000,
  101. stackId: 0,
  102. },
  103. ],
  104. stacks: [
  105. {
  106. frameId: 0,
  107. },
  108. ],
  109. };
  110. const imported = importProfile(
  111. {
  112. activeProfileIndex: 0,
  113. durationNS: 0,
  114. platform: 'typescript',
  115. profileID: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
  116. profiles: [jsSelfProfile],
  117. projectID: 1,
  118. shared: {
  119. frames: [],
  120. },
  121. transactionName: 'profile',
  122. version: '7.14.0 (build 1)',
  123. },
  124. ''
  125. );
  126. expect(imported.profiles[0]).toBeInstanceOf(JSSelfProfile);
  127. });
  128. it('imports JS self profile from raw Profiling output', () => {
  129. const jsSelfProfile: JSSelfProfiling.Trace = {
  130. resources: ['app.js', 'vendor.js'],
  131. frames: [{name: 'ReactDOM.render', line: 1, column: 1, resourceId: 0}],
  132. samples: [
  133. {
  134. timestamp: 0,
  135. },
  136. {
  137. timestamp: 1000,
  138. stackId: 0,
  139. },
  140. ],
  141. stacks: [
  142. {
  143. frameId: 0,
  144. },
  145. ],
  146. };
  147. const imported = importProfile(jsSelfProfile, 'profile');
  148. expect(imported.profiles[0]).toBeInstanceOf(JSSelfProfile);
  149. });
  150. it('throws on unrecognized profile type', () => {
  151. expect(() =>
  152. importProfile(
  153. // @ts-ignore
  154. {name: 'profile', activeProfileIndex: 0, profiles: [{type: 'unrecognized'}]},
  155. ''
  156. )
  157. ).toThrow();
  158. });
  159. });
  160. describe('importDroppedProfile', () => {
  161. beforeEach(() => {
  162. jest.restoreAllMocks();
  163. });
  164. it('throws if file has no string contents', async () => {
  165. // @ts-ignore we are just setting null on the file, we are not actually reading it because our event is mocked
  166. const file = new File([null], 'test.tsx');
  167. const reader = new FileReader();
  168. jest.spyOn(window, 'FileReader').mockImplementation(() => reader);
  169. jest.spyOn(reader, 'readAsText').mockImplementation(() => {
  170. const loadEvent = new CustomEvent('load', {
  171. detail: {target: {result: null}},
  172. });
  173. reader.dispatchEvent(loadEvent);
  174. });
  175. await expect(importDroppedProfile(file)).rejects.toEqual(
  176. 'Failed to read string contents of input file'
  177. );
  178. });
  179. it('throws if FileReader errors', async () => {
  180. const file = new File(['{json: true}'], 'test.tsx');
  181. const reader = new FileReader();
  182. jest.spyOn(window, 'FileReader').mockImplementation(() => reader);
  183. jest.spyOn(reader, 'readAsText').mockImplementation(() => {
  184. const loadEvent = new CustomEvent('error', {
  185. detail: {target: {result: null}},
  186. });
  187. reader.dispatchEvent(loadEvent);
  188. });
  189. await expect(importDroppedProfile(file)).rejects.toEqual(
  190. 'Failed to read string contents of input file'
  191. );
  192. });
  193. it('throws if contents are not valid JSON', async () => {
  194. const file = new File(['{"json": true'], 'test.tsx');
  195. await expect(importDroppedProfile(file)).rejects.toBeInstanceOf(Error);
  196. });
  197. it('imports dropped schema file', async () => {
  198. const schema: Profiling.Schema = {
  199. activeProfileIndex: 0,
  200. durationNS: 0,
  201. platform: 'cocoa',
  202. profileID: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
  203. profiles: [
  204. {
  205. name: 'profile',
  206. startValue: 0,
  207. endValue: 1000,
  208. threadID: 0,
  209. unit: 'milliseconds',
  210. type: 'sampled',
  211. weights: [],
  212. samples: [],
  213. },
  214. ],
  215. projectID: 1,
  216. shared: {
  217. frames: [],
  218. },
  219. transactionName: 'profile',
  220. version: '7.14.0 (build 1)',
  221. };
  222. const file = new File([JSON.stringify(schema)], 'test.tsx');
  223. const imported = await importDroppedProfile(file);
  224. expect(imported.profiles[0]).toBeInstanceOf(SampledProfile);
  225. });
  226. it('imports dropped typescript profile', async () => {
  227. const typescriptProfile: ChromeTrace.ArrayFormat = [
  228. {
  229. ph: 'B',
  230. ts: 1000,
  231. cat: 'program',
  232. pid: 0,
  233. tid: 0,
  234. name: 'createProgram',
  235. args: {configFilePath: '/Users/jonasbadalic/Work/sentry/tsconfig.json'},
  236. },
  237. {
  238. ph: 'E',
  239. ts: 2000,
  240. cat: 'program',
  241. pid: 0,
  242. tid: 0,
  243. name: 'createProgram',
  244. args: {configFilePath: '/Users/jonasbadalic/Work/sentry/tsconfig.json'},
  245. },
  246. ];
  247. const file = new File([JSON.stringify(typescriptProfile)], 'test.tsx');
  248. const imported = await importDroppedProfile(file);
  249. expect(imported.profiles[0]).toBeInstanceOf(ChromeTraceProfile);
  250. });
  251. it('imports dropped JS self profile', async () => {
  252. const jsSelfProfile: JSSelfProfiling.Trace = {
  253. resources: ['app.js', 'vendor.js'],
  254. frames: [{name: 'ReactDOM.render', line: 1, column: 1, resourceId: 0}],
  255. samples: [
  256. {
  257. timestamp: 0,
  258. },
  259. {
  260. timestamp: 1000,
  261. stackId: 0,
  262. },
  263. ],
  264. stacks: [
  265. {
  266. frameId: 0,
  267. },
  268. ],
  269. };
  270. const file = new File([JSON.stringify(jsSelfProfile)], 'test.tsx');
  271. const imported = await importDroppedProfile(file);
  272. expect(imported.profiles[0]).toBeInstanceOf(JSSelfProfile);
  273. });
  274. });