heatmap_data_converter.jest.ts 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. import _ from 'lodash';
  2. import { describe, beforeEach, it, expect } from '../../../../../test/lib/common';
  3. import TimeSeries from 'app/core/time_series2';
  4. import {convertToHeatMap, convertToCards, elasticHistogramToHeatmap,
  5. calculateBucketSize, isHeatmapDataEqual} from '../heatmap_data_converter';
  6. describe('isHeatmapDataEqual', () => {
  7. let ctx: any = {};
  8. beforeEach(() => {
  9. ctx.heatmapA = {
  10. '1422774000000': {
  11. x: 1422774000000,
  12. buckets: {
  13. '1': { y: 1, values: [1, 1.5] },
  14. '2': { y: 2, values: [1] }
  15. }
  16. }
  17. };
  18. ctx.heatmapB = {
  19. '1422774000000': {
  20. x: 1422774000000,
  21. buckets: {
  22. '1': { y: 1, values: [1.5, 1] },
  23. '2': { y: 2, values: [1] }
  24. }
  25. }
  26. };
  27. });
  28. it('should proper compare objects', () => {
  29. let heatmapC = _.cloneDeep(ctx.heatmapA);
  30. heatmapC['1422774000000'].buckets['1'].values = [1, 1.5];
  31. let heatmapD = _.cloneDeep(ctx.heatmapA);
  32. heatmapD['1422774000000'].buckets['1'].values = [1.5, 1, 1.6];
  33. let heatmapE = _.cloneDeep(ctx.heatmapA);
  34. heatmapE['1422774000000'].buckets['1'].values = [1, 1.6];
  35. let empty = {};
  36. let emptyValues = _.cloneDeep(ctx.heatmapA);
  37. emptyValues['1422774000000'].buckets['1'].values = [];
  38. expect(isHeatmapDataEqual(ctx.heatmapA, ctx.heatmapB)).toBe(true);
  39. expect(isHeatmapDataEqual(ctx.heatmapB, ctx.heatmapA)).toBe(true);
  40. expect(isHeatmapDataEqual(ctx.heatmapA, heatmapC)).toBe(true);
  41. expect(isHeatmapDataEqual(heatmapC, ctx.heatmapA)).toBe(true);
  42. expect(isHeatmapDataEqual(ctx.heatmapA, heatmapD)).toBe(false);
  43. expect(isHeatmapDataEqual(heatmapD, ctx.heatmapA)).toBe(false);
  44. expect(isHeatmapDataEqual(ctx.heatmapA, heatmapE)).toBe(false);
  45. expect(isHeatmapDataEqual(heatmapE, ctx.heatmapA)).toBe(false);
  46. expect(isHeatmapDataEqual(empty, ctx.heatmapA)).toBe(false);
  47. expect(isHeatmapDataEqual(ctx.heatmapA, empty)).toBe(false);
  48. expect(isHeatmapDataEqual(emptyValues, ctx.heatmapA)).toBe(false);
  49. expect(isHeatmapDataEqual(ctx.heatmapA, emptyValues)).toBe(false);
  50. });
  51. });
  52. describe('calculateBucketSize', () => {
  53. let ctx: any = {};
  54. describe('when logBase is 1 (linear scale)', () => {
  55. beforeEach(() => {
  56. ctx.logBase = 1;
  57. ctx.bounds_set = [
  58. { bounds: [], size: 0 },
  59. { bounds: [0], size: 0 },
  60. { bounds: [4], size: 4 },
  61. { bounds: [0, 1, 2, 3, 4], size: 1 },
  62. { bounds: [0, 1, 3, 5, 7], size: 1 },
  63. { bounds: [0, 3, 7, 9, 15], size: 2 },
  64. { bounds: [0, 7, 3, 15, 9], size: 2 },
  65. { bounds: [0, 5, 10, 15, 50], size: 5 }
  66. ];
  67. });
  68. it('should properly calculate bucket size', () => {
  69. _.each(ctx.bounds_set, (b) => {
  70. let bucketSize = calculateBucketSize(b.bounds, ctx.logBase);
  71. expect(bucketSize).toBe(b.size);
  72. });
  73. });
  74. });
  75. describe('when logBase is 2', () => {
  76. beforeEach(() => {
  77. ctx.logBase = 2;
  78. ctx.bounds_set = [
  79. { bounds: [], size: 0 },
  80. { bounds: [0], size: 0 },
  81. { bounds: [4], size: 4 },
  82. { bounds: [1, 2, 4, 8], size: 1 },
  83. { bounds: [1, Math.SQRT2, 2, 8, 16], size: 0.5 }
  84. ];
  85. });
  86. it('should properly calculate bucket size', () => {
  87. _.each(ctx.bounds_set, (b) => {
  88. let bucketSize = calculateBucketSize(b.bounds, ctx.logBase);
  89. expect(isEqual(bucketSize, b.size)).toBe(true);
  90. });
  91. });
  92. });
  93. });
  94. describe('HeatmapDataConverter', () => {
  95. let ctx: any = {};
  96. beforeEach(() => {
  97. ctx.series = [];
  98. ctx.series.push(new TimeSeries({
  99. datapoints: [[1, 1422774000000], [1, 1422774000010], [2, 1422774060000]],
  100. alias: 'series1'
  101. }));
  102. ctx.series.push(new TimeSeries({
  103. datapoints: [[2, 1422774000000], [2, 1422774000010], [3, 1422774060000]],
  104. alias: 'series2'
  105. }));
  106. ctx.series.push(new TimeSeries({
  107. datapoints: [[5, 1422774000000], [3, 1422774000010], [4, 1422774060000]],
  108. alias: 'series3'
  109. }));
  110. ctx.xBucketSize = 60000; // 60s
  111. ctx.yBucketSize = 2;
  112. ctx.logBase = 1;
  113. });
  114. describe('when logBase is 1 (linear scale)', () => {
  115. beforeEach(() => {
  116. ctx.logBase = 1;
  117. });
  118. it('should build proper heatmap data', () => {
  119. let expectedHeatmap = {
  120. '1422774000000': {
  121. x: 1422774000000,
  122. buckets: {
  123. '0': {y: 0, values: [1, 1], count: 2, bounds: {bottom: 0, top: 2}},
  124. '2': {y: 2, values: [2, 2, 3], count: 3, bounds: {bottom: 2, top: 4}},
  125. '4': {y: 4, values: [5], count: 1, bounds: {bottom: 4, top: 6}},
  126. }
  127. },
  128. '1422774060000': {
  129. x: 1422774060000,
  130. buckets: {
  131. '2': {y: 2, values: [2, 3], count: 3, bounds: {bottom: 2, top: 4}},
  132. '4': {y: 4, values: [4], count: 1, bounds: {bottom: 4, top: 6}},
  133. }
  134. },
  135. };
  136. let heatmap = convertToHeatMap(ctx.series, ctx.yBucketSize, ctx.xBucketSize, ctx.logBase);
  137. expect(isHeatmapDataEqual(heatmap, expectedHeatmap)).toBe(true);
  138. });
  139. });
  140. describe.skip('when logBase is 2', () => {
  141. beforeEach(() => {
  142. ctx.logBase = 2;
  143. });
  144. it('should build proper heatmap data', () => {
  145. let expectedHeatmap = {
  146. '1422774000000': {
  147. x: 1422774000000,
  148. buckets: {
  149. '1': { y: 1, values: [1] },
  150. '2': { y: 2, values: [2] }
  151. }
  152. },
  153. '1422774060000': {
  154. x: 1422774060000,
  155. buckets: {
  156. '2': { y: 2, values: [2, 3] }
  157. }
  158. },
  159. };
  160. let heatmap = convertToHeatMap(ctx.series, ctx.yBucketSize, ctx.xBucketSize, ctx.logBase);
  161. expect(isHeatmapDataEqual(heatmap, expectedHeatmap)).toBe(true);
  162. });
  163. });
  164. });
  165. describe('ES Histogram converter', () => {
  166. let ctx: any = {};
  167. beforeEach(() => {
  168. ctx.series = [];
  169. ctx.series.push(new TimeSeries({
  170. datapoints: [[1, 1422774000000], [0, 1422774060000]],
  171. alias: '1', label: '1'
  172. }));
  173. ctx.series.push(new TimeSeries({
  174. datapoints: [[5, 1422774000000], [3, 1422774060000]],
  175. alias: '2', label: '2'
  176. }));
  177. ctx.series.push(new TimeSeries({
  178. datapoints: [[0, 1422774000000], [1, 1422774060000]],
  179. alias: '3', label: '3'
  180. }));
  181. });
  182. describe('when converting ES histogram', () => {
  183. beforeEach(() => {
  184. });
  185. it('should build proper heatmap data', () => {
  186. let expectedHeatmap = {
  187. '1422774000000': {
  188. x: 1422774000000,
  189. buckets: {
  190. '1': { y: 1, count: 1, values: [], points: [], bounds: {bottom: 1, top: null}},
  191. '2': { y: 2, count: 5, values: [], points: [], bounds: {bottom: 2, top: null}},
  192. '3': { y: 3, count: 0, values: [], points: [], bounds: {bottom: 3, top: null}}
  193. }
  194. },
  195. '1422774060000': {
  196. x: 1422774060000,
  197. buckets: {
  198. '1': { y: 1, count: 0, values: [], points: [], bounds: {bottom: 1, top: null}},
  199. '2': { y: 2, count: 3, values: [], points: [], bounds: {bottom: 2, top: null}},
  200. '3': { y: 3, count: 1, values: [], points: [], bounds: {bottom: 3, top: null}}
  201. }
  202. },
  203. };
  204. let heatmap = elasticHistogramToHeatmap(ctx.series);
  205. expect(heatmap).toEqual(expectedHeatmap);
  206. });
  207. });
  208. });
  209. describe('convertToCards', () => {
  210. let buckets = {};
  211. beforeEach(() => {
  212. buckets = {
  213. '1422774000000': {
  214. x: 1422774000000,
  215. buckets: {
  216. '1': { y: 1, values: [1], count: 1, bounds: {} },
  217. '2': { y: 2, values: [2], count: 1, bounds: {} }
  218. }
  219. },
  220. '1422774060000': {
  221. x: 1422774060000,
  222. buckets: {
  223. '2': { y: 2, values: [2, 3], count: 2, bounds: {} }
  224. }
  225. },
  226. };
  227. });
  228. it('should build proper cards data', () => {
  229. let expectedCards = [
  230. {x: 1422774000000, y: 1, count: 1, values: [1], yBounds: {}},
  231. {x: 1422774000000, y: 2, count: 1, values: [2], yBounds: {}},
  232. {x: 1422774060000, y: 2, count: 2, values: [2, 3], yBounds: {}}
  233. ];
  234. let res = convertToCards(buckets);
  235. expect(res.cards).toMatchObject(expectedCards);
  236. });
  237. it('should build proper cards stats', () => {
  238. let expectedStats = {min: 1, max: 2};
  239. let res = convertToCards(buckets);
  240. expect(res.cardStats).toMatchObject(expectedStats);
  241. });
  242. });
  243. /**
  244. * Compare two numbers with given precision. Suitable for compare float numbers after conversions with precision loss.
  245. * @param a
  246. * @param b
  247. * @param precision
  248. */
  249. function isEqual(a: number, b: number, precision = 0.000001): boolean {
  250. if (a === b) {
  251. return true;
  252. } else {
  253. return Math.abs(1 - a / b) <= precision;
  254. }
  255. }