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