@here/harp-mapview
Version:
Functionality needed to render a map.
981 lines • 31.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PerformanceStatistics = exports.FrameStatsArray = exports.FrameStats = exports.Statistics = exports.MultiStageTimer = exports.computeArrayAverage = exports.computeArrayStats = exports.SampledTimer = exports.SimpleTimer = exports.RingBuffer = void 0;
/*
* Copyright (C) 2019-2021 HERE Europe B.V.
* Licensed under Apache 2.0, see full license in LICENSE
* SPDX-License-Identifier: Apache-2.0
*/
const harp_utils_1 = require("@here/harp-utils");
const logger = harp_utils_1.LoggerManager.instance.create("Statistics");
/**
* A simple ring buffer to store the last `n` values of the timer. The buffer works on
* a First-In-First-Out (FIFO) basis.
*/
class RingBuffer {
/**
* Sets up the ring buffer.
*
* @param capacity - The buffer's capacity.
*/
constructor(capacity) {
this.capacity = capacity;
this.buffer = new Array(capacity);
this.capacity = capacity;
this.head = this.tail = this.size = 0;
}
/**
* Clears the contents, removes all elements.
*/
clear() {
this.head = this.tail = this.size = 0;
}
/**
* Adds a single element to the ring buffer.
*
* @param data - Data element.
*/
enqOne(data) {
let next = this.head + 1;
if (next >= this.capacity) {
next = 0;
}
if (this.size < this.capacity) {
this.size++;
}
this.buffer[this.head] = data;
this.head = next;
if (this.size === this.capacity) {
this.tail = this.head;
}
}
/**
* Adds one or more elements.
*
* @param data - The elements to add.
*/
enq(...data) {
for (const v of data) {
this.enqOne(v);
}
}
/**
* Obtains the oldest element (FIFO). May throw an exception if a buffer underrun occurs.
* Before calling this method, make sure that `size > 0`.
*/
deq() {
if (this.size === 0) {
throw new Error("Ringbuffer underrun");
}
const data = this.buffer[this.tail];
let next = this.tail + 1;
if (next >= this.capacity) {
next = 0;
}
if (this.size > 0) {
this.size--;
}
this.tail = next;
return data;
}
/**
* Obtains the oldest element (FIFO) without removing it. Throws an exception if a buffer is
* empty. Before calling this method, make sure that `size > 0`.
*/
get top() {
if (this.size === 0) {
throw new Error("Ringbuffer underrun");
}
return this.buffer[this.tail];
}
/**
* Obtains the latest element (LIFO) without removing it. Throws an exception if a buffer is
* empty. Before calling this method, make sure that `size > 0`.
*/
get bottom() {
if (this.size === 0) {
throw new Error("Ringbuffer underrun");
}
let previous = this.head - 1;
if (previous < 0) {
previous = this.capacity - 1;
}
return this.buffer[previous];
}
/**
* Creates an iterator for the buffer.
*/
iterator() {
return new RingBuffer.Iterator(this);
}
/**
* Returns a copy of the buffer, where the elements are properly sorted from oldest to newest.
*/
asArray() {
const array = new Array();
for (let i = 0; i < this.size; i++) {
array.push(this.buffer[(this.tail + i) % this.capacity]);
}
return array;
}
}
exports.RingBuffer = RingBuffer;
(function (RingBuffer) {
/**
* A local class for RingBuffer<T>
*/
class Iterator {
/**
* Creates an iterator for the ring buffer.
*
* @param m_buffer - `Ringbuffer` to iterate over.
* @param m_index - Start index.
*/
constructor(m_buffer, m_index = 0) {
this.m_buffer = m_buffer;
this.m_index = m_index;
}
/**
* Gets the iterator's current value. This function does not fail even if an overrun occurs.
* To detect an overrun, watch the result for [[next]].
*/
get value() {
return this.m_buffer.buffer[(this.m_buffer.tail + this.m_index) % this.m_buffer.capacity];
}
/**
* Advances the iterator to the next element.
*
* @returns `true` if the iterator is still valid; `false` if an overrun occurs.
*/
next() {
this.m_index++;
return this.m_index < this.m_buffer.size;
}
}
RingBuffer.Iterator = Iterator;
})(RingBuffer = exports.RingBuffer || (exports.RingBuffer = {}));
/**
* A simple timer that stores only the latest measurement.
*
* @internal
*/
class SimpleTimer {
constructor(statistics, name) {
this.statistics = statistics;
this.name = name;
/** `true` if timer has been started. */
this.running = false;
}
/**
* Gets the latest measurement. This function may return `undefined` if no measurement
* was done.
*/
get value() {
return this.m_currentValue;
}
/**
* Sets the measurement value for the amount of time that has elapsed from start() to stop().
* Use this function to override the timer's duration.
*
* @param val - The timer's duration.
*/
setValue(val) {
this.m_currentValue = val;
}
/**
* Resets the value to be able to start again.
*/
reset() {
this.m_currentValue = undefined;
}
/**
* Starts the timer. Returns the current time, based on `Performance.now()`.
*/
start() {
if (!this.statistics.enabled) {
return -1;
}
if (this.running) {
throw new Error("Timer '" + this.name + "' is already running");
}
this.running = true;
return (this.m_currentValue = harp_utils_1.PerformanceTimer.now());
}
/**
* Stops the timer. Requires that the timer has started.
*/
stop() {
var _a;
if (!this.statistics.enabled) {
return -1;
}
if (!this.running) {
throw new Error("Timer '" + this.name + "' has not been started");
}
else {
// this.currentValue is a number now!
const t = harp_utils_1.PerformanceTimer.now() - ((_a = this.m_currentValue) !== null && _a !== void 0 ? _a : 0);
this.m_currentValue = t;
this.setValue(t);
this.running = false;
return t;
}
}
/**
* Samples the timer. Requires that the timer has started.
*
* @returns the current timer value; `-1` if statistics are disabled.
*/
now() {
var _a;
if (!this.statistics.enabled) {
return -1;
}
if (!this.running) {
throw new Error("Timer '" + this.name + "' has not been started");
}
else {
const t = harp_utils_1.PerformanceTimer.now() - ((_a = this.m_currentValue) !== null && _a !== void 0 ? _a : 0);
return t;
}
}
}
exports.SimpleTimer = SimpleTimer;
/**
* A timer that stores the last `n` samples in a ring buffer.
*
* @internal
*/
class SampledTimer extends SimpleTimer {
/**
* Creates a `SampledTimer` instance. Must still be added to statistics if it should be logged!
*
* @param statistics - Statistics to use for management.
* @param name - Name of the timer. Use colons to build a hierarchy.
*/
constructor(statistics, name) {
super(statistics, name);
this.statistics = statistics;
this.name = name;
/**
* The number of times the timer has reset.
*/
this.numResets = 0;
/**
* Maximum samples until the statistics are reset and updated, which may destroy a median
* computation.
*/
this.maxNumSamples = 1000;
/**
* The array of sampled values, its length cannot exceed `maxNumSamples`.
*/
this.samples = new RingBuffer(this.maxNumSamples);
}
/**
* Resets the timer and clears all of its historical values.
* @override
*/
reset() {
super.reset();
this.getStats();
this.samples.clear();
this.numResets++;
}
/**
* Add a single measurement to the sample.
*
* @param val - A measurement to add.
* @override
*/
setValue(val) {
super.setValue(val);
if (val !== undefined) {
this.samples.enqOne(val);
}
}
/**
* Updates the `min`, `max`, `avg`, and `median` values. Currently, this function is expensive,
* as it requires a copy of the sampled values.
*/
getStats() {
return computeArrayStats(this.samples.asArray());
}
}
exports.SampledTimer = SampledTimer;
/**
* Only exported for testing
* @ignore
*
* @remarks
* Compute the [[ArrayStats]] for the passed in array of numbers.
*
* @param {number[]} samples Array containing sampled values. Will be modified (!) by sorting the
* entries.
* @returns {(Stats | undefined)}
*
* @internal
*/
function computeArrayStats(samples) {
if (samples.length === 0) {
return undefined;
}
samples.sort((a, b) => {
return a - b;
});
const min = samples[0];
const max = samples[samples.length - 1];
let median;
let median75;
let median90;
let median95;
let median97;
let median99;
let median999;
if (samples.length === 1) {
median75 = median90 = median95 = median97 = median99 = median999 = median = samples[0];
}
else if (samples.length === 2) {
median = samples[0] * 0.5 + samples[1] * 0.5;
median75 = median90 = median95 = median97 = median99 = median999 = samples[1];
}
else {
const mid = Math.floor(samples.length / 2);
median =
samples.length % 2 === 0 ? samples[mid - 1] * 0.5 + samples[mid] * 0.5 : samples[mid];
const mid75 = Math.round(samples.length * 0.75) - 1;
median75 = samples[mid75];
const mid90 = Math.round(samples.length * 0.9) - 1;
median90 = samples[mid90];
const mid95 = Math.round(samples.length * 0.95) - 1;
median95 = samples[mid95];
const mid97 = Math.round(samples.length * 0.97) - 1;
median97 = samples[mid97];
const mid99 = Math.round(samples.length * 0.99) - 1;
median99 = samples[mid99];
const mid999 = Math.round(samples.length * 0.999) - 1;
median999 = samples[mid999];
}
let sum = 0;
for (let i = 0, l = samples.length; i < l; i++) {
sum += samples[i];
}
const avg = sum / samples.length;
return {
min,
max,
avg,
median,
median75,
median90,
median95,
median97,
median99,
median999,
numSamples: samples.length
};
}
exports.computeArrayStats = computeArrayStats;
/**
* Only exported for testing
* @ignore
*
* @remarks
* Compute the averages for the passed in array of numbers.
*
* @param {number[]} samples Array containing sampled values.
* @returns {(Stats | undefined)}
*
* @internal
*/
function computeArrayAverage(samples) {
if (samples.length === 0) {
return undefined;
}
let sum = 0;
for (let i = 0, l = samples.length; i < l; i++) {
sum += samples[i];
}
const avg = sum / samples.length;
return avg;
}
exports.computeArrayAverage = computeArrayAverage;
/**
* Measures a sequence of connected events, such as multiple processing stages in a function.
*
* @remarks
* Each stage is identified with a timer name, that must be a valid timer in the statistics
* object. Additionally, all timers within a `MultiStageTimer` must be unique.
*
* Internally, the `MultiStageTimer` manages a list of timers where at the end of each stage,
* one timer stops and the next timer starts.
*
* @internal
*/
class MultiStageTimer {
/**
* Defines the `MultiStageTimer` with a list of timer names that represent its stages.
*
* @param statistics - The statistics object that manages the timers.
* @param name - Name of this `MultiStageTimer`.
* @param stages - List of timer names.
*/
constructor(statistics, name, stages) {
this.statistics = statistics;
this.name = name;
this.stages = stages;
if (stages.length < 1) {
throw new Error("MultiStageTimer needs stages");
}
stages.forEach(stage => {
if (!statistics.hasTimer(stage)) {
throw new Error("Unknown timer: " + stage);
}
});
}
/**
* Gets the timer value for the last stage. If the `MultiStageTimer` did not finish its
* last stage, the value is `undefined`.
*/
get value() {
return this.statistics.getTimer(this.stages[this.stages.length - 1]).value;
}
/**
* Resets the timers across all stages.
*/
reset() {
if (!this.statistics.enabled) {
return;
}
this.stages.forEach(stage => {
this.statistics.getTimer(stage).reset();
});
}
/**
* Starts the `MultiStageTimer` at its first stage.
*/
start() {
var _a;
this.stage = this.stages[0];
return (_a = this.statistics.getTimer(this.stages[0]).value) !== null && _a !== void 0 ? _a : -1;
}
/**
* Stops the `MultiStageTimer`. Returns the measurement of the last stage, which may be
* `undefined` if not all stages started.
*/
stop() {
this.stage = undefined;
return this.value !== undefined ? this.value : -1;
}
/**
* Gets the current stage.
*/
get stage() {
return this.currentStage;
}
/**
* Sets the current stage. If a new stage is provided, the current timer (if available) is
* stopped, and the next timer is started. If the timer in the next stage is `undefined`,
* this is equivalent to calling `stop` on the `MultiStageTimer`.
*
* @param stage - The next stage to start.
*/
set stage(stage) {
if (this.currentStage === stage) {
return;
}
if (this.statistics.enabled && this.currentStage !== undefined) {
this.statistics.getTimer(this.currentStage).stop();
}
this.currentStage = stage;
if (this.statistics.enabled && this.currentStage !== undefined) {
this.statistics.getTimer(this.currentStage).start();
}
}
}
exports.MultiStageTimer = MultiStageTimer;
/**
* Manages a set of timers.
*
* @remarks
* The main objective of `Statistics` is to log these timers. You can
* disable statistics to minimize their impact on performance.
*
* @internal
*/
class Statistics {
/**
* Sets up a group of timers.
*
* @param name - The statistics name, for logging purposes.
* @param enabled - If `false`, the timers do not measure the performance.
*/
constructor(name, enabled = false) {
this.name = name;
this.enabled = enabled;
this.timers = new Map();
this.nullTimer = new SimpleTimer(this, "<null>");
}
/**
* Adds a timer, based on the name specified.
*
* @param name - The timer's name; must be unique.
*/
createTimer(name, keepSamples = true) {
const timer = keepSamples ? new SampledTimer(this, name) : new SimpleTimer(this, name);
return this.addTimer(timer);
}
/**
* Adds the timer specified.
*
* @param timer - The timer's name, which must be unique within this statistics object.
*/
addTimer(timer) {
if (this.timers.get(timer.name) !== undefined) {
throw new Error("Duplicate timer name: '" + timer.name + "'");
}
this.timers.set(timer.name, timer);
return timer;
}
/**
* Gets a timer by name.
*
* @param name - The timer's name.
*/
getTimer(name) {
if (!this.enabled) {
return this.nullTimer;
}
const t = this.timers.get(name);
return t === undefined ? this.nullTimer : t;
}
/**
* Checks if a timer with the specified name already exists.
*
* @param name - The timer's name.
* @returns `true` if a timer with `name` already exists; `false` otherwise.
*/
hasTimer(name) {
const t = this.timers.get(name);
return t !== undefined;
}
/**
* Resets all timers.
*/
reset() {
this.timers.forEach((timer) => {
timer.reset();
});
}
/**
* Prints all values to the console.
*
* @param header - Optional header line.
* @param footer - Optional footer line.
*/
log(header, footer) {
if (header !== undefined || this.name !== undefined) {
logger.log(header !== undefined ? header : this.name);
}
let maxNameLength = 0;
this.timers.forEach((timer) => {
maxNameLength = Math.max(maxNameLength, timer.name.length);
});
// simple printing function for number limits the number of decimal points.
const print = (v) => {
return v !== undefined ? v.toFixed(5) : "?";
};
this.timers.forEach((timer) => {
let s = timer.name + ": " + " ".repeat(maxNameLength - timer.name.length);
s += print(timer.value);
// sampled timers also update their stats and log them
if (timer instanceof SampledTimer) {
const simpleStats = timer.getStats();
if (simpleStats !== undefined) {
s +=
` [ min=${print(simpleStats.min)}, max=${print(simpleStats.max)}, ` +
`avg=${print(simpleStats.avg)}, med=${print(simpleStats.median)}, ` +
`med95=${print(simpleStats.median95)}, med99=${print(simpleStats.median99)}, ` +
`N=${print(simpleStats.numSamples)} ]`;
}
}
logger.log(s);
});
if (footer !== undefined) {
logger.log(footer);
}
}
}
exports.Statistics = Statistics;
/**
* Class containing all counters, timers and events of the current frame.
*
* @internal
*/
class FrameStats {
constructor() {
this.entries = new Map();
this.messages = undefined;
}
/**
* Retrieve the value of the performance number.
*
* @param name - Name of the performance number.
* @returns The value of the performance number or `undefined` if it has not been declared by
* `setValue` before.
*/
getValue(name) {
return this.entries.get(name);
}
/**
* Set the value of the performance number.
*
* @param name - Name of the performance number.
* @param name - New value of the performance number.
*/
setValue(name, value) {
this.entries.set(name, value);
}
/**
* Add a value to the current value of the performance number. If the performance is not known,
* it will be initialized with `value`.
*
* @param name - Name of the performance number.
* @param name - Value to be added to the performance number.
*/
addValue(name, value) {
const oldValue = this.entries.get(name);
this.entries.set(name, value + (oldValue === undefined ? 0 : oldValue));
}
/**
* Add a text message to the frame, like "Font XYZ has been loaded"
*
* @param message - The message to add.
*/
addMessage(message) {
if (this.messages === undefined) {
this.messages = [];
}
this.messages.push(message);
}
/**
* Reset all known performance values to `0` and the messages to `undefined`.
*/
reset() {
this.entries.forEach((value, name) => {
this.entries.set(name, 0);
});
this.messages = undefined;
}
}
exports.FrameStats = FrameStats;
/**
* @ignore
* Only exported for testing.
*
* @remarks
* Instead of passing around an array of objects, we store the frame statistics as an object of
* arrays. This allows convenient computations from {@link RingBuffer},
*/
class FrameStatsArray {
constructor(capacity = 0) {
this.capacity = capacity;
this.frameEntries = new Map();
this.messages = new RingBuffer(capacity);
}
get length() {
return this.messages.size;
}
reset() {
this.frameEntries.forEach((buffer, name) => {
buffer.clear();
});
this.messages.clear();
}
addFrame(frameStats) {
const currentSize = this.length;
const frameEntries = this.frameEntries;
frameStats.entries.forEach((value, name) => {
let buffer = frameEntries.get(name);
if (buffer === undefined) {
// If there is a buffer that has not been known before, add it to the known buffers,
// fill it up with with 0 to the size of all the other buffers to make them of equal
// size to make PerfViz happy.
buffer = new RingBuffer(this.capacity);
for (let i = 0; i < currentSize; i++) {
buffer.enqOne(0);
}
this.frameEntries.set(name, buffer);
}
buffer.enqOne(value);
});
this.messages.enq(frameStats.messages);
}
/**
* Prints all values to the console.
*/
log() {
let maxNameLength = 0;
this.frameEntries.forEach((buffer, name) => {
maxNameLength = Math.max(maxNameLength, name.length);
});
// simple printing function for number limits the number of decimal points.
const print = (v) => {
return v !== undefined ? v.toFixed(5) : "?";
};
this.frameEntries.forEach((buffer, name) => {
let s = name + ": " + " ".repeat(maxNameLength - name.length);
const simpleStats = computeArrayStats(buffer.asArray());
if (simpleStats !== undefined) {
s +=
` [ min=${print(simpleStats.min)}, max=${print(simpleStats.max)}, ` +
`avg=${print(simpleStats.avg)}, med=${print(simpleStats.median)}, ` +
`med95=${print(simpleStats.median95)}, med99=${print(simpleStats.median99)}, ` +
`N=${print(simpleStats.numSamples)} ]`;
}
logger.log(s);
});
}
}
exports.FrameStatsArray = FrameStatsArray;
/**
* Performance measurement central.
*
* @remarks
* Maintains the current. Implemented as an instance for easy access.
*
* {@link FrameStats}, which holds all individual performance numbers.
*
* @internal
*/
class PerformanceStatistics {
/**
* Creates an instance of PerformanceStatistics. Overrides the current `instance`.
*
* @param {boolean} [enabled=true] If `false` the performance values will not be stored.
* @param {number} [maxNumFrames=1000] The maximum number of frames that are to be stored.
* @memberof PerformanceStatistics
*/
constructor(enabled = true, maxNumFrames = 1000) {
this.enabled = enabled;
this.maxNumFrames = maxNumFrames;
/**
* Current frame statistics. Contains all values for the current frame. Will be cleared when
* [[PerformanceStatistics#storeFrameInfo]] is called.
*
* @type {FrameStats}
* @memberof PerformanceStatistics
*/
this.currentFrame = new FrameStats();
/**
* Additional results stored for the current application run, not per frame. Only the last value
* is stored.
*
* @type {(Map<string, number>)}
*/
this.appResults = new Map();
/**
* Additional configuration values stored for the current application run, not per frame. Only
* the last value is stored.
*
* @type {(Map<string, string>)}
* @memberof PerformanceStatistics
*/
this.configs = new Map();
PerformanceStatistics.m_instance = this;
this.m_frameEvents = new FrameStatsArray(maxNumFrames);
}
/**
* Returns `true` when the maximum number of storable frames is reached.
*
* @readonly
* @type {boolean}
* @memberof PerformanceStatistics
*/
get isFull() {
return this.m_frameEvents.length >= this.maxNumFrames;
}
/**
* Global instance to the instance. The current instance can be overridden by creating a new
* `PerformanceStatistics`.
*/
static get instance() {
if (PerformanceStatistics.m_instance === undefined) {
PerformanceStatistics.m_instance = new PerformanceStatistics(false, 0);
}
return PerformanceStatistics.m_instance;
}
/**
* @ignore
* Only exported for testing.
*
* Return the array of frame events.
*/
get frameEvents() {
return this.m_frameEvents;
}
/**
* Clears all settings, all stored frame events as well as the current frame values.
*
* @memberof PerformanceStatistics
*/
clear() {
this.clearFrames();
this.configs.clear();
this.appResults.clear();
}
/**
* Clears only all stored frame events as well as the current frame values.
*
* @memberof PerformanceStatistics
*/
clearFrames() {
this.m_frameEvents.reset();
this.currentFrame.reset();
}
/**
* Add the render state information from [[THREE.WebGLInfo]] to the current frame.
* @param {THREE.WebGLInfo} webGlInfo
*/
addWebGLInfo(webGlInfo) {
if (webGlInfo.render !== undefined) {
this.currentFrame.setValue("gl.numCalls", webGlInfo.render.calls === null ? 0 : webGlInfo.render.calls);
this.currentFrame.setValue("gl.numPoints", webGlInfo.render.points === null ? 0 : webGlInfo.render.points);
this.currentFrame.setValue("gl.numLines", webGlInfo.render.lines === null ? 0 : webGlInfo.render.lines);
this.currentFrame.setValue("gl.numTriangles", webGlInfo.render.triangles === null ? 0 : webGlInfo.render.triangles);
}
if (webGlInfo.memory !== undefined) {
this.currentFrame.setValue("gl.numGeometries", webGlInfo.memory.geometries === null ? 0 : webGlInfo.memory.geometries);
this.currentFrame.setValue("gl.numTextures", webGlInfo.memory.textures === null ? 0 : webGlInfo.memory.textures);
}
if (webGlInfo.programs !== undefined) {
this.currentFrame.setValue("gl.numPrograms", webGlInfo.programs === null ? 0 : webGlInfo.programs.length);
}
}
/**
* Add memory statistics to the current frame if available.
* @note Currently only supported on Chrome
*/
addMemoryInfo() {
if (window !== undefined && window.performance !== undefined) {
const memory = window.performance.memory;
if (memory !== undefined) {
this.currentFrame.setValue("memory.totalJSHeapSize", memory.totalJSHeapSize);
this.currentFrame.setValue("memory.usedJSHeapSize", memory.usedJSHeapSize);
this.currentFrame.setValue("memory.jsHeapSizeLimit", memory.jsHeapSizeLimit);
}
}
}
/**
* Stores the current frame events into the array of events and clears all values.
*
* @returns {boolean} Returns `false` if the maximum number of storable frames has been reached.
* @memberof PerformanceStatistics
*/
storeAndClearFrameInfo() {
if (this.m_frameEvents.length >= this.maxNumFrames) {
return false;
}
this.m_frameEvents.addFrame(this.currentFrame);
this.currentFrame.reset();
return true;
}
/**
* Logs all values to the logger.
*
* @param header - Optional header line.
* @param footer - Optional footer line.
*/
log(header, footer) {
logger.log(header !== undefined ? header : "PerformanceStatistics");
const appResults = this.appResults;
appResults.forEach((value, name) => {
logger.log(name, value);
});
const configs = this.configs;
configs.forEach((value, name) => {
logger.log(name, value);
});
this.m_frameEvents.log();
if (footer !== undefined) {
logger.log(footer);
}
}
/**
* Convert to a plain object that can be serialized. Required to copy the test results over to
* nightwatch.
*/
getAsPlainObject(onlyLastFrame = false) {
const appResults = {};
const configs = {};
const frames = {};
const plainObject = {
configs,
appResults,
frames
};
const appResultValues = this.appResults;
appResultValues.forEach((value, name) => {
appResults[name] = value;
});
const configValues = this.configs;
configValues.forEach((value, name) => {
configs[name] = value;
});
if (onlyLastFrame) {
for (const [name, buffer] of this.m_frameEvents.frameEntries) {
frames[name] = buffer.bottom;
}
}
else {
for (const [name, buffer] of this.m_frameEvents.frameEntries) {
frames[name] = buffer.asArray();
}
}
plainObject.messages = this.m_frameEvents.messages.asArray();
return plainObject;
}
/**
* Convert the last frame values to a plain object that can be serialized. Required to copy the
* test results over to nightwatch.
*/
getLastFrameStatistics() {
return this.getAsPlainObject(true);
}
/**
* Convert to a plain object that can be serialized. Required to copy the test results over to
* nightwatch.
*/
getAsSimpleFrameStatistics(onlyLastFrame = false) {
const configs = new Map();
const appResults = new Map();
const frames = new Map();
const simpleStatistics = {
configs,
appResults,
frames,
messages: this.m_frameEvents.messages.asArray()
};
const appResultValues = this.appResults;
appResultValues.forEach((value, name) => {
appResults.set(name, value);
});
const configValues = this.configs;
configValues.forEach((value, name) => {
configs.set(name, value);
});
if (onlyLastFrame) {
for (const [name, buffer] of this.m_frameEvents.frameEntries) {
frames.set(name, buffer.bottom);
}
}
else {
for (const [name, buffer] of this.m_frameEvents.frameEntries) {
frames.set(name, buffer.asArray());
}
}
return simpleStatistics;
}
}
exports.PerformanceStatistics = PerformanceStatistics;
PerformanceStatistics.m_instance = undefined;
//# sourceMappingURL=Statistics.js.map