1/* 2 * Copyright (C) 2022 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 {TimeRange} from 'common/time'; 19import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils'; 20import {TracesBuilder} from 'test/unit/traces_builder'; 21import {TraceBuilder} from 'test/unit/trace_builder'; 22import {TracePosition} from 'trace/trace_position'; 23import {TraceType} from 'trace/trace_type'; 24import {TimelineData} from './timeline_data'; 25 26describe('TimelineData', () => { 27 let timelineData: TimelineData; 28 29 const timestamp0 = TimestampConverterUtils.makeRealTimestamp(0n); 30 const timestamp5 = TimestampConverterUtils.makeRealTimestamp(5n); 31 const timestamp9 = TimestampConverterUtils.makeRealTimestamp(9n); 32 const timestamp10 = TimestampConverterUtils.makeRealTimestamp(10n); 33 const timestamp11 = TimestampConverterUtils.makeRealTimestamp(11n); 34 35 const traces = new TracesBuilder() 36 .setTimestamps(TraceType.PROTO_LOG, [timestamp9]) 37 .setTimestamps(TraceType.EVENT_LOG, [timestamp9]) 38 .setTimestamps(TraceType.SURFACE_FLINGER, [timestamp10]) 39 .setTimestamps(TraceType.WINDOW_MANAGER, [timestamp11]) 40 .build(); 41 42 const traceSf = assertDefined(traces.getTrace(TraceType.SURFACE_FLINGER)); 43 const traceWm = assertDefined(traces.getTrace(TraceType.WINDOW_MANAGER)); 44 45 const position10 = TracePosition.fromTraceEntry( 46 assertDefined(traces.getTrace(TraceType.SURFACE_FLINGER)).getEntry(0), 47 ); 48 const position11 = TracePosition.fromTraceEntry( 49 assertDefined(traces.getTrace(TraceType.WINDOW_MANAGER)).getEntry(0), 50 ); 51 const position1000 = TracePosition.fromTimestamp( 52 TimestampConverterUtils.makeRealTimestamp(1000n), 53 ); 54 55 beforeEach(() => { 56 timelineData = new TimelineData(); 57 }); 58 59 it('can be initialized', () => { 60 expect(timelineData.getCurrentPosition()).toBeUndefined(); 61 62 timelineData.initialize( 63 traces, 64 undefined, 65 TimestampConverterUtils.TIMESTAMP_CONVERTER, 66 ); 67 expect(timelineData.getCurrentPosition()).toBeDefined(); 68 }); 69 70 describe('dumps', () => { 71 const traces = new TracesBuilder() 72 .setTimestamps(TraceType.SURFACE_FLINGER, [timestamp10, timestamp11]) 73 .setTimestamps(TraceType.WINDOW_MANAGER, [timestamp0]) 74 .build(); 75 76 const dumpWm = assertDefined(traces.getTrace(TraceType.WINDOW_MANAGER)); 77 78 it('drops trace if it is a dump (will not display in timeline UI)', () => { 79 timelineData.initialize( 80 traces, 81 undefined, 82 TimestampConverterUtils.TIMESTAMP_CONVERTER, 83 ); 84 expect( 85 timelineData.getTraces().getTrace(TraceType.WINDOW_MANAGER), 86 ).toBeUndefined(); 87 expect(timelineData.getFullTimeRange().from).toBe(timestamp10); 88 expect(timelineData.getFullTimeRange().to).toBe(timestamp11); 89 }); 90 91 it('is robust to prev/next entry request of a dump', () => { 92 timelineData.initialize( 93 traces, 94 undefined, 95 TimestampConverterUtils.TIMESTAMP_CONVERTER, 96 ); 97 expect(timelineData.getPreviousEntryFor(dumpWm)).toBeUndefined(); 98 expect(timelineData.getNextEntryFor(dumpWm)).toBeUndefined(); 99 }); 100 }); 101 102 it('uses first entry of first active trace by default', () => { 103 timelineData.initialize( 104 traces, 105 undefined, 106 TimestampConverterUtils.TIMESTAMP_CONVERTER, 107 ); 108 expect(timelineData.getCurrentPosition()).toEqual(position10); 109 }); 110 111 it('uses explicit position if set', () => { 112 timelineData.initialize( 113 traces, 114 undefined, 115 TimestampConverterUtils.TIMESTAMP_CONVERTER, 116 ); 117 expect(timelineData.getCurrentPosition()).toEqual(position10); 118 119 timelineData.setPosition(position1000); 120 expect(timelineData.getCurrentPosition()).toEqual(position1000); 121 122 timelineData.trySetActiveTrace(traceSf); 123 expect(timelineData.getCurrentPosition()).toEqual(position1000); 124 125 timelineData.trySetActiveTrace(traceWm); 126 expect(timelineData.getCurrentPosition()).toEqual(position1000); 127 }); 128 129 it('sets active trace and update current position accordingly', () => { 130 timelineData.initialize( 131 traces, 132 undefined, 133 TimestampConverterUtils.TIMESTAMP_CONVERTER, 134 ); 135 136 expect(timelineData.getCurrentPosition()).toEqual(position10); 137 138 timelineData.trySetActiveTrace(traceWm); 139 expect(timelineData.getCurrentPosition()).toEqual(position11); 140 141 timelineData.trySetActiveTrace(traceSf); 142 expect(timelineData.getCurrentPosition()).toEqual(position10); 143 }); 144 145 it('does not set active trace if not present in timeline, or already set', () => { 146 timelineData.initialize( 147 traces, 148 undefined, 149 TimestampConverterUtils.TIMESTAMP_CONVERTER, 150 ); 151 152 expect(timelineData.getCurrentPosition()).toEqual(position10); 153 154 let success = timelineData.trySetActiveTrace(traceWm); 155 expect(timelineData.getActiveTrace()).toEqual(traceWm); 156 expect(success).toBeTrue(); 157 158 success = timelineData.trySetActiveTrace(traceWm); 159 expect(timelineData.getActiveTrace()).toEqual(traceWm); 160 expect(success).toBeFalse(); 161 162 success = timelineData.trySetActiveTrace( 163 new TraceBuilder<{}>() 164 .setType(TraceType.SURFACE_FLINGER) 165 .setEntries([]) 166 .build(), 167 ); 168 expect(timelineData.getActiveTrace()).toEqual(traceWm); 169 expect(success).toBeFalse(); 170 }); 171 172 it('hasTimestamps()', () => { 173 expect(timelineData.hasTimestamps()).toBeFalse(); 174 175 // no trace 176 { 177 const traces = new TracesBuilder().build(); 178 timelineData.initialize( 179 traces, 180 undefined, 181 TimestampConverterUtils.TIMESTAMP_CONVERTER, 182 ); 183 expect(timelineData.hasTimestamps()).toBeFalse(); 184 } 185 // trace without timestamps 186 { 187 const traces = new TracesBuilder() 188 .setTimestamps(TraceType.SURFACE_FLINGER, []) 189 .build(); 190 timelineData.initialize( 191 traces, 192 undefined, 193 TimestampConverterUtils.TIMESTAMP_CONVERTER, 194 ); 195 expect(timelineData.hasTimestamps()).toBeFalse(); 196 } 197 // trace with timestamps 198 { 199 const traces = new TracesBuilder() 200 .setTimestamps(TraceType.SURFACE_FLINGER, [timestamp10]) 201 .build(); 202 timelineData.initialize( 203 traces, 204 undefined, 205 TimestampConverterUtils.TIMESTAMP_CONVERTER, 206 ); 207 expect(timelineData.hasTimestamps()).toBeTrue(); 208 } 209 }); 210 211 it('hasMoreThanOneDistinctTimestamp()', () => { 212 expect(timelineData.hasMoreThanOneDistinctTimestamp()).toBeFalse(); 213 214 // no trace 215 { 216 const traces = new TracesBuilder().build(); 217 timelineData.initialize( 218 traces, 219 undefined, 220 TimestampConverterUtils.TIMESTAMP_CONVERTER, 221 ); 222 expect(timelineData.hasMoreThanOneDistinctTimestamp()).toBeFalse(); 223 } 224 // no distinct timestamps 225 { 226 const traces = new TracesBuilder() 227 .setTimestamps(TraceType.SURFACE_FLINGER, [timestamp10]) 228 .setTimestamps(TraceType.WINDOW_MANAGER, [timestamp10]) 229 .build(); 230 timelineData.initialize( 231 traces, 232 undefined, 233 TimestampConverterUtils.TIMESTAMP_CONVERTER, 234 ); 235 expect(timelineData.hasMoreThanOneDistinctTimestamp()).toBeFalse(); 236 } 237 // distinct timestamps 238 { 239 const traces = new TracesBuilder() 240 .setTimestamps(TraceType.SURFACE_FLINGER, [timestamp10]) 241 .setTimestamps(TraceType.WINDOW_MANAGER, [timestamp11]) 242 .build(); 243 timelineData.initialize( 244 traces, 245 undefined, 246 TimestampConverterUtils.TIMESTAMP_CONVERTER, 247 ); 248 expect(timelineData.hasMoreThanOneDistinctTimestamp()).toBeTrue(); 249 } 250 }); 251 252 it('getCurrentPosition() returns same object if no change to range', () => { 253 timelineData.initialize( 254 traces, 255 undefined, 256 TimestampConverterUtils.TIMESTAMP_CONVERTER, 257 ); 258 259 expect(timelineData.getCurrentPosition()).toBe( 260 timelineData.getCurrentPosition(), 261 ); 262 263 timelineData.setPosition(position11); 264 265 expect(timelineData.getCurrentPosition()).toBe( 266 timelineData.getCurrentPosition(), 267 ); 268 }); 269 270 it('makePositionFromActiveTrace()', () => { 271 timelineData.initialize( 272 traces, 273 undefined, 274 TimestampConverterUtils.TIMESTAMP_CONVERTER, 275 ); 276 const time100 = TimestampConverterUtils.makeRealTimestamp(100n); 277 278 { 279 timelineData.trySetActiveTrace(traceSf); 280 const position = timelineData.makePositionFromActiveTrace(time100); 281 expect(position.timestamp).toEqual(time100); 282 expect(position.entry).toEqual(traceSf.getEntry(0)); 283 } 284 285 { 286 timelineData.trySetActiveTrace(traceWm); 287 const position = timelineData.makePositionFromActiveTrace(time100); 288 expect(position.timestamp).toEqual(time100); 289 expect(position.entry).toEqual(traceWm.getEntry(0)); 290 } 291 }); 292 293 it('getFullTimeRange() returns same object if no change to range', () => { 294 timelineData.initialize( 295 traces, 296 undefined, 297 TimestampConverterUtils.TIMESTAMP_CONVERTER, 298 ); 299 300 expect(timelineData.getFullTimeRange()).toBe( 301 timelineData.getFullTimeRange(), 302 ); 303 }); 304 305 it('getSelectionTimeRange() returns same object if no change to range', () => { 306 timelineData.initialize( 307 traces, 308 undefined, 309 TimestampConverterUtils.TIMESTAMP_CONVERTER, 310 ); 311 312 expect(timelineData.getSelectionTimeRange()).toBe( 313 timelineData.getSelectionTimeRange(), 314 ); 315 316 timelineData.setSelectionTimeRange(new TimeRange(timestamp0, timestamp5)); 317 318 expect(timelineData.getSelectionTimeRange()).toBe( 319 timelineData.getSelectionTimeRange(), 320 ); 321 }); 322 323 it('getZoomRange() returns same object if no change to range', () => { 324 timelineData.initialize( 325 traces, 326 undefined, 327 TimestampConverterUtils.TIMESTAMP_CONVERTER, 328 ); 329 330 expect(timelineData.getZoomRange()).toBe(timelineData.getZoomRange()); 331 332 timelineData.setZoom(new TimeRange(timestamp0, timestamp5)); 333 334 expect(timelineData.getZoomRange()).toBe(timelineData.getZoomRange()); 335 }); 336 337 it("getCurrentPosition() prioritizes active trace's first entry", () => { 338 timelineData.initialize( 339 traces, 340 undefined, 341 TimestampConverterUtils.TIMESTAMP_CONVERTER, 342 ); 343 timelineData.trySetActiveTrace(traceWm); 344 345 expect(timelineData.getCurrentPosition()?.timestamp).toBe(timestamp11); 346 }); 347}); 348