heatmap_data_converter.jest.ts 9.4 KB

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