1/* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17import {TimestampConverterUtils} from 'common/time/test_utils'; 18import {TracesUtils} from 'test/unit/traces_utils'; 19import {TraceBuilder} from 'test/unit/trace_builder'; 20import {CustomQueryType} from './custom_query'; 21import {FrameMapper} from './frame_mapper'; 22import {AbsoluteFrameIndex} from './index_types'; 23import {MediaBasedTraceEntry} from './media_based_trace_entry'; 24import {Trace} from './trace'; 25import {Traces} from './traces'; 26import {TraceType} from './trace_type'; 27import {HierarchyTreeNode} from './tree_node/hierarchy_tree_node'; 28import {PropertyTreeNode} from './tree_node/property_tree_node'; 29 30describe('FrameMapper', () => { 31 const time0 = TimestampConverterUtils.makeRealTimestamp(0n); 32 const time1 = TimestampConverterUtils.makeRealTimestamp(1n); 33 const time2 = TimestampConverterUtils.makeRealTimestamp(2n); 34 const time3 = TimestampConverterUtils.makeRealTimestamp(3n); 35 const time4 = TimestampConverterUtils.makeRealTimestamp(4n); 36 const time5 = TimestampConverterUtils.makeRealTimestamp(5n); 37 const time6 = TimestampConverterUtils.makeRealTimestamp(6n); 38 const time7 = TimestampConverterUtils.makeRealTimestamp(7n); 39 const time8 = TimestampConverterUtils.makeRealTimestamp(8n); 40 const time10seconds = TimestampConverterUtils.makeRealTimestamp( 41 10n * 1000000000n, 42 ); 43 44 describe('ProtoLog <-> WindowManager', () => { 45 let protoLog: Trace<PropertyTreeNode>; 46 let windowManager: Trace<HierarchyTreeNode>; 47 let traces: Traces; 48 49 beforeAll(async () => { 50 // Frames F0 F1 51 // |<------>| |<->| 52 // PROTO_LOG: 0 1 2 3 4 5 53 // WINDOW_MANAGER: 0 1 54 // Time: 0 1 2 3 4 5 6 55 protoLog = new TraceBuilder<PropertyTreeNode>() 56 .setType(TraceType.PROTO_LOG) 57 .setEntries([ 58 'entry-0' as unknown as PropertyTreeNode, 59 'entry-1' as unknown as PropertyTreeNode, 60 'entry-2' as unknown as PropertyTreeNode, 61 'entry-3' as unknown as PropertyTreeNode, 62 'entry-4' as unknown as PropertyTreeNode, 63 'entry-5' as unknown as PropertyTreeNode, 64 ]) 65 .setTimestamps([time0, time1, time2, time4, time5, time6]) 66 .build(); 67 68 windowManager = new TraceBuilder<HierarchyTreeNode>() 69 .setType(TraceType.WINDOW_MANAGER) 70 .setEntries([ 71 'entry-0' as unknown as HierarchyTreeNode, 72 'entry-1' as unknown as HierarchyTreeNode, 73 ]) 74 .setTimestamps([time3, time5]) 75 .build(); 76 77 traces = new Traces(); 78 traces.addTrace(protoLog); 79 traces.addTrace(windowManager); 80 await new FrameMapper(traces).computeMapping(); 81 }); 82 83 it('associates entries/frames', async () => { 84 const expectedFrames = new Map< 85 AbsoluteFrameIndex, 86 Map<TraceType, Array<{}>> 87 >(); 88 expectedFrames.set( 89 0, 90 new Map<TraceType, Array<{}>>([ 91 [TraceType.PROTO_LOG, ['entry-0', 'entry-1', 'entry-2']], 92 [TraceType.WINDOW_MANAGER, ['entry-0']], 93 ]), 94 ); 95 expectedFrames.set( 96 1, 97 new Map<TraceType, Array<{}>>([ 98 [TraceType.PROTO_LOG, ['entry-3', 'entry-4']], 99 [TraceType.WINDOW_MANAGER, ['entry-1']], 100 ]), 101 ); 102 103 expect(await TracesUtils.extractFrames(traces)).toEqual(expectedFrames); 104 }); 105 }); 106 107 describe('IME <-> WindowManager', () => { 108 let ime: Trace<HierarchyTreeNode>; 109 let windowManager: Trace<HierarchyTreeNode>; 110 let traces: Traces; 111 112 beforeAll(async () => { 113 // IME: 0--1--2 3 114 // | | 115 // WINDOW_MANAGER: 0 1 2 116 // Time: 0 1 2 3 4 5 117 ime = new TraceBuilder<HierarchyTreeNode>() 118 .setType(TraceType.INPUT_METHOD_CLIENTS) 119 .setEntries([ 120 'entry-0' as unknown as HierarchyTreeNode, 121 'entry-1' as unknown as HierarchyTreeNode, 122 'entry-2' as unknown as HierarchyTreeNode, 123 'entry-3' as unknown as HierarchyTreeNode, 124 ]) 125 .setTimestamps([time0, time1, time2, time4]) 126 .build(); 127 128 windowManager = new TraceBuilder<HierarchyTreeNode>() 129 .setType(TraceType.WINDOW_MANAGER) 130 .setEntries([ 131 'entry-0' as unknown as HierarchyTreeNode, 132 'entry-1' as unknown as HierarchyTreeNode, 133 'entry-2' as unknown as HierarchyTreeNode, 134 ]) 135 .setTimestamps([time1, time4, time5]) 136 .build(); 137 138 traces = new Traces(); 139 traces.addTrace(ime); 140 traces.addTrace(windowManager); 141 await new FrameMapper(traces).computeMapping(); 142 }); 143 144 it('associates entries/frames', async () => { 145 const expectedFrames = new Map< 146 AbsoluteFrameIndex, 147 Map<TraceType, Array<{}>> 148 >(); 149 expectedFrames.set( 150 0, 151 new Map<TraceType, Array<{}>>([ 152 [TraceType.INPUT_METHOD_CLIENTS, ['entry-0', 'entry-1', 'entry-2']], 153 [TraceType.WINDOW_MANAGER, ['entry-0']], 154 ]), 155 ); 156 expectedFrames.set( 157 1, 158 new Map<TraceType, Array<{}>>([ 159 [TraceType.INPUT_METHOD_CLIENTS, ['entry-3']], 160 [TraceType.WINDOW_MANAGER, ['entry-1']], 161 ]), 162 ); 163 expectedFrames.set( 164 2, 165 new Map<TraceType, Array<{}>>([ 166 [TraceType.INPUT_METHOD_CLIENTS, []], 167 [TraceType.WINDOW_MANAGER, ['entry-2']], 168 ]), 169 ); 170 171 expect(await TracesUtils.extractFrames(traces)).toEqual(expectedFrames); 172 }); 173 }); 174 175 describe('WindowManager <-> Transactions', () => { 176 let windowManager: Trace<HierarchyTreeNode>; 177 let transactions: Trace<PropertyTreeNode>; 178 let traces: Traces; 179 180 beforeAll(async () => { 181 // WINDOW_MANAGER: 0 1 2 3 182 // | | | \ 183 // TRANSACTIONS: 0 1 2--3 4 5 ... 6 <-- ignored (not connected) because too far 184 // | | | | | | 185 // Frames: 0 1 2 3 4 ... 5 186 // Time: 0 1 2 3 4 5 6 ... 10s 187 windowManager = new TraceBuilder<HierarchyTreeNode>() 188 .setType(TraceType.WINDOW_MANAGER) 189 .setEntries([ 190 'entry-0' as unknown as HierarchyTreeNode, 191 'entry-1' as unknown as HierarchyTreeNode, 192 'entry-2' as unknown as HierarchyTreeNode, 193 'entry-3' as unknown as HierarchyTreeNode, 194 ]) 195 .setTimestamps([time1, time2, time4, time5]) 196 .build(); 197 198 transactions = new TraceBuilder<PropertyTreeNode>() 199 .setType(TraceType.TRANSACTIONS) 200 .setEntries([ 201 'entry-0' as unknown as PropertyTreeNode, 202 'entry-1' as unknown as PropertyTreeNode, 203 'entry-2' as unknown as PropertyTreeNode, 204 'entry-3' as unknown as PropertyTreeNode, 205 'entry-4' as unknown as PropertyTreeNode, 206 'entry-5' as unknown as PropertyTreeNode, 207 'entry-6' as unknown as PropertyTreeNode, 208 ]) 209 .setTimestamps([ 210 time0, 211 time1, 212 time2, 213 time3, 214 time4, 215 time5, 216 time10seconds, 217 ]) 218 .setFrame(0, 0) 219 .setFrame(1, 1) 220 .setFrame(2, 2) 221 .setFrame(3, 2) 222 .setFrame(4, 3) 223 .setFrame(5, 4) 224 .setFrame(6, 5) 225 .build(); 226 227 traces = new Traces(); 228 traces.addTrace(windowManager); 229 traces.addTrace(transactions); 230 await new FrameMapper(traces).computeMapping(); 231 }); 232 233 it('associates entries/frames', async () => { 234 const expectedFrames = new Map< 235 AbsoluteFrameIndex, 236 Map<TraceType, Array<{}>> 237 >(); 238 expectedFrames.set( 239 0, 240 new Map<TraceType, Array<{}>>([ 241 [TraceType.WINDOW_MANAGER, []], 242 [TraceType.TRANSACTIONS, ['entry-0']], 243 ]), 244 ); 245 expectedFrames.set( 246 1, 247 new Map<TraceType, Array<{}>>([ 248 [TraceType.WINDOW_MANAGER, ['entry-0']], 249 [TraceType.TRANSACTIONS, ['entry-1']], 250 ]), 251 ); 252 expectedFrames.set( 253 2, 254 new Map<TraceType, Array<{}>>([ 255 [TraceType.WINDOW_MANAGER, ['entry-1']], 256 [TraceType.TRANSACTIONS, ['entry-2', 'entry-3']], 257 ]), 258 ); 259 expectedFrames.set( 260 3, 261 new Map<TraceType, Array<{}>>([ 262 [TraceType.WINDOW_MANAGER, ['entry-2']], 263 [TraceType.TRANSACTIONS, ['entry-4']], 264 ]), 265 ); 266 expectedFrames.set( 267 4, 268 new Map<TraceType, Array<{}>>([ 269 [TraceType.WINDOW_MANAGER, ['entry-3']], 270 [TraceType.TRANSACTIONS, ['entry-5']], 271 ]), 272 ); 273 expectedFrames.set( 274 5, 275 new Map<TraceType, Array<{}>>([ 276 [TraceType.WINDOW_MANAGER, []], 277 [TraceType.TRANSACTIONS, ['entry-6']], 278 ]), 279 ); 280 281 expect(await TracesUtils.extractFrames(traces)).toEqual(expectedFrames); 282 }); 283 }); 284 285 describe('ViewCapture <-> SurfaceFlinger', () => { 286 let viewCapture: Trace<PropertyTreeNode>; 287 let surfaceFlinger: Trace<HierarchyTreeNode>; 288 let traces: Traces; 289 290 beforeAll(async () => { 291 // VIEW_CAPTURE: 0 1 2--- 3 292 // \ \ \ \ 293 // \ \ \ \ 294 // SURFACE_FLINGER: 0 1 2 3 295 // Time: 0 1 2 3 4 5 6 296 viewCapture = new TraceBuilder<PropertyTreeNode>() 297 .setType(TraceType.VIEW_CAPTURE) 298 .setEntries([ 299 'entry-0' as unknown as PropertyTreeNode, 300 'entry-1' as unknown as PropertyTreeNode, 301 'entry-2' as unknown as PropertyTreeNode, 302 'entry-3' as unknown as PropertyTreeNode, 303 ]) 304 .setTimestamps([time0, time1, time2, time5]) 305 .build(); 306 307 surfaceFlinger = new TraceBuilder<HierarchyTreeNode>() 308 .setType(TraceType.SURFACE_FLINGER) 309 .setEntries([ 310 'entry-0' as unknown as HierarchyTreeNode, 311 'entry-1' as unknown as HierarchyTreeNode, 312 'entry-2' as unknown as HierarchyTreeNode, 313 'entry-3' as unknown as HierarchyTreeNode, 314 ]) 315 .setTimestamps([time1, time3, time4, time6]) 316 .setFrame(0, 0) 317 .setFrame(1, 1) 318 .setFrame(2, 2) 319 .setFrame(3, 3) 320 .build(); 321 322 traces = new Traces(); 323 traces.addTrace(viewCapture); 324 traces.addTrace(surfaceFlinger); 325 await new FrameMapper(traces).computeMapping(); 326 }); 327 328 it('associates entries/frames', async () => { 329 const expectedFrames = new Map< 330 AbsoluteFrameIndex, 331 Map<TraceType, Array<{}>> 332 >(); 333 expectedFrames.set( 334 0, 335 new Map<TraceType, Array<{}>>([ 336 [TraceType.VIEW_CAPTURE, [await viewCapture.getEntry(0).getValue()]], 337 [ 338 TraceType.SURFACE_FLINGER, 339 [await surfaceFlinger.getEntry(0).getValue()], 340 ], 341 ]), 342 ); 343 expectedFrames.set( 344 1, 345 new Map<TraceType, Array<{}>>([ 346 [TraceType.VIEW_CAPTURE, [await viewCapture.getEntry(2).getValue()]], 347 [ 348 TraceType.SURFACE_FLINGER, 349 [await surfaceFlinger.getEntry(1).getValue()], 350 ], 351 ]), 352 ); 353 expectedFrames.set( 354 2, 355 new Map<TraceType, Array<{}>>([ 356 [TraceType.VIEW_CAPTURE, [await viewCapture.getEntry(2).getValue()]], 357 [ 358 TraceType.SURFACE_FLINGER, 359 [await surfaceFlinger.getEntry(2).getValue()], 360 ], 361 ]), 362 ); 363 expectedFrames.set( 364 3, 365 new Map<TraceType, Array<{}>>([ 366 [TraceType.VIEW_CAPTURE, [await viewCapture.getEntry(3).getValue()]], 367 [ 368 TraceType.SURFACE_FLINGER, 369 [await surfaceFlinger.getEntry(3).getValue()], 370 ], 371 ]), 372 ); 373 374 expect(await TracesUtils.extractFrames(traces)).toEqual(expectedFrames); 375 }); 376 }); 377 378 const TRACES_WITH_VSYNC_IDS = [ 379 TraceType.TRANSACTIONS, 380 TraceType.INPUT_EVENT_MERGED, 381 ]; 382 383 TRACES_WITH_VSYNC_IDS.forEach((traceType) => { 384 describe(`TraceType[${traceType}] <-> SurfaceFlinger`, () => { 385 const sfTrace = new TraceBuilder<HierarchyTreeNode>() 386 .setType(TraceType.SURFACE_FLINGER) 387 .setEntries([ 388 'sfentry-0' as unknown as HierarchyTreeNode, 389 'sfentry-1' as unknown as HierarchyTreeNode, 390 'sfentry-2' as unknown as HierarchyTreeNode, 391 ]) 392 .setTimestamps([time0, time1, time2]) 393 .setParserCustomQueryResult(CustomQueryType.VSYNCID, [10n, 20n, 30n]) 394 .build(); 395 const entries = [ 396 'entry-0' as unknown as PropertyTreeNode, 397 'entry-1' as unknown as PropertyTreeNode, 398 'entry-2' as unknown as PropertyTreeNode, 399 'entry-3' as unknown as PropertyTreeNode, 400 'entry-4' as unknown as PropertyTreeNode, 401 ]; 402 let trace: Trace<PropertyTreeNode>; 403 let traces: Traces; 404 405 it('associates entries/frames', async () => { 406 // TRACE: 0 1--2 3 4 407 // \ \ \ 408 // \ \ \ 409 // SURFACE_FLINGER: 0 1 2 410 trace = new TraceBuilder<PropertyTreeNode>() 411 .setType(traceType) 412 .setEntries(entries) 413 .setTimestamps([time0, time1, time2, time5, time6]) 414 .setParserCustomQueryResult(CustomQueryType.VSYNCID, [ 415 10n, 416 20n, 417 20n, 418 30n, 419 40n, 420 ]) 421 .build(); 422 await computeMapping(); 423 const expectedFrames = await getExpectedFrameMap([ 424 [[0], 0], 425 [[1, 2], 1], 426 [[3], 2], 427 ]); 428 expect(await TracesUtils.extractFrames(traces)).toEqual(expectedFrames); 429 }); 430 431 it('does not propagate mapping if all vsync ids invalid', async () => { 432 trace = new TraceBuilder<PropertyTreeNode>() 433 .setType(traceType) 434 .setEntries(['entry-0' as unknown as PropertyTreeNode]) 435 .setTimestamps([time1]) 436 .setParserCustomQueryResult(CustomQueryType.VSYNCID, [-1n]) 437 .build(); 438 439 const sfTrace = new TraceBuilder<HierarchyTreeNode>() 440 .setType(TraceType.SURFACE_FLINGER) 441 .setEntries(['entry-0' as unknown as HierarchyTreeNode]) 442 .setTimestamps([time1]) 443 .setParserCustomQueryResult(CustomQueryType.VSYNCID, [1n]) 444 .build(); 445 446 await computeMapping(sfTrace); 447 expect(sfTrace.getEntry(0).getFramesRange()).toBeDefined(); 448 expect(trace.hasFrameInfo()).toBeFalse(); 449 }); 450 451 it('skips invalid vsync ids', async () => { 452 // SURFACE_FLINGER: 0 1 2 453 // | \ \___ 454 // | \ \ 455 // TRACE: 0 1 2 3 4 456 trace = new TraceBuilder<PropertyTreeNode>() 457 .setType(traceType) 458 .setEntries(entries) 459 .setTimestamps([time0, time1, time2, time3, time4]) 460 .setParserCustomQueryResult(CustomQueryType.VSYNCID, [ 461 10n, 462 0n, 463 20n, 464 -1n, 465 30n, 466 ]) 467 .build(); 468 469 await computeMapping(); 470 const expectedFrames = await getExpectedFrameMap([ 471 [[0], 0], 472 [[2], 1], 473 [[4], 2], 474 ]); 475 expect(await TracesUtils.extractFrames(traces)).toEqual(expectedFrames); 476 }); 477 478 async function computeMapping(surfaceFlingerTrace = sfTrace) { 479 traces = new Traces(); 480 traces.addTrace(trace); 481 traces.addTrace(surfaceFlingerTrace); 482 await new FrameMapper(traces).computeMapping(); 483 } 484 485 async function getExpectedFrameMap( 486 expected: Array<[number[], number]>, 487 ): Promise<Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>> { 488 const expectedFrames = new Map(); 489 for (const [ 490 frameIndex, 491 [traceIndexes, sfIndex], 492 ] of expected.entries()) { 493 const traceEntries = await Promise.all( 494 traceIndexes.map((i) => trace.getEntry(i).getValue()), 495 ); 496 const sfEntries = [await sfTrace.getEntry(sfIndex).getValue()]; 497 expectedFrames.set( 498 frameIndex, 499 new Map<TraceType, Array<{}>>([ 500 [traceType, traceEntries], 501 [TraceType.SURFACE_FLINGER, sfEntries], 502 ]), 503 ); 504 } 505 return expectedFrames; 506 } 507 }); 508 }); 509 510 describe('SurfaceFlinger <-> ScreenRecording', () => { 511 let surfaceFlinger: Trace<HierarchyTreeNode>; 512 let screenRecording: Trace<MediaBasedTraceEntry>; 513 let traces: Traces; 514 515 beforeAll(async () => { 516 // SURFACE_FLINGER: 0 1 2--- 3 4 5 6 517 // \ \ \ \ 518 // \ \ \ \ 519 // SCREEN_RECORDING: 0 1 2 3 4 ... 5 <-- ignored (not connected) because too far 520 // Time: 0 1 2 3 4 5 6 7 8 10s 521 surfaceFlinger = new TraceBuilder<HierarchyTreeNode>() 522 .setType(TraceType.SURFACE_FLINGER) 523 .setEntries([ 524 'entry-0' as unknown as HierarchyTreeNode, 525 'entry-1' as unknown as HierarchyTreeNode, 526 'entry-2' as unknown as HierarchyTreeNode, 527 'entry-3' as unknown as HierarchyTreeNode, 528 'entry-4' as unknown as HierarchyTreeNode, 529 'entry-5' as unknown as HierarchyTreeNode, 530 'entry-6' as unknown as HierarchyTreeNode, 531 ]) 532 .setTimestamps([time0, time1, time2, time4, time6, time7, time8]) 533 .build(); 534 535 screenRecording = new TraceBuilder<MediaBasedTraceEntry>() 536 .setType(TraceType.SCREEN_RECORDING) 537 .setEntries([ 538 'entry-0' as unknown as MediaBasedTraceEntry, 539 'entry-1' as unknown as MediaBasedTraceEntry, 540 'entry-2' as unknown as MediaBasedTraceEntry, 541 'entry-3' as unknown as MediaBasedTraceEntry, 542 'entry-4' as unknown as MediaBasedTraceEntry, 543 'entry-5' as unknown as MediaBasedTraceEntry, 544 ]) 545 .setTimestamps([time0, time3, time4, time5, time8, time10seconds]) 546 .build(); 547 548 traces = new Traces(); 549 traces.addTrace(surfaceFlinger); 550 traces.addTrace(screenRecording); 551 await new FrameMapper(traces).computeMapping(); 552 }); 553 554 it('associates entries/frames', async () => { 555 const expectedFrames = new Map< 556 AbsoluteFrameIndex, 557 Map<TraceType, Array<{}>> 558 >(); 559 expectedFrames.set( 560 0, 561 new Map<TraceType, Array<{}>>([ 562 [TraceType.SURFACE_FLINGER, []], 563 [TraceType.SCREEN_RECORDING, ['entry-0']], 564 ]), 565 ); 566 expectedFrames.set( 567 1, 568 new Map<TraceType, Array<{}>>([ 569 [TraceType.SURFACE_FLINGER, ['entry-2']], 570 [TraceType.SCREEN_RECORDING, ['entry-1']], 571 ]), 572 ); 573 expectedFrames.set( 574 2, 575 new Map<TraceType, Array<{}>>([ 576 [TraceType.SURFACE_FLINGER, ['entry-2']], 577 [TraceType.SCREEN_RECORDING, ['entry-2']], 578 ]), 579 ); 580 expectedFrames.set( 581 3, 582 new Map<TraceType, Array<{}>>([ 583 [TraceType.SURFACE_FLINGER, ['entry-3']], 584 [TraceType.SCREEN_RECORDING, ['entry-3']], 585 ]), 586 ); 587 expectedFrames.set( 588 4, 589 new Map<TraceType, Array<{}>>([ 590 [TraceType.SURFACE_FLINGER, ['entry-5']], 591 [TraceType.SCREEN_RECORDING, ['entry-4']], 592 ]), 593 ); 594 expectedFrames.set( 595 5, 596 new Map<TraceType, Array<{}>>([ 597 [TraceType.SURFACE_FLINGER, []], 598 [TraceType.SCREEN_RECORDING, ['entry-5']], 599 ]), 600 ); 601 602 expect(await TracesUtils.extractFrames(traces)).toEqual(expectedFrames); 603 }); 604 }); 605 606 it('supports multiple traces with same type', async () => { 607 // SURFACE_FLINGER_0: 0 608 // \ 609 // \ 610 // SURFACE_FLINGER_1: 0 \ 611 // \ | 612 // \| 613 // SCREEN_RECORDING: 0 614 // Time: 0 1 615 const surfaceFlinger0 = new TraceBuilder<HierarchyTreeNode>() 616 .setType(TraceType.SURFACE_FLINGER) 617 .setEntries(['entry-0' as unknown as HierarchyTreeNode]) 618 .setTimestamps([time0]) 619 .build(); 620 621 const surfaceFlinger1 = new TraceBuilder<HierarchyTreeNode>() 622 .setType(TraceType.SURFACE_FLINGER) 623 .setEntries(['entry-0' as unknown as HierarchyTreeNode]) 624 .setTimestamps([time0]) 625 .build(); 626 627 const screenRecording = new TraceBuilder<MediaBasedTraceEntry>() 628 .setType(TraceType.SCREEN_RECORDING) 629 .setEntries(['entry-0' as unknown as MediaBasedTraceEntry]) 630 .setTimestamps([time1]) 631 .build(); 632 633 const traces = new Traces(); 634 traces.addTrace(surfaceFlinger0); 635 traces.addTrace(surfaceFlinger1); 636 traces.addTrace(screenRecording); 637 await new FrameMapper(traces).computeMapping(); 638 639 expect(surfaceFlinger0.getEntry(0).getFramesRange()).toEqual({ 640 start: 0, 641 end: 1, 642 }); 643 expect(surfaceFlinger1.getEntry(0).getFramesRange()).toEqual({ 644 start: 0, 645 end: 1, 646 }); 647 expect(screenRecording.getEntry(0).getFramesRange()).toEqual({ 648 start: 0, 649 end: 1, 650 }); 651 }); 652 653 it('does not propagate mapping if frames range undefined', async () => { 654 const validTs = time1.add(2000000000n); 655 const transactions = new TraceBuilder<PropertyTreeNode>() 656 .setType(TraceType.TRANSACTIONS) 657 .setEntries(['entry-0' as unknown as PropertyTreeNode]) 658 .setTimestamps([validTs]) 659 .setFrame(0, 0) 660 .build(); 661 const windowManager = new TraceBuilder<HierarchyTreeNode>() 662 .setType(TraceType.WINDOW_MANAGER) 663 .setEntries(['entry-0' as unknown as HierarchyTreeNode]) 664 .setTimestamps([time0]) 665 .build(); 666 const ime = new TraceBuilder<MediaBasedTraceEntry>() 667 .setType(TraceType.INPUT_METHOD_MANAGER_SERVICE) 668 .setEntries(['entry-0' as unknown as MediaBasedTraceEntry]) 669 .setTimestamps([validTs]) 670 .build(); 671 672 const traces = new Traces(); 673 traces.addTrace(transactions); 674 traces.addTrace(windowManager); 675 traces.addTrace(ime); 676 await new FrameMapper(traces).computeMapping(); 677 678 expect(transactions.getEntry(0).getFramesRange()).toBeDefined(); 679 expect(windowManager.getEntry(0).getFramesRange()).toBeDefined(); 680 expect(ime.hasFrameInfo()).toBeFalse(); 681 }); 682}); 683