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