import * as fs from "fs"; import Histogram from "./Histogram"; import HistogramLogReader, { listTags } from "./HistogramLogReader"; import PackedHistogram from "./PackedHistogram"; import { initWebAssembly, WasmHistogram } from "./wasm"; const { floor } = Math; function checkNotNull(actual: T | null): asserts actual is T { expect(actual).not.toBeNull(); } describe("Histogram Log Reader", () => { let fileContent: string; let tagFileContent: string; let fileContentWithBaseTime: string; let fileContentWithoutHeader: string; let fileContentWithTrailingWhitespace: string; beforeAll(() => { // when using mutation testing tool stryker, source code // is copied in a sandbox directory without the test_files // directory... const runFromStryker = __dirname.includes("stryker"); const prefix = runFromStryker ? "../.." : "."; fileContent = fs.readFileSync( `${prefix}/test_files/jHiccup-2.0.7S.logV2.hlog`, "UTF-8" ); fileContentWithBaseTime = fs.readFileSync( `${prefix}/test_files/jHiccup-with-basetime-2.0.7S.logV2.hlog`, "UTF-8" ); fileContentWithoutHeader = fs.readFileSync( `${prefix}/test_files/jHiccup-no-header-2.0.7S.logV2.hlog`, "UTF-8" ); tagFileContent = fs.readFileSync( `${prefix}/test_files/tagged-Log.logV2.hlog`, "UTF-8" ); fileContentWithTrailingWhitespace = fs.readFileSync( `${prefix}/test_files/bug-whitespace.hlog`, "UTF-8" ); }); it("should update startTimeSec reading first histogram", () => { // given const reader = new HistogramLogReader(fileContent); // when reader.nextIntervalHistogram(); // then expect(reader.startTimeSec).toBe(1441812279.474); }); it("should read first histogram starting from the beginning", () => { // given const reader = new HistogramLogReader(fileContent); // when const histogram = reader.nextIntervalHistogram(); // then checkNotNull(histogram); // if mean is good, strong probability everything else is good as well expect(floor(histogram.mean)).toBe(301998); }); it("should read encoded histogram and use provided constructor", () => { // given const reader = new HistogramLogReader(fileContent, "packed"); // when const histogram = reader.nextIntervalHistogram(); // then checkNotNull(histogram); // if mean is good, strong probability everything else is good as well expect(floor(histogram.mean)).toBe(301998); }); it("should return null if no histogram in the logs", () => { // given const reader = new HistogramLogReader("# empty"); // when const histogram = reader.nextIntervalHistogram(); // then expect(histogram).toBeNull(); }); it("should return next histogram in the logs", () => { // given const reader = new HistogramLogReader(fileContent); reader.nextIntervalHistogram(); // when const histogram = reader.nextIntervalHistogram(); // then checkNotNull(histogram); // if mean is good, strong probability everything else is good as well expect(floor(histogram.mean)).toBe(293719); }); it("should return null if all histograms are after specified time range", () => { // given const reader = new HistogramLogReader(fileContent); // when const histogram = reader.nextIntervalHistogram(0.01, 0.1); // then expect(histogram).toBeNull(); }); it("should return null if all histograms are before specified time range", () => { // given const reader = new HistogramLogReader(fileContent); // when const histogram = reader.nextIntervalHistogram(62, 63); // then expect(histogram).toBeNull(); }); it("should parse histogram even if there are trailing whitespaces", () => { // given const reader = new HistogramLogReader(fileContentWithTrailingWhitespace); // when const histogram = reader.nextIntervalHistogram(); // then // no error }); it("should return histograms within specified time range", () => { // given const reader = new HistogramLogReader(fileContent); // when const firstHistogram = reader.nextIntervalHistogram(0, 2); const secondHistogram = reader.nextIntervalHistogram(0, 2); const thirdHistogram = reader.nextIntervalHistogram(0, 2); // then checkNotNull(firstHistogram); checkNotNull(secondHistogram); expect(thirdHistogram).toBeNull(); // if mean is good, strong probability everything else is good as well expect(floor(firstHistogram.mean)).toBe(301998); expect(floor(secondHistogram.mean)).toBe(293719); }); it("should set start timestamp on histogram", () => { // given const reader = new HistogramLogReader(fileContent); // when const histogram = reader.nextIntervalHistogram(); // then checkNotNull(histogram); expect(histogram.startTimeStampMsec).toBe(1441812279601); }); it("should set end timestamp on histogram", () => { // given const reader = new HistogramLogReader(fileContent); // when const histogram = reader.nextIntervalHistogram(); // then checkNotNull(histogram); expect(histogram.endTimeStampMsec).toBe(1441812280608); }); it("should parse tagged histogram", () => { // given const reader = new HistogramLogReader(tagFileContent); reader.nextIntervalHistogram(); // when const histogram = reader.nextIntervalHistogram(); // then checkNotNull(histogram); expect(histogram.tag).toBe("A"); expect(floor(histogram.mean)).toBe(301998); }); it("should use basetime to set timestamps on histogram", () => { // given const reader = new HistogramLogReader(fileContentWithBaseTime); // when const histogram = reader.nextIntervalHistogram(); // then checkNotNull(histogram); expect(histogram.startTimeStampMsec).toBe(1441812123250); expect(histogram.endTimeStampMsec).toBe(1441812124257); }); it("should default startTime using 1st observed time", () => { // given const reader = new HistogramLogReader(fileContentWithoutHeader); // when const histogram = reader.nextIntervalHistogram(); // then checkNotNull(histogram); expect(histogram.startTimeStampMsec).toBe(127); expect(histogram.endTimeStampMsec).toBe(1134); }); it("should list all the tags of a log file", () => { // given // when const tags = listTags(tagFileContent); // then expect(tags).toEqual(["NO TAG", "A"]); }); it("should list all the tags of a log file where all histograms are tagged", () => { // given const content = `#[Fake log chunk] #[Histogram log format version 1.2] #[StartTime: 1441812279.474 (seconds since epoch), Wed Sep 09 08:24:39 PDT 2015] "StartTimestamp","Interval_Length","Interval_Max","Interval_Compressed_Histogram" Tag=NOT-EMPTY,0.127,1.007,2.769,HISTFAAAAEV42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBEJISEuATEZMQ4uASkhIR4nrxg9v2lMaxhvMekILGZkKmcCAEf2CsI= Tag=A,0.127,1.007,2.769,HISTFAAAAEV42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBEJISEuATEZMQ4uASkhIR4nrxg9v2lMaxhvMekILGZkKmcCAEf2CsI= `; // when const tags = listTags(content); // then expect(tags).toEqual(["NOT-EMPTY", "A"]); }); describe("with WASM", () => { let accumulatedHistogram: Histogram; beforeAll(initWebAssembly); afterEach(() => { accumulatedHistogram.destroy(); }); it("should do the whole 9 yards just like the original Java version :-)", () => { // given const reader = new HistogramLogReader(fileContent, 32, true); accumulatedHistogram = WasmHistogram.build(); let histogram: Histogram | null; let histogramCount = 0; let totalCount = 0; // when while ((histogram = reader.nextIntervalHistogram()) != null) { histogramCount++; totalCount += histogram.totalCount; accumulatedHistogram.add(histogram as any); histogram.destroy(); } // then expect(histogramCount).toBe(62); expect(totalCount).toBe(48761); expect(accumulatedHistogram.getValueAtPercentile(99.9)).toBe(1745879039); expect(reader.startTimeSec).toBe(1441812279.474); }); }); });