grafana/public/app/plugins/panel/heatmap-old/specs/heatmap_data_converter.test.ts

398 lines
10 KiB
TypeScript

import { cloneDeep, each, map } from 'lodash';
import TimeSeries from 'app/core/time_series2';
import {
convertToHeatMap,
convertToCards,
histogramToHeatmap,
calculateBucketSize,
isHeatmapDataEqual,
} from '../heatmap_data_converter';
import { HeatmapData } from '../types';
describe('isHeatmapDataEqual', () => {
const ctx: any = {};
beforeEach(() => {
ctx.heatmapA = {
'1422774000000': {
x: 1422774000000,
buckets: {
'1': { y: 1, values: [1, 1.5] },
'2': { y: 2, values: [1] },
},
},
};
ctx.heatmapB = {
'1422774000000': {
x: 1422774000000,
buckets: {
'1': { y: 1, values: [1.5, 1] },
'2': { y: 2, values: [1] },
},
},
};
});
it('should proper compare objects', () => {
const heatmapC = cloneDeep(ctx.heatmapA);
heatmapC['1422774000000'].buckets['1'].values = [1, 1.5];
const heatmapD = cloneDeep(ctx.heatmapA);
heatmapD['1422774000000'].buckets['1'].values = [1.5, 1, 1.6];
const heatmapE = cloneDeep(ctx.heatmapA);
heatmapE['1422774000000'].buckets['1'].values = [1, 1.6];
const empty = {};
const emptyValues = cloneDeep(ctx.heatmapA);
emptyValues['1422774000000'].buckets['1'].values = [];
expect(isHeatmapDataEqual(ctx.heatmapA, ctx.heatmapB)).toBe(true);
expect(isHeatmapDataEqual(ctx.heatmapB, ctx.heatmapA)).toBe(true);
expect(isHeatmapDataEqual(ctx.heatmapA, heatmapC)).toBe(true);
expect(isHeatmapDataEqual(heatmapC, ctx.heatmapA)).toBe(true);
expect(isHeatmapDataEqual(ctx.heatmapA, heatmapD)).toBe(false);
expect(isHeatmapDataEqual(heatmapD, ctx.heatmapA)).toBe(false);
expect(isHeatmapDataEqual(ctx.heatmapA, heatmapE)).toBe(false);
expect(isHeatmapDataEqual(heatmapE, ctx.heatmapA)).toBe(false);
expect(isHeatmapDataEqual(empty, ctx.heatmapA)).toBe(false);
expect(isHeatmapDataEqual(ctx.heatmapA, empty)).toBe(false);
expect(isHeatmapDataEqual(emptyValues, ctx.heatmapA)).toBe(false);
expect(isHeatmapDataEqual(ctx.heatmapA, emptyValues)).toBe(false);
});
});
describe('calculateBucketSize', () => {
const ctx: any = {};
describe('when logBase is 1 (linear scale)', () => {
beforeEach(() => {
ctx.logBase = 1;
ctx.bounds_set = [
{ bounds: [], size: 0 },
{ bounds: [0], size: 0 },
{ bounds: [4], size: 4 },
{ bounds: [0, 1, 2, 3, 4], size: 1 },
{ bounds: [0, 1, 3, 5, 7], size: 1 },
{ bounds: [0, 3, 7, 9, 15], size: 2 },
{ bounds: [0, 7, 3, 15, 9], size: 2 },
{ bounds: [0, 5, 10, 15, 50], size: 5 },
];
});
it('should properly calculate bucket size', () => {
each(ctx.bounds_set, (b) => {
const bucketSize = calculateBucketSize(b.bounds, ctx.logBase);
expect(bucketSize).toBe(b.size);
});
});
});
describe('when logBase is 2', () => {
beforeEach(() => {
ctx.logBase = 2;
ctx.bounds_set = [
{ bounds: [], size: 0 },
{ bounds: [0], size: 0 },
{ bounds: [4], size: 4 },
{ bounds: [1, 2, 4, 8], size: 1 },
{ bounds: [1, Math.SQRT2, 2, 8, 16], size: 0.5 },
];
});
it('should properly calculate bucket size', () => {
each(ctx.bounds_set, (b) => {
const bucketSize = calculateBucketSize(b.bounds, ctx.logBase);
expect(isEqual(bucketSize, b.size)).toBe(true);
});
});
});
});
describe('HeatmapDataConverter', () => {
const ctx: any = {};
beforeEach(() => {
ctx.series = [];
ctx.series.push(
new TimeSeries({
datapoints: [
[1, 1422774000000],
[1, 1422774000010],
[2, 1422774060000],
],
alias: 'series1',
})
);
ctx.series.push(
new TimeSeries({
datapoints: [
[2, 1422774000000],
[2, 1422774000010],
[3, 1422774060000],
],
alias: 'series2',
})
);
ctx.series.push(
new TimeSeries({
datapoints: [
[5, 1422774000000],
[3, 1422774000010],
[4, 1422774060000],
],
alias: 'series3',
})
);
ctx.xBucketSize = 60000; // 60s
ctx.yBucketSize = 2;
ctx.logBase = 1;
});
describe('when logBase is 1 (linear scale)', () => {
beforeEach(() => {
ctx.logBase = 1;
});
it('should build proper heatmap data', () => {
const expectedHeatmap = {
'1422774000000': {
x: 1422774000000,
buckets: {
'0': {
y: 0,
values: [1, 1],
count: 2,
bounds: { bottom: 0, top: 2 },
},
'2': {
y: 2,
values: [2, 2, 3],
count: 3,
bounds: { bottom: 2, top: 4 },
},
'4': { y: 4, values: [5], count: 1, bounds: { bottom: 4, top: 6 } },
},
},
'1422774060000': {
x: 1422774060000,
buckets: {
'2': {
y: 2,
values: [2, 3],
count: 3,
bounds: { bottom: 2, top: 4 },
},
'4': { y: 4, values: [4], count: 1, bounds: { bottom: 4, top: 6 } },
},
},
};
const heatmap = convertToHeatMap(ctx.series, ctx.yBucketSize, ctx.xBucketSize, ctx.logBase);
expect(isHeatmapDataEqual(heatmap, expectedHeatmap)).toBe(true);
});
});
describe.skip('when logBase is 2', () => {
beforeEach(() => {
ctx.logBase = 2;
});
it('should build proper heatmap data', () => {
const expectedHeatmap = {
'1422774000000': {
x: 1422774000000,
buckets: {
'1': { y: 1, values: [1] },
'2': { y: 2, values: [2] },
},
},
'1422774060000': {
x: 1422774060000,
buckets: {
'2': { y: 2, values: [2, 3] },
},
},
};
const heatmap = convertToHeatMap(ctx.series, ctx.yBucketSize, ctx.xBucketSize, ctx.logBase);
expect(isHeatmapDataEqual(heatmap, expectedHeatmap)).toBe(true);
});
});
});
describe('Histogram converter', () => {
const ctx: any = {};
beforeEach(() => {
ctx.series = [];
ctx.series.push(
new TimeSeries({
datapoints: [
[1, 1422774000000],
[0, 1422774060000],
],
alias: '1',
label: '1',
})
);
ctx.series.push(
new TimeSeries({
datapoints: [
[5, 1422774000000],
[3, 1422774060000],
],
alias: '2',
label: '2',
})
);
ctx.series.push(
new TimeSeries({
datapoints: [
[0, 1422774000000],
[1, 1422774060000],
],
alias: '3',
label: '3',
})
);
});
describe('when converting histogram', () => {
beforeEach(() => {});
it('should build proper heatmap data', () => {
const expectedHeatmap: HeatmapData = {
'1422774000000': {
x: 1422774000000,
buckets: {
'0': {
y: 0,
count: 1,
bounds: { bottom: 0, top: null },
values: [],
points: [],
},
'1': {
y: 1,
count: 5,
bounds: { bottom: 1, top: null },
values: [],
points: [],
},
'2': {
y: 2,
count: 0,
bounds: { bottom: 2, top: null },
values: [],
points: [],
},
},
},
'1422774060000': {
x: 1422774060000,
buckets: {
'0': {
y: 0,
count: 0,
bounds: { bottom: 0, top: null },
values: [],
points: [],
},
'1': {
y: 1,
count: 3,
bounds: { bottom: 1, top: null },
values: [],
points: [],
},
'2': {
y: 2,
count: 1,
bounds: { bottom: 2, top: null },
values: [],
points: [],
},
},
},
};
const heatmap = histogramToHeatmap(ctx.series);
expect(heatmap).toEqual(expectedHeatmap);
});
it('should use bucket index as a bound', () => {
const heatmap = histogramToHeatmap(ctx.series);
const bucketLabels = map(heatmap['1422774000000'].buckets, (b, label) => label);
const bucketYs = map(heatmap['1422774000000'].buckets, 'y');
const bucketBottoms = map(heatmap['1422774000000'].buckets, (b) => b.bounds.bottom);
const expectedBounds = [0, 1, 2];
expect(bucketLabels).toEqual(map(expectedBounds, (b) => b.toString()));
expect(bucketYs).toEqual(expectedBounds);
expect(bucketBottoms).toEqual(expectedBounds);
});
});
});
describe('convertToCards', () => {
let buckets: HeatmapData = {};
beforeEach(() => {
buckets = {
'1422774000000': {
x: 1422774000000,
buckets: {
'1': { y: 1, values: [1], count: 1, bounds: {} },
'2': { y: 2, values: [2], count: 1, bounds: {} },
},
},
'1422774060000': {
x: 1422774060000,
buckets: {
'2': { y: 2, values: [2, 3], count: 2, bounds: {} },
},
},
};
});
it('should build proper cards data', () => {
const expectedCards = [
{ x: 1422774000000, y: 1, count: 1, values: [1], yBounds: {} },
{ x: 1422774000000, y: 2, count: 1, values: [2], yBounds: {} },
{ x: 1422774060000, y: 2, count: 2, values: [2, 3], yBounds: {} },
];
const res = convertToCards(buckets);
expect(res.cards).toMatchObject(expectedCards);
});
it('should build proper cards stats', () => {
const expectedStats = { min: 1, max: 2 };
const res = convertToCards(buckets);
expect(res.cardStats).toMatchObject(expectedStats);
});
});
/**
* Compare two numbers with given precision. Suitable for compare float numbers after conversions with precision loss.
* @param a
* @param b
* @param precision
*/
function isEqual(a: number, b: number, precision = 0.000001): boolean {
if (a === b) {
return true;
} else {
return Math.abs(1 - a / b) <= precision;
}
}