Merge changes from topic "traces-with-realtime"
* changes: add support for traces real timestamp refactor test code refactor Parser's interface parser for new screen recording metadata
This commit is contained in:
committed by
Android (Google) Code Review
commit
fce1e58e06
@@ -13,12 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Component, Injector, Inject } from "@angular/core";
|
||||
import { createCustomElement } from "@angular/elements";
|
||||
import { ViewerWindowManagerComponent } from "viewers/viewer_window_manager/viewer_window_manager.component";
|
||||
import { Core } from "./core";
|
||||
import { ProxyState } from "trace_collection/proxy_client";
|
||||
import { PersistentStore } from "../common/persistent_store";
|
||||
import {Component, Inject, Injector} from "@angular/core";
|
||||
import {createCustomElement} from "@angular/elements";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {PersistentStore} from "common/persistent_store";
|
||||
import {ViewerWindowManagerComponent} from "viewers/viewer_window_manager/viewer_window_manager.component";
|
||||
import {Core} from "./core";
|
||||
import {ProxyState} from "trace_collection/proxy_client";
|
||||
|
||||
@Component({
|
||||
selector: "app-root",
|
||||
@@ -81,7 +82,7 @@ export class AppComponent {
|
||||
}
|
||||
|
||||
public notifyCurrentTimestamp() {
|
||||
const dummyTimestamp = 1000000; //TODO: get timestamp from time scrub
|
||||
const dummyTimestamp = new Timestamp(TimestampType.ELAPSED, 1000000n);
|
||||
this.core.notifyCurrentTimestamp(dummyTimestamp);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "parsers/parser";
|
||||
import {ParserFactory} from "parsers/parser_factory";
|
||||
import { setTraces } from "trace_collection/set_traces";
|
||||
@@ -35,10 +36,10 @@ class Core {
|
||||
this.parsers = await new ParserFactory().createParsers(traces);
|
||||
console.log("created parsers: ", this.parsers);
|
||||
|
||||
const activeTraceTypes = this.parsers.map(parser => parser.getTraceTypeId());
|
||||
const activeTraceTypes = this.parsers.map(parser => parser.getTraceType());
|
||||
console.log("active trace types: ", activeTraceTypes);
|
||||
|
||||
this.viewers = new ViewerFactory().createViewers(new Set<TraceTypeId>(activeTraceTypes));
|
||||
this.viewers = new ViewerFactory().createViewers(new Set<TraceType>(activeTraceTypes));
|
||||
console.log("created viewers: ", this.viewers);
|
||||
}
|
||||
|
||||
@@ -46,27 +47,37 @@ class Core {
|
||||
return this.viewers.map(viewer => viewer.getView());
|
||||
}
|
||||
|
||||
getTimestamps(): number[] {
|
||||
const mergedTimestamps: number[] = [];
|
||||
getTimestamps(): Timestamp[] {
|
||||
for (const type of [TimestampType.REAL, TimestampType.ELAPSED]) {
|
||||
const mergedTimestamps: Timestamp[] = [];
|
||||
|
||||
this.parsers
|
||||
.map(parser => parser.getTimestamps())
|
||||
.forEach(timestamps => {
|
||||
mergedTimestamps.push(...timestamps);
|
||||
});
|
||||
let isTypeProvidedByAllParsers = true;
|
||||
|
||||
const uniqueTimestamps = [... new Set<number>(mergedTimestamps)];
|
||||
for(const timestamps of this.parsers.map(parser => parser.getTimestamps(type))) {
|
||||
if (timestamps === undefined) {
|
||||
isTypeProvidedByAllParsers = false;
|
||||
break;
|
||||
}
|
||||
mergedTimestamps.push(...timestamps!);
|
||||
}
|
||||
|
||||
return uniqueTimestamps;
|
||||
if (isTypeProvidedByAllParsers) {
|
||||
const uniqueTimestamps = [... new Set<Timestamp>(mergedTimestamps)];
|
||||
uniqueTimestamps.sort();
|
||||
return uniqueTimestamps;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("Failed to create aggregated timestamps (any type)");
|
||||
}
|
||||
|
||||
notifyCurrentTimestamp(timestamp: number) {
|
||||
const traceEntries: Map<TraceTypeId, any> = new Map<TraceTypeId, any>();
|
||||
notifyCurrentTimestamp(timestamp: Timestamp) {
|
||||
const traceEntries: Map<TraceType, any> = new Map<TraceType, any>();
|
||||
|
||||
this.parsers.forEach(parser => {
|
||||
const entry = parser.getTraceEntry(timestamp);
|
||||
if (entry != undefined) {
|
||||
traceEntries.set(parser.getTraceTypeId(), entry);
|
||||
traceEntries.set(parser.getTraceType(), entry);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class ScreenRecordingTraceEntry {
|
||||
constructor(public timestamp: number,
|
||||
public videoTimeSeconds: number,
|
||||
constructor(public videoTimeSeconds: number,
|
||||
public videoData: Blob) {
|
||||
}
|
||||
}
|
||||
|
||||
44
tools/winscope-ng/src/common/trace/timestamp.ts
Normal file
44
tools/winscope-ng/src/common/trace/timestamp.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
enum TimestampType {
|
||||
ELAPSED,
|
||||
REAL,
|
||||
}
|
||||
|
||||
class Timestamp {
|
||||
constructor(type: TimestampType, valueNs: bigint) {
|
||||
this.type = type;
|
||||
this.valueNs = valueNs;
|
||||
}
|
||||
|
||||
public getType(): TimestampType {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public getValueNs(): bigint {
|
||||
return this.valueNs;
|
||||
}
|
||||
|
||||
public valueOf(): bigint {
|
||||
return this.getValueNs();
|
||||
}
|
||||
|
||||
private readonly type: TimestampType;
|
||||
private readonly valueNs: bigint;
|
||||
}
|
||||
|
||||
export {Timestamp, TimestampType};
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
enum TraceTypeId {
|
||||
enum TraceType {
|
||||
ACCESSIBILITY,
|
||||
WINDOW_MANAGER,
|
||||
SURFACE_FLINGER,
|
||||
@@ -32,4 +32,4 @@ enum TraceTypeId {
|
||||
ERROR,
|
||||
}
|
||||
|
||||
export {TraceTypeId};
|
||||
export {TraceType};
|
||||
@@ -81,15 +81,74 @@ describe("ArrayUtils", () => {
|
||||
it("toUintLittleEndian", () => {
|
||||
const buffer = new Uint8Array([0, 0, 1, 1]);
|
||||
|
||||
expect(ArrayUtils.toUintLittleEndian(buffer, 0, -1)).toEqual(0);
|
||||
expect(ArrayUtils.toUintLittleEndian(buffer, 0, 0)).toEqual(0);
|
||||
expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff]), 0, -1)).toEqual(0n);
|
||||
expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff]), 0, 0)).toEqual(0n);
|
||||
expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff]), 1, 1)).toEqual(0n);
|
||||
|
||||
expect(ArrayUtils.toUintLittleEndian(buffer, 0, 1)).toEqual(0);
|
||||
expect(ArrayUtils.toUintLittleEndian(buffer, 0, 2)).toEqual(0);
|
||||
expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x01, 0xff]), 0, 1)).toEqual(0n);
|
||||
expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x01, 0xff]), 1, 2)).toEqual(1n);
|
||||
expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x01, 0xff]), 2, 3)).toEqual(255n);
|
||||
|
||||
expect(ArrayUtils.toUintLittleEndian(buffer, 3, 4)).toEqual(1);
|
||||
expect(ArrayUtils.toUintLittleEndian(buffer, 2, 4)).toEqual(1 + 256);
|
||||
expect(ArrayUtils.toUintLittleEndian(buffer, 1, 4)).toEqual(256 + 256*256);
|
||||
expect(ArrayUtils.toUintLittleEndian(buffer, 0, 3)).toEqual(256*256);
|
||||
expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x00]), 0, 2)).toEqual(0n);
|
||||
expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x01, 0x00]), 0, 2)).toEqual(1n);
|
||||
expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x01]), 0, 2)).toEqual(256n);
|
||||
expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff]), 0, 2)).toEqual(0xffffn);
|
||||
|
||||
expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff]), 0, 4)).toEqual(0xffffffffn);
|
||||
|
||||
expect(
|
||||
ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 0, 8))
|
||||
.toEqual(0xffffffffffffffffn);
|
||||
|
||||
expect(
|
||||
ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 0, 9))
|
||||
.toEqual(0xffffffffffffffffffn);
|
||||
});
|
||||
|
||||
it("toIntLittleEndian", () => {
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff]), 0, -1)).toEqual(0n);
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff]), 0, 0)).toEqual(0n);
|
||||
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x00]), 0, 1)).toEqual(0n);
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x01]), 0, 1)).toEqual(1n);
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x7f]), 0, 1)).toEqual(127n);
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x80]), 0, 1)).toEqual(-128n);
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff]), 0, 1)).toEqual(-1n);
|
||||
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0x7f]), 0, 2)).toEqual(32767n);
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x00, 0x80]), 0, 2)).toEqual(-32768n);
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x01, 0x80]), 0, 2)).toEqual(-32767n);
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff]), 0, 2)).toEqual(-1n);
|
||||
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0x7f]), 0, 4)).toEqual(0x7fffffffn);
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x00, 0x00, 0x00, 0x80]), 0, 4)).toEqual(-0x80000000n);
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x01, 0x00, 0x00, 0x80]), 0, 4)).toEqual(-0x7fffffffn);
|
||||
expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff]), 0, 4)).toEqual(-1n);
|
||||
|
||||
expect(
|
||||
ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]), 0, 8)
|
||||
).toEqual(0x7fffffffffffffffn);
|
||||
expect(
|
||||
ArrayUtils.toIntLittleEndian(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), 0, 8)
|
||||
).toEqual(-0x8000000000000000n);
|
||||
expect(
|
||||
ArrayUtils.toIntLittleEndian(new Uint8Array([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), 0, 8)
|
||||
).toEqual(-0x7fffffffffffffffn);
|
||||
expect(
|
||||
ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 0, 8)
|
||||
).toEqual(-1n);
|
||||
|
||||
expect(
|
||||
ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]), 0, 9)
|
||||
).toEqual(0x7fffffffffffffffffn);
|
||||
expect(
|
||||
ArrayUtils.toIntLittleEndian(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), 0, 9)
|
||||
).toEqual(-0x800000000000000000n);
|
||||
expect(
|
||||
ArrayUtils.toIntLittleEndian(new Uint8Array([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), 0, 9)
|
||||
).toEqual(-0x7fffffffffffffffffn);
|
||||
expect(
|
||||
ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 0, 9)
|
||||
).toEqual(-1n);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -86,14 +86,30 @@ class ArrayUtils {
|
||||
return result;
|
||||
}
|
||||
|
||||
static toUintLittleEndian(buffer: Uint8Array, start: number, end: number) {
|
||||
let result = 0;
|
||||
for (let i = end-1; i>=start; --i) {
|
||||
result *= 256;
|
||||
result += buffer[i];
|
||||
static toUintLittleEndian(buffer: Uint8Array, start: number, end: number): bigint {
|
||||
let result = 0n;
|
||||
for (let i = end-1; i >= start; --i) {
|
||||
result *= 256n;
|
||||
result += BigInt(buffer[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static toIntLittleEndian(buffer: Uint8Array, start: number, end: number): bigint {
|
||||
const numOfBits = BigInt(Math.max(0, 8 * (end-start)));
|
||||
if (numOfBits <= 0n) {
|
||||
return 0n;
|
||||
}
|
||||
|
||||
let result = ArrayUtils.toUintLittleEndian(buffer, start, end);
|
||||
const maxSignedValue = 2n ** (numOfBits - 1n) - 1n;
|
||||
if (result > maxSignedValue) {
|
||||
const valuesRange = 2n ** numOfBits;
|
||||
result -= valuesRange;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export {ArrayUtils};
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {ArrayUtils} from "../common/utils/array_utils";
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {ArrayUtils} from "common/utils/array_utils";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
|
||||
abstract class Parser {
|
||||
protected constructor(trace: Blob) {
|
||||
@@ -34,17 +35,39 @@ abstract class Parser {
|
||||
}
|
||||
|
||||
this.decodedEntries = this.decodeTrace(traceBuffer);
|
||||
this.timestamps = this.decodedEntries.map((entry: any) => this.getTimestamp(entry));
|
||||
|
||||
for (const type of [TimestampType.ELAPSED, TimestampType.REAL]) {
|
||||
const timestamps: Timestamp[] = [];
|
||||
let areTimestampsValid = true;
|
||||
|
||||
for (const entry of this.decodedEntries) {
|
||||
const timestamp = this.getTimestamp(type, entry);
|
||||
if (timestamp === undefined) {
|
||||
areTimestampsValid = false;
|
||||
break;
|
||||
}
|
||||
timestamps.push(timestamp);
|
||||
}
|
||||
|
||||
if (areTimestampsValid) {
|
||||
this.timestamps.set(type, timestamps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract getTraceTypeId(): TraceTypeId;
|
||||
public abstract getTraceType(): TraceType;
|
||||
|
||||
public getTimestamps(): number[] {
|
||||
return this.timestamps;
|
||||
public getTimestamps(type: TimestampType): undefined|Timestamp[] {
|
||||
return this.timestamps.get(type);
|
||||
}
|
||||
|
||||
public getTraceEntry(timestamp: number): undefined|any {
|
||||
const index = ArrayUtils.binarySearchLowerOrEqual(this.getTimestamps(), timestamp);
|
||||
public getTraceEntry(timestamp: Timestamp): undefined|any {
|
||||
const timestamps = this.getTimestamps(timestamp.getType());
|
||||
if (timestamps === undefined) {
|
||||
throw TypeError(`Timestamps with type "${timestamp.getType()}" not available`);
|
||||
}
|
||||
|
||||
const index = ArrayUtils.binarySearchLowerOrEqual(timestamps, timestamp);
|
||||
if (index === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -59,12 +82,12 @@ abstract class Parser {
|
||||
|
||||
protected abstract getMagicNumber(): undefined|number[];
|
||||
protected abstract decodeTrace(trace: Uint8Array): any[];
|
||||
protected abstract getTimestamp(decodedEntry: any): number;
|
||||
protected abstract getTimestamp(type: TimestampType, decodedEntry: any): undefined|Timestamp;
|
||||
protected abstract processDecodedEntry(decodedEntry: any): any;
|
||||
|
||||
protected trace: Blob;
|
||||
protected decodedEntries: any[] = [];
|
||||
protected timestamps: number[] = [];
|
||||
private timestamps: Map<TimestampType, Timestamp[]> = new Map<TimestampType, Timestamp[]>();
|
||||
}
|
||||
|
||||
export {Parser};
|
||||
|
||||
@@ -13,32 +13,75 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {ParserFactory} from "./parser_factory";
|
||||
import {TestUtils} from "test/test_utils";
|
||||
import {UnitTestUtils} from "test/unit/utils";
|
||||
|
||||
describe("ParserAccessibility", () => {
|
||||
let parser: Parser;
|
||||
describe("trace with elapsed + real timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
beforeAll(async () => {
|
||||
const trace = TestUtils.getFixtureBlob("trace_Accessibility.pb");
|
||||
const parsers = await new ParserFactory().createParsers([trace]);
|
||||
expect(parsers.length).toEqual(1);
|
||||
parser = parsers[0];
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/Accessibility.pb");
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.ACCESSIBILITY);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamps", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 14499089524n),
|
||||
new Timestamp(TimestampType.ELAPSED, 14499599656n),
|
||||
new Timestamp(TimestampType.ELAPSED, 14953120693n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED)!.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("provides real timestamps", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.REAL, 1659107089100052652n),
|
||||
new Timestamp(TimestampType.REAL, 1659107089100562784n),
|
||||
new Timestamp(TimestampType.REAL, 1659107089554083821n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from elapsed timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 14499599656n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
|
||||
.toEqual(14499599656n);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from real timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.REAL, 1659107089100562784n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
|
||||
.toEqual(14499599656n);
|
||||
});
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceTypeId()).toEqual(TraceTypeId.ACCESSIBILITY);
|
||||
});
|
||||
describe("trace with elapsed (only) timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
it("provides timestamps", () => {
|
||||
expect(parser.getTimestamps())
|
||||
.toEqual([850297444302, 850297882046, 850756176154, 850773581835]);
|
||||
});
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/Accessibility.pb");
|
||||
});
|
||||
|
||||
it("retrieves trace entry", () => {
|
||||
expect(Number(parser.getTraceEntry(850297444302)!.elapsedRealtimeNanos))
|
||||
.toEqual(850297444302);
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.ACCESSIBILITY);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED)![0])
|
||||
.toEqual(new Timestamp(TimestampType.ELAPSED, 850297444302n));
|
||||
});
|
||||
|
||||
it("doesn't provide real timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.REAL))
|
||||
.toEqual(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,17 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {AccessibilityTraceFileProto} from "./proto_types";
|
||||
|
||||
class ParserAccessibility extends Parser {
|
||||
constructor(trace: Blob) {
|
||||
super(trace);
|
||||
this.realToElapsedTimeOffsetNs = undefined;
|
||||
}
|
||||
|
||||
override getTraceTypeId(): TraceTypeId {
|
||||
return TraceTypeId.ACCESSIBILITY;
|
||||
override getTraceType(): TraceType {
|
||||
return TraceType.ACCESSIBILITY;
|
||||
}
|
||||
|
||||
override getMagicNumber(): number[] {
|
||||
@@ -31,17 +33,31 @@ class ParserAccessibility extends Parser {
|
||||
}
|
||||
|
||||
override decodeTrace(buffer: Uint8Array): any[] {
|
||||
return (<any>AccessibilityTraceFileProto.decode(buffer)).entry;
|
||||
const decoded = <any>AccessibilityTraceFileProto.decode(buffer);
|
||||
if (Object.prototype.hasOwnProperty.call(decoded, "realToElapsedTimeOffsetNanos")) {
|
||||
this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
|
||||
}
|
||||
else {
|
||||
this.realToElapsedTimeOffsetNs = undefined;
|
||||
}
|
||||
return decoded.entry;
|
||||
}
|
||||
|
||||
override getTimestamp(entryProto: any): number {
|
||||
return Number(entryProto.elapsedRealtimeNanos);
|
||||
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
|
||||
if (type === TimestampType.ELAPSED) {
|
||||
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
|
||||
}
|
||||
else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
|
||||
return new Timestamp(type, this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
override processDecodedEntry(entryProto: any): any {
|
||||
return entryProto;
|
||||
}
|
||||
|
||||
private realToElapsedTimeOffsetNs: undefined|bigint;
|
||||
private static readonly MAGIC_NUMBER = [0x09, 0x41, 0x31, 0x31, 0x59, 0x54, 0x52, 0x41, 0x43]; // .A11YTRAC
|
||||
}
|
||||
|
||||
|
||||
@@ -13,48 +13,98 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {ParserFactory} from "./parser_factory";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {Parser} from "./parser";
|
||||
import {TestUtils} from "test/test_utils";
|
||||
import {UnitTestUtils} from "test/unit/utils";
|
||||
|
||||
describe("Parser", () => {
|
||||
let parser: Parser;
|
||||
describe("real timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
beforeAll(async () => {
|
||||
const buffer = TestUtils.getFixtureBlob("trace_WindowManager.pb");
|
||||
const parsers = await new ParserFactory().createParsers([buffer]);
|
||||
expect(parsers.length).toEqual(1);
|
||||
parser = parsers[0];
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/WindowManager.pb");
|
||||
});
|
||||
|
||||
it("provides timestamps", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.REAL, 1659107089075566202n),
|
||||
new Timestamp(TimestampType.REAL, 1659107089999048990n),
|
||||
new Timestamp(TimestampType.REAL, 1659107090010194213n)
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("retrieves trace entry (no timestamp matches)", () => {
|
||||
const timestamp = new Timestamp(TimestampType.REAL, 1659107089075566201n);
|
||||
expect(parser.getTraceEntry(timestamp))
|
||||
.toEqual(undefined);
|
||||
});
|
||||
|
||||
it("retrieves trace entry (equal timestamp matches)", () => {
|
||||
const timestamp = new Timestamp(TimestampType.REAL, 1659107089075566202n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
|
||||
.toEqual(14474594000n);
|
||||
});
|
||||
|
||||
it("retrieves trace entry (equal timestamp matches)", () => {
|
||||
const timestamp = new Timestamp(TimestampType.REAL, 1659107089999048990n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
|
||||
.toEqual(15398076788n);
|
||||
});
|
||||
|
||||
it("retrieves trace entry (lower timestamp matches)", () => {
|
||||
const timestamp = new Timestamp(TimestampType.REAL, 1659107089999048991n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
|
||||
.toEqual(15398076788n);
|
||||
});
|
||||
});
|
||||
|
||||
it("provides timestamps", () => {
|
||||
expect(parser.getTimestamps())
|
||||
.toEqual([850254319343, 850763506110, 850782750048]);
|
||||
});
|
||||
describe("elapsed timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
it("retrieves trace entry (no timestamp matches)", () => {
|
||||
expect(parser.getTraceEntry(850254319342))
|
||||
.toEqual(undefined);
|
||||
});
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/WindowManager.pb");
|
||||
});
|
||||
|
||||
it("retrieves trace entry (equal timestamp matches)", () => {
|
||||
expect(Number(parser.getTraceEntry(850254319343)!.timestampMs))
|
||||
.toEqual(850254319343);
|
||||
});
|
||||
it("provides timestamps", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 850254319343n),
|
||||
new Timestamp(TimestampType.ELAPSED, 850763506110n),
|
||||
new Timestamp(TimestampType.ELAPSED, 850782750048n)
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("retrieves trace entry (equal timestamp matches)", () => {
|
||||
expect(Number(parser.getTraceEntry(850763506110)!.timestampMs))
|
||||
.toEqual(850763506110);
|
||||
});
|
||||
it("retrieves trace entry (no timestamp matches)", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 850254319342n);
|
||||
expect(parser.getTraceEntry(timestamp))
|
||||
.toEqual(undefined);
|
||||
});
|
||||
|
||||
it("retrieves trace entry (lower timestamp matches)", () => {
|
||||
expect(Number(parser.getTraceEntry(850254319344)!.timestampMs))
|
||||
.toEqual(850254319343);
|
||||
});
|
||||
it("retrieves trace entry (equal timestamp matches)", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 850254319343n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
|
||||
.toEqual(850254319343n);
|
||||
});
|
||||
|
||||
it("retrieves trace entry (equal timestamp matches)", () => {
|
||||
expect(Number(parser.getTraceEntry(850763506111)!.timestampMs))
|
||||
.toEqual(850763506110);
|
||||
it("retrieves trace entry (equal timestamp matches)", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 850763506110n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
|
||||
.toEqual(850763506110n);
|
||||
});
|
||||
|
||||
it("retrieves trace entry (lower timestamp matches)", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 850254319344n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
|
||||
.toEqual(850254319343n);
|
||||
});
|
||||
|
||||
it("retrieves trace entry (equal timestamp matches)", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 850763506111n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
|
||||
.toEqual(850763506110n);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ import {ParserInputMethodManagerService} from "./parser_input_method_manager_ser
|
||||
import {ParserInputMethodService} from "./parser_input_method_service";
|
||||
import {ParserProtoLog} from "./parser_protolog";
|
||||
import {ParserScreenRecording} from "./parser_screen_recording";
|
||||
import {ParserScreenRecordingLegacy} from "./parser_screen_recording_legacy";
|
||||
import {ParserSurfaceFlinger} from "./parser_surface_flinger";
|
||||
import {ParserTransactions} from "./parser_transactions";
|
||||
import {ParserWindowManager} from "./parser_window_manager";
|
||||
@@ -33,6 +34,7 @@ class ParserFactory {
|
||||
ParserInputMethodService,
|
||||
ParserProtoLog,
|
||||
ParserScreenRecording,
|
||||
ParserScreenRecordingLegacy,
|
||||
ParserSurfaceFlinger,
|
||||
ParserTransactions,
|
||||
ParserWindowManager,
|
||||
|
||||
@@ -13,34 +13,78 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {ParserFactory} from "./parser_factory";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {TestUtils} from "test/test_utils";
|
||||
import {UnitTestUtils} from "test/unit/utils";
|
||||
|
||||
describe("ParserInputMethodlClients", () => {
|
||||
let parser: Parser;
|
||||
describe("trace with elapsed + real timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
beforeAll(async () => {
|
||||
const buffer = TestUtils.getFixtureBlob("trace_InputMethodClients.pb");
|
||||
const parsers = await new ParserFactory().createParsers([buffer]);
|
||||
expect(parsers.length).toEqual(1);
|
||||
parser = parsers[0];
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/InputMethodClients.pb");
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_CLIENTS);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED)!.length)
|
||||
.toEqual(13);
|
||||
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 15613638434n),
|
||||
new Timestamp(TimestampType.ELAPSED, 15647516364n),
|
||||
new Timestamp(TimestampType.ELAPSED, 15677650967n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED)!.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("provides real timestamps", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.REAL, 1659107090215405395n),
|
||||
new Timestamp(TimestampType.REAL, 1659107090249283325n),
|
||||
new Timestamp(TimestampType.REAL, 1659107090279417928n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from elapsed timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 15647516364n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
|
||||
.toEqual(15647516364n);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from real timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.REAL, 1659107090249283325n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
|
||||
.toEqual(15647516364n);
|
||||
});
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceTypeId()).toEqual(TraceTypeId.INPUT_METHOD_CLIENTS);
|
||||
});
|
||||
describe("trace with elapsed (only) timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
it("provides timestamps", () => {
|
||||
expect(parser.getTimestamps().length)
|
||||
.toEqual(33);
|
||||
expect(parser.getTimestamps().slice(0, 3))
|
||||
.toEqual([1149083651642, 1149083950633, 1149127567251]);
|
||||
});
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/InputMethodClients.pb");
|
||||
});
|
||||
|
||||
it("retrieves trace entry", () => {
|
||||
expect(Number(parser.getTraceEntry(1149083651642)!.elapsedRealtimeNanos))
|
||||
.toEqual(1149083651642);
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_CLIENTS);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED)![0])
|
||||
.toEqual(new Timestamp(TimestampType.ELAPSED, 1149083651642n));
|
||||
});
|
||||
|
||||
it("doesn't provide real timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED)![0])
|
||||
.toEqual(new Timestamp(TimestampType.ELAPSED, 1149083651642n));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,17 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {InputMethodClientsTraceFileProto} from "./proto_types";
|
||||
|
||||
class ParserInputMethodClients extends Parser {
|
||||
constructor(trace: Blob) {
|
||||
super(trace);
|
||||
this.realToElapsedTimeOffsetNs = undefined;
|
||||
}
|
||||
|
||||
getTraceTypeId(): TraceTypeId {
|
||||
return TraceTypeId.INPUT_METHOD_CLIENTS;
|
||||
getTraceType(): TraceType {
|
||||
return TraceType.INPUT_METHOD_CLIENTS;
|
||||
}
|
||||
|
||||
override getMagicNumber(): number[] {
|
||||
@@ -31,17 +33,32 @@ class ParserInputMethodClients extends Parser {
|
||||
}
|
||||
|
||||
override decodeTrace(buffer: Uint8Array): any[] {
|
||||
const decoded = <any>InputMethodClientsTraceFileProto.decode(buffer);
|
||||
if (Object.prototype.hasOwnProperty.call(decoded, "realToElapsedTimeOffsetNanos")) {
|
||||
this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
|
||||
}
|
||||
else {
|
||||
this.realToElapsedTimeOffsetNs = undefined;
|
||||
}
|
||||
|
||||
return (<any>InputMethodClientsTraceFileProto.decode(buffer)).entry;
|
||||
}
|
||||
|
||||
override getTimestamp(entryProto: any): number {
|
||||
return Number(entryProto.elapsedRealtimeNanos);
|
||||
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
|
||||
if (type === TimestampType.ELAPSED) {
|
||||
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
|
||||
}
|
||||
else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs != undefined) {
|
||||
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
override processDecodedEntry(entryProto: any): any {
|
||||
return entryProto;
|
||||
}
|
||||
|
||||
private realToElapsedTimeOffsetNs: undefined|bigint;
|
||||
private static readonly MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x43, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMCTRACE
|
||||
}
|
||||
|
||||
|
||||
@@ -13,32 +13,65 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {ParserFactory} from "./parser_factory";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {TestUtils} from "test/test_utils";
|
||||
import {UnitTestUtils} from "test/unit/utils";
|
||||
|
||||
describe("ParserInputMethodManagerService", () => {
|
||||
let parser: Parser;
|
||||
describe("trace with elapsed + real timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
beforeAll(async () => {
|
||||
const buffer = TestUtils.getFixtureBlob("trace_InputMethodManagerService.pb");
|
||||
const parsers = await new ParserFactory().createParsers([buffer]);
|
||||
expect(parsers.length).toEqual(1);
|
||||
parser = parsers[0];
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/InputMethodManagerService.pb");
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_MANAGER_SERVICE);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED))
|
||||
.toEqual([new Timestamp(TimestampType.ELAPSED, 15963782518n)]);
|
||||
});
|
||||
|
||||
it("provides real timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.REAL))
|
||||
.toEqual([new Timestamp(TimestampType.REAL, 1659107090565549479n)]);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from elapsed timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 15963782518n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
|
||||
.toEqual(15963782518n);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from real timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.REAL, 1659107090565549479n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
|
||||
.toEqual(15963782518n);
|
||||
});
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceTypeId()).toEqual(TraceTypeId.INPUT_METHOD_MANAGER_SERVICE);
|
||||
});
|
||||
describe("trace with elapsed (only) timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
it("provides timestamps", () => {
|
||||
expect(parser.getTimestamps())
|
||||
.toEqual([1149226290110, 1149237707591, 1149238950389]);
|
||||
});
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/InputMethodManagerService.pb");
|
||||
});
|
||||
|
||||
it("retrieves trace entry", () => {
|
||||
expect(Number(parser.getTraceEntry(1149226290110)!.elapsedRealtimeNanos))
|
||||
.toEqual(1149226290110);
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_MANAGER_SERVICE);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED)![0])
|
||||
.toEqual(new Timestamp(TimestampType.ELAPSED, 1149226290110n));
|
||||
});
|
||||
|
||||
it("doesn't provide real timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.REAL))
|
||||
.toEqual(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,17 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {InputMethodManagerServiceTraceFileProto} from "./proto_types";
|
||||
|
||||
class ParserInputMethodManagerService extends Parser {
|
||||
constructor(trace: Blob) {
|
||||
super(trace);
|
||||
this.realToElapsedTimeOffsetNs = undefined;
|
||||
}
|
||||
|
||||
getTraceTypeId(): TraceTypeId {
|
||||
return TraceTypeId.INPUT_METHOD_MANAGER_SERVICE;
|
||||
getTraceType(): TraceType {
|
||||
return TraceType.INPUT_METHOD_MANAGER_SERVICE;
|
||||
}
|
||||
|
||||
override getMagicNumber(): number[] {
|
||||
@@ -31,17 +33,31 @@ class ParserInputMethodManagerService extends Parser {
|
||||
}
|
||||
|
||||
override decodeTrace(buffer: Uint8Array): any[] {
|
||||
return (<any>InputMethodManagerServiceTraceFileProto.decode(buffer)).entry;
|
||||
const decoded = <any>InputMethodManagerServiceTraceFileProto.decode(buffer);
|
||||
if (Object.prototype.hasOwnProperty.call(decoded, "realToElapsedTimeOffsetNanos")) {
|
||||
this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
|
||||
}
|
||||
else {
|
||||
this.realToElapsedTimeOffsetNs = undefined;
|
||||
}
|
||||
return decoded.entry;
|
||||
}
|
||||
|
||||
protected override getTimestamp(entryProto: any): number {
|
||||
return Number(entryProto.elapsedRealtimeNanos);
|
||||
protected override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
|
||||
if (type === TimestampType.ELAPSED) {
|
||||
return new Timestamp(TimestampType.ELAPSED, BigInt(entryProto.elapsedRealtimeNanos));
|
||||
}
|
||||
else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
|
||||
return new Timestamp(type, this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected override processDecodedEntry(entryProto: any): any {
|
||||
return entryProto;
|
||||
}
|
||||
|
||||
private realToElapsedTimeOffsetNs: undefined|bigint;
|
||||
private static readonly MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x4d, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMMTRACE
|
||||
}
|
||||
|
||||
|
||||
@@ -13,32 +13,72 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {ParserFactory} from "./parser_factory";
|
||||
import {Parser} from "./parser";
|
||||
import {TestUtils} from "test/test_utils";
|
||||
import {UnitTestUtils} from "test/unit/utils";
|
||||
|
||||
describe("ParserInputMethodService", () => {
|
||||
let parser: Parser;
|
||||
describe("trace with elapsed + real timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
beforeAll(async () => {
|
||||
const buffer = TestUtils.getFixtureBlob("trace_InputMethodService.pb");
|
||||
const parsers = await new ParserFactory().createParsers([buffer]);
|
||||
expect(parsers.length).toEqual(1);
|
||||
parser = parsers[0];
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/InputMethodService.pb");
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_SERVICE);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamps", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 16578752896n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("provides real timestamps", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.REAL, 1659107091180519857n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.REAL))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from elapsed timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 16578752896n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
|
||||
.toEqual(16578752896n);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from elapsed timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.REAL, 1659107091180519857n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
|
||||
.toEqual(16578752896n);
|
||||
});
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceTypeId()).toEqual(TraceTypeId.INPUT_METHOD_SERVICE);
|
||||
});
|
||||
describe("trace with elapsed (only) timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
it("provides timestamps", () => {
|
||||
expect(parser.getTimestamps())
|
||||
.toEqual([1149230019887, 1149234359324, 1149241227244, 1149243083608, 1149249518016, 1149249784617, 1149272993520]);
|
||||
});
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/InputMethodService.pb");
|
||||
});
|
||||
|
||||
it("retrieves trace entry", () => {
|
||||
expect(Number(parser.getTraceEntry(1149230019887)!.elapsedRealtimeNanos))
|
||||
.toEqual(1149230019887);
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_SERVICE);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED)![0])
|
||||
.toEqual(new Timestamp(TimestampType.ELAPSED, 1149230019887n));
|
||||
});
|
||||
|
||||
it("doesn't provide real timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.REAL))
|
||||
.toEqual(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,17 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {InputMethodServiceTraceFileProto} from "./proto_types";
|
||||
|
||||
class ParserInputMethodService extends Parser {
|
||||
constructor(trace: Blob) {
|
||||
super(trace);
|
||||
this.realToElapsedTimeOffsetNs = undefined;
|
||||
}
|
||||
|
||||
getTraceTypeId(): TraceTypeId {
|
||||
return TraceTypeId.INPUT_METHOD_SERVICE;
|
||||
getTraceType(): TraceType {
|
||||
return TraceType.INPUT_METHOD_SERVICE;
|
||||
}
|
||||
|
||||
override getMagicNumber(): number[] {
|
||||
@@ -31,17 +33,31 @@ class ParserInputMethodService extends Parser {
|
||||
}
|
||||
|
||||
override decodeTrace(buffer: Uint8Array): any[] {
|
||||
return (<any>InputMethodServiceTraceFileProto.decode(buffer)).entry;
|
||||
const decoded = <any>InputMethodServiceTraceFileProto.decode(buffer);
|
||||
if (Object.prototype.hasOwnProperty.call(decoded, "realToElapsedTimeOffsetNanos")) {
|
||||
this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
|
||||
}
|
||||
else {
|
||||
this.realToElapsedTimeOffsetNs = undefined;
|
||||
}
|
||||
return decoded.entry;
|
||||
}
|
||||
|
||||
override getTimestamp(entryProto: any): number {
|
||||
return Number(entryProto.elapsedRealtimeNanos);
|
||||
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
|
||||
if (type === TimestampType.ELAPSED) {
|
||||
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
|
||||
}
|
||||
else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
|
||||
return new Timestamp(type, this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
override processDecodedEntry(entryProto: any): any {
|
||||
return entryProto;
|
||||
}
|
||||
|
||||
private realToElapsedTimeOffsetNs: undefined|bigint;
|
||||
private static readonly MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMSTRACE
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {ParserFactory} from "./parser_factory";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {TestUtils} from "test/test_utils";
|
||||
import {UnitTestUtils} from "test/unit/utils";
|
||||
import {LogMessage} from "../common/trace/protolog";
|
||||
|
||||
describe("ParserProtoLog", () => {
|
||||
@@ -32,26 +32,44 @@ describe("ParserProtoLog", () => {
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const buffer = TestUtils.getFixtureBlob("trace_ProtoLog.pb");
|
||||
const parsers = await new ParserFactory().createParsers([buffer]);
|
||||
expect(parsers.length).toEqual(1);
|
||||
parser = parsers[0];
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/ProtoLog.pb");
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceTypeId()).toEqual(TraceTypeId.PROTO_LOG);
|
||||
expect(parser.getTraceType()).toEqual(TraceType.PROTO_LOG);
|
||||
});
|
||||
|
||||
it("provides timestamps", () => {
|
||||
const timestamps = parser.getTimestamps();
|
||||
it("provides elapsed timestamps", () => {
|
||||
const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
|
||||
expect(timestamps.length)
|
||||
.toEqual(50);
|
||||
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 850746266486n),
|
||||
new Timestamp(TimestampType.ELAPSED, 850746336718n),
|
||||
new Timestamp(TimestampType.ELAPSED, 850746350430n),
|
||||
];
|
||||
expect(timestamps.slice(0, 3))
|
||||
.toEqual([850746266486, 850746336718, 850746350430]);
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("provides real timestamps", () => {
|
||||
const timestamps = parser.getTimestamps(TimestampType.REAL)!;
|
||||
expect(timestamps.length)
|
||||
.toEqual(50);
|
||||
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.REAL, 1655727125377266486n),
|
||||
new Timestamp(TimestampType.REAL, 1655727125377336718n),
|
||||
new Timestamp(TimestampType.REAL, 1655727125377350430n),
|
||||
];
|
||||
expect(timestamps.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("reconstructs human-readable log message", () => {
|
||||
const actualMessage = parser.getTraceEntry(850746266486)!;
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 850746266486n);
|
||||
const actualMessage = parser.getTraceEntry(timestamp)!;
|
||||
|
||||
expect(actualMessage).toBeInstanceOf(LogMessage);
|
||||
expect(Object.assign({}, actualMessage)).toEqual(expectedFirstLogMessage);
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {LogMessage, FormattedLogMessage, UnformattedLogMessage} from "common/trace/protolog";
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {FormattedLogMessage, LogMessage, UnformattedLogMessage} from "common/trace/protolog";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {ProtoLogFileProto} from "./proto_types";
|
||||
import configJson from "../../../../../frameworks/base/data/etc/services.core.protolog.json";
|
||||
@@ -24,8 +25,8 @@ class ParserProtoLog extends Parser {
|
||||
super(trace);
|
||||
}
|
||||
|
||||
override getTraceTypeId(): TraceTypeId {
|
||||
return TraceTypeId.PROTO_LOG;
|
||||
override getTraceType(): TraceType {
|
||||
return TraceType.PROTO_LOG;
|
||||
}
|
||||
|
||||
override getMagicNumber(): number[] {
|
||||
@@ -47,6 +48,8 @@ class ParserProtoLog extends Parser {
|
||||
throw new TypeError(message);
|
||||
}
|
||||
|
||||
this.realToElapsedTimeOffsetNs = BigInt(fileProto.realTimeToElapsedTimeOffsetMillis) * 1000000n;
|
||||
|
||||
fileProto.log.sort((a: any, b: any) => {
|
||||
return Number(a.elapsedRealtimeNanos) - Number(b.elapsedRealtimeNanos);
|
||||
});
|
||||
@@ -54,8 +57,14 @@ class ParserProtoLog extends Parser {
|
||||
return fileProto.log;
|
||||
}
|
||||
|
||||
override getTimestamp(entryProto: any): number {
|
||||
return Number(entryProto.elapsedRealtimeNanos);
|
||||
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
|
||||
if (type == TimestampType.ELAPSED) {
|
||||
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
|
||||
}
|
||||
else if (type == TimestampType.REAL) {
|
||||
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs!);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
override processDecodedEntry(entryProto: any): LogMessage {
|
||||
@@ -81,6 +90,7 @@ class ParserProtoLog extends Parser {
|
||||
});
|
||||
}
|
||||
|
||||
private realToElapsedTimeOffsetNs: undefined|bigint = undefined;
|
||||
private static readonly MAGIC_NUMBER = [0x09, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47]; // .PROTOLOG
|
||||
private static readonly PROTOLOG_VERSION = "1.0.0";
|
||||
}
|
||||
|
||||
@@ -14,49 +14,81 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {ScreenRecordingTraceEntry} from "common/trace/screen_recording";
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {TestUtils} from "test/test_utils";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {UnitTestUtils} from "test/unit/utils";
|
||||
import {Parser} from "./parser";
|
||||
import {ParserFactory} from "./parser_factory";
|
||||
|
||||
describe("ParserScreenRecording", () => {
|
||||
let parser: Parser;
|
||||
|
||||
beforeAll(async () => {
|
||||
const buffer = TestUtils.getFixtureBlob("screen_recording.mp4");
|
||||
const parsers = await new ParserFactory().createParsers([buffer]);
|
||||
expect(parsers.length).toEqual(1);
|
||||
parser = parsers[0];
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/screen_recording.mp4");
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceTypeId()).toEqual(TraceTypeId.SCREEN_RECORDING);
|
||||
expect(parser.getTraceType()).toEqual(TraceType.SCREEN_RECORDING);
|
||||
});
|
||||
|
||||
it("provides timestamps", () => {
|
||||
const timestamps = parser.getTimestamps();
|
||||
it ("provides elapsed timestamps", () => {
|
||||
const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
|
||||
|
||||
expect(timestamps.length)
|
||||
.toEqual(85);
|
||||
.toEqual(15);
|
||||
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 144857685000n),
|
||||
new Timestamp(TimestampType.ELAPSED, 144866679000n),
|
||||
new Timestamp(TimestampType.ELAPSED, 144875772000n),
|
||||
];
|
||||
expect(timestamps.slice(0, 3))
|
||||
.toEqual([19446131807000, 19446158500000, 19446167117000]);
|
||||
|
||||
expect(timestamps.slice(timestamps.length-3, timestamps.length))
|
||||
.toEqual([19448470076000, 19448487525000, 19448501007000]);
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("retrieves trace entry", () => {
|
||||
it("provides real timestamps", () => {
|
||||
const timestamps = parser.getTimestamps(TimestampType.REAL)!;
|
||||
|
||||
expect(timestamps.length)
|
||||
.toEqual(15);
|
||||
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.REAL, 1659687791485257266n),
|
||||
new Timestamp(TimestampType.REAL, 1659687791494251266n),
|
||||
new Timestamp(TimestampType.REAL, 1659687791503344266n),
|
||||
];
|
||||
expect(timestamps.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from elapsed timestamp", () => {
|
||||
{
|
||||
const entry = parser.getTraceEntry(19446131807000)!;
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 144857685000n);
|
||||
const entry = parser.getTraceEntry(timestamp)!;
|
||||
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
|
||||
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0);
|
||||
}
|
||||
|
||||
{
|
||||
const entry = parser.getTraceEntry(19448501007000)!;
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 145300550000n);
|
||||
const entry = parser.getTraceEntry(timestamp)!;
|
||||
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
|
||||
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(2.37, 0.001);
|
||||
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0.442, 0.001);
|
||||
}
|
||||
});
|
||||
|
||||
it("retrieves trace entry from real timestamp", () => {
|
||||
{
|
||||
const timestamp = new Timestamp(TimestampType.REAL, 1659687791485257266n);
|
||||
const entry = parser.getTraceEntry(timestamp)!;
|
||||
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
|
||||
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0);
|
||||
}
|
||||
|
||||
{
|
||||
const timestamp = new Timestamp(TimestampType.REAL, 1659687791928122266n);
|
||||
const entry = parser.getTraceEntry(timestamp)!;
|
||||
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
|
||||
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0.322, 0.001);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,38 +13,72 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {ArrayUtils} from "common/utils/array_utils";
|
||||
import {Parser} from "./parser";
|
||||
import {ScreenRecordingTraceEntry} from "common/trace/screen_recording";
|
||||
|
||||
class ScreenRecordingMetadataEntry {
|
||||
constructor(public timestampMonotonicNs: bigint, public timestampRealtimeNs: bigint) {
|
||||
}
|
||||
}
|
||||
|
||||
class ParserScreenRecording extends Parser {
|
||||
constructor(trace: Blob) {
|
||||
super(trace);
|
||||
}
|
||||
|
||||
override getTraceTypeId(): TraceTypeId {
|
||||
return TraceTypeId.SCREEN_RECORDING;
|
||||
override getTraceType(): TraceType {
|
||||
return TraceType.SCREEN_RECORDING;
|
||||
}
|
||||
|
||||
override getMagicNumber(): number[] {
|
||||
return ParserScreenRecording.MPEG4_MAGIC_NMBER;
|
||||
}
|
||||
|
||||
override decodeTrace(videoData: Uint8Array): number[] {
|
||||
const posCount = this.searchMagicString(videoData);
|
||||
const [posTimestamps, count] = this.parseTimestampsCount(videoData, posCount);
|
||||
return this.parseTimestamps(videoData, posTimestamps, count);
|
||||
override decodeTrace(videoData: Uint8Array): ScreenRecordingMetadataEntry[] {
|
||||
const posVersion = this.searchMagicString(videoData);
|
||||
const [posTimeOffset, metadataVersion] = this.parseMetadataVersion(videoData, posVersion);
|
||||
if (metadataVersion !== 1) {
|
||||
throw TypeError(`Metadata version "${metadataVersion}" not supported`);
|
||||
}
|
||||
const [posCount, timeOffsetNs] = this.parseRealToMonotonicTimeOffsetNs(videoData, posTimeOffset);
|
||||
const [posTimestamps, count] = this.parseFramesCount(videoData, posCount);
|
||||
const timestampsMonotonicNs = this.parseTimestampsMonotonicNs(videoData, posTimestamps, count);
|
||||
|
||||
return timestampsMonotonicNs.map((timestampMonotonicNs: bigint) => {
|
||||
return new ScreenRecordingMetadataEntry(timestampMonotonicNs, timestampMonotonicNs + timeOffsetNs);
|
||||
});
|
||||
}
|
||||
|
||||
override getTimestamp(decodedEntry: number): number {
|
||||
return decodedEntry;
|
||||
override getTimestamp(type: TimestampType, decodedEntry: ScreenRecordingMetadataEntry): undefined|Timestamp {
|
||||
if (type !== TimestampType.ELAPSED && type !== TimestampType.REAL) {
|
||||
return undefined;
|
||||
}
|
||||
if (type === TimestampType.ELAPSED) {
|
||||
// Traces typically contain "elapsed" timestamps (SYSTEM_TIME_BOOTTIME),
|
||||
// whereas screen recordings contain SYSTEM_TIME_MONOTONIC timestamps.
|
||||
//
|
||||
// Here we are pretending that screen recordings contain "elapsed" timestamps
|
||||
// as well, in order to synchronize with the other traces.
|
||||
//
|
||||
// If no device suspensions are involved, SYSTEM_TIME_MONOTONIC should indeed
|
||||
// correspond to SYSTEM_TIME_BOOTTIME and things will work as expected.
|
||||
return new Timestamp(type, decodedEntry.timestampMonotonicNs);
|
||||
}
|
||||
else if (type === TimestampType.REAL) {
|
||||
return new Timestamp(type, decodedEntry.timestampRealtimeNs);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
override processDecodedEntry(timestamp: number): ScreenRecordingTraceEntry {
|
||||
const videoTimeSeconds = (timestamp - this.timestamps[0]) / 1000000000 + ParserScreenRecording.EPSILON;
|
||||
override processDecodedEntry(entry: ScreenRecordingMetadataEntry): ScreenRecordingTraceEntry {
|
||||
const initialTimestampNs = this.getTimestamps(TimestampType.ELAPSED)![0].getValueNs();
|
||||
const currentTimestampNs = entry.timestampMonotonicNs;
|
||||
const videoTimeSeconds = Number(currentTimestampNs - initialTimestampNs) / 1000000000;
|
||||
const videoData = this.trace;
|
||||
return new ScreenRecordingTraceEntry(timestamp, videoTimeSeconds, videoData);
|
||||
return new ScreenRecordingTraceEntry(videoTimeSeconds, videoData);
|
||||
}
|
||||
|
||||
private searchMagicString(videoData: Uint8Array): number {
|
||||
@@ -56,22 +90,40 @@ class ParserScreenRecording extends Parser {
|
||||
return pos;
|
||||
}
|
||||
|
||||
private parseTimestampsCount(videoData: Uint8Array, pos: number) : [number, number] {
|
||||
if (pos + 4 >= videoData.length) {
|
||||
throw new TypeError("video data is too short. Expected timestamps count doesn't fit");
|
||||
private parseMetadataVersion(videoData: Uint8Array, pos: number) : [number, number] {
|
||||
if (pos + 4 > videoData.length) {
|
||||
throw new TypeError("Failed to parse metadata version. Video data is too short.");
|
||||
}
|
||||
const timestampsCount = ArrayUtils.toUintLittleEndian(videoData, pos, pos+4);
|
||||
const version = Number(ArrayUtils.toUintLittleEndian(videoData, pos, pos+4));
|
||||
pos += 4;
|
||||
return [pos, timestampsCount];
|
||||
return [pos, version];
|
||||
}
|
||||
|
||||
private parseTimestamps(videoData: Uint8Array, pos: number, count: number): number[] {
|
||||
if (pos + count * 8 >= videoData.length) {
|
||||
throw new TypeError("video data is too short. Expected timestamps do not fit");
|
||||
private parseRealToMonotonicTimeOffsetNs(videoData: Uint8Array, pos: number) : [number, bigint] {
|
||||
if (pos + 8 > videoData.length) {
|
||||
throw new TypeError("Failed to parse realtime-to-monotonic time offset. Video data is too short.");
|
||||
}
|
||||
const timestamps: number[] = [];
|
||||
const offset = ArrayUtils.toIntLittleEndian(videoData, pos, pos+8);
|
||||
pos += 8;
|
||||
return [pos, offset];
|
||||
}
|
||||
|
||||
private parseFramesCount(videoData: Uint8Array, pos: number) : [number, number] {
|
||||
if (pos + 4 > videoData.length) {
|
||||
throw new TypeError("Failed to parse frames count. Video data is too short.");
|
||||
}
|
||||
const count = Number(ArrayUtils.toUintLittleEndian(videoData, pos, pos+4));
|
||||
pos += 4;
|
||||
return [pos, count];
|
||||
}
|
||||
|
||||
private parseTimestampsMonotonicNs(videoData: Uint8Array, pos: number, count: number) : bigint[] {
|
||||
if (pos + count * 16 > videoData.length) {
|
||||
throw new TypeError("Failed to parse monotonic timestamps. Video data is too short.");
|
||||
}
|
||||
const timestamps: bigint[] = [];
|
||||
for (let i = 0; i < count; ++i) {
|
||||
const timestamp = ArrayUtils.toUintLittleEndian(videoData, pos, pos+8) * 1000;
|
||||
const timestamp = ArrayUtils.toUintLittleEndian(videoData, pos, pos+8);
|
||||
pos += 8;
|
||||
timestamps.push(timestamp);
|
||||
}
|
||||
@@ -79,8 +131,7 @@ class ParserScreenRecording extends Parser {
|
||||
}
|
||||
|
||||
private static readonly MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32]; // ....ftypmp42
|
||||
private static readonly WINSCOPE_META_MAGIC_STRING = [0x23, 0x56, 0x56, 0x31, 0x4e, 0x53, 0x43, 0x30, 0x50, 0x45, 0x54, 0x31, 0x4d, 0x45, 0x21, 0x23]; // #VV1NSC0PET1ME!#
|
||||
private static readonly EPSILON = 0.00001;
|
||||
private static readonly WINSCOPE_META_MAGIC_STRING = [0x23, 0x56, 0x56, 0x31, 0x4e, 0x53, 0x43, 0x30, 0x50, 0x45, 0x54, 0x31, 0x4d, 0x45, 0x32, 0x23]; // #VV1NSC0PET1ME2#
|
||||
}
|
||||
|
||||
export {ParserScreenRecording};
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {ScreenRecordingTraceEntry} from "common/trace/screen_recording";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {UnitTestUtils} from "test/unit/utils";
|
||||
import {Parser} from "./parser";
|
||||
|
||||
describe("ParserScreenRecordingLegacy", () => {
|
||||
let parser: Parser;
|
||||
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/screen_recording.mp4");
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.SCREEN_RECORDING);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamps", () => {
|
||||
const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
|
||||
|
||||
expect(timestamps.length)
|
||||
.toEqual(85);
|
||||
|
||||
let expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 19446131807000n),
|
||||
new Timestamp(TimestampType.ELAPSED, 19446158500000n),
|
||||
new Timestamp(TimestampType.ELAPSED, 19446167117000n),
|
||||
];
|
||||
expect(timestamps.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
|
||||
expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 19448470076000n),
|
||||
new Timestamp(TimestampType.ELAPSED, 19448487525000n),
|
||||
new Timestamp(TimestampType.ELAPSED, 19448501007000n),
|
||||
];
|
||||
expect(timestamps.slice(timestamps.length-3, timestamps.length))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("doesn't provide real timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.REAL))
|
||||
.toEqual(undefined);
|
||||
});
|
||||
|
||||
it("retrieves trace entry", () => {
|
||||
{
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 19446131807000n);
|
||||
const entry = parser.getTraceEntry(timestamp)!;
|
||||
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
|
||||
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0);
|
||||
}
|
||||
|
||||
{
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 19448501007000n);
|
||||
const entry = parser.getTraceEntry(timestamp)!;
|
||||
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
|
||||
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(2.37, 0.001);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {ArrayUtils} from "common/utils/array_utils";
|
||||
import {Parser} from "./parser";
|
||||
import {ScreenRecordingTraceEntry} from "common/trace/screen_recording";
|
||||
|
||||
class ParserScreenRecordingLegacy extends Parser {
|
||||
constructor(trace: Blob) {
|
||||
super(trace);
|
||||
}
|
||||
|
||||
override getTraceType(): TraceType {
|
||||
return TraceType.SCREEN_RECORDING;
|
||||
}
|
||||
|
||||
override getMagicNumber(): number[] {
|
||||
return ParserScreenRecordingLegacy.MPEG4_MAGIC_NMBER;
|
||||
}
|
||||
|
||||
override decodeTrace(videoData: Uint8Array): Timestamp[] {
|
||||
const posCount = this.searchMagicString(videoData);
|
||||
const [posTimestamps, count] = this.parseFramesCount(videoData, posCount);
|
||||
return this.parseTimestamps(videoData, posTimestamps, count);
|
||||
}
|
||||
|
||||
override getTimestamp(type: TimestampType, decodedEntry: Timestamp): undefined|Timestamp {
|
||||
if (type !== TimestampType.ELAPSED) {
|
||||
return undefined;
|
||||
}
|
||||
return decodedEntry;
|
||||
}
|
||||
|
||||
override processDecodedEntry(entry: Timestamp): ScreenRecordingTraceEntry {
|
||||
const currentTimestamp = entry;
|
||||
const initialTimestamp = this.getTimestamps(TimestampType.ELAPSED)![0];
|
||||
const videoTimeSeconds =
|
||||
Number(currentTimestamp.getValueNs() - initialTimestamp.getValueNs()) / 1000000000
|
||||
+ ParserScreenRecordingLegacy.EPSILON;
|
||||
const videoData = this.trace;
|
||||
return new ScreenRecordingTraceEntry(videoTimeSeconds, videoData);
|
||||
}
|
||||
|
||||
private searchMagicString(videoData: Uint8Array): number {
|
||||
let pos = ArrayUtils.searchSubarray(videoData, ParserScreenRecordingLegacy.WINSCOPE_META_MAGIC_STRING);
|
||||
if (pos === undefined) {
|
||||
throw new TypeError("video data doesn't contain winscope magic string");
|
||||
}
|
||||
pos += ParserScreenRecordingLegacy.WINSCOPE_META_MAGIC_STRING.length;
|
||||
return pos;
|
||||
}
|
||||
|
||||
private parseFramesCount(videoData: Uint8Array, pos: number) : [number, number] {
|
||||
if (pos + 4 > videoData.length) {
|
||||
throw new TypeError("Failed to parse frames count. Video data is too short.");
|
||||
}
|
||||
const framesCount = Number(ArrayUtils.toUintLittleEndian(videoData, pos, pos+4));
|
||||
pos += 4;
|
||||
return [pos, framesCount];
|
||||
}
|
||||
|
||||
private parseTimestamps(videoData: Uint8Array, pos: number, count: number): Timestamp[] {
|
||||
if (pos + count * 8 > videoData.length) {
|
||||
throw new TypeError("Failed to parse timestamps. Video data is too short.");
|
||||
}
|
||||
const timestamps: Timestamp[] = [];
|
||||
for (let i = 0; i < count; ++i) {
|
||||
const value = ArrayUtils.toUintLittleEndian(videoData, pos, pos+8) * 1000n;
|
||||
pos += 8;
|
||||
timestamps.push(new Timestamp(TimestampType.ELAPSED, value));
|
||||
}
|
||||
return timestamps;
|
||||
}
|
||||
|
||||
private static readonly MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32]; // ....ftypmp42
|
||||
private static readonly WINSCOPE_META_MAGIC_STRING = [0x23, 0x56, 0x56, 0x31, 0x4e, 0x53, 0x43, 0x30, 0x50, 0x45, 0x54, 0x31, 0x4d, 0x45, 0x21, 0x23]; // #VV1NSC0PET1ME!#
|
||||
private static readonly EPSILON = 0.00001;
|
||||
}
|
||||
|
||||
export {ParserScreenRecordingLegacy};
|
||||
@@ -13,34 +13,78 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {LayerTraceEntry} from "common/trace/flickerlib/layers/LayerTraceEntry";
|
||||
import {TestUtils} from "test/test_utils";
|
||||
import {UnitTestUtils} from "test/unit/utils";
|
||||
import {Parser} from "./parser";
|
||||
import {ParserFactory} from "./parser_factory";
|
||||
|
||||
describe("ParserSurfaceFlinger", () => {
|
||||
let parser: Parser;
|
||||
describe("trace with elapsed + real timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
beforeAll(async () => {
|
||||
const buffer = TestUtils.getFixtureBlob("trace_SurfaceFlinger.pb");
|
||||
const parsers = await new ParserFactory().createParsers([buffer]);
|
||||
expect(parsers.length).toEqual(1);
|
||||
parser = parsers[0];
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/SurfaceFlinger.pb");
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.SURFACE_FLINGER);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamps", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 14500282843n),
|
||||
new Timestamp(TimestampType.ELAPSED, 14631249355n),
|
||||
new Timestamp(TimestampType.ELAPSED, 15403446377n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED)!.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("provides real timestamps", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.REAL, 1659107089102062832n),
|
||||
new Timestamp(TimestampType.REAL, 1659107089233029344n),
|
||||
new Timestamp(TimestampType.REAL, 1659107090005226366n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from elapsed timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 14631249355n);
|
||||
const entry = parser.getTraceEntry(timestamp)!;
|
||||
expect(entry).toBeInstanceOf(LayerTraceEntry);
|
||||
expect(BigInt(entry.timestampMs)).toEqual(14631249355n);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from elapsed timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.REAL, 1659107089233029344n);
|
||||
const entry = parser.getTraceEntry(timestamp)!;
|
||||
expect(entry).toBeInstanceOf(LayerTraceEntry);
|
||||
expect(BigInt(entry.timestampMs)).toEqual(14631249355n);
|
||||
});
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceTypeId()).toEqual(TraceTypeId.SURFACE_FLINGER);
|
||||
});
|
||||
describe("trace with elapsed (only) timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
it("provides timestamps", () => {
|
||||
expect(parser.getTimestamps())
|
||||
.toEqual([850335483446, 850686322883, 850736507697]);
|
||||
});
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/SurfaceFlinger.pb");
|
||||
});
|
||||
|
||||
it("retrieves trace entry", () => {
|
||||
const entry = parser.getTraceEntry(850335483446)!;
|
||||
expect(entry).toBeInstanceOf(LayerTraceEntry);
|
||||
expect(Number(entry.timestampMs)).toEqual(850335483446);
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.SURFACE_FLINGER);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED)![0])
|
||||
.toEqual(new Timestamp(TimestampType.ELAPSED, 850335483446n));
|
||||
});
|
||||
|
||||
it("doesn't provide real timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.REAL))
|
||||
.toEqual(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,18 +13,20 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {LayerTraceEntry} from "common/trace/flickerlib/layers/LayerTraceEntry";
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {LayersTraceFileProto} from "./proto_types";
|
||||
|
||||
class ParserSurfaceFlinger extends Parser {
|
||||
constructor(trace: Blob) {
|
||||
super(trace);
|
||||
this.realToElapsedTimeOffsetNs = undefined;
|
||||
}
|
||||
|
||||
override getTraceTypeId(): TraceTypeId {
|
||||
return TraceTypeId.SURFACE_FLINGER;
|
||||
override getTraceType(): TraceType {
|
||||
return TraceType.SURFACE_FLINGER;
|
||||
}
|
||||
|
||||
override getMagicNumber(): number[] {
|
||||
@@ -32,17 +34,36 @@ class ParserSurfaceFlinger extends Parser {
|
||||
}
|
||||
|
||||
override decodeTrace(buffer: Uint8Array): any[] {
|
||||
return (<any>LayersTraceFileProto.decode(buffer)).entry;
|
||||
const decoded = <any>LayersTraceFileProto.decode(buffer);
|
||||
if (Object.prototype.hasOwnProperty.call(decoded, "realToElapsedTimeOffsetNanos")) {
|
||||
this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
|
||||
}
|
||||
else {
|
||||
this.realToElapsedTimeOffsetNs = undefined;
|
||||
}
|
||||
return decoded.entry;
|
||||
}
|
||||
|
||||
override getTimestamp(entryProto: any): number {
|
||||
return Number(entryProto.elapsedRealtimeNanos);
|
||||
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
|
||||
const isDump = !Object.prototype.hasOwnProperty.call(entryProto, "elapsedRealtimeNanos");
|
||||
if (type === TimestampType.ELAPSED) {
|
||||
return isDump
|
||||
? new Timestamp(type, 0n)
|
||||
: new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
|
||||
}
|
||||
else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
|
||||
return isDump
|
||||
? new Timestamp(type, 0n)
|
||||
: new Timestamp(type, this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
override processDecodedEntry(entryProto: any): any {
|
||||
return LayerTraceEntry.fromProto(entryProto.layers.layers, entryProto.displays, entryProto.elapsedRealtimeNanos, entryProto.hwcBlob);
|
||||
}
|
||||
|
||||
private realToElapsedTimeOffsetNs: undefined|bigint;
|
||||
private static readonly MAGIC_NUMBER = [0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45]; // .LYRTRACE
|
||||
}
|
||||
|
||||
|
||||
@@ -13,33 +13,73 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {LayerTraceEntry} from "common/trace/flickerlib/layers/LayerTraceEntry";
|
||||
import {TestUtils} from "test/test_utils";
|
||||
import {UnitTestUtils} from "test/unit/utils";
|
||||
import {Parser} from "./parser";
|
||||
import {ParserFactory} from "./parser_factory";
|
||||
|
||||
describe("ParserSurfaceFlingerDump", () => {
|
||||
let parser: Parser;
|
||||
describe("trace with elapsed + real timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
beforeAll(async () => {
|
||||
const buffer = TestUtils.getFixtureBlob("dump_SurfaceFlinger.pb");
|
||||
const parsers = await new ParserFactory().createParsers([buffer]);
|
||||
expect(parsers.length).toEqual(1);
|
||||
parser = parsers[0];
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/dump_SurfaceFlinger.pb");
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.SURFACE_FLINGER);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamp", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 0n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
|
||||
});
|
||||
|
||||
it("provides real timestamp (always zero)", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.REAL, 0n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.REAL)).toEqual(expected);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from elapsed timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 0n);
|
||||
const entry = parser.getTraceEntry(timestamp)!;
|
||||
expect(entry).toBeInstanceOf(LayerTraceEntry);
|
||||
expect(BigInt(entry.timestampMs)).toEqual(0n);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from elapsed timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.REAL, 0n);
|
||||
const entry = parser.getTraceEntry(timestamp)!;
|
||||
expect(entry).toBeInstanceOf(LayerTraceEntry);
|
||||
expect(BigInt(entry.timestampMs)).toEqual(0n);
|
||||
});
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceTypeId()).toEqual(TraceTypeId.SURFACE_FLINGER);
|
||||
});
|
||||
describe("trace with elapsed (only) timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
it("provides timestamp", () => {
|
||||
expect(parser.getTimestamps()).toEqual([0]);
|
||||
});
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/dump_SurfaceFlinger.pb");
|
||||
});
|
||||
|
||||
it("retrieves trace entry", () => {
|
||||
const entry = parser.getTraceEntry(0)!;
|
||||
expect(entry).toBeInstanceOf(LayerTraceEntry);
|
||||
expect(Number(entry.timestampMs)).toEqual(0);
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.SURFACE_FLINGER);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamp (always zero)", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 0n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
|
||||
});
|
||||
|
||||
it("doesn't provide real timestamp", () => {
|
||||
expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,35 +13,95 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {ParserFactory} from "./parser_factory";
|
||||
import {TestUtils} from "test/test_utils";
|
||||
import {UnitTestUtils} from "test/unit/utils";
|
||||
|
||||
describe("ParserTransactions", () => {
|
||||
let parser: Parser;
|
||||
describe("trace with elapsed + real timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
beforeAll(async () => {
|
||||
const buffer = TestUtils.getFixtureBlob("trace_Transactions.pb");
|
||||
const parsers = await new ParserFactory().createParsers([buffer]);
|
||||
expect(parsers.length).toEqual(1);
|
||||
parser = parsers[0];
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/Transactions.pb");
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.TRANSACTIONS);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamps", () => {
|
||||
const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
|
||||
|
||||
expect(timestamps.length)
|
||||
.toEqual(712);
|
||||
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 2450981445n),
|
||||
new Timestamp(TimestampType.ELAPSED, 2517952515n),
|
||||
new Timestamp(TimestampType.ELAPSED, 4021151449n),
|
||||
];
|
||||
expect(timestamps.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("provides real timestamps", () => {
|
||||
const timestamps = parser.getTimestamps(TimestampType.REAL)!;
|
||||
|
||||
expect(timestamps.length)
|
||||
.toEqual(712);
|
||||
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.REAL, 1659507541051480997n),
|
||||
new Timestamp(TimestampType.REAL, 1659507541118452067n),
|
||||
new Timestamp(TimestampType.REAL, 1659507542621651001n),
|
||||
];
|
||||
expect(timestamps.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from elsapsed timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 2517952515n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
|
||||
.toEqual(2517952515n);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from real timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.REAL, 1659507541118452067n);
|
||||
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
|
||||
.toEqual(2517952515n);
|
||||
});
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceTypeId()).toEqual(TraceTypeId.TRANSACTIONS);
|
||||
});
|
||||
describe("trace with elapsed (only) timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
it("provides timestamps", () => {
|
||||
const timestamps = parser.getTimestamps();
|
||||
expect(timestamps.length)
|
||||
.toEqual(4997);
|
||||
expect(timestamps.slice(0, 3))
|
||||
.toEqual([14862317023, 14873423549, 14884850511]);
|
||||
});
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/Transactions.pb");
|
||||
});
|
||||
|
||||
it("retrieves trace entry", () => {
|
||||
expect(Number(parser.getTraceEntry(14862317023)!.elapsedRealtimeNanos))
|
||||
.toEqual(14862317023);
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.TRANSACTIONS);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamps", () => {
|
||||
const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
|
||||
|
||||
expect(timestamps.length)
|
||||
.toEqual(4997);
|
||||
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 14862317023n),
|
||||
new Timestamp(TimestampType.ELAPSED, 14873423549n),
|
||||
new Timestamp(TimestampType.ELAPSED, 14884850511n),
|
||||
];
|
||||
expect(timestamps.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("doesn't provide real timestamps", () => {
|
||||
expect(parser.getTimestamps(TimestampType.REAL))
|
||||
.toEqual(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,17 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {TransactionsTraceFileProto} from "./proto_types";
|
||||
import {AccessibilityTraceFileProto, TransactionsTraceFileProto} from "./proto_types";
|
||||
|
||||
class ParserTransactions extends Parser {
|
||||
constructor(trace: Blob) {
|
||||
super(trace);
|
||||
this.realToElapsedTimeOffsetNs = undefined;
|
||||
}
|
||||
|
||||
override getTraceTypeId(): TraceTypeId {
|
||||
return TraceTypeId.TRANSACTIONS;
|
||||
override getTraceType(): TraceType {
|
||||
return TraceType.TRANSACTIONS;
|
||||
}
|
||||
|
||||
override getMagicNumber(): number[] {
|
||||
@@ -31,17 +33,31 @@ class ParserTransactions extends Parser {
|
||||
}
|
||||
|
||||
override decodeTrace(buffer: Uint8Array): any[] {
|
||||
return (<any>TransactionsTraceFileProto.decode(buffer)).entry;
|
||||
const decoded = <any>TransactionsTraceFileProto.decode(buffer);
|
||||
if (Object.prototype.hasOwnProperty.call(decoded, "realToElapsedTimeOffsetNanos")) {
|
||||
this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
|
||||
}
|
||||
else {
|
||||
this.realToElapsedTimeOffsetNs = undefined;
|
||||
}
|
||||
return decoded.entry;
|
||||
}
|
||||
|
||||
override getTimestamp(entryProto: any): number {
|
||||
return Number(entryProto.elapsedRealtimeNanos);
|
||||
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
|
||||
if (type === TimestampType.ELAPSED) {
|
||||
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
|
||||
}
|
||||
else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
|
||||
return new Timestamp(type, this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
override processDecodedEntry(entryProto: any): any {
|
||||
return entryProto;
|
||||
}
|
||||
|
||||
private realToElapsedTimeOffsetNs: undefined|bigint;
|
||||
private static readonly MAGIC_NUMBER = [0x09, 0x54, 0x4e, 0x58, 0x54, 0x52, 0x41, 0x43, 0x45]; // .TNXTRACE
|
||||
}
|
||||
|
||||
|
||||
@@ -14,33 +14,84 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {WindowManagerState} from "common/trace/flickerlib/windows/WindowManagerState";
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {ParserFactory} from "./parser_factory";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {TestUtils} from "test/test_utils";
|
||||
import {UnitTestUtils} from "test/unit/utils";
|
||||
|
||||
describe("ParserWindowManager", () => {
|
||||
let parser: Parser;
|
||||
describe("trace with elapsed + real timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
beforeAll(async () => {
|
||||
const buffer = TestUtils.getFixtureBlob("trace_WindowManager.pb");
|
||||
const parsers = await new ParserFactory().createParsers([buffer]);
|
||||
expect(parsers.length).toEqual(1);
|
||||
parser = parsers[0];
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/WindowManager.pb");
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.WINDOW_MANAGER);
|
||||
});
|
||||
|
||||
it("provides elapsed timestamps", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 14474594000n),
|
||||
new Timestamp(TimestampType.ELAPSED, 15398076788n),
|
||||
new Timestamp(TimestampType.ELAPSED, 15409222011n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED)!.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("provides real timestamps", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.REAL, 1659107089075566202n),
|
||||
new Timestamp(TimestampType.REAL, 1659107089999048990n),
|
||||
new Timestamp(TimestampType.REAL, 1659107090010194213n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from elapsed timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 15398076788n);
|
||||
const entry = parser.getTraceEntry(timestamp)!;
|
||||
expect(entry).toBeInstanceOf(WindowManagerState);
|
||||
expect(BigInt(entry.timestampMs)).toEqual(15398076788n);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from real timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.REAL, 1659107089999048990n);
|
||||
const entry = parser.getTraceEntry(timestamp)!;
|
||||
expect(entry).toBeInstanceOf(WindowManagerState);
|
||||
expect(BigInt(entry.timestampMs)).toEqual(15398076788n);
|
||||
});
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceTypeId()).toEqual(TraceTypeId.WINDOW_MANAGER);
|
||||
});
|
||||
describe("trace elapsed timestamp", () => {
|
||||
let parser: Parser;
|
||||
|
||||
it("provides timestamps", () => {
|
||||
expect(parser.getTimestamps())
|
||||
.toEqual([850254319343, 850763506110, 850782750048]);
|
||||
});
|
||||
beforeAll(async () => {
|
||||
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/WindowManager.pb");
|
||||
});
|
||||
|
||||
it("retrieves trace entry", () => {
|
||||
const entry = parser.getTraceEntry(850254319343)!;
|
||||
expect(entry).toBeInstanceOf(WindowManagerState);
|
||||
expect(Number(entry.timestampMs)).toEqual(850254319343);
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceType()).toEqual(TraceType.WINDOW_MANAGER);
|
||||
});
|
||||
|
||||
it("provides timestamps", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 850254319343n),
|
||||
new Timestamp(TimestampType.ELAPSED, 850763506110n),
|
||||
new Timestamp(TimestampType.ELAPSED, 850782750048n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("retrieves trace entry", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 850254319343n);
|
||||
const entry = parser.getTraceEntry(timestamp)!;
|
||||
expect(entry).toBeInstanceOf(WindowManagerState);
|
||||
expect(BigInt(entry.timestampMs)).toEqual(850254319343n);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {WindowManagerTraceFileProto} from "./proto_types";
|
||||
import {WindowManagerState} from "common/trace/flickerlib/windows/WindowManagerState";
|
||||
@@ -21,10 +22,11 @@ import {WindowManagerState} from "common/trace/flickerlib/windows/WindowManagerS
|
||||
class ParserWindowManager extends Parser {
|
||||
constructor(trace: Blob) {
|
||||
super(trace);
|
||||
this.realToElapsedTimeOffsetNs = undefined;
|
||||
}
|
||||
|
||||
override getTraceTypeId(): TraceTypeId {
|
||||
return TraceTypeId.WINDOW_MANAGER;
|
||||
override getTraceType(): TraceType {
|
||||
return TraceType.WINDOW_MANAGER;
|
||||
}
|
||||
|
||||
override getMagicNumber(): number[] {
|
||||
@@ -32,17 +34,31 @@ class ParserWindowManager extends Parser {
|
||||
}
|
||||
|
||||
override decodeTrace(buffer: Uint8Array): any[] {
|
||||
return (<any>WindowManagerTraceFileProto.decode(buffer)).entry;
|
||||
const decoded = <any>WindowManagerTraceFileProto.decode(buffer);
|
||||
if (Object.prototype.hasOwnProperty.call(decoded, "realToElapsedTimeOffsetNanos")) {
|
||||
this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
|
||||
}
|
||||
else {
|
||||
this.realToElapsedTimeOffsetNs = undefined;
|
||||
}
|
||||
return decoded.entry;
|
||||
}
|
||||
|
||||
override getTimestamp(entryProto: any): number {
|
||||
return Number(entryProto.elapsedRealtimeNanos);
|
||||
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
|
||||
if (type === TimestampType.ELAPSED) {
|
||||
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
|
||||
}
|
||||
else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
|
||||
return new Timestamp(type, this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
override processDecodedEntry(entryProto: any): WindowManagerState {
|
||||
return WindowManagerState.fromProto(entryProto.windowManagerService, entryProto.elapsedRealtimeNanos, entryProto.where);
|
||||
}
|
||||
|
||||
private realToElapsedTimeOffsetNs: undefined|bigint;
|
||||
private static readonly MAGIC_NUMBER = [0x09, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WINTRACE
|
||||
}
|
||||
|
||||
|
||||
@@ -14,33 +14,39 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {WindowManagerState} from "common/trace/flickerlib/windows/WindowManagerState";
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {ParserFactory} from "./parser_factory";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {TestUtils} from "test/test_utils";
|
||||
import {UnitTestUtils} from "test/unit/utils";
|
||||
|
||||
describe("ParserWindowManagerDump", () => {
|
||||
let parser: Parser;
|
||||
|
||||
beforeAll(async () => {
|
||||
const buffer = TestUtils.getFixtureBlob("dump_WindowManager.pb");
|
||||
const parsers = await new ParserFactory().createParsers([buffer]);
|
||||
expect(parsers.length).toEqual(1);
|
||||
parser = parsers[0];
|
||||
parser = await UnitTestUtils.getParser("traces/dump_WindowManager.pb");
|
||||
});
|
||||
|
||||
it("has expected trace type", () => {
|
||||
expect(parser.getTraceTypeId()).toEqual(TraceTypeId.WINDOW_MANAGER);
|
||||
expect(parser.getTraceType()).toEqual(TraceType.WINDOW_MANAGER);
|
||||
});
|
||||
|
||||
it("provides timestamps", () => {
|
||||
expect(parser.getTimestamps())
|
||||
.toEqual([0]);
|
||||
it("provides elapsed timestamp (always zero)", () => {
|
||||
const expected = [
|
||||
new Timestamp(TimestampType.ELAPSED, 0n),
|
||||
];
|
||||
expect(parser.getTimestamps(TimestampType.ELAPSED))
|
||||
.toEqual(expected);
|
||||
});
|
||||
|
||||
it("retrieves trace entry", () => {
|
||||
const entry = parser.getTraceEntry(0)!;
|
||||
it("doesn't provide real timestamp (never)", () => {
|
||||
expect(parser.getTimestamps(TimestampType.REAL))
|
||||
.toEqual(undefined);
|
||||
});
|
||||
|
||||
it("retrieves trace entry from elapsed timestamp", () => {
|
||||
const timestamp = new Timestamp(TimestampType.ELAPSED, 0n);
|
||||
const entry = parser.getTraceEntry(timestamp)!;
|
||||
expect(entry).toBeInstanceOf(WindowManagerState);
|
||||
expect(Number(entry.timestampMs)).toEqual(0);
|
||||
expect(BigInt(entry.timestampMs)).toEqual(0n);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {WindowManagerServiceDumpProto} from "./proto_types";
|
||||
import {WindowManagerState} from "common/trace/flickerlib/windows/WindowManagerState";
|
||||
@@ -23,8 +24,8 @@ class ParserWindowManagerDump extends Parser {
|
||||
super(trace);
|
||||
}
|
||||
|
||||
override getTraceTypeId(): TraceTypeId {
|
||||
return TraceTypeId.WINDOW_MANAGER;
|
||||
override getTraceType(): TraceType {
|
||||
return TraceType.WINDOW_MANAGER;
|
||||
}
|
||||
|
||||
override getMagicNumber(): undefined {
|
||||
@@ -35,8 +36,11 @@ class ParserWindowManagerDump extends Parser {
|
||||
return [WindowManagerServiceDumpProto.decode(buffer)];
|
||||
}
|
||||
|
||||
override getTimestamp(entryProto: any): number {
|
||||
return 0;
|
||||
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
|
||||
if (type !== TimestampType.ELAPSED) {
|
||||
return undefined;
|
||||
}
|
||||
return new Timestamp(TimestampType.ELAPSED, 0n);
|
||||
}
|
||||
|
||||
override processDecodedEntry(entryProto: any): WindowManagerState {
|
||||
|
||||
@@ -17,27 +17,30 @@ import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import {Blob} from "./blob";
|
||||
|
||||
class TestUtils {
|
||||
class CommonTestUtils {
|
||||
static getFixtureBlob(filename: string): Blob {
|
||||
const buffer = TestUtils.loadFixture(filename);
|
||||
const buffer = CommonTestUtils.loadFixture(filename);
|
||||
return new Blob(buffer);
|
||||
}
|
||||
|
||||
static loadFixture(filename: string): ArrayBuffer {
|
||||
return fs.readFileSync(TestUtils.getFixturePath(filename));
|
||||
return fs.readFileSync(CommonTestUtils.getFixturePath(filename));
|
||||
}
|
||||
|
||||
static getFixturePath(filename: string): string {
|
||||
return path.join(TestUtils.getProjectRootPath(), "src/test/fixtures", filename);
|
||||
}
|
||||
|
||||
static getProductionIndexHtmlPath(): string {
|
||||
return path.join(TestUtils.getProjectRootPath(), "dist/prod/index.html");
|
||||
if (path.isAbsolute(filename)) {
|
||||
return filename;
|
||||
}
|
||||
return path.join(CommonTestUtils.getProjectRootPath(), "src/test/fixtures", filename);
|
||||
}
|
||||
|
||||
static getProjectRootPath(): string {
|
||||
return path.join(__dirname, "../..");
|
||||
let root = __dirname;
|
||||
while (path.basename(root) !== "winscope-ng") {
|
||||
root = path.dirname(root);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
export {TestUtils};
|
||||
export {CommonTestUtils};
|
||||
25
tools/winscope-ng/src/test/e2e/utils.ts
Normal file
25
tools/winscope-ng/src/test/e2e/utils.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import * as path from "path";
|
||||
import {CommonTestUtils} from "../common/utils";
|
||||
|
||||
class E2eTestUtils extends CommonTestUtils {
|
||||
static getProductionIndexHtmlPath(): string {
|
||||
return path.join(CommonTestUtils.getProjectRootPath(), "dist/prod/index.html");
|
||||
}
|
||||
}
|
||||
|
||||
export {E2eTestUtils};
|
||||
@@ -14,17 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {browser, element, by} from "protractor";
|
||||
import {TestUtils} from "../test_utils";
|
||||
import {E2eTestUtils} from "./utils";
|
||||
|
||||
describe("Viewer WindowManager", () => {
|
||||
beforeAll(async () => {
|
||||
browser.manage().timeouts().implicitlyWait(1000);
|
||||
browser.get("file://" + TestUtils.getProductionIndexHtmlPath());
|
||||
browser.get("file://" + E2eTestUtils.getProductionIndexHtmlPath());
|
||||
}),
|
||||
|
||||
it("processes trace and renders view", () => {
|
||||
const inputFile = element(by.css("input[type=\"file\"]"));
|
||||
inputFile.sendKeys(TestUtils.getFixturePath("trace_WindowManager.pb"));
|
||||
inputFile.sendKeys(E2eTestUtils.getFixturePath("traces/elapsed_and_real_timestamp/WindowManager.pb"));
|
||||
|
||||
const windowManagerViewerTitle = element(by.css(".viewer-window-manager .title"));
|
||||
expect(windowManagerViewerTitle.getText()).toContain("Window Manager");
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {browser, element, by} from "protractor";
|
||||
import {TestUtils} from "../test_utils";
|
||||
import {E2eTestUtils} from "./utils";
|
||||
|
||||
describe("winscope", () => {
|
||||
beforeAll(() => {
|
||||
browser.get("file://" + TestUtils.getProductionIndexHtmlPath());
|
||||
browser.get("file://" + E2eTestUtils.getProductionIndexHtmlPath());
|
||||
}),
|
||||
|
||||
it("has title", () => {
|
||||
|
||||
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/Accessibility.pb
vendored
Normal file
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/Accessibility.pb
vendored
Normal file
Binary file not shown.
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodClients.pb
vendored
Normal file
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodClients.pb
vendored
Normal file
Binary file not shown.
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodManagerService.pb
vendored
Normal file
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodManagerService.pb
vendored
Normal file
Binary file not shown.
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodService.pb
vendored
Normal file
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodService.pb
vendored
Normal file
Binary file not shown.
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/SurfaceFlinger.pb
vendored
Normal file
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/SurfaceFlinger.pb
vendored
Normal file
Binary file not shown.
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/Transactions.pb
vendored
Normal file
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/Transactions.pb
vendored
Normal file
Binary file not shown.
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/WindowManager.pb
vendored
Normal file
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/WindowManager.pb
vendored
Normal file
Binary file not shown.
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/dump_SurfaceFlinger.pb
vendored
Normal file
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/dump_SurfaceFlinger.pb
vendored
Normal file
Binary file not shown.
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/screen_recording.mp4
vendored
Normal file
BIN
tools/winscope-ng/src/test/fixtures/traces/elapsed_and_real_timestamp/screen_recording.mp4
vendored
Normal file
Binary file not shown.
29
tools/winscope-ng/src/test/unit/utils.ts
Normal file
29
tools/winscope-ng/src/test/unit/utils.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {Parser} from "parsers/parser";
|
||||
import {ParserFactory} from "parsers/parser_factory";
|
||||
import {CommonTestUtils} from "test/common/utils";
|
||||
|
||||
class UnitTestUtils extends CommonTestUtils {
|
||||
static async getParser(filename: string): Promise<Parser> {
|
||||
const trace = CommonTestUtils.getFixtureBlob(filename);
|
||||
const parsers = await new ParserFactory().createParsers([trace]);
|
||||
expect(parsers.length).toEqual(1);
|
||||
return parsers[0];
|
||||
}
|
||||
}
|
||||
|
||||
export {UnitTestUtils};
|
||||
@@ -13,11 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { TraceTypeId } from "common/trace/type_id";
|
||||
import { TraceType } from "common/trace/trace_type";
|
||||
|
||||
interface Viewer {
|
||||
//TODO: add TraceEntry data type
|
||||
notifyCurrentTraceEntries(entries: Map<TraceTypeId, any>): void;
|
||||
notifyCurrentTraceEntries(entries: Map<TraceType, any>): void;
|
||||
getView(): HTMLElement;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { TraceTypeId } from "common/trace/type_id";
|
||||
import { TraceType } from "common/trace/trace_type";
|
||||
import { Viewer } from "./viewer";
|
||||
import { ViewerWindowManager } from "./viewer_window_manager/viewer_window_manager";
|
||||
|
||||
@@ -22,11 +22,11 @@ class ViewerFactory {
|
||||
ViewerWindowManager,
|
||||
];
|
||||
|
||||
public createViewers(activeTraceTypes: Set<TraceTypeId>): Viewer[] {
|
||||
public createViewers(activeTraceTypes: Set<TraceType>): Viewer[] {
|
||||
const viewers: Viewer[] = [];
|
||||
|
||||
for (const Viewer of ViewerFactory.VIEWERS) {
|
||||
const areViewerDepsSatisfied = Viewer.DEPENDENCIES.every((traceType: TraceTypeId) =>
|
||||
const areViewerDepsSatisfied = Viewer.DEPENDENCIES.every((traceType: TraceType) =>
|
||||
activeTraceTypes.has(traceType)
|
||||
);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {UiData} from "./ui_data";
|
||||
|
||||
type UiDataCallbackType = (uiData: UiData) => void;
|
||||
@@ -25,7 +25,7 @@ class Presenter {
|
||||
this.uiDataCallback(this.uiData);
|
||||
}
|
||||
|
||||
public notifyCurrentTraceEntries(entries: Map<TraceTypeId, any>) {
|
||||
public notifyCurrentTraceEntries(entries: Map<TraceType, any>) {
|
||||
this.uiData = new UiData("UI data selected by user on time scrub");
|
||||
this.uiDataCallback(this.uiData);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceTypeId} from "common/trace/type_id";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Viewer} from "viewers/viewer";
|
||||
import {Presenter} from "./presenter";
|
||||
import {UiData} from "./ui_data";
|
||||
@@ -27,7 +27,7 @@ class ViewerWindowManager implements Viewer {
|
||||
this.view.addEventListener("outputEvent", () => this.presenter.notifyUiEvent());
|
||||
}
|
||||
|
||||
public notifyCurrentTraceEntries(entries: Map<TraceTypeId, any>): void {
|
||||
public notifyCurrentTraceEntries(entries: Map<TraceType, any>): void {
|
||||
this.presenter.notifyCurrentTraceEntries(entries);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ class ViewerWindowManager implements Viewer {
|
||||
return this.view;
|
||||
}
|
||||
|
||||
public static readonly DEPENDENCIES: TraceTypeId[] = [TraceTypeId.WINDOW_MANAGER];
|
||||
public static readonly DEPENDENCIES: TraceType[] = [TraceType.WINDOW_MANAGER];
|
||||
private view: HTMLElement;
|
||||
private presenter: Presenter;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user