"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const _1 = require("."); const JsHistogram_1 = require("./JsHistogram"); const Histogram_1 = require("./Histogram"); const Int32Histogram_1 = require("./Int32Histogram"); const wasm_1 = require("./wasm"); class HistogramForTests extends JsHistogram_1.default { //constructor() {} clearCounts() { } incrementCountAtIndex(index) { } incrementTotalCount() { } addToTotalCount(value) { } setTotalCount(totalCount) { } resize(newHighestTrackableValue) { this.establishSize(newHighestTrackableValue); } addToCountAtIndex(index, value) { } setCountAtIndex(index, value) { } getTotalCount() { return 0; } getCountAtIndex(index) { return 0; } _getEstimatedFootprintInBytes() { return 42; } copyCorrectedForCoordinatedOmission(expectedIntervalBetweenValueSamples) { return this; } } describe("Histogram initialization", () => { let histogram; beforeEach(() => { histogram = new HistogramForTests(1, Number.MAX_SAFE_INTEGER, 3); }); it("should set sub bucket size", () => { expect(histogram.subBucketCount).toBe(2048); }); it("should set resize to false when max value specified", () => { expect(histogram.autoResize).toBe(false); }); it("should compute counts array length", () => { expect(histogram.countsArrayLength).toBe(45056); }); it("should compute bucket count", () => { expect(histogram.bucketCount).toBe(43); }); it("should set min non zero value", () => { expect(histogram.minNonZeroValue).toBe(Number.MAX_SAFE_INTEGER); }); it("should set max value", () => { expect(histogram.maxValue).toBe(0); }); }); describe("Histogram recording values", () => { it("should compute count index when value in first bucket", () => { // given const histogram = new HistogramForTests(1, Number.MAX_SAFE_INTEGER, 3); // when const index = histogram.countsArrayIndex(2000); // 2000 < 2048 expect(index).toBe(2000); }); it("should compute count index when value outside first bucket", () => { // given const histogram = new HistogramForTests(1, Number.MAX_SAFE_INTEGER, 3); // when const index = histogram.countsArrayIndex(2050); // 2050 > 2048 // then expect(index).toBe(2049); }); it("should compute count index taking into account lowest discernible value", () => { // given const histogram = new HistogramForTests(2000, Number.MAX_SAFE_INTEGER, 2); // when const index = histogram.countsArrayIndex(16000); // then expect(index).toBe(15); }); it("should compute count index of a big value taking into account lowest discernible value", () => { // given const histogram = new HistogramForTests(2000, Number.MAX_SAFE_INTEGER, 2); // when const bigValue = Number.MAX_SAFE_INTEGER - 1; const index = histogram.countsArrayIndex(bigValue); // then expect(index).toBe(4735); }); it("should update min non zero value", () => { // given const histogram = new HistogramForTests(1, Number.MAX_SAFE_INTEGER, 3); // when histogram.recordValue(123); // then expect(histogram.minNonZeroValue).toBe(123); }); it("should update max value", () => { // given const histogram = new HistogramForTests(1, Number.MAX_SAFE_INTEGER, 3); // when histogram.recordValue(123); // then expect(histogram.maxValue).toBe(123); }); it("should throw an error when value bigger than highest trackable value", () => { // given const histogram = new HistogramForTests(1, 4096, 3); // when then expect(() => histogram.recordValue(9000)).toThrowError(); }); it("should not throw an error when autoresize enable and value bigger than highest trackable value", () => { // given const histogram = new HistogramForTests(1, 4096, 3); histogram.autoResize = true; // when then expect(() => histogram.recordValue(9000)).not.toThrowError(); }); it("should increase counts array size when recording value bigger than highest trackable value", () => { // given const histogram = new HistogramForTests(1, 4096, 3); histogram.autoResize = true; // when histogram.recordValue(9000); // then expect(histogram.highestTrackableValue).toBeGreaterThan(9000); }); }); describe("Histogram computing statistics", () => { const histogram = new Int32Histogram_1.default(1, Number.MAX_SAFE_INTEGER, 3); it("should compute mean value", () => { // given histogram.reset(); // when histogram.recordValue(25); histogram.recordValue(50); histogram.recordValue(75); // then expect(histogram.mean).toBe(50); }); it("should compute standard deviation", () => { // given histogram.reset(); // when histogram.recordValue(25); histogram.recordValue(50); histogram.recordValue(75); // then expect(histogram.stdDeviation).toBeGreaterThan(20.4124); expect(histogram.stdDeviation).toBeLessThan(20.4125); }); it("should compute percentile distribution", () => { // given histogram.reset(); // when histogram.recordValue(25); histogram.recordValue(50); histogram.recordValue(75); // then const expectedResult = ` Value Percentile TotalCount 1/(1-Percentile) 25.000 0.000000000000 1 1.00 25.000 0.100000000000 1 1.11 25.000 0.200000000000 1 1.25 25.000 0.300000000000 1 1.43 50.000 0.400000000000 2 1.67 50.000 0.500000000000 2 2.00 50.000 0.550000000000 2 2.22 50.000 0.600000000000 2 2.50 50.000 0.650000000000 2 2.86 75.000 0.700000000000 3 3.33 75.000 1.000000000000 3 #[Mean = 50.000, StdDeviation = 20.412] #[Max = 75.000, Total count = 3] #[Buckets = 43, SubBuckets = 2048] `; expect(histogram.outputPercentileDistribution()).toBe(expectedResult); }); it("should compute percentile distribution in csv format", () => { // given histogram.reset(); // when histogram.recordValue(25); histogram.recordValue(50); histogram.recordValue(75); // then const expectedResult = `"Value","Percentile","TotalCount","1/(1-Percentile)" 25.000,0.000000000000,1,1.00 25.000,0.100000000000,1,1.11 25.000,0.200000000000,1,1.25 25.000,0.300000000000,1,1.43 50.000,0.400000000000,2,1.67 50.000,0.500000000000,2,2.00 50.000,0.550000000000,2,2.22 50.000,0.600000000000,2,2.50 50.000,0.650000000000,2,2.86 75.000,0.700000000000,3,3.33 75.000,1.000000000000,3,Infinity `; expect(histogram.outputPercentileDistribution(undefined, undefined, true)).toBe(expectedResult); }); it("should compute percentile distribution in JSON format with rounding according to number of significant digits", () => { // given histogram.reset(); // when histogram.recordValue(25042); histogram.recordValue(50042); histogram.recordValue(75042); // then const { summary } = histogram; expect(summary.p50).toEqual(50000); }); }); describe("Histogram correcting coordinated omissions", () => { const histogram = new Int32Histogram_1.default(1, Number.MAX_SAFE_INTEGER, 3); it("should generate additional values when recording", () => { // given histogram.reset(); // when histogram.recordValueWithExpectedInterval(207, 100); // then expect(histogram.totalCount).toBe(2); expect(histogram.minNonZeroValue).toBe(107); expect(histogram.maxValue).toBe(207); }); it("should generate additional values when correcting after recording", () => { // given histogram.reset(); histogram.recordValue(207); histogram.recordValue(207); // when const correctedHistogram = histogram.copyCorrectedForCoordinatedOmission(100); // then expect(correctedHistogram.totalCount).toBe(4); expect(correctedHistogram.minNonZeroValue).toBe(107); expect(correctedHistogram.maxValue).toBe(207); }); it("should not generate additional values when correcting after recording", () => { // given histogram.reset(); histogram.recordValue(207); histogram.recordValue(207); // when const correctedHistogram = histogram.copyCorrectedForCoordinatedOmission(1000); // then expect(correctedHistogram.totalCount).toBe(2); expect(correctedHistogram.minNonZeroValue).toBe(207); expect(correctedHistogram.maxValue).toBe(207); }); }); describe("WASM Histogram not initialized", () => { it("should throw a clear error message", () => { expect(() => _1.build({ useWebAssembly: true })).toThrow("WebAssembly is not ready yet"); expect(() => wasm_1.WasmHistogram.build()).toThrow("WebAssembly is not ready yet"); expect(() => wasm_1.WasmHistogram.decode(null)).toThrow("WebAssembly is not ready yet"); }); }); describe("WASM Histogram not happy path", () => { beforeEach(wasm_1.initWebAssemblySync); it("should throw a clear error message when used after destroy", () => { const destroyedHistogram = _1.build({ useWebAssembly: true }); destroyedHistogram.destroy(); expect(() => destroyedHistogram.recordValue(42)).toThrow("Cannot use a destroyed histogram"); }); it("should not crash when displayed after destroy", () => { const destroyedHistogram = _1.build({ useWebAssembly: true }); destroyedHistogram.destroy(); expect(destroyedHistogram + "").toEqual("Destroyed WASM histogram"); }); it("should throw a clear error message when added to a JS regular Histogram", () => { const wasmHistogram = _1.build({ useWebAssembly: true }); const jsHistogram = _1.build({ useWebAssembly: false }); expect(() => jsHistogram.add(wasmHistogram)).toThrow("Cannot add a WASM histogram to a regular JS histogram"); }); it("should throw a clear error message when trying to add a JS regular Histogram", () => { const wasmHistogram = _1.build({ useWebAssembly: true }); const jsHistogram = _1.build({ useWebAssembly: false }); expect(() => wasmHistogram.add(jsHistogram)).toThrow("Cannot add a regular JS histogram to a WASM histogram"); }); it("should throw a clear error message when substracted to a JS regular Histogram", () => { const wasmHistogram = _1.build({ useWebAssembly: true }); const jsHistogram = _1.build({ useWebAssembly: false }); expect(() => jsHistogram.subtract(wasmHistogram)).toThrow("Cannot subtract a WASM histogram to a regular JS histogram"); }); it("should throw a clear error message when trying to add a JS regular Histogram", () => { const wasmHistogram = _1.build({ useWebAssembly: true }); const jsHistogram = _1.build({ useWebAssembly: false }); expect(() => wasmHistogram.subtract(jsHistogram)).toThrow("Cannot subtract a regular JS histogram to a WASM histogram"); }); }); describe("WASM estimated memory footprint", () => { let wasmHistogram; beforeAll(wasm_1.initWebAssembly); afterEach(() => wasmHistogram.destroy()); it("should be a little bit more than js footprint for packed histograms", () => { wasmHistogram = _1.build({ useWebAssembly: true, bitBucketSize: "packed" }); expect(wasmHistogram.estimatedFootprintInBytes).toBeGreaterThan(_1.build({ bitBucketSize: "packed" }).estimatedFootprintInBytes); }); }); describe("WASM Histogram correcting coordinated omissions", () => { let histogram; beforeAll(wasm_1.initWebAssembly); beforeEach(() => { histogram = _1.build({ useWebAssembly: true }); }); afterEach(() => histogram.destroy()); it("should generate additional values when recording", () => { // given histogram.reset(); // when histogram.recordValueWithExpectedInterval(207, 100); // then expect(histogram.totalCount).toBe(2); expect(histogram.minNonZeroValue).toBe(107); expect(histogram.maxValue).toBe(207); }); it("should generate additional values when correcting after recording", () => { // given histogram.reset(); histogram.recordValue(207); histogram.recordValue(207); // when const correctedHistogram = histogram.copyCorrectedForCoordinatedOmission(100); // then expect(correctedHistogram.totalCount).toBe(4); expect(correctedHistogram.minNonZeroValue).toBe(107); expect(correctedHistogram.maxValue).toBe(207); }); it("should not generate additional values when correcting after recording", () => { // given histogram.reset(); histogram.recordValue(207); histogram.recordValue(207); // when const correctedHistogram = histogram.copyCorrectedForCoordinatedOmission(1000); // then expect(correctedHistogram.totalCount).toBe(2); expect(correctedHistogram.minNonZeroValue).toBe(207); expect(correctedHistogram.maxValue).toBe(207); }); }); describe("Histogram add & substract", () => { beforeAll(wasm_1.initWebAssembly); it("should add histograms of same size", () => { // given const histogram = new Int32Histogram_1.default(1, Number.MAX_SAFE_INTEGER, 2); const histogram2 = new Int32Histogram_1.default(1, Number.MAX_SAFE_INTEGER, 2); histogram.recordValue(42); histogram2.recordValue(158); // when histogram.add(histogram2); // then expect(histogram.totalCount).toBe(2); expect(histogram.mean).toBe(100); }); it("should add histograms of different sizes & precisions", () => { // given const histogram = _1.build({ lowestDiscernibleValue: 1, highestTrackableValue: 1024, autoResize: true, numberOfSignificantValueDigits: 2, bitBucketSize: "packed", useWebAssembly: true, }); const histogram2 = _1.build({ lowestDiscernibleValue: 1, highestTrackableValue: 1024, autoResize: true, numberOfSignificantValueDigits: 3, bitBucketSize: 32, useWebAssembly: true, }); //histogram2.autoResize = true; histogram.recordValue(42000); histogram2.recordValue(1000); // when histogram.add(histogram2); // then expect(histogram.totalCount).toBe(2); expect(Math.floor(histogram.mean / 100)).toBe(215); }); it("should add histograms of different sizes", () => { // given const histogram = new Int32Histogram_1.default(1, Number.MAX_SAFE_INTEGER, 2); const histogram2 = new Int32Histogram_1.default(1, 1024, 2); histogram2.autoResize = true; histogram.recordValue(42000); histogram2.recordValue(1000); // when histogram.add(histogram2); // then expect(histogram.totalCount).toBe(2); expect(Math.floor(histogram.mean / 100)).toBe(215); }); it("should be equal when another histogram of lower precision is added then subtracted", () => { // given const histogram = _1.build({ numberOfSignificantValueDigits: 5 }); const histogram2 = _1.build({ numberOfSignificantValueDigits: 3 }); histogram.recordValue(100); histogram2.recordValue(42000); // when const before = histogram.summary; histogram.add(histogram2); histogram.subtract(histogram2); // then expect(histogram.summary).toStrictEqual(before); }); it("should update percentiles when another histogram of same characteristics is substracted", () => { // given const histogram = _1.build({ numberOfSignificantValueDigits: 3 }); const histogram2 = _1.build({ numberOfSignificantValueDigits: 3 }); histogram.recordValueWithCount(100, 2); histogram2.recordValueWithCount(100, 1); histogram.recordValueWithCount(200, 2); histogram2.recordValueWithCount(200, 1); histogram.recordValueWithCount(300, 2); histogram2.recordValueWithCount(300, 1); // when histogram.subtract(histogram2); // then expect(histogram.getValueAtPercentile(50)).toBe(200); }); }); describe("Histogram clearing support", () => { beforeAll(wasm_1.initWebAssembly); it("should reset data in order to reuse histogram", () => { // given const histogram = _1.build({ lowestDiscernibleValue: 1, highestTrackableValue: Number.MAX_SAFE_INTEGER, numberOfSignificantValueDigits: 5, useWebAssembly: true, }); histogram.startTimeStampMsec = 42; histogram.endTimeStampMsec = 56; histogram.tag = "blabla"; histogram.recordValue(1000); // when histogram.reset(); // then expect(histogram.totalCount).toBe(0); expect(histogram.startTimeStampMsec).toBe(0); expect(histogram.endTimeStampMsec).toBe(0); expect(histogram.tag).toBe(Histogram_1.NO_TAG); expect(histogram.maxValue).toBe(0); expect(histogram.minNonZeroValue).toBeGreaterThan(Number.MAX_SAFE_INTEGER); expect(histogram.getValueAtPercentile(99.999)).toBe(0); }); it("should behave as new when reseted", () => { // given const histogram = _1.build({ lowestDiscernibleValue: 1, highestTrackableValue: 15000, numberOfSignificantValueDigits: 2, }); const histogram2 = _1.build({ lowestDiscernibleValue: 1, highestTrackableValue: 15000, numberOfSignificantValueDigits: 2, }); histogram.recordValue(1); histogram.recordValue(100); histogram.recordValue(2000); histogram.reset(); // when histogram.recordValue(1000); histogram2.recordValue(1000); // then expect(histogram.mean).toBe(histogram2.mean); }); }); //# sourceMappingURL=Histogram.spec.js.map