From d097cc84716e86827fb7ed8a29bcac29afcc9934 Mon Sep 17 00:00:00 2001 From: Kir_Antipov Date: Tue, 5 Jul 2022 17:47:28 +0300 Subject: [PATCH] Made a logging version of Stopwatch --- src/utils/logging-stopwatch.ts | 46 +++++++++++++++++++ test/logging-stopwatch.test.ts | 81 ++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 src/utils/logging-stopwatch.ts create mode 100644 test/logging-stopwatch.test.ts diff --git a/src/utils/logging-stopwatch.ts b/src/utils/logging-stopwatch.ts new file mode 100644 index 0000000..3a653a2 --- /dev/null +++ b/src/utils/logging-stopwatch.ts @@ -0,0 +1,46 @@ +import Logger from "./logger"; +import Stopwatch from "./stopwatch"; + +interface StartCallback { + (currentDate: Date, stopwatch: Stopwatch): string; +} + +interface StopCallback { + (elapsedMilliseconds: number, currentDate: Date, stopwatch: Stopwatch): string; +} + +type VoidCallback any> = (...args: Parameters) => void; + +function toCallback string>(func: string | T): T { + if (typeof func === "string") { + return (() => func) as T; + } + return func; +} + +function loggingCallbackToVoidCallback any>(logger: Logger, func: T): VoidCallback { + if (!func) { + return func; + } + + return (...args: any[]) => { + const msg = func(...args) as string; + if (typeof msg === "string") { + logger?.info(msg); + } + }; +} + +// eslint-disable-next-line +// @ts-ignore: ts2417 +export default class LoggingStopwatch extends Stopwatch { + public constructor(logger: Logger, onStart?: string | StartCallback, onStop?: string | StopCallback) { + super(loggingCallbackToVoidCallback(logger, toCallback(onStart)), loggingCallbackToVoidCallback(logger, toCallback(onStop))); + } + + public static startNew(logger: Logger, onStart?: string | StartCallback, onStop?: string | StopCallback): LoggingStopwatch { + const stopwatch = new LoggingStopwatch(logger, onStart, onStop); + stopwatch.start(); + return stopwatch; + } +} diff --git a/test/logging-stopwatch.test.ts b/test/logging-stopwatch.test.ts new file mode 100644 index 0000000..549aa93 --- /dev/null +++ b/test/logging-stopwatch.test.ts @@ -0,0 +1,81 @@ +import { describe, test, expect } from "@jest/globals"; +import sleep from "../src/utils/sleep"; +import Logger from "../src/utils/logger"; +import LogginStopwatch from "../src/utils/logging-stopwatch"; + +function createLogger(info?: (msg: string) => void): Logger { + const notImplementedLogger = () => { + throw new Error("Not implemented and should never be called"); + }; + + return { + fatal: notImplementedLogger, + error: notImplementedLogger, + warn: notImplementedLogger, + debug: notImplementedLogger, + info: info ?? notImplementedLogger + }; +} + +describe("LogginStopwatch", () => { + test("base functionality of LogginStopwatch works", async () => { + const stopwatch = new LogginStopwatch(createLogger()); + expect(stopwatch.isRunning).toBe(false); + expect(stopwatch.elapsedMilliseconds).toBe(0); + expect(stopwatch.start()).toBe(true); + await sleep(100); + expect(stopwatch.start()).toBe(false); + expect(stopwatch.stop()).toBe(true); + expect(stopwatch.stop()).toBe(false); + expect(stopwatch.elapsedMilliseconds).toBeGreaterThan(50); + expect(stopwatch.elapsedMilliseconds).toBeLessThan(200); + stopwatch.reset(); + expect(stopwatch.elapsedMilliseconds).toBe(0); + }); + + test("LogginStopwatch executes callbacks on start and end", async () => { + let started = 0; + let stopped = 0; + let ms = 0; + const logger = createLogger(msg => { + if (msg.startsWith("start")) { + ++started; + } else if (msg.startsWith("stop")) { + ++stopped; + ms = +msg.split(" ")[1]; + } else { + throw new Error("Unrecognized message"); + } + }); + + const stopwatch = new LogginStopwatch(logger, "start", ms => `stop ${ms}`); + + expect(stopwatch.isRunning).toBe(false); + expect(stopwatch.elapsedMilliseconds).toBe(0); + + expect(stopwatch.start()).toBe(true); + expect(started).toBe(1); + expect(stopped).toBe(0); + + await sleep(100); + + expect(stopwatch.start()).toBe(false); + expect(started).toBe(1); + expect(stopped).toBe(0); + + expect(stopwatch.stop()).toBe(true); + expect(started).toBe(1); + expect(stopped).toBe(1); + + expect(stopwatch.stop()).toBe(false); + expect(started).toBe(1); + expect(stopped).toBe(1); + + expect(stopwatch.elapsedMilliseconds).toBeGreaterThan(50); + expect(stopwatch.elapsedMilliseconds).toBeLessThan(200); + expect(stopwatch.elapsedMilliseconds).toBe(ms); + + stopwatch.reset(); + expect(stopwatch.elapsedMilliseconds).toBe(0); + }); +});