source: trip-planner-front/node_modules/hdr-histogram-js/src/Histogram.spec.ts@ 571e0df

Last change on this file since 571e0df was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 17.3 KB
RevLine 
[6a3a178]1import { build } from ".";
2import JsHistogram from "./JsHistogram";
3import { NO_TAG } from "./Histogram";
4import Int32Histogram from "./Int32Histogram";
5import { initWebAssembly, WasmHistogram, initWebAssemblySync } from "./wasm";
6import Int8Histogram from "./Int8Histogram";
7import Histogram from "./Histogram";
8
9class HistogramForTests extends JsHistogram {
10 //constructor() {}
11
12 clearCounts() {}
13
14 incrementCountAtIndex(index: number): void {}
15
16 incrementTotalCount(): void {}
17
18 addToTotalCount(value: number) {}
19
20 setTotalCount(totalCount: number) {}
21
22 resize(newHighestTrackableValue: number): void {
23 this.establishSize(newHighestTrackableValue);
24 }
25
26 addToCountAtIndex(index: number, value: number): void {}
27
28 setCountAtIndex(index: number, value: number): void {}
29
30 getTotalCount() {
31 return 0;
32 }
33
34 getCountAtIndex(index: number): number {
35 return 0;
36 }
37
38 protected _getEstimatedFootprintInBytes() {
39 return 42;
40 }
41
42 copyCorrectedForCoordinatedOmission(
43 expectedIntervalBetweenValueSamples: number
44 ) {
45 return this;
46 }
47}
48
49describe("Histogram initialization", () => {
50 let histogram: JsHistogram;
51 beforeEach(() => {
52 histogram = new HistogramForTests(1, Number.MAX_SAFE_INTEGER, 3);
53 });
54
55 it("should set sub bucket size", () => {
56 expect(histogram.subBucketCount).toBe(2048);
57 });
58
59 it("should set resize to false when max value specified", () => {
60 expect(histogram.autoResize).toBe(false);
61 });
62
63 it("should compute counts array length", () => {
64 expect(histogram.countsArrayLength).toBe(45056);
65 });
66
67 it("should compute bucket count", () => {
68 expect(histogram.bucketCount).toBe(43);
69 });
70
71 it("should set min non zero value", () => {
72 expect(histogram.minNonZeroValue).toBe(Number.MAX_SAFE_INTEGER);
73 });
74
75 it("should set max value", () => {
76 expect(histogram.maxValue).toBe(0);
77 });
78});
79
80describe("Histogram recording values", () => {
81 it("should compute count index when value in first bucket", () => {
82 // given
83 const histogram = new HistogramForTests(1, Number.MAX_SAFE_INTEGER, 3);
84 // when
85 const index = histogram.countsArrayIndex(2000); // 2000 < 2048
86 expect(index).toBe(2000);
87 });
88
89 it("should compute count index when value outside first bucket", () => {
90 // given
91 const histogram = new HistogramForTests(1, Number.MAX_SAFE_INTEGER, 3);
92 // when
93 const index = histogram.countsArrayIndex(2050); // 2050 > 2048
94 // then
95 expect(index).toBe(2049);
96 });
97
98 it("should compute count index taking into account lowest discernible value", () => {
99 // given
100 const histogram = new HistogramForTests(2000, Number.MAX_SAFE_INTEGER, 2);
101 // when
102 const index = histogram.countsArrayIndex(16000);
103 // then
104 expect(index).toBe(15);
105 });
106
107 it("should compute count index of a big value taking into account lowest discernible value", () => {
108 // given
109 const histogram = new HistogramForTests(2000, Number.MAX_SAFE_INTEGER, 2);
110 // when
111 const bigValue = Number.MAX_SAFE_INTEGER - 1;
112 const index = histogram.countsArrayIndex(bigValue);
113 // then
114 expect(index).toBe(4735);
115 });
116
117 it("should update min non zero value", () => {
118 // given
119 const histogram = new HistogramForTests(1, Number.MAX_SAFE_INTEGER, 3);
120 // when
121 histogram.recordValue(123);
122 // then
123 expect(histogram.minNonZeroValue).toBe(123);
124 });
125
126 it("should update max value", () => {
127 // given
128 const histogram = new HistogramForTests(1, Number.MAX_SAFE_INTEGER, 3);
129 // when
130 histogram.recordValue(123);
131 // then
132 expect(histogram.maxValue).toBe(123);
133 });
134
135 it("should throw an error when value bigger than highest trackable value", () => {
136 // given
137 const histogram = new HistogramForTests(1, 4096, 3);
138 // when then
139 expect(() => histogram.recordValue(9000)).toThrowError();
140 });
141
142 it("should not throw an error when autoresize enable and value bigger than highest trackable value", () => {
143 // given
144 const histogram = new HistogramForTests(1, 4096, 3);
145 histogram.autoResize = true;
146 // when then
147 expect(() => histogram.recordValue(9000)).not.toThrowError();
148 });
149
150 it("should increase counts array size when recording value bigger than highest trackable value", () => {
151 // given
152 const histogram = new HistogramForTests(1, 4096, 3);
153 histogram.autoResize = true;
154 // when
155 histogram.recordValue(9000);
156 // then
157 expect(histogram.highestTrackableValue).toBeGreaterThan(9000);
158 });
159});
160
161describe("Histogram computing statistics", () => {
162 const histogram = new Int32Histogram(1, Number.MAX_SAFE_INTEGER, 3);
163
164 it("should compute mean value", () => {
165 // given
166 histogram.reset();
167 // when
168 histogram.recordValue(25);
169 histogram.recordValue(50);
170 histogram.recordValue(75);
171 // then
172 expect(histogram.mean).toBe(50);
173 });
174
175 it("should compute standard deviation", () => {
176 // given
177 histogram.reset();
178 // when
179 histogram.recordValue(25);
180 histogram.recordValue(50);
181 histogram.recordValue(75);
182 // then
183 expect(histogram.stdDeviation).toBeGreaterThan(20.4124);
184 expect(histogram.stdDeviation).toBeLessThan(20.4125);
185 });
186
187 it("should compute percentile distribution", () => {
188 // given
189 histogram.reset();
190 // when
191 histogram.recordValue(25);
192 histogram.recordValue(50);
193 histogram.recordValue(75);
194 // then
195 const expectedResult = ` Value Percentile TotalCount 1/(1-Percentile)
196
197 25.000 0.000000000000 1 1.00
198 25.000 0.100000000000 1 1.11
199 25.000 0.200000000000 1 1.25
200 25.000 0.300000000000 1 1.43
201 50.000 0.400000000000 2 1.67
202 50.000 0.500000000000 2 2.00
203 50.000 0.550000000000 2 2.22
204 50.000 0.600000000000 2 2.50
205 50.000 0.650000000000 2 2.86
206 75.000 0.700000000000 3 3.33
207 75.000 1.000000000000 3
208#[Mean = 50.000, StdDeviation = 20.412]
209#[Max = 75.000, Total count = 3]
210#[Buckets = 43, SubBuckets = 2048]
211`;
212 expect(histogram.outputPercentileDistribution()).toBe(expectedResult);
213 });
214
215 it("should compute percentile distribution in csv format", () => {
216 // given
217 histogram.reset();
218 // when
219 histogram.recordValue(25);
220 histogram.recordValue(50);
221 histogram.recordValue(75);
222 // then
223 const expectedResult = `"Value","Percentile","TotalCount","1/(1-Percentile)"
22425.000,0.000000000000,1,1.00
22525.000,0.100000000000,1,1.11
22625.000,0.200000000000,1,1.25
22725.000,0.300000000000,1,1.43
22850.000,0.400000000000,2,1.67
22950.000,0.500000000000,2,2.00
23050.000,0.550000000000,2,2.22
23150.000,0.600000000000,2,2.50
23250.000,0.650000000000,2,2.86
23375.000,0.700000000000,3,3.33
23475.000,1.000000000000,3,Infinity
235`;
236 expect(
237 histogram.outputPercentileDistribution(undefined, undefined, true)
238 ).toBe(expectedResult);
239 });
240
241 it("should compute percentile distribution in JSON format with rounding according to number of significant digits", () => {
242 // given
243 histogram.reset();
244 // when
245 histogram.recordValue(25042);
246 histogram.recordValue(50042);
247 histogram.recordValue(75042);
248 // then
249 const { summary } = histogram;
250 expect(summary.p50).toEqual(50000);
251 });
252});
253
254describe("Histogram correcting coordinated omissions", () => {
255 const histogram = new Int32Histogram(1, Number.MAX_SAFE_INTEGER, 3);
256
257 it("should generate additional values when recording", () => {
258 // given
259 histogram.reset();
260 // when
261 histogram.recordValueWithExpectedInterval(207, 100);
262 // then
263 expect(histogram.totalCount).toBe(2);
264 expect(histogram.minNonZeroValue).toBe(107);
265 expect(histogram.maxValue).toBe(207);
266 });
267
268 it("should generate additional values when correcting after recording", () => {
269 // given
270 histogram.reset();
271 histogram.recordValue(207);
272 histogram.recordValue(207);
273 // when
274 const correctedHistogram = histogram.copyCorrectedForCoordinatedOmission(
275 100
276 );
277 // then
278 expect(correctedHistogram.totalCount).toBe(4);
279 expect(correctedHistogram.minNonZeroValue).toBe(107);
280 expect(correctedHistogram.maxValue).toBe(207);
281 });
282
283 it("should not generate additional values when correcting after recording", () => {
284 // given
285 histogram.reset();
286 histogram.recordValue(207);
287 histogram.recordValue(207);
288 // when
289 const correctedHistogram = histogram.copyCorrectedForCoordinatedOmission(
290 1000
291 );
292 // then
293 expect(correctedHistogram.totalCount).toBe(2);
294 expect(correctedHistogram.minNonZeroValue).toBe(207);
295 expect(correctedHistogram.maxValue).toBe(207);
296 });
297});
298
299describe("WASM Histogram not initialized", () => {
300 it("should throw a clear error message", () => {
301 expect(() => build({ useWebAssembly: true })).toThrow(
302 "WebAssembly is not ready yet"
303 );
304 expect(() => WasmHistogram.build()).toThrow("WebAssembly is not ready yet");
305 expect(() => WasmHistogram.decode(null as any)).toThrow(
306 "WebAssembly is not ready yet"
307 );
308 });
309});
310
311describe("WASM Histogram not happy path", () => {
312 beforeEach(initWebAssemblySync);
313 it("should throw a clear error message when used after destroy", () => {
314 const destroyedHistogram = build({ useWebAssembly: true });
315 destroyedHistogram.destroy();
316 expect(() => destroyedHistogram.recordValue(42)).toThrow(
317 "Cannot use a destroyed histogram"
318 );
319 });
320 it("should not crash when displayed after destroy", () => {
321 const destroyedHistogram = build({ useWebAssembly: true });
322 destroyedHistogram.destroy();
323 expect(destroyedHistogram + "").toEqual("Destroyed WASM histogram");
324 });
325 it("should throw a clear error message when added to a JS regular Histogram", () => {
326 const wasmHistogram = build({ useWebAssembly: true });
327 const jsHistogram = build({ useWebAssembly: false });
328 expect(() => jsHistogram.add(wasmHistogram)).toThrow(
329 "Cannot add a WASM histogram to a regular JS histogram"
330 );
331 });
332 it("should throw a clear error message when trying to add a JS regular Histogram", () => {
333 const wasmHistogram = build({ useWebAssembly: true });
334 const jsHistogram = build({ useWebAssembly: false });
335 expect(() => wasmHistogram.add(jsHistogram)).toThrow(
336 "Cannot add a regular JS histogram to a WASM histogram"
337 );
338 });
339
340 it("should throw a clear error message when substracted to a JS regular Histogram", () => {
341 const wasmHistogram = build({ useWebAssembly: true });
342 const jsHistogram = build({ useWebAssembly: false });
343 expect(() => jsHistogram.subtract(wasmHistogram)).toThrow(
344 "Cannot subtract a WASM histogram to a regular JS histogram"
345 );
346 });
347 it("should throw a clear error message when trying to add a JS regular Histogram", () => {
348 const wasmHistogram = build({ useWebAssembly: true });
349 const jsHistogram = build({ useWebAssembly: false });
350 expect(() => wasmHistogram.subtract(jsHistogram)).toThrow(
351 "Cannot subtract a regular JS histogram to a WASM histogram"
352 );
353 });
354});
355
356describe("WASM estimated memory footprint", () => {
357 let wasmHistogram: Histogram;
358 beforeAll(initWebAssembly);
359 afterEach(() => wasmHistogram.destroy());
360
361 it("should be a little bit more than js footprint for packed histograms", () => {
362 wasmHistogram = build({ useWebAssembly: true, bitBucketSize: "packed" });
363 expect(wasmHistogram.estimatedFootprintInBytes).toBeGreaterThan(
364 build({ bitBucketSize: "packed" }).estimatedFootprintInBytes
365 );
366 });
367});
368
369describe("WASM Histogram correcting coordinated omissions", () => {
370 let histogram: Histogram;
371
372 beforeAll(initWebAssembly);
373 beforeEach(() => {
374 histogram = build({ useWebAssembly: true });
375 });
376 afterEach(() => histogram.destroy());
377
378 it("should generate additional values when recording", () => {
379 // given
380 histogram.reset();
381 // when
382 histogram.recordValueWithExpectedInterval(207, 100);
383 // then
384 expect(histogram.totalCount).toBe(2);
385 expect(histogram.minNonZeroValue).toBe(107);
386 expect(histogram.maxValue).toBe(207);
387 });
388
389 it("should generate additional values when correcting after recording", () => {
390 // given
391 histogram.reset();
392 histogram.recordValue(207);
393 histogram.recordValue(207);
394 // when
395 const correctedHistogram = histogram.copyCorrectedForCoordinatedOmission(
396 100
397 );
398 // then
399 expect(correctedHistogram.totalCount).toBe(4);
400 expect(correctedHistogram.minNonZeroValue).toBe(107);
401 expect(correctedHistogram.maxValue).toBe(207);
402 });
403
404 it("should not generate additional values when correcting after recording", () => {
405 // given
406 histogram.reset();
407 histogram.recordValue(207);
408 histogram.recordValue(207);
409 // when
410 const correctedHistogram = histogram.copyCorrectedForCoordinatedOmission(
411 1000
412 );
413 // then
414 expect(correctedHistogram.totalCount).toBe(2);
415 expect(correctedHistogram.minNonZeroValue).toBe(207);
416 expect(correctedHistogram.maxValue).toBe(207);
417 });
418});
419
420describe("Histogram add & substract", () => {
421 beforeAll(initWebAssembly);
422
423 it("should add histograms of same size", () => {
424 // given
425 const histogram = new Int32Histogram(1, Number.MAX_SAFE_INTEGER, 2);
426 const histogram2 = new Int32Histogram(1, Number.MAX_SAFE_INTEGER, 2);
427 histogram.recordValue(42);
428 histogram2.recordValue(158);
429 // when
430 histogram.add(histogram2);
431 // then
432 expect(histogram.totalCount).toBe(2);
433 expect(histogram.mean).toBe(100);
434 });
435
436 it("should add histograms of different sizes & precisions", () => {
437 // given
438 const histogram = build({
439 lowestDiscernibleValue: 1,
440 highestTrackableValue: 1024,
441 autoResize: true,
442 numberOfSignificantValueDigits: 2,
443 bitBucketSize: "packed",
444 useWebAssembly: true,
445 });
446 const histogram2 = build({
447 lowestDiscernibleValue: 1,
448 highestTrackableValue: 1024,
449 autoResize: true,
450 numberOfSignificantValueDigits: 3,
451 bitBucketSize: 32,
452 useWebAssembly: true,
453 });
454 //histogram2.autoResize = true;
455 histogram.recordValue(42000);
456 histogram2.recordValue(1000);
457 // when
458 histogram.add(histogram2);
459 // then
460 expect(histogram.totalCount).toBe(2);
461 expect(Math.floor(histogram.mean / 100)).toBe(215);
462 });
463
464 it("should add histograms of different sizes", () => {
465 // given
466 const histogram = new Int32Histogram(1, Number.MAX_SAFE_INTEGER, 2);
467 const histogram2 = new Int32Histogram(1, 1024, 2);
468 histogram2.autoResize = true;
469 histogram.recordValue(42000);
470 histogram2.recordValue(1000);
471 // when
472 histogram.add(histogram2);
473 // then
474 expect(histogram.totalCount).toBe(2);
475 expect(Math.floor(histogram.mean / 100)).toBe(215);
476 });
477
478 it("should be equal when another histogram of lower precision is added then subtracted", () => {
479 // given
480 const histogram = build({ numberOfSignificantValueDigits: 5 });
481 const histogram2 = build({ numberOfSignificantValueDigits: 3 });
482 histogram.recordValue(100);
483 histogram2.recordValue(42000);
484 // when
485 const before = histogram.summary;
486 histogram.add(histogram2);
487 histogram.subtract(histogram2);
488 // then
489 expect(histogram.summary).toStrictEqual(before);
490 });
491
492 it("should update percentiles when another histogram of same characteristics is substracted", () => {
493 // given
494 const histogram = build({ numberOfSignificantValueDigits: 3 });
495 const histogram2 = build({ numberOfSignificantValueDigits: 3 });
496 histogram.recordValueWithCount(100, 2);
497 histogram2.recordValueWithCount(100, 1);
498 histogram.recordValueWithCount(200, 2);
499 histogram2.recordValueWithCount(200, 1);
500 histogram.recordValueWithCount(300, 2);
501 histogram2.recordValueWithCount(300, 1);
502 // when
503 histogram.subtract(histogram2);
504 // then
505 expect(histogram.getValueAtPercentile(50)).toBe(200);
506 });
507});
508
509describe("Histogram clearing support", () => {
510 beforeAll(initWebAssembly);
511
512 it("should reset data in order to reuse histogram", () => {
513 // given
514 const histogram = build({
515 lowestDiscernibleValue: 1,
516 highestTrackableValue: Number.MAX_SAFE_INTEGER,
517 numberOfSignificantValueDigits: 5,
518 useWebAssembly: true,
519 });
520 histogram.startTimeStampMsec = 42;
521 histogram.endTimeStampMsec = 56;
522 histogram.tag = "blabla";
523 histogram.recordValue(1000);
524 // when
525 histogram.reset();
526 // then
527 expect(histogram.totalCount).toBe(0);
528 expect(histogram.startTimeStampMsec).toBe(0);
529 expect(histogram.endTimeStampMsec).toBe(0);
530 expect(histogram.tag).toBe(NO_TAG);
531 expect(histogram.maxValue).toBe(0);
532 expect(histogram.minNonZeroValue).toBeGreaterThan(Number.MAX_SAFE_INTEGER);
533 expect(histogram.getValueAtPercentile(99.999)).toBe(0);
534 });
535
536 it("should behave as new when reseted", () => {
537 // given
538 const histogram = build({
539 lowestDiscernibleValue: 1,
540 highestTrackableValue: 15000,
541 numberOfSignificantValueDigits: 2,
542 });
543 const histogram2 = build({
544 lowestDiscernibleValue: 1,
545 highestTrackableValue: 15000,
546 numberOfSignificantValueDigits: 2,
547 });
548 histogram.recordValue(1);
549 histogram.recordValue(100);
550 histogram.recordValue(2000);
551 histogram.reset();
552 // when
553 histogram.recordValue(1000);
554 histogram2.recordValue(1000);
555
556 // then
557 expect(histogram.mean).toBe(histogram2.mean);
558 });
559});
Note: See TracBrowser for help on using the repository browser.