• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {TraceOverridden} from 'messaging/user_warnings';
18import {getFixtureFile} from 'test/unit/fixture_utils';
19import {UserNotifierChecker} from 'test/unit/user_notifier_checker';
20import {TraceFile} from 'trace/trace_file';
21import {TraceFileFilter} from './trace_file_filter';
22
23describe('TraceFileFilter', () => {
24  const filter = new TraceFileFilter();
25
26  // Could be any file, we just need an instance of File to be used as a fake bugreport archive
27  const bugreportArchive = new File(
28    [new ArrayBuffer(0)],
29    'test_bugreport.zip',
30  ) as unknown as File;
31
32  let userNotifierChecker: UserNotifierChecker;
33
34  beforeAll(() => {
35    userNotifierChecker = new UserNotifierChecker();
36  });
37
38  beforeEach(() => {
39    userNotifierChecker.reset();
40  });
41
42  describe('bugreport (detects it is a bugreport)', () => {
43    it('ignores non-trace dirs', async () => {
44      const pickedBugreportFiles = [
45        makeTraceFile(
46          'FS/data/misc/wmtrace/surface_flinger.bp',
47          bugreportArchive,
48        ),
49        makeTraceFile('FS/data/misc/wmtrace/transactions.bp', bugreportArchive),
50        makeTraceFile('proto/window_CRITICAL.proto', bugreportArchive),
51        makeTraceFile('proto/input_method_CRITICAL.proto', bugreportArchive),
52        makeTraceFile('proto/SurfaceFlinger_CRITICAL.proto', bugreportArchive),
53      ];
54
55      const ignoredBugreportFile = makeTraceFile(
56        'FS/data/misc/ignored-dir/wm_transition_trace.bp',
57        bugreportArchive,
58      );
59
60      const bugreportFiles = [
61        await makeBugreportMainEntryTraceFile(),
62        await makeBugreportCodenameTraceFile(),
63        ...pickedBugreportFiles,
64        ignoredBugreportFile,
65      ];
66
67      // Corner case:
68      // A plain trace file is loaded along the bugreport
69      //    -> trace file must not be ignored
70      //
71      // Note:
72      // The even weirder corner case where two bugreports are loaded at the same time is
73      // currently not properly handled.
74      const plainTraceFile = makeTraceFile(
75        'would-be-ignored-if-was-part-of-bugreport/input_method_clients.pb',
76      );
77
78      const result = await filter.filter([...bugreportFiles, plainTraceFile]);
79      expect(result.perfetto).toBeUndefined();
80
81      const expectedLegacy = new Set([...pickedBugreportFiles, plainTraceFile]);
82      const actualLegacy = new Set(result.legacy);
83      expect(actualLegacy).toEqual(expectedLegacy);
84      userNotifierChecker.expectNone();
85    });
86
87    it('picks perfetto systrace.pftrace', async () => {
88      const perfettoSystemTrace = makeTraceFile(
89        'FS/data/misc/perfetto-traces/bugreport/systrace.pftrace',
90        bugreportArchive,
91      );
92      const bugreportFiles = [
93        await makeBugreportMainEntryTraceFile(),
94        await makeBugreportCodenameTraceFile(),
95        perfettoSystemTrace,
96        makeTraceFile(
97          'FS/data/misc/perfetto-traces/other.perfetto-trace',
98          bugreportArchive,
99        ),
100        makeTraceFile(
101          'FS/data/misc/perfetto-traces/other.pftrace',
102          bugreportArchive,
103        ),
104      ];
105      const result = await filter.filter(bugreportFiles);
106      expect(result.perfetto).toEqual(perfettoSystemTrace);
107      expect(result.legacy).toEqual([]);
108      userNotifierChecker.expectNone();
109    });
110
111    it('ignores perfetto traces other than systrace.pftrace', async () => {
112      const bugreportFiles = [
113        await makeBugreportMainEntryTraceFile(),
114        await makeBugreportCodenameTraceFile(),
115        makeTraceFile(
116          'FS/data/misc/perfetto-traces/other.perfetto-trace',
117          bugreportArchive,
118        ),
119        makeTraceFile(
120          'FS/data/misc/perfetto-traces/other.pftrace',
121          bugreportArchive,
122        ),
123      ];
124      const result = await filter.filter(bugreportFiles);
125      expect(result.perfetto).toBeUndefined();
126      expect(result.legacy).toEqual([]);
127      userNotifierChecker.expectNone();
128    });
129
130    it('identifies timezone information from bugreport codename file', async () => {
131      const legacyFile = makeTraceFile(
132        'proto/window_CRITICAL.proto',
133        bugreportArchive,
134      );
135      const bugreportFiles = [
136        await makeBugreportMainEntryTraceFile(),
137        await makeBugreportCodenameTraceFile(),
138        legacyFile,
139      ];
140      const result = await filter.filter(bugreportFiles);
141      expect(result.legacy).toEqual([legacyFile]);
142      expect(result.perfetto).toBeUndefined();
143      expect(result.timezoneInfo).toEqual({
144        timezone: 'Asia/Kolkata',
145        locale: 'en-US',
146      });
147      userNotifierChecker.expectNone();
148    });
149
150    it('unzips trace files within bugreport zip', async () => {
151      const zippedTraceFile = await makeZippedTraceFile();
152
153      const bugreportFiles = [
154        await makeBugreportMainEntryTraceFile(),
155        await makeBugreportCodenameTraceFile(),
156        zippedTraceFile,
157      ];
158
159      const result = await filter.filter(bugreportFiles);
160      expect(result.perfetto).toBeUndefined();
161      expect(result.legacy.map((file) => file.file.name)).toEqual([
162        'Surface Flinger/SurfaceFlinger.pb',
163        'Window Manager/WindowManager.pb',
164      ]);
165      userNotifierChecker.expectNone();
166    });
167  });
168
169  describe('plain input (no bugreport)', () => {
170    it('picks perfetto trace with .perfetto-trace extension', async () => {
171      const perfettoTrace = makeTraceFile('file.perfetto-trace');
172      await checkPerfettoFilePickedWithoutErrors(perfettoTrace);
173    });
174
175    it('picks perfetto trace with .pftrace extension', async () => {
176      const pftrace = makeTraceFile('file.pftrace');
177      await checkPerfettoFilePickedWithoutErrors(pftrace);
178    });
179
180    it('picks perfetto trace with .perfetto extension', async () => {
181      const perfetto = makeTraceFile('file.perfetto');
182      await checkPerfettoFilePickedWithoutErrors(perfetto);
183    });
184
185    it('picks perfetto trace with .perfetto-trace.gz extension', async () => {
186      const perfettoTraceGz = makeTraceFile('file.perfetto-trace.gz');
187      await checkPerfettoFilePickedWithoutErrors(perfettoTraceGz);
188    });
189
190    it('picks perfetto trace with .pftrace.gz extension', async () => {
191      const pftraceGz = makeTraceFile('file.pftrace.gz');
192      await checkPerfettoFilePickedWithoutErrors(pftraceGz);
193    });
194
195    it('picks perfetto trace with .perfetto.gz extension', async () => {
196      const perfettoGz = makeTraceFile('file.perfetto.gz');
197      await checkPerfettoFilePickedWithoutErrors(perfettoGz);
198    });
199
200    it('picks largest perfetto trace', async () => {
201      const small = makeTraceFile('small.perfetto-trace', undefined, 10);
202      const medium = makeTraceFile('medium.perfetto-trace', undefined, 20);
203      const large = makeTraceFile('large.perfetto-trace', undefined, 30);
204      const result = await filter.filter([small, large, medium]);
205      expect(result.perfetto).toEqual(large);
206      expect(result.legacy).toEqual([]);
207      userNotifierChecker.expectAdded([
208        new TraceOverridden(small.getDescriptor()),
209        new TraceOverridden(medium.getDescriptor()),
210      ]);
211    });
212
213    it('extracts screen recording metadata', async () => {
214      const metadataJson = await makeMetadataJsonFile();
215      const screenRecording = makeTraceFile('screen_recording.mp4');
216      const result = await filter.filter([screenRecording, metadataJson]);
217      expect(result.legacy).toEqual([screenRecording]);
218      expect(result.metadata.screenRecordingOffsets).toEqual({
219        elapsedRealTimeNanos: 0n,
220        realToElapsedTimeOffsetNanos: 1732721670187419904n,
221      });
222      userNotifierChecker.expectNone();
223    });
224
225    async function checkPerfettoFilePickedWithoutErrors(
226      perfettoFile: TraceFile,
227    ) {
228      const result = await filter.filter([perfettoFile]);
229      expect(result.perfetto).toEqual(perfettoFile);
230      expect(result.legacy).toEqual([]);
231      userNotifierChecker.expectNone();
232    }
233  });
234
235  function makeTraceFile(
236    filename: string,
237    parentArchive?: File,
238    size?: number,
239  ): TraceFile {
240    size = size ?? 0;
241    const file = new File([new ArrayBuffer(size)], filename);
242    return new TraceFile(file as unknown as File, parentArchive);
243  }
244
245  async function makeBugreportMainEntryTraceFile(): Promise<TraceFile> {
246    const file = await getFixtureFile(
247      'bugreports/main_entry.txt',
248      'main_entry.txt',
249    );
250    return new TraceFile(file, bugreportArchive);
251  }
252
253  async function makeBugreportCodenameTraceFile(): Promise<TraceFile> {
254    const file = await getFixtureFile(
255      'bugreports/bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt',
256      'bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt',
257    );
258    return new TraceFile(file, bugreportArchive);
259  }
260
261  async function makeZippedTraceFile(): Promise<TraceFile> {
262    const file = await getFixtureFile(
263      'traces/winscope.zip',
264      'FS/data/misc/wmtrace/winscope.zip',
265    );
266    return new TraceFile(file, bugreportArchive);
267  }
268
269  async function makeMetadataJsonFile(): Promise<TraceFile> {
270    const file = await getFixtureFile(
271      'traces/elapsed_and_real_timestamp/screen_recording_metadata.json',
272    );
273    return new TraceFile(file, bugreportArchive);
274  }
275});
276