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