mirror of
https://github.com/Kir-Antipov/mc-publish.git
synced 2024-11-29 03:40:56 -05:00
Stopwatch
refactoring
This commit is contained in:
parent
a33f82afe1
commit
d1b07fe763
2 changed files with 290 additions and 37 deletions
|
@ -1,66 +1,140 @@
|
||||||
|
/**
|
||||||
|
* A callback type for when a {@link Stopwatch} is started.
|
||||||
|
*/
|
||||||
interface StartCallback {
|
interface StartCallback {
|
||||||
(currentDate: Date, stopwatch: Stopwatch): void;
|
/**
|
||||||
|
* @param date - The date when the {@link Stopwatch} was started.
|
||||||
|
* @param stopwatch - The {@link Stopwatch} instance.
|
||||||
|
*/
|
||||||
|
(date: Date, stopwatch: Stopwatch): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback type for when a {@link Stopwatch} is stopped.
|
||||||
|
*/
|
||||||
interface StopCallback {
|
interface StopCallback {
|
||||||
(elapsedMilliseconds: number, currentDate: Date, stopwatch: Stopwatch): void;
|
/**
|
||||||
|
* @param elapsedTime - The elapsed time in milliseconds.
|
||||||
|
* @param date - The date when the {@link Stopwatch} was stopped.
|
||||||
|
* @param stopwatch - The {@link Stopwatch} instance.
|
||||||
|
*/
|
||||||
|
(elapsedTime: number, date: Date, stopwatch: Stopwatch): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Stopwatch {
|
/**
|
||||||
#initialDate = 0;
|
* A class for measuring elapsed time.
|
||||||
#isRunning = false;
|
*/
|
||||||
#elapsedMilliseconds = 0;
|
export class Stopwatch {
|
||||||
#onStart: StartCallback = null;
|
/**
|
||||||
#onStop: StopCallback = null;
|
* Indicates whether the stopwatch is currently running.
|
||||||
|
*/
|
||||||
|
private _isRunning: boolean;
|
||||||
|
|
||||||
public constructor(onStart?: StartCallback, onStop?: StopCallback) {
|
/**
|
||||||
this.#onStart = onStart;
|
* The time when stopwatch was started.
|
||||||
this.#onStop = onStop;
|
*/
|
||||||
|
private _startTime: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The elapsed time in milliseconds since the stopwatch was started.
|
||||||
|
*/
|
||||||
|
private _elapsedTime: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback function that will be called when the stopwatch is started.
|
||||||
|
*/
|
||||||
|
private readonly _onStart?: StartCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback function that will be called when the stopwatch is stopped.
|
||||||
|
*/
|
||||||
|
private readonly _onStop?: StopCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@link Stopwatch}.
|
||||||
|
*
|
||||||
|
* @param onStart - A callback function that will be called when the stopwatch is started.
|
||||||
|
* @param onStop - A callback function that will be called when the stopwatch is stopped.
|
||||||
|
*/
|
||||||
|
constructor(onStart?: StartCallback, onStop?: StopCallback) {
|
||||||
|
this._isRunning = false;
|
||||||
|
this._startTime = 0;
|
||||||
|
this._elapsedTime = 0;
|
||||||
|
this._onStart = onStart;
|
||||||
|
this._onStop = onStop;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get elapsedMilliseconds(): number {
|
/**
|
||||||
return this.#isRunning
|
* Gets the elapsed time in milliseconds since the stopwatch was started.
|
||||||
? (this.#elapsedMilliseconds + new Date().valueOf() - this.#initialDate)
|
*/
|
||||||
: this.#elapsedMilliseconds;
|
get elapsedMilliseconds(): number {
|
||||||
|
return this._elapsedTime + (this._isRunning ? Date.now() - this._startTime : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isRunning(): boolean {
|
/**
|
||||||
return this.#isRunning;
|
* Gets a value indicating whether the stopwatch is currently running.
|
||||||
|
*/
|
||||||
|
get isRunning(): boolean {
|
||||||
|
return this._isRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
public start(): boolean {
|
/**
|
||||||
if (!this.#isRunning) {
|
* Starts the stopwatch.
|
||||||
const currentDate = new Date();
|
*
|
||||||
this.#initialDate = currentDate.valueOf();
|
* @returns `true` if the stopwatch was successfully started; `false` if it was already running.
|
||||||
this.#isRunning = true;
|
*/
|
||||||
this.#onStart?.(currentDate, this);
|
start(): boolean {
|
||||||
return true;
|
if (this._isRunning) {
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public stop(): boolean {
|
this._startTime = Date.now();
|
||||||
if (this.#isRunning) {
|
this._isRunning = true;
|
||||||
const currentDate = new Date();
|
this._onStart?.(new Date(), this);
|
||||||
this.#elapsedMilliseconds += currentDate.valueOf() - this.#initialDate;
|
|
||||||
this.#isRunning = false;
|
|
||||||
this.#onStop?.(this.#elapsedMilliseconds, currentDate, this);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the stopwatch.
|
||||||
|
*
|
||||||
|
* @returns `true` if the stopwatch was successfully stopped; `false` if it was already stopped.
|
||||||
|
*/
|
||||||
|
stop(): boolean {
|
||||||
|
if (!this._isRunning) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public reset(): void {
|
this._elapsedTime += Date.now() - this._startTime;
|
||||||
|
this._isRunning = false;
|
||||||
|
this._onStop?.(this._elapsedTime, new Date(), this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the stopwatch.
|
||||||
|
*/
|
||||||
|
reset(): void {
|
||||||
this.stop();
|
this.stop();
|
||||||
this.#elapsedMilliseconds = 0;
|
this._elapsedTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public restart(): void {
|
/**
|
||||||
|
* Restarts the stopwatch.
|
||||||
|
*/
|
||||||
|
restart(): void {
|
||||||
this.reset();
|
this.reset();
|
||||||
this.start();
|
this.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static startNew(onStart?: StartCallback, onStop?: StopCallback): Stopwatch {
|
/**
|
||||||
|
* Creates a new instance of {@link Stopwatch} and starts it.
|
||||||
|
*
|
||||||
|
* @param onStart - A callback function that will be called when the stopwatch is started.
|
||||||
|
* @param onStop - A callback function that will be called when the stopwatch is stopped.
|
||||||
|
*
|
||||||
|
* @returns The newly created and started stopwatch.
|
||||||
|
*/
|
||||||
|
static startNew(onStart?: StartCallback, onStop?: StopCallback): Stopwatch {
|
||||||
const stopwatch = new Stopwatch(onStart, onStop);
|
const stopwatch = new Stopwatch(onStart, onStop);
|
||||||
stopwatch.start();
|
stopwatch.start();
|
||||||
return stopwatch;
|
return stopwatch;
|
||||||
|
|
179
tests/unit/utils/diagnostics/stopwatch.spec.ts
Normal file
179
tests/unit/utils/diagnostics/stopwatch.spec.ts
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
import { Stopwatch } from "@/utils/diagnostics/stopwatch";
|
||||||
|
|
||||||
|
describe("Stopwatch", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
jest.setSystemTime(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("constructor", () => {
|
||||||
|
test("initializes with default values", () => {
|
||||||
|
const stopwatch = new Stopwatch();
|
||||||
|
|
||||||
|
expect(stopwatch.isRunning).toBe(false);
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("start", () => {
|
||||||
|
test("starts stopwatch", () => {
|
||||||
|
const onStart = jest.fn();
|
||||||
|
const onStop = jest.fn();
|
||||||
|
const stopwatch = new Stopwatch(onStart, onStop);
|
||||||
|
|
||||||
|
expect(stopwatch.start()).toBe(true);
|
||||||
|
expect(stopwatch.isRunning).toBe(true);
|
||||||
|
expect(onStart).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onStart).toHaveBeenCalledWith(new Date(0), stopwatch);
|
||||||
|
expect(onStop).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("doesn't start if stopwatch is already running", () => {
|
||||||
|
const onStart = jest.fn();
|
||||||
|
const stopwatch = Stopwatch.startNew(onStart);
|
||||||
|
|
||||||
|
expect(stopwatch.start()).toBe(false);
|
||||||
|
expect(onStart).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("stop", () => {
|
||||||
|
test("stops stopwatch", () => {
|
||||||
|
const onStart = jest.fn();
|
||||||
|
const onStop = jest.fn();
|
||||||
|
const stopwatch = Stopwatch.startNew(onStart, onStop);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(1000);
|
||||||
|
|
||||||
|
expect(stopwatch.stop()).toBe(true);
|
||||||
|
expect(stopwatch.isRunning).toBe(false);
|
||||||
|
expect(onStart).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onStart).toHaveBeenCalledWith(new Date(0), stopwatch);
|
||||||
|
expect(onStop).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onStop).toHaveBeenCalledWith(1000, new Date(1000), stopwatch);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("doesn't stop if stopwatch is already stopped", () => {
|
||||||
|
const onStop = jest.fn();
|
||||||
|
const stopwatch = new Stopwatch(undefined, onStop);
|
||||||
|
|
||||||
|
expect(stopwatch.stop()).toBe(false);
|
||||||
|
expect(onStop).not.toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("elapsedMilliseconds", () => {
|
||||||
|
test("measures elapsed time while stopwatch is running", () => {
|
||||||
|
const stopwatch = Stopwatch.startNew();
|
||||||
|
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(0);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(1000);
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(1000);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(1000);
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("measures elapsed time correctly when stopwatch is stopped", () => {
|
||||||
|
const stopwatch = Stopwatch.startNew();
|
||||||
|
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(0);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(1000);
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(1000);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(1000);
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(2000);
|
||||||
|
|
||||||
|
stopwatch.stop();
|
||||||
|
jest.advanceTimersByTime(1000);
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns 0 if the stopwatch was never started", () => {
|
||||||
|
const stopwatch = new Stopwatch();
|
||||||
|
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("reset", () => {
|
||||||
|
test("resets stopwatch correctly while it's running", () => {
|
||||||
|
const stopwatch = Stopwatch.startNew();
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(1000);
|
||||||
|
stopwatch.reset();
|
||||||
|
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(0);
|
||||||
|
expect(stopwatch.isRunning).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("resets stopwatch correctly when it's stopped", () => {
|
||||||
|
const stopwatch = Stopwatch.startNew();
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(1000);
|
||||||
|
stopwatch.stop();
|
||||||
|
stopwatch.reset();
|
||||||
|
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(0);
|
||||||
|
expect(stopwatch.isRunning).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("does nothing if the stopwatch was never started", () => {
|
||||||
|
const stopwatch = new Stopwatch();
|
||||||
|
stopwatch.reset();
|
||||||
|
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(0);
|
||||||
|
expect(stopwatch.isRunning).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("restart", () => {
|
||||||
|
test("restarts stopwatch correctly while it's running", () => {
|
||||||
|
const stopwatch = Stopwatch.startNew();
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(1000);
|
||||||
|
stopwatch.restart();
|
||||||
|
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(0);
|
||||||
|
expect(stopwatch.isRunning).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("restarts stopwatch correctly when it's stopped", () => {
|
||||||
|
const stopwatch = Stopwatch.startNew();
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(1000);
|
||||||
|
stopwatch.stop();
|
||||||
|
stopwatch.restart();
|
||||||
|
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(0);
|
||||||
|
expect(stopwatch.isRunning).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("starts the stopwatch if it was never started", () => {
|
||||||
|
const stopwatch = new Stopwatch();
|
||||||
|
stopwatch.restart();
|
||||||
|
|
||||||
|
expect(stopwatch.elapsedMilliseconds).toBe(0);
|
||||||
|
expect(stopwatch.isRunning).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("startNew", () => {
|
||||||
|
test("starts new stopwatch correctly", () => {
|
||||||
|
const onStart = jest.fn();
|
||||||
|
const onStop = jest.fn();
|
||||||
|
const stopwatch = Stopwatch.startNew(onStart, onStop);
|
||||||
|
|
||||||
|
expect(stopwatch.isRunning).toBe(true);
|
||||||
|
expect(onStart).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onStart).toHaveBeenCalledWith(new Date(0), stopwatch);
|
||||||
|
expect(onStop).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue