| /* |
| * Copyright (C) 2023 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import {TracesUtils} from 'test/unit/traces_utils'; |
| import {TraceBuilder} from 'test/unit/trace_builder'; |
| import {RealTimestamp} from '../common/time'; |
| import {CustomQueryType} from './custom_query'; |
| import {FrameMapper} from './frame_mapper'; |
| import {AbsoluteFrameIndex} from './index_types'; |
| import {LogMessage} from './protolog'; |
| import {ScreenRecordingTraceEntry} from './screen_recording'; |
| import {Trace} from './trace'; |
| import {Traces} from './traces'; |
| import {TraceType} from './trace_type'; |
| import {HierarchyTreeNode} from './tree_node/hierarchy_tree_node'; |
| |
| describe('FrameMapper', () => { |
| const time0 = new RealTimestamp(0n); |
| const time1 = new RealTimestamp(1n); |
| const time2 = new RealTimestamp(2n); |
| const time3 = new RealTimestamp(3n); |
| const time4 = new RealTimestamp(4n); |
| const time5 = new RealTimestamp(5n); |
| const time6 = new RealTimestamp(6n); |
| const time7 = new RealTimestamp(7n); |
| const time8 = new RealTimestamp(8n); |
| const time9 = new RealTimestamp(9n); |
| const time10 = new RealTimestamp(10n); |
| const time10seconds = new RealTimestamp(10n * 1000000000n); |
| |
| describe('ProtoLog <-> WindowManager', () => { |
| let protoLog: Trace<LogMessage>; |
| let windowManager: Trace<HierarchyTreeNode>; |
| let traces: Traces; |
| |
| beforeAll(async () => { |
| // Frames F0 F1 |
| // |<------>| |<->| |
| // PROTO_LOG: 0 1 2 3 4 5 |
| // WINDOW_MANAGER: 0 1 |
| // Time: 0 1 2 3 4 5 6 |
| protoLog = new TraceBuilder<LogMessage>() |
| .setEntries([ |
| 'entry-0' as unknown as LogMessage, |
| 'entry-1' as unknown as LogMessage, |
| 'entry-2' as unknown as LogMessage, |
| 'entry-3' as unknown as LogMessage, |
| 'entry-4' as unknown as LogMessage, |
| 'entry-5' as unknown as LogMessage, |
| ]) |
| .setTimestamps([time0, time1, time2, time4, time5, time6]) |
| .build(); |
| |
| windowManager = new TraceBuilder<HierarchyTreeNode>() |
| .setEntries([ |
| 'entry-0' as unknown as HierarchyTreeNode, |
| 'entry-1' as unknown as HierarchyTreeNode, |
| ]) |
| .setTimestamps([time3, time5]) |
| .build(); |
| |
| traces = new Traces(); |
| traces.setTrace(TraceType.PROTO_LOG, protoLog); |
| traces.setTrace(TraceType.WINDOW_MANAGER, windowManager); |
| await new FrameMapper(traces).computeMapping(); |
| }); |
| |
| it('associates entries/frames', async () => { |
| const expectedFrames = new Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>(); |
| expectedFrames.set( |
| 0, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.PROTO_LOG, ['entry-0', 'entry-1', 'entry-2']], |
| [TraceType.WINDOW_MANAGER, ['entry-0']], |
| ]) |
| ); |
| expectedFrames.set( |
| 1, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.PROTO_LOG, ['entry-3', 'entry-4']], |
| [TraceType.WINDOW_MANAGER, ['entry-1']], |
| ]) |
| ); |
| |
| expect(await TracesUtils.extractFrames(traces)).toEqual(expectedFrames); |
| }); |
| }); |
| |
| describe('IME <-> WindowManager', () => { |
| let ime: Trace<HierarchyTreeNode>; |
| let windowManager: Trace<HierarchyTreeNode>; |
| let traces: Traces; |
| |
| beforeAll(async () => { |
| // IME: 0--1--2 3 |
| // | | |
| // WINDOW_MANAGER: 0 1 2 |
| // Time: 0 1 2 3 4 5 |
| ime = new TraceBuilder<HierarchyTreeNode>() |
| .setEntries([ |
| 'entry-0' as unknown as HierarchyTreeNode, |
| 'entry-1' as unknown as HierarchyTreeNode, |
| 'entry-2' as unknown as HierarchyTreeNode, |
| 'entry-3' as unknown as HierarchyTreeNode, |
| ]) |
| .setTimestamps([time0, time1, time2, time4]) |
| .build(); |
| |
| windowManager = new TraceBuilder<HierarchyTreeNode>() |
| .setEntries([ |
| 'entry-0' as unknown as HierarchyTreeNode, |
| 'entry-1' as unknown as HierarchyTreeNode, |
| 'entry-2' as unknown as HierarchyTreeNode, |
| ]) |
| .setTimestamps([time1, time4, time5]) |
| .build(); |
| |
| traces = new Traces(); |
| traces.setTrace(TraceType.INPUT_METHOD_CLIENTS, ime); |
| traces.setTrace(TraceType.WINDOW_MANAGER, windowManager); |
| await new FrameMapper(traces).computeMapping(); |
| }); |
| |
| it('associates entries/frames', async () => { |
| const expectedFrames = new Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>(); |
| expectedFrames.set( |
| 0, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.INPUT_METHOD_CLIENTS, ['entry-0', 'entry-1', 'entry-2']], |
| [TraceType.WINDOW_MANAGER, ['entry-0']], |
| ]) |
| ); |
| expectedFrames.set( |
| 1, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.INPUT_METHOD_CLIENTS, ['entry-3']], |
| [TraceType.WINDOW_MANAGER, ['entry-1']], |
| ]) |
| ); |
| expectedFrames.set( |
| 2, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.INPUT_METHOD_CLIENTS, []], |
| [TraceType.WINDOW_MANAGER, ['entry-2']], |
| ]) |
| ); |
| |
| expect(await TracesUtils.extractFrames(traces)).toEqual(expectedFrames); |
| }); |
| }); |
| |
| describe('WindowManager <-> Transactions', () => { |
| let windowManager: Trace<HierarchyTreeNode>; |
| let transactions: Trace<object>; |
| let traces: Traces; |
| |
| beforeAll(async () => { |
| // WINDOW_MANAGER: 0 1 2 3 |
| // | | | \ |
| // TRANSACTIONS: 0 1 2--3 4 5 ... 6 <-- ignored (not connected) because too far |
| // | | | | | | |
| // Frames: 0 1 2 3 4 ... 5 |
| // Time: 0 1 2 3 4 5 6 ... 10s |
| windowManager = new TraceBuilder<HierarchyTreeNode>() |
| .setEntries([ |
| 'entry-0' as unknown as HierarchyTreeNode, |
| 'entry-1' as unknown as HierarchyTreeNode, |
| 'entry-2' as unknown as HierarchyTreeNode, |
| 'entry-3' as unknown as HierarchyTreeNode, |
| ]) |
| .setTimestamps([time1, time2, time4, time5]) |
| .build(); |
| |
| transactions = new TraceBuilder<object>() |
| .setEntries([ |
| 'entry-0' as unknown as object, |
| 'entry-1' as unknown as object, |
| 'entry-2' as unknown as object, |
| 'entry-3' as unknown as object, |
| 'entry-4' as unknown as object, |
| 'entry-5' as unknown as object, |
| 'entry-6' as unknown as object, |
| ]) |
| .setTimestamps([time0, time1, time2, time3, time4, time5, time10seconds]) |
| .setFrame(0, 0) |
| .setFrame(1, 1) |
| .setFrame(2, 2) |
| .setFrame(3, 2) |
| .setFrame(4, 3) |
| .setFrame(5, 4) |
| .setFrame(6, 5) |
| .build(); |
| |
| traces = new Traces(); |
| traces.setTrace(TraceType.WINDOW_MANAGER, windowManager); |
| traces.setTrace(TraceType.TRANSACTIONS, transactions); |
| await new FrameMapper(traces).computeMapping(); |
| }); |
| |
| it('associates entries/frames', async () => { |
| const expectedFrames = new Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>(); |
| expectedFrames.set( |
| 0, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.WINDOW_MANAGER, []], |
| [TraceType.TRANSACTIONS, ['entry-0']], |
| ]) |
| ); |
| expectedFrames.set( |
| 1, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.WINDOW_MANAGER, ['entry-0']], |
| [TraceType.TRANSACTIONS, ['entry-1']], |
| ]) |
| ); |
| expectedFrames.set( |
| 2, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.WINDOW_MANAGER, ['entry-1']], |
| [TraceType.TRANSACTIONS, ['entry-2', 'entry-3']], |
| ]) |
| ); |
| expectedFrames.set( |
| 3, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.WINDOW_MANAGER, ['entry-2']], |
| [TraceType.TRANSACTIONS, ['entry-4']], |
| ]) |
| ); |
| expectedFrames.set( |
| 4, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.WINDOW_MANAGER, ['entry-3']], |
| [TraceType.TRANSACTIONS, ['entry-5']], |
| ]) |
| ); |
| expectedFrames.set( |
| 5, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.WINDOW_MANAGER, []], |
| [TraceType.TRANSACTIONS, ['entry-6']], |
| ]) |
| ); |
| |
| expect(await TracesUtils.extractFrames(traces)).toEqual(expectedFrames); |
| }); |
| }); |
| |
| describe('Transactions <-> SurfaceFlinger', () => { |
| let transactions: Trace<object>; |
| let surfaceFlinger: Trace<HierarchyTreeNode>; |
| let traces: Traces; |
| |
| beforeAll(async () => { |
| // TRANSACTIONS: 0 1--2 3 4 |
| // \ \ \ |
| // \ \ \ |
| // SURFACE_FLINGER: 0 1 2 |
| transactions = new TraceBuilder<object>() |
| .setEntries([ |
| 'entry-0' as unknown as HierarchyTreeNode, |
| 'entry-1' as unknown as HierarchyTreeNode, |
| 'entry-2' as unknown as HierarchyTreeNode, |
| 'entry-3' as unknown as HierarchyTreeNode, |
| 'entry-4' as unknown as HierarchyTreeNode, |
| ]) |
| .setTimestamps([time0, time1, time2, time5, time6]) |
| .setParserCustomQueryResult(CustomQueryType.VSYNCID, [0n, 10n, 10n, 20n, 30n]) |
| .build(); |
| |
| surfaceFlinger = new TraceBuilder<HierarchyTreeNode>() |
| .setEntries([ |
| 'entry-0' as unknown as HierarchyTreeNode, |
| 'entry-1' as unknown as HierarchyTreeNode, |
| 'entry-2' as unknown as HierarchyTreeNode, |
| ]) |
| .setTimestamps([time0, time1, time2]) |
| .setParserCustomQueryResult(CustomQueryType.VSYNCID, [0n, 10n, 20n]) |
| .build(); |
| |
| traces = new Traces(); |
| traces.setTrace(TraceType.TRANSACTIONS, transactions); |
| traces.setTrace(TraceType.SURFACE_FLINGER, surfaceFlinger); |
| await new FrameMapper(traces).computeMapping(); |
| }); |
| |
| it('associates entries/frames', async () => { |
| const expectedFrames = new Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>(); |
| expectedFrames.set( |
| 0, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.TRANSACTIONS, [await transactions.getEntry(0).getValue()]], |
| [TraceType.SURFACE_FLINGER, [await surfaceFlinger.getEntry(0).getValue()]], |
| ]) |
| ); |
| expectedFrames.set( |
| 1, |
| new Map<TraceType, Array<{}>>([ |
| [ |
| TraceType.TRANSACTIONS, |
| [await transactions.getEntry(1).getValue(), await transactions.getEntry(2).getValue()], |
| ], |
| [TraceType.SURFACE_FLINGER, [await surfaceFlinger.getEntry(1).getValue()]], |
| ]) |
| ); |
| expectedFrames.set( |
| 2, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.TRANSACTIONS, [await transactions.getEntry(3).getValue()]], |
| [TraceType.SURFACE_FLINGER, [await surfaceFlinger.getEntry(2).getValue()]], |
| ]) |
| ); |
| |
| expect(await TracesUtils.extractFrames(traces)).toEqual(expectedFrames); |
| }); |
| }); |
| |
| describe('SurfaceFlinger <-> ScreenRecording', () => { |
| let surfaceFlinger: Trace<HierarchyTreeNode>; |
| let screenRecording: Trace<ScreenRecordingTraceEntry>; |
| let traces: Traces; |
| |
| beforeAll(async () => { |
| // SURFACE_FLINGER: 0 1 2--- 3 4 5 6 |
| // \ \ \ \ |
| // \ \ \ \ |
| // SCREEN_RECORDING: 0 1 2 3 4 ... 5 <-- ignored (not connected) because too far |
| // Time: 0 1 2 3 4 5 6 7 8 10s |
| surfaceFlinger = new TraceBuilder<HierarchyTreeNode>() |
| .setEntries([ |
| 'entry-0' as unknown as HierarchyTreeNode, |
| 'entry-1' as unknown as HierarchyTreeNode, |
| 'entry-2' as unknown as HierarchyTreeNode, |
| 'entry-3' as unknown as HierarchyTreeNode, |
| 'entry-4' as unknown as HierarchyTreeNode, |
| 'entry-5' as unknown as HierarchyTreeNode, |
| 'entry-6' as unknown as HierarchyTreeNode, |
| ]) |
| .setTimestamps([time0, time1, time2, time4, time6, time7, time8]) |
| .build(); |
| |
| screenRecording = new TraceBuilder<ScreenRecordingTraceEntry>() |
| .setEntries([ |
| 'entry-0' as unknown as ScreenRecordingTraceEntry, |
| 'entry-1' as unknown as ScreenRecordingTraceEntry, |
| 'entry-2' as unknown as ScreenRecordingTraceEntry, |
| 'entry-3' as unknown as ScreenRecordingTraceEntry, |
| 'entry-4' as unknown as ScreenRecordingTraceEntry, |
| 'entry-5' as unknown as ScreenRecordingTraceEntry, |
| ]) |
| .setTimestamps([time0, time3, time4, time5, time8, time10seconds]) |
| .build(); |
| |
| traces = new Traces(); |
| traces.setTrace(TraceType.SURFACE_FLINGER, surfaceFlinger); |
| traces.setTrace(TraceType.SCREEN_RECORDING, screenRecording); |
| await new FrameMapper(traces).computeMapping(); |
| }); |
| |
| it('associates entries/frames', async () => { |
| const expectedFrames = new Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>(); |
| expectedFrames.set( |
| 0, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.SURFACE_FLINGER, []], |
| [TraceType.SCREEN_RECORDING, ['entry-0']], |
| ]) |
| ); |
| expectedFrames.set( |
| 1, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.SURFACE_FLINGER, ['entry-2']], |
| [TraceType.SCREEN_RECORDING, ['entry-1']], |
| ]) |
| ); |
| expectedFrames.set( |
| 2, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.SURFACE_FLINGER, ['entry-2']], |
| [TraceType.SCREEN_RECORDING, ['entry-2']], |
| ]) |
| ); |
| expectedFrames.set( |
| 3, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.SURFACE_FLINGER, ['entry-3']], |
| [TraceType.SCREEN_RECORDING, ['entry-3']], |
| ]) |
| ); |
| expectedFrames.set( |
| 4, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.SURFACE_FLINGER, ['entry-5']], |
| [TraceType.SCREEN_RECORDING, ['entry-4']], |
| ]) |
| ); |
| expectedFrames.set( |
| 5, |
| new Map<TraceType, Array<{}>>([ |
| [TraceType.SURFACE_FLINGER, []], |
| [TraceType.SCREEN_RECORDING, ['entry-5']], |
| ]) |
| ); |
| |
| expect(await TracesUtils.extractFrames(traces)).toEqual(expectedFrames); |
| }); |
| }); |
| }); |