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 {assertDefined} from 'common/assert_utils'; 18import {FunctionUtils} from 'common/function_utils'; 19import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils'; 20import {TracesBuilder} from 'test/unit/traces_builder'; 21import {TracesUtils} from 'test/unit/traces_utils'; 22import {TraceBuilder} from 'test/unit/trace_builder'; 23import {TraceUtils} from 'test/unit/trace_utils'; 24import {UnitTestUtils} from 'test/unit/utils'; 25import {FrameMapBuilder} from './frame_map_builder'; 26import {AbsoluteFrameIndex} from './index_types'; 27import {Traces} from './traces'; 28import {TraceType} from './trace_type'; 29 30describe('Traces', () => { 31 let traces: Traces; 32 33 const time1 = TimestampConverterUtils.makeRealTimestamp(1n); 34 const time2 = TimestampConverterUtils.makeRealTimestamp(2n); 35 const time3 = TimestampConverterUtils.makeRealTimestamp(3n); 36 const time4 = TimestampConverterUtils.makeRealTimestamp(4n); 37 const time5 = TimestampConverterUtils.makeRealTimestamp(5n); 38 const time6 = TimestampConverterUtils.makeRealTimestamp(6n); 39 const time7 = TimestampConverterUtils.makeRealTimestamp(7n); 40 const time8 = TimestampConverterUtils.makeRealTimestamp(8n); 41 const time9 = TimestampConverterUtils.makeRealTimestamp(9n); 42 const time10 = TimestampConverterUtils.makeRealTimestamp(10n); 43 44 let extractedEntriesEmpty: Map<TraceType, Array<{}>>; 45 let extractedEntriesFull: Map<TraceType, Array<{}>>; 46 let extractedFramesEmpty: Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>; 47 let extractedFramesFull: Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>; 48 49 beforeAll(() => { 50 // Time: 1 2 3 4 5 6 7 8 9 10 51 // 52 // TEST_TRACE_STRING: 0 1--2 3 4 53 // \ \ \ \ 54 // \ \ \ \ 55 // TEST_TRACE_NUMBER: 0 1 2--3 4 56 // \ \ \ \ 57 // \ \ \ \ 58 // Frame on screen: 0 1 2 3---4 59 traces = new Traces(); 60 traces.addTrace( 61 new TraceBuilder<string>() 62 .setType(TraceType.TEST_TRACE_STRING) 63 .setEntries(['0', '1', '2', '3', '4']) 64 .setTimestamps([time1, time3, time4, time6, time9]) 65 .setFrame(0, 0) 66 .setFrame(1, 1) 67 .setFrame(2, 1) 68 .setFrame(3, 2) 69 .setFrame(4, 3) 70 .setFrame(4, 4) 71 .build(), 72 ); 73 traces.addTrace( 74 new TraceBuilder<number>() 75 .setType(TraceType.TEST_TRACE_NUMBER) 76 .setEntries([0, 1, 2, 3, 4]) 77 .setTimestamps([time2, time5, time7, time8, time10]) 78 .setFrame(0, 0) 79 .setFrame(1, 1) 80 .setFrame(2, 2) 81 .setFrame(3, 2) 82 .setFrame(4, 3) 83 .setFrame(4, 4) 84 .build(), 85 ); 86 87 extractedEntriesEmpty = new Map<TraceType, Array<{}>>([ 88 [TraceType.TEST_TRACE_STRING, []], 89 [TraceType.TEST_TRACE_NUMBER, []], 90 ]); 91 92 extractedEntriesFull = new Map<TraceType, Array<{}>>([ 93 [TraceType.TEST_TRACE_STRING, ['0', '1', '2', '3', '4']], 94 [TraceType.TEST_TRACE_NUMBER, [0, 1, 2, 3, 4]], 95 ]); 96 97 extractedFramesEmpty = new Map< 98 AbsoluteFrameIndex, 99 Map<TraceType, Array<{}>> 100 >(); 101 102 extractedFramesFull = new Map< 103 AbsoluteFrameIndex, 104 Map<TraceType, Array<{}>> 105 >(); 106 extractedFramesFull.set( 107 0, 108 new Map<TraceType, Array<{}>>([ 109 [TraceType.TEST_TRACE_STRING, ['0']], 110 [TraceType.TEST_TRACE_NUMBER, [0]], 111 ]), 112 ); 113 extractedFramesFull.set( 114 1, 115 new Map<TraceType, Array<{}>>([ 116 [TraceType.TEST_TRACE_STRING, ['1', '2']], 117 [TraceType.TEST_TRACE_NUMBER, [1]], 118 ]), 119 ); 120 extractedFramesFull.set( 121 2, 122 new Map<TraceType, Array<{}>>([ 123 [TraceType.TEST_TRACE_STRING, ['3']], 124 [TraceType.TEST_TRACE_NUMBER, [2, 3]], 125 ]), 126 ); 127 extractedFramesFull.set( 128 3, 129 new Map<TraceType, Array<{}>>([ 130 [TraceType.TEST_TRACE_STRING, ['4']], 131 [TraceType.TEST_TRACE_NUMBER, [4]], 132 ]), 133 ); 134 extractedFramesFull.set( 135 4, 136 new Map<TraceType, Array<{}>>([ 137 [TraceType.TEST_TRACE_STRING, ['4']], 138 [TraceType.TEST_TRACE_NUMBER, [4]], 139 ]), 140 ); 141 }); 142 143 it('getTrace()', async () => { 144 expect( 145 await TraceUtils.extractEntries( 146 assertDefined(traces.getTrace(TraceType.TEST_TRACE_STRING)), 147 ), 148 ).toEqual( 149 extractedEntriesFull.get(TraceType.TEST_TRACE_STRING) as string[], 150 ); 151 expect( 152 await TraceUtils.extractEntries( 153 assertDefined(traces.getTrace(TraceType.TEST_TRACE_NUMBER)), 154 ), 155 ).toEqual( 156 extractedEntriesFull.get(TraceType.TEST_TRACE_NUMBER) as number[], 157 ); 158 expect(traces.getTrace(TraceType.SURFACE_FLINGER)).toBeUndefined(); 159 }); 160 161 it('getTraces()', async () => { 162 expect(traces.getTraces(TraceType.TEST_TRACE_NUMBER)).toEqual([ 163 assertDefined(traces.getTrace(TraceType.TEST_TRACE_NUMBER)), 164 ]); 165 }); 166 167 it('deleteTrace()', () => { 168 const trace0 = UnitTestUtils.makeEmptyTrace(TraceType.TEST_TRACE_STRING); 169 const trace1 = UnitTestUtils.makeEmptyTrace(TraceType.TEST_TRACE_NUMBER); 170 171 const traces = new Traces(); 172 traces.addTrace(trace0); 173 traces.addTrace(trace1); 174 175 expect(TracesUtils.extractTraces(traces)).toEqual([trace0, trace1]); 176 177 traces.deleteTrace(trace0); 178 expect(TracesUtils.extractTraces(traces)).toEqual([trace1]); 179 180 traces.deleteTrace(trace1); 181 expect(TracesUtils.extractTraces(traces)).toEqual([]); 182 183 traces.deleteTrace(trace1); 184 expect(TracesUtils.extractTraces(traces)).toEqual([]); 185 }); 186 187 it('hasTrace()', () => { 188 const trace0 = UnitTestUtils.makeEmptyTrace(TraceType.TEST_TRACE_STRING); 189 const trace1 = UnitTestUtils.makeEmptyTrace(TraceType.TEST_TRACE_NUMBER); 190 191 const traces = new Traces(); 192 traces.addTrace(trace0); 193 194 expect(traces.hasTrace(trace0)).toBeTrue(); 195 expect(traces.hasTrace(trace1)).toBeFalse(); 196 }); 197 198 it('sliceTime()', async () => { 199 // empty 200 { 201 const slice = traces.sliceTime(time3, time3); 202 expect(await TracesUtils.extractEntries(slice)).toEqual( 203 extractedEntriesEmpty, 204 ); 205 } 206 // full 207 { 208 const slice = traces.sliceTime(); 209 expect(await TracesUtils.extractEntries(slice)).toEqual( 210 extractedEntriesFull, 211 ); 212 } 213 // middle 214 { 215 const slice = traces.sliceTime(time4, time8); 216 expect(await TracesUtils.extractEntries(slice)).toEqual( 217 new Map<TraceType, Array<{}>>([ 218 [TraceType.TEST_TRACE_STRING, ['2', '3']], 219 [TraceType.TEST_TRACE_NUMBER, [1, 2]], 220 ]), 221 ); 222 } 223 // slice away front 224 { 225 const slice = traces.sliceTime(time8); 226 expect(await TracesUtils.extractEntries(slice)).toEqual( 227 new Map<TraceType, Array<{}>>([ 228 [TraceType.TEST_TRACE_STRING, ['4']], 229 [TraceType.TEST_TRACE_NUMBER, [3, 4]], 230 ]), 231 ); 232 } 233 // slice away back 234 { 235 const slice = traces.sliceTime(undefined, time8); 236 expect(await TracesUtils.extractEntries(slice)).toEqual( 237 new Map<TraceType, Array<{}>>([ 238 [TraceType.TEST_TRACE_STRING, ['0', '1', '2', '3']], 239 [TraceType.TEST_TRACE_NUMBER, [0, 1, 2]], 240 ]), 241 ); 242 } 243 }); 244 245 it('sliceFrames()', async () => { 246 // empty 247 { 248 const slice = traces.sliceFrames(1, 1); 249 expect(await TracesUtils.extractFrames(slice)).toEqual( 250 extractedFramesEmpty, 251 ); 252 } 253 // full 254 { 255 const slice = traces.sliceFrames(); 256 expect(await TracesUtils.extractFrames(slice)).toEqual( 257 extractedFramesFull, 258 ); 259 } 260 // middle 261 { 262 const slice = traces.sliceFrames(1, 4); 263 const expectedFrames = structuredClone(extractedFramesFull); 264 expectedFrames.delete(0); 265 expectedFrames.delete(4); 266 expect(await TracesUtils.extractFrames(slice)).toEqual(expectedFrames); 267 } 268 // slice away front 269 { 270 const slice = traces.sliceFrames(2); 271 const expectedFrames = structuredClone(extractedFramesFull); 272 expectedFrames.delete(0); 273 expectedFrames.delete(1); 274 expect(await TracesUtils.extractFrames(slice)).toEqual(expectedFrames); 275 } 276 // slice away back 277 { 278 const slice = traces.sliceFrames(undefined, 2); 279 const expectedFrames = structuredClone(extractedFramesFull); 280 expectedFrames.delete(2); 281 expectedFrames.delete(3); 282 expectedFrames.delete(4); 283 expect(await TracesUtils.extractFrames(slice)).toEqual(expectedFrames); 284 } 285 }); 286 287 it('mapTrace()', async () => { 288 const promises = traces.mapTrace(async (trace) => { 289 const expectedEntries = extractedEntriesFull.get(trace.type) as Array<{}>; 290 const actualEntries = await TraceUtils.extractEntries(trace); 291 expect(actualEntries).toEqual(expectedEntries); 292 }); 293 await Promise.all(promises); 294 }); 295 296 it('mapFrame()', async () => { 297 expect(await TracesUtils.extractFrames(traces)).toEqual( 298 extractedFramesFull, 299 ); 300 }); 301 302 it('supports empty traces', async () => { 303 const traces = new TracesBuilder() 304 .setEntries(TraceType.TEST_TRACE_STRING, []) 305 .setFrameMap( 306 TraceType.TEST_TRACE_STRING, 307 new FrameMapBuilder(0, 0).build(), 308 ) 309 310 .setEntries(TraceType.TEST_TRACE_NUMBER, []) 311 .setFrameMap( 312 TraceType.TEST_TRACE_NUMBER, 313 new FrameMapBuilder(0, 0).build(), 314 ) 315 .build(); 316 317 expect(await TracesUtils.extractEntries(traces)).toEqual( 318 extractedEntriesEmpty, 319 ); 320 expect(await TracesUtils.extractFrames(traces)).toEqual( 321 extractedFramesEmpty, 322 ); 323 324 expect( 325 await TracesUtils.extractEntries(traces.sliceTime(time1, time10)), 326 ).toEqual(extractedEntriesEmpty); 327 expect( 328 await TracesUtils.extractFrames(traces.sliceTime(time1, time10)), 329 ).toEqual(extractedFramesEmpty); 330 331 expect(await TracesUtils.extractEntries(traces.sliceFrames(0, 10))).toEqual( 332 extractedEntriesEmpty, 333 ); 334 expect(await TracesUtils.extractFrames(traces.sliceFrames(0, 10))).toEqual( 335 extractedFramesEmpty, 336 ); 337 }); 338 339 it('supports unavailable frame mapping', async () => { 340 const traces = new TracesBuilder() 341 .setEntries(TraceType.TEST_TRACE_STRING, ['entry-0']) 342 .setTimestamps(TraceType.TEST_TRACE_STRING, [time1]) 343 .setFrameMap(TraceType.TEST_TRACE_STRING, undefined) 344 345 .setEntries(TraceType.TEST_TRACE_NUMBER, [0]) 346 .setTimestamps(TraceType.TEST_TRACE_NUMBER, [time1]) 347 .setFrameMap(TraceType.TEST_TRACE_NUMBER, undefined) 348 .build(); 349 350 const expectedEntries = new Map<TraceType, Array<{}>>([ 351 [TraceType.TEST_TRACE_STRING, ['entry-0']], 352 [TraceType.TEST_TRACE_NUMBER, [0]], 353 ]); 354 355 expect(await TracesUtils.extractEntries(traces)).toEqual(expectedEntries); 356 expect(await TracesUtils.extractEntries(traces.sliceTime())).toEqual( 357 expectedEntries, 358 ); 359 360 expect(() => { 361 traces.sliceFrames(); 362 }).toThrow(); 363 expect(() => { 364 traces.forEachFrame(FunctionUtils.DO_NOTHING); 365 }).toThrow(); 366 expect(() => { 367 traces.mapFrame(FunctionUtils.DO_NOTHING); 368 }).toThrow(); 369 }); 370 371 it('supports multiple traces with same type', () => { 372 const traceShort = new TraceBuilder<number>() 373 .setType(TraceType.TEST_TRACE_NUMBER) 374 .setEntries([0]) 375 .build(); 376 const traceLong = new TraceBuilder<number>() 377 .setType(TraceType.TEST_TRACE_NUMBER) 378 .setEntries([1, 2]) 379 .build(); 380 381 const traces = new Traces(); 382 traces.addTrace(traceShort); 383 traces.addTrace(traceLong); 384 385 expect(traces.getTraces(TraceType.TEST_TRACE_NUMBER)).toEqual([ 386 traceShort, 387 traceLong, 388 ]); 389 expect(traces.getTrace(TraceType.TEST_TRACE_NUMBER)).toEqual(traceLong); 390 }); 391}); 392