github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/packages/pyroscope-flamegraph/src/index.spec.tsx (about) 1 import React from 'react'; 2 import { render, screen } from '@testing-library/react'; 3 import { FlamegraphRenderer } from './index'; 4 5 const RawProfile = { 6 version: 1, 7 flamebearer: { 8 names: [ 9 'total', 10 'runtime.mcall', 11 'runtime.park_m', 12 'runtime.schedule', 13 'runtime.resetspinning', 14 'runtime.wakep', 15 'runtime.startm', 16 'runtime.notewakeup', 17 'runtime.futexwakeup', 18 'runtime.futex', 19 'runtime.findrunnable', 20 'runtime.write', 21 'runtime.write1', 22 'runtime.stopm', 23 'runtime.mput', 24 'runtime.mPark', 25 'runtime.notesleep', 26 'runtime.futexsleep', 27 'runtime.stealWork', 28 'runtime.checkTimers', 29 'runtime.runtimer', 30 'runtime.runOneTimer', 31 'time.sendTime', 32 'runtime.selectnbsend', 33 'runtime.chansend', 34 'runtime.adjusttimers', 35 'runtime.netpoll', 36 'runtime.read', 37 'runtime.epollwait', 38 'runtime.siftdownTimer', 39 'runtime.gosched_m', 40 'runtime.unlockWithRank', 41 'runtime.unlock2', 42 'runtime.lockWithRank', 43 'runtime.lock2', 44 'runtime.procyield', 45 'runtime.goschedImpl', 46 'runtime.nanotime', 47 'runtime.globrunqget', 48 'runtime.(*gQueue).pop', 49 'runtime.execute', 50 'runtime.casgstatus', 51 'runtime.gcBgMarkWorker', 52 'runtime.systemstack', 53 'runtime.gcBgMarkWorker.func2', 54 'runtime.gcDrain', 55 'runtime.spanOfUnchecked', 56 'runtime.scanobject', 57 'runtime.spanOf', 58 'runtime.pageIndexOf', 59 'runtime.markBits.isMarked', 60 'runtime.greyobject', 61 'runtime.findObject', 62 'runtime.arenaIndex', 63 'runtime.(*mspan).divideByElemSize', 64 'runtime.(*gcWork).putFast', 65 'runtime.heapBitsForAddr', 66 'runtime.heapBits.next', 67 'runtime.heapBits.bits', 68 'runtime.gcFlushBgCredit', 69 'runtime.bgsweep', 70 'runtime.sweepone', 71 'runtime.(*sweepLocked).sweep', 72 'runtime.(*gcBitsArena).tryAlloc', 73 'runtime.(*sweepLocker).dispose', 74 'runtime.(*sweepLocker).blockCompletion', 75 'net/http.(*conn).serve', 76 'net/http.serverHandler.ServeHTTP', 77 'net/http.HandlerFunc.ServeHTTP', 78 'github.com/klauspost/compress/gzhttp.NewWrapper.func1.1', 79 'github.com/gorilla/mux.(*Router).ServeHTTP', 80 'github.com/pyroscope-io/pyroscope/pkg/server.(*Controller).drainMiddleware.func1', 81 'github.com/pyroscope-io/pyroscope/pkg/server.ingestHandler.ServeHTTP', 82 'github.com/pyroscope-io/pyroscope/pkg/structs/transporttrie.IterateRaw', 83 'io.LimitReader', 84 'runtime.newobject', 85 'runtime.nextFreeFast', 86 'io.Copy', 87 'io.copyBuffer', 88 'bytes.(*Buffer).ReadFrom', 89 'io.(*LimitedReader).Read', 90 'net/http.(*response).finishRequest', 91 'bufio.(*Writer).Flush', 92 'net/http.checkConnErrorWriter.Write', 93 'net.(*conn).Write', 94 'net.(*netFD).Write', 95 'syscall.Write', 96 'syscall.write', 97 'syscall.Syscall', 98 'github.com/pyroscope-io/pyroscope/pkg/agent/upstream/direct.(*Direct).uploadLoop', 99 'github.com/pyroscope-io/pyroscope/pkg/agent/upstream/direct.(*Direct).safeUpload', 100 'github.com/pyroscope-io/pyroscope/pkg/agent/upstream/direct.(*Direct).uploadProfile', 101 'github.com/pyroscope-io/pyroscope/pkg/structs/transporttrie.(*Trie).Iterate', 102 'runtime.growslice', 103 'runtime.mallocgc', 104 'github.com/pyroscope-io/pyroscope/pkg/agent.(*ProfileSession).takeSnapshots', 105 'runtime.selectgo', 106 'runtime.gopark', 107 'runtime.(*mcache).nextFree', 108 'runtime.(*mcache).refill', 109 'runtime.(*mcentral).cacheSpan', 110 'runtime.mapiterinit', 111 'runtime.makemap_small', 112 'github.com/pyroscope-io/pyroscope/pkg/agent/gospy.(*GoSpy).Snapshot', 113 'runtime/pprof.writeHeap', 114 'runtime/pprof.writeHeapInternal', 115 'runtime/pprof.writeHeapProto', 116 'runtime/pprof.(*profileBuilder).pbSample', 117 'runtime/pprof.(*profileBuilder).flush', 118 'compress/flate.(*Writer).Write', 119 'compress/flate.(*compressor).write', 120 'compress/flate.(*compressor).encSpeed', 121 'compress/flate.(*deflateFast).encode', 122 'compress/flate.emitLiteral', 123 'runtime/pprof.(*profileBuilder).appendLocsForStack', 124 'runtime/pprof.allFrames', 125 'runtime.(*Frames).Next', 126 'runtime.funcline1', 127 'runtime.pcvalue', 128 'runtime.step', 129 'runtime.GC', 130 'runtime.(*spanSet).push', 131 'runtime.(*headTailIndex).incTail', 132 'runtime.(*mheap).freeSpan', 133 'runtime.(*mheap).nextSpanForSweep', 134 'runtime.(*spanSet).pop', 135 'github.com/pyroscope-io/pyroscope/pkg/storage/tree.(*Profile).Get', 136 'github.com/pyroscope-io/pyroscope/pkg/storage/tree.FindFunctionName', 137 'runtime.asyncPreempt', 138 'github.com/pyroscope-io/pyroscope/pkg/storage/tree.FindLocation', 139 'sort.Search', 140 'github.com/pyroscope-io/pyroscope/pkg/storage/tree.FindFunction', 141 'github.com/pyroscope-io/pyroscope/pkg/storage/tree.FindFunction.func1', 142 'github.com/pyroscope-io/pyroscope/pkg/agent/gospy.(*GoSpy).Snapshot.func3', 143 'github.com/pyroscope-io/pyroscope/pkg/agent.(*ProfileSession).takeSnapshots.func1', 144 'github.com/pyroscope-io/pyroscope/pkg/structs/transporttrie.(*Trie).Insert', 145 'github.com/pyroscope-io/pyroscope/pkg/structs/transporttrie.(*trieNode).findNodeAt', 146 'runtime.(*mcentral).grow', 147 'runtime.(*mheap).alloc', 148 'runtime.(*mheap).alloc.func1', 149 'runtime.(*mheap).allocSpan', 150 'runtime.(*mheap).allocMSpanLocked', 151 'runtime.(*fixalloc).alloc', 152 'github.com/pyroscope-io/pyroscope/pkg/agent/gospy.getHeapProfile', 153 'github.com/pyroscope-io/pyroscope/pkg/convert.ParsePprof', 154 'google.golang.org/protobuf/proto.Unmarshal', 155 'google.golang.org/protobuf/proto.UnmarshalOptions.unmarshal', 156 'google.golang.org/protobuf/internal/impl.(*MessageInfo).unmarshal', 157 'google.golang.org/protobuf/internal/impl.(*MessageInfo).unmarshalPointer', 158 'google.golang.org/protobuf/internal/impl.consumeMessageSliceInfo', 159 'github.com/pyroscope-io/pyroscope/pkg/agent.(*ProfileSession).isDueForReset', 160 'time.Time.Truncate', 161 'github.com/dgraph-io/badger/v2.(*levelsController).runCompactor', 162 'github.com/dgraph-io/badger/v2.(*levelsController).pickCompactLevels', 163 'sort.Slice', 164 'github.com/dgraph-io/badger/v2.(*DB).doWrites.func1', 165 'github.com/dgraph-io/badger/v2.(*DB).writeRequests', 166 'github.com/dgraph-io/badger/v2.(*valueLog).write', 167 'github.com/dgraph-io/badger/v2.(*logFile).encodeEntry', 168 'hash/crc32.New', 169 'runtime.heapBitsSetType', 170 'runtime.newstack', 171 'runtime.copystack', 172 'runtime.memmove', 173 ], 174 levels: [ 175 [0, 236, 0, 0], 176 [ 177 0, 1, 0, 155, 0, 1, 0, 152, 0, 29, 0, 95, 0, 1, 0, 89, 0, 3, 0, 66, 0, 178 11, 0, 60, 0, 154, 0, 42, 0, 36, 0, 1, 179 ], 180 [ 181 0, 1, 0, 156, 0, 1, 0, 153, 0, 1, 0, 150, 0, 23, 0, 103, 0, 1, 0, 102, 182 0, 1, 1, 101, 0, 1, 0, 75, 0, 2, 1, 96, 0, 1, 0, 90, 0, 1, 0, 81, 0, 2, 183 0, 67, 0, 2, 2, 65, 0, 3, 3, 64, 0, 6, 3, 61, 0, 154, 0, 43, 0, 19, 0, 184 30, 0, 17, 0, 2, 185 ], 186 [ 187 0, 1, 0, 157, 0, 1, 1, 154, 0, 1, 1, 151, 0, 1, 0, 143, 0, 12, 0, 126, 188 0, 7, 0, 120, 0, 3, 0, 104, 0, 1, 0, 75, 1, 1, 0, 94, 1, 1, 1, 97, 0, 1, 189 0, 91, 0, 1, 0, 82, 0, 2, 0, 68, 8, 3, 2, 62, 0, 154, 0, 44, 0, 15, 0, 190 36, 0, 2, 0, 33, 0, 2, 0, 31, 0, 17, 0, 3, 191 ], 192 [ 193 0, 1, 0, 158, 2, 1, 0, 144, 0, 7, 0, 133, 0, 5, 0, 127, 0, 7, 1, 61, 0, 194 3, 0, 105, 0, 1, 1, 94, 1, 1, 0, 98, 2, 1, 0, 92, 0, 1, 0, 83, 0, 2, 0, 195 69, 10, 1, 1, 63, 0, 154, 14, 45, 0, 1, 1, 41, 0, 14, 0, 3, 0, 2, 0, 34, 196 0, 2, 2, 32, 0, 16, 1, 10, 0, 1, 0, 4, 197 ], 198 [ 199 0, 1, 0, 159, 2, 1, 0, 145, 0, 7, 0, 134, 0, 3, 0, 131, 0, 1, 0, 129, 0, 200 1, 1, 128, 1, 1, 0, 124, 0, 5, 0, 62, 0, 3, 0, 106, 2, 1, 0, 99, 2, 1, 201 0, 93, 0, 1, 0, 84, 0, 2, 0, 70, 25, 1, 1, 59, 0, 7, 7, 58, 0, 5, 5, 57, 202 0, 6, 6, 56, 0, 119, 61, 47, 0, 2, 2, 46, 1, 2, 1, 40, 0, 4, 0, 10, 0, 203 3, 0, 33, 0, 2, 2, 37, 0, 3, 0, 31, 0, 2, 2, 35, 3, 1, 0, 19, 0, 5, 0, 204 26, 0, 2, 0, 18, 0, 6, 0, 13, 0, 1, 0, 11, 0, 1, 0, 5, 205 ], 206 [ 207 0, 1, 0, 75, 2, 1, 0, 146, 0, 7, 0, 135, 0, 3, 2, 130, 0, 1, 1, 130, 2, 208 1, 1, 125, 0, 1, 0, 123, 0, 4, 3, 121, 0, 1, 0, 114, 0, 2, 0, 107, 2, 1, 209 1, 100, 2, 1, 1, 94, 0, 1, 0, 85, 0, 2, 0, 68, 105, 2, 2, 55, 0, 3, 3, 210 54, 0, 1, 1, 53, 0, 17, 17, 52, 0, 3, 3, 51, 0, 5, 5, 50, 0, 11, 11, 49, 211 0, 16, 16, 48, 4, 1, 1, 41, 0, 1, 1, 39, 0, 1, 1, 38, 0, 1, 1, 37, 0, 1, 212 0, 26, 0, 3, 0, 34, 2, 3, 3, 32, 5, 1, 0, 20, 0, 4, 4, 28, 0, 1, 1, 27, 213 0, 2, 0, 19, 0, 5, 0, 15, 0, 1, 1, 14, 0, 1, 1, 12, 0, 1, 0, 6, 214 ], 215 [ 216 0, 1, 0, 94, 2, 1, 0, 147, 0, 7, 6, 136, 2, 1, 1, 132, 4, 1, 0, 43, 3, 217 1, 1, 122, 0, 1, 0, 115, 0, 2, 0, 108, 6, 1, 0, 86, 0, 2, 0, 71, 171, 1, 218 1, 28, 0, 3, 3, 35, 10, 1, 0, 21, 5, 1, 1, 25, 0, 1, 0, 20, 0, 5, 0, 16, 219 2, 1, 0, 7, 220 ], 221 [ 222 0, 1, 0, 160, 2, 1, 0, 148, 6, 1, 0, 94, 7, 1, 0, 33, 4, 1, 0, 116, 0, 223 2, 0, 109, 6, 1, 0, 87, 0, 2, 0, 72, 185, 1, 1, 29, 6, 1, 0, 21, 0, 5, 224 0, 17, 2, 1, 0, 8, 225 ], 226 [ 227 0, 1, 0, 161, 2, 1, 0, 149, 6, 1, 0, 98, 7, 1, 1, 34, 4, 1, 0, 117, 0, 228 2, 0, 110, 6, 1, 1, 88, 0, 2, 0, 73, 192, 1, 0, 22, 0, 5, 5, 9, 2, 1, 1, 229 9, 230 ], 231 [ 232 0, 1, 0, 162, 2, 1, 1, 148, 6, 1, 0, 99, 12, 1, 0, 118, 0, 2, 0, 111, 7, 233 1, 0, 77, 0, 1, 0, 74, 192, 1, 0, 23, 234 ], 235 [ 236 0, 1, 1, 163, 9, 1, 0, 100, 12, 1, 1, 119, 0, 2, 1, 112, 7, 1, 0, 78, 0, 237 1, 0, 75, 192, 1, 1, 24, 238 ], 239 [10, 1, 0, 137, 14, 1, 1, 113, 7, 1, 0, 79, 0, 1, 1, 76], 240 [10, 1, 0, 138, 22, 1, 1, 80], 241 [10, 1, 0, 43], 242 [10, 1, 0, 139], 243 [10, 1, 0, 140], 244 [10, 1, 0, 141], 245 [10, 1, 1, 142], 246 ], 247 numTicks: 236, 248 maxSelf: 61, 249 }, 250 timeline: { 251 startTime: 1646760440, 252 samples: [237], 253 durationDelta: 10, 254 watermarks: {}, 255 }, 256 metadata: { 257 format: 'single' as const, 258 spyName: 'gospy' as const, 259 sampleRate: 100, 260 units: 'samples' as const, 261 name: 'pyroscope.server.cpu 2022-03-08T17:27:23Z', 262 appName: 'pyroscope.server.cpu', 263 startTime: 1646760443, 264 endTime: 1646760446, 265 query: 'pyroscope.server.cpu{}', 266 maxNodes: 1024, 267 }, 268 }; 269 270 const SimpleTree = { 271 topLevel: 0, 272 rangeMin: 0, 273 format: 'single' as const, 274 numTicks: 988, 275 sampleRate: 100, 276 names: [ 277 'total', 278 'runtime.main', 279 'main.slowFunction', 280 'main.work', 281 'main.main', 282 'main.fastFunction', 283 ], 284 levels: [ 285 [0, 988, 0, 0], 286 [0, 988, 0, 1], 287 [0, 214, 0, 5, 214, 3, 2, 4, 217, 771, 0, 2], 288 [0, 214, 214, 3, 216, 1, 1, 5, 217, 771, 771, 3], 289 ], 290 291 rangeMax: 1, 292 units: 'samples', 293 fitMode: 'HEAD', 294 295 spyName: 'gospy', 296 }; 297 298 // describe.skip('Pyroscope Library', () => { 299 // it('should not be possible to override the pyroscope logo using props', () => { 300 // render( 301 // <FlamegraphRenderer 302 // flamebearer={SimpleTree} 303 // display="flamegraph" 304 // viewType="single" 305 // showPyroscopeLogo={false} 306 // /> 307 // ); 308 // 309 // expect( 310 // screen.getByRole('link', { name: /pyroscope/i }) 311 // ).toBeInTheDocument(); 312 // }); 313 // }); 314 // 315 // 316 // TODO a test saying going over rendering an empty flamegraph 317 describe.only('positions', () => { 318 beforeAll(() => { 319 window.HTMLElement.prototype.getBoundingClientRect = function () { 320 return { 321 x: 0, 322 y: 0, 323 bottom: 0, 324 right: 0, 325 toJSON: () => {}, 326 height: 0, 327 top: 0, 328 left: 0, 329 width: 900, 330 }; 331 }; 332 }); 333 334 describe('Allow changing visualization mode', () => { 335 it('should allow changing view when "onlyDisplay" is not set', () => { 336 const { getByTestId, queryByRole } = render( 337 <FlamegraphRenderer profile={RawProfile} /> 338 ); 339 340 expect(getByTestId('table-ui')).toBeInTheDocument(); 341 expect(getByTestId('flamegraph-view')).toBeInTheDocument(); 342 expect(getByTestId('flamegraph')).toBeInTheDocument(); 343 expect(getByTestId('both')).toBeInTheDocument(); 344 expect(getByTestId('table')).toBeInTheDocument(); 345 }); 346 347 it('should restrict changing view when "onlyDisplay" is set', () => { 348 const { getByTestId, queryByRole } = render( 349 <FlamegraphRenderer profile={RawProfile} onlyDisplay="both" /> 350 ); 351 352 expect(getByTestId('table-ui')).toBeInTheDocument(); 353 expect(getByTestId('flamegraph-view')).toBeInTheDocument(); 354 expect(queryByRole('combobox', { name: 'view' })).not.toBeInTheDocument(); 355 }); 356 }); 357 358 it('should display only the flamegraph when specified', () => { 359 const { getByTestId, queryByTestId } = render( 360 <FlamegraphRenderer profile={RawProfile} onlyDisplay="flamegraph" /> 361 ); 362 363 expect(queryByTestId('table-ui')).not.toBeInTheDocument(); 364 expect(getByTestId('flamegraph-view')).toBeInTheDocument(); 365 }); 366 367 it('should display only the table when specified', () => { 368 const { getByTestId, queryByTestId } = render( 369 <FlamegraphRenderer profile={RawProfile} onlyDisplay="table" /> 370 ); 371 372 expect(getByTestId('table-ui')).toBeInTheDocument(); 373 expect(queryByTestId('flamegraph-view')).not.toBeInTheDocument(); 374 }); 375 }); 376 377 it('should work', () => { 378 render(<FlamegraphRenderer flamebearer={SimpleTree as any} />); 379 }); 380 381 it('should work with raw profile from /render endpoint', () => { 382 render(<FlamegraphRenderer profile={RawProfile} />); 383 });