| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| /* global Float32Array */ |
| |
| import SeriesData from '@/src/data/SeriesData'; |
| import Model from '@/src/model/Model'; |
| import { createSourceFromSeriesDataOption, Source, createSource } from '@/src/data/Source'; |
| import { OptionDataItemObject, |
| OptionDataValue, |
| SOURCE_FORMAT_ARRAY_ROWS, |
| SOURCE_FORMAT_ORIGINAL } from '@/src/util/types'; |
| import SeriesDimensionDefine from '@/src/data/SeriesDimensionDefine'; |
| import OrdinalMeta from '@/src/data/OrdinalMeta'; |
| import DataStore from '@/src/data/DataStore'; |
| import { DefaultDataProvider } from '@/src/data/helper/dataProvider'; |
| import { SeriesDataSchema } from '@/src/data/helper/SeriesDataSchema'; |
| |
| |
| const ID_PREFIX = 'e\0\0'; |
| const NAME_REPEAT_PREFIX = '__ec__'; |
| |
| |
| describe('SeriesData', function () { |
| |
| describe('Data Manipulation', function () { |
| |
| it('initData 1d', function () { |
| const data = new SeriesData(['x', 'y'], new Model()); |
| data.initData([10, 20, 30]); |
| expect(data.get('x', 0)).toEqual(10); |
| expect(data.get('x', 1)).toEqual(20); |
| expect(data.get('x', 2)).toEqual(30); |
| expect(data.get('y', 1)).toEqual(20); |
| }); |
| |
| it('initData 2d', function () { |
| const data = new SeriesData(['x', 'y'], new Model()); |
| data.initData([[10, 15], [20, 25], [30, 35]]); |
| expect(data.get('x', 1)).toEqual(20); |
| expect(data.get('y', 1)).toEqual(25); |
| }); |
| |
| it('initData 2d yx', function () { |
| const data = new SeriesData(['y', 'x'], new Model()); |
| data.initData([[10, 15], [20, 25], [30, 35]]); |
| expect(data.get('x', 1)).toEqual(25); |
| expect(data.get('y', 1)).toEqual(20); |
| }); |
| |
| it('Data with option 1d', function () { |
| const data = new SeriesData(['x', 'y'], new Model()); |
| data.initData([ |
| 1, |
| { |
| value: 2, |
| somProp: 'foo' |
| } as OptionDataItemObject<OptionDataValue> |
| ]); |
| expect(data.getItemModel(1).get('somProp' as any)).toEqual('foo'); |
| expect(data.getItemModel(0).get('somProp' as any)).toBeNull(); |
| }); |
| |
| it('Empty data', function () { |
| const data = new SeriesData(['x', 'y'], new Model()); |
| data.initData([1, '-']); |
| expect(data.get('y', 1)).toBeNaN(); |
| }); |
| |
| it('getRawValue', function () { |
| const data1 = new SeriesData(['x', 'y'], new Model()); |
| // here construct a new data2 because if we only use one data |
| // to call initData() twice, data._chunkCount will be accumulated |
| // to 1 instead of 0. |
| const data2 = new SeriesData(['x', 'y'], new Model()); |
| |
| data1.initData([1, 2, 3]); |
| expect(data1.getItemModel(1).option).toEqual(2); |
| |
| data2.initData([[10, 15], [20, 25], [30, 35]]); |
| expect(data2.getItemModel(1).option).toEqual([20, 25]); |
| }); |
| |
| it('indexOfRawIndex', function () { |
| const data = new SeriesData(['x'], new Model()); |
| data.initData([]); |
| expect(data.indexOfRawIndex(1)).toEqual(-1); |
| |
| const data1 = new SeriesData(['x'], new Model()); |
| data1.initData([0]); |
| expect(data1.indexOfRawIndex(0)).toEqual(0); |
| expect(data1.indexOfRawIndex(1)).toEqual(-1); |
| |
| const data2 = new SeriesData(['x'], new Model()); |
| data2.initData([0, 1, 2, 3]); |
| expect(data2.indexOfRawIndex(1)).toEqual(1); |
| expect(data2.indexOfRawIndex(2)).toEqual(2); |
| expect(data2.indexOfRawIndex(5)).toEqual(-1); |
| |
| const data3 = new SeriesData(['x'], new Model()); |
| data3.initData([0, 1, 2, 3, 4]); |
| expect(data3.indexOfRawIndex(2)).toEqual(2); |
| expect(data3.indexOfRawIndex(3)).toEqual(3); |
| expect(data3.indexOfRawIndex(5)).toEqual(-1); |
| |
| data3.filterSelf(function (idx) { |
| return idx >= 2; |
| }); |
| expect(data3.indexOfRawIndex(2)).toEqual(0); |
| }); |
| |
| it('getDataExtent', function () { |
| const data = new SeriesData(['x', 'y'], new Model()); |
| data.initData([1, 2, 3]); |
| expect(data.getDataExtent('x')).toEqual([1, 3]); |
| expect(data.getDataExtent('y')).toEqual([1, 3]); |
| }); |
| |
| it('Data types', function () { |
| const data = new SeriesData([{ |
| name: 'x', |
| type: 'int' |
| }, { |
| name: 'y', |
| type: 'float' |
| }], new Model()); |
| data.initData([[1.1, 1.1]]); |
| expect(data.get('x', 0)).toEqual(1); |
| expect(data.get('y', 0)).toBeCloseTo(1.1, 5); |
| }); |
| |
| it('map', function () { |
| const data = new SeriesData(['x', 'y'], new Model()); |
| data.initData([[10, 15], [20, 25], [30, 35]]); |
| expect(data.map(['x', 'y'], function (x: number, y: number) { |
| return [x + 2, y + 2]; |
| }).mapArray('x', function (x) { |
| return x; |
| })).toEqual([12, 22, 32]); |
| }); |
| |
| it('mapArray', function () { |
| const data = new SeriesData(['x', 'y'], new Model()); |
| data.initData([[10, 15], [20, 25], [30, 35]]); |
| expect(data.mapArray(['x', 'y'], function (x, y) { |
| return [x, y]; |
| })).toEqual([[10, 15], [20, 25], [30, 35]]); |
| }); |
| |
| it('filterSelf', function () { |
| const data = new SeriesData(['x', 'y'], new Model()); |
| data.initData([[10, 15], [20, 25], [30, 35]]); |
| expect(data.filterSelf(['x', 'y'], function (x, y) { |
| return x < 30 && x > 10; |
| }).mapArray('x', function (x) { |
| return x; |
| })).toEqual([20]); |
| }); |
| |
| it('dataProvider', function () { |
| const data = new SeriesData(['x', 'y'], new Model()); |
| const typedArray = new Float32Array([10, 10, 20, 20]); |
| const source = createSourceFromSeriesDataOption(typedArray); |
| data.initData({ |
| count: function (): number { |
| return typedArray.length / 2; |
| }, |
| getItem: function (idx: number): number[] { |
| return [typedArray[idx * 2], typedArray[idx * 2 + 1]]; |
| }, |
| getSource: function (): Source { |
| return source; |
| } |
| }); |
| expect(data.mapArray(['x', 'y'], function (x, y) { |
| return [x, y]; |
| })).toEqual([[10, 10], [20, 20]]); |
| expect(data.getRawDataItem(0)).toEqual([10, 10]); |
| expect(data.getItemModel(0).option).toEqual([10, 10]); |
| }); |
| }); |
| |
| describe('Data store', function () { |
| it('should guess ordinal correctly', function () { |
| const source = createSource([['A', 15], ['B', 25], ['C', 35]], { |
| dimensions: ['A', 'B'], |
| sourceLayout: null, |
| sourceHeader: false |
| }, SOURCE_FORMAT_ORIGINAL); |
| expect(source.dimensionsDefine[0].type).toEqual('ordinal'); |
| }); |
| |
| function createStore() { |
| const provider = new DefaultDataProvider([['A', 15], ['B', 25], ['C', 35]]); |
| const store = new DataStore(); |
| store.initData(provider, [{type: 'ordinal'}, {type: 'float'}]); |
| return store; |
| } |
| |
| |
| it('SeriesData can still get other dims value from store when only part of dims are given.', function () { |
| const source = createSource( |
| [['A', 15, 20, 'cat'], ['B', 25, 30, 'mouse'], ['C', 35, 40, 'dog']], |
| { |
| dimensions: null, |
| sourceLayout: null, |
| sourceHeader: false |
| }, |
| SOURCE_FORMAT_ARRAY_ROWS |
| ); |
| const store = new DataStore(); |
| store.initData(new DefaultDataProvider(source), [ |
| {type: 'ordinal'}, {type: 'float'}, {type: 'float'}, {type: 'ordinal'} |
| ]); |
| const schema = new SeriesDataSchema({ |
| source: source, |
| dimensions: [ |
| { type: 'float', name: 'dim1', storeDimIndex: 1 }, |
| { type: 'ordinal', name: 'dim3', storeDimIndex: 3 } |
| ], |
| fullDimensionCount: 2, |
| dimensionOmitted: true |
| }); |
| const data = new SeriesData(schema, null); |
| data.initData(store); |
| // Store should be the same. |
| expect(data.getStore()).toBe(store); |
| // Get self dim |
| expect(data.get('dim1', 0)).toEqual(15); |
| expect(data.get('dim1', 1)).toEqual(25); |
| // Get other dim |
| expect(data.getStore().get(0, 0)).toEqual('A'); |
| expect(data.getStore().get(0, 1)).toEqual('B'); |
| expect(data.getStore().get(2, 0)).toEqual(20); |
| expect(data.getStore().get(2, 1)).toEqual(30); |
| // Get all |
| expect(data.getValues(['dim3', 'dim1'], 0)).toEqual(['cat', 15]); |
| expect(data.getValues(1)).toEqual(['B', 25, 30, 'mouse']); |
| }); |
| |
| it('SeriesData#cloneShallow should share store', function () { |
| const store = createStore(); |
| const dims = [{ type: 'float', name: 'dim2' }]; |
| const data = new SeriesData(dims, null); |
| data.initData(store); |
| const data2 = data.cloneShallow(); |
| expect(data2.getStore()).toBe(data.getStore()); |
| }); |
| }); |
| |
| describe('Data read', function () { |
| it('indicesOfNearest', function () { |
| const data = new SeriesData(['value'], new Model()); |
| // ---- index: 0 1 2 3 4 5 6 7 |
| data.initData([10, 20, 30, 35, 40, 40, 35, 50]); |
| |
| expect(data.indicesOfNearest('value', 24.5)).toEqual([1]); |
| expect(data.indicesOfNearest('value', 25)).toEqual([1]); |
| expect(data.indicesOfNearest('value', 25.5)).toEqual([2]); |
| expect(data.indicesOfNearest('value', 25.5)).toEqual([2]); |
| expect(data.indicesOfNearest('value', 41)).toEqual([4, 5]); |
| expect(data.indicesOfNearest('value', 39)).toEqual([4, 5]); |
| expect(data.indicesOfNearest('value', 41)).toEqual([4, 5]); |
| expect(data.indicesOfNearest('value', 36)).toEqual([3, 6]); |
| |
| expect(data.indicesOfNearest('value', 50.6, 0.5)).toEqual([]); |
| expect(data.indicesOfNearest('value', 50.5, 0.5)).toEqual([7]); |
| }); |
| }); |
| |
| describe('id_and_name', function () { |
| |
| function makeOneByOneChecker(list: SeriesData) { |
| let getIdDataIndex = 0; |
| let getNameDataIndex = 0; |
| |
| return { |
| nextIdEqualsTo: function (expectedId: string): void { |
| expect(list.getId(getIdDataIndex)).toEqual(expectedId); |
| getIdDataIndex++; |
| }, |
| nextNameEqualsTo: function (expectedName: string): void { |
| expect(list.getName(getNameDataIndex)).toEqual(expectedName); |
| getNameDataIndex++; |
| }, |
| currGetIdDataIndex: function (): number { |
| return getIdDataIndex; |
| }, |
| currGetNameDataIndex: function (): number { |
| return getNameDataIndex; |
| } |
| }; |
| } |
| |
| describe('only_name_declared', function () { |
| |
| function doChecks(list: SeriesData) { |
| const oneByOne = makeOneByOneChecker(list); |
| |
| oneByOne.nextIdEqualsTo('a'); |
| oneByOne.nextIdEqualsTo('b'); |
| oneByOne.nextIdEqualsTo(`b${NAME_REPEAT_PREFIX}2`); |
| oneByOne.nextIdEqualsTo('c'); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}4`); |
| oneByOne.nextIdEqualsTo(`c${NAME_REPEAT_PREFIX}2`); |
| oneByOne.nextIdEqualsTo('d'); |
| oneByOne.nextIdEqualsTo(`c${NAME_REPEAT_PREFIX}3`); |
| |
| oneByOne.nextNameEqualsTo('a'); |
| oneByOne.nextNameEqualsTo('b'); |
| oneByOne.nextNameEqualsTo('b'); |
| oneByOne.nextNameEqualsTo('c'); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo('c'); |
| oneByOne.nextNameEqualsTo('d'); |
| oneByOne.nextNameEqualsTo('c'); |
| } |
| |
| it('sourceFormatOriginal', function () { |
| const list = new SeriesData(['x', 'y'], new Model()); |
| list.initData([ |
| { value: 10, name: 'a' }, |
| { value: 20, name: 'b' }, |
| { value: 30, name: 'b' }, |
| { value: 40, name: 'c' }, |
| { value: 50 }, // name not declared |
| { value: 60, name: 'c' }, |
| { value: 70, name: 'd' }, |
| { value: 80, name: 'c' } |
| ]); |
| |
| doChecks(list); |
| }); |
| |
| it('sourceFormatArrayRows', function () { |
| const list = new SeriesData( |
| [ |
| 'x', |
| { name: 'q', type: 'ordinal', otherDims: { itemName: 0 } } |
| ], |
| new Model() |
| ); |
| const source = createSource( |
| [ |
| [ 10, 'a' ], |
| [ 20, 'b' ], |
| [ 30, 'b' ], |
| [ 40, 'c' ], |
| [ 50, null ], |
| [ 60, 'c' ], |
| [ 70, 'd' ], |
| [ 80, 'c' ] |
| ], |
| { |
| sourceLayout: 'column', |
| sourceHeader: 0, |
| dimensions: null |
| }, |
| SOURCE_FORMAT_ARRAY_ROWS |
| ); |
| list.initData(source); |
| |
| doChecks(list); |
| }); |
| }); |
| |
| |
| describe('id_name_declared_sourceFormat_original', function () { |
| |
| it('sourceFormatOriginal', function () { |
| const list = new SeriesData(['x'], new Model()); |
| const oneByOne = makeOneByOneChecker(list); |
| |
| list.initData([ |
| { value: 0, id: 'myId_10' }, |
| { value: 10, id: 555 }, // numeric id. |
| { value: 20, id: '666%' }, |
| { value: 30, id: 'myId_good', name: 'b' }, |
| { value: 40, name: 'b' }, |
| { value: 50, id: null }, |
| { value: 60, id: undefined }, |
| { value: 70, id: NaN }, |
| { value: 80, id: '' }, |
| { value: 90, name: 'b' }, |
| { value: 100 }, |
| { value: 110, id: 'myId_better' }, |
| { value: 120, id: 'myId_better' } // duplicated id. |
| ]); |
| |
| oneByOne.nextIdEqualsTo('myId_10'); |
| oneByOne.nextIdEqualsTo('555'); |
| oneByOne.nextIdEqualsTo('666%'); |
| oneByOne.nextIdEqualsTo('myId_good'); |
| oneByOne.nextIdEqualsTo('b'); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| oneByOne.nextIdEqualsTo('NaN'); |
| oneByOne.nextIdEqualsTo(''); |
| oneByOne.nextIdEqualsTo(`b${NAME_REPEAT_PREFIX}2`); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| oneByOne.nextIdEqualsTo('myId_better'); |
| oneByOne.nextIdEqualsTo('myId_better'); |
| |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo('b'); |
| oneByOne.nextNameEqualsTo('b'); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo('b'); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| |
| list.appendData([ |
| { value: 200, id: 'myId_best' }, |
| { value: 210, id: 999 }, // numeric id. |
| { value: 220, id: '777px' }, |
| { value: 230, name: 'b' }, |
| { value: 240 } |
| ]); |
| |
| oneByOne.nextIdEqualsTo('myId_best'); |
| oneByOne.nextIdEqualsTo('999'); |
| oneByOne.nextIdEqualsTo('777px'); |
| oneByOne.nextIdEqualsTo(`b${NAME_REPEAT_PREFIX}3`); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo('b'); |
| oneByOne.nextNameEqualsTo(''); |
| |
| list.appendValues([], ['b', 'c', null]); |
| |
| oneByOne.nextIdEqualsTo(`b${NAME_REPEAT_PREFIX}4`); |
| oneByOne.nextIdEqualsTo('c'); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| |
| oneByOne.nextNameEqualsTo('b'); |
| oneByOne.nextNameEqualsTo('c'); |
| oneByOne.nextNameEqualsTo(''); |
| }); |
| |
| }); |
| |
| describe('id_name_declared_sourceFormat_arrayRows', function () { |
| |
| it('no_ordinalMeta', function () { |
| testArrayRowsInSource([ |
| { name: 'x', type: 'number' }, |
| { name: 'p', type: 'ordinal', otherDims: { itemId: 0 } }, |
| { name: 'q', type: 'ordinal', otherDims: { itemName: 0 } } |
| ]); |
| }); |
| |
| it('has_ordinalMeta', function () { |
| const ordinalMetaP = new OrdinalMeta({ |
| categories: [], |
| needCollect: true, |
| deduplication: true |
| }); |
| const ordinalMetaQ = new OrdinalMeta({ |
| categories: [], |
| needCollect: true, |
| deduplication: true |
| }); |
| testArrayRowsInSource([ |
| { name: 'x', type: 'number' }, |
| { name: 'p', type: 'ordinal', otherDims: { itemId: 0 }, ordinalMeta: ordinalMetaP }, |
| { name: 'q', type: 'ordinal', otherDims: { itemName: 0 }, ordinalMeta: ordinalMetaQ } |
| ]); |
| }); |
| |
| function testArrayRowsInSource(dimensionsInfo: SeriesDimensionDefine[]): void { |
| const list = new SeriesData(dimensionsInfo, new Model()); |
| const oneByOne = makeOneByOneChecker(list); |
| |
| const source = createSource( |
| [ |
| [0, 'myId_10', null], |
| [10, 555, null], // numeric id. |
| [20, '666%', null], |
| [30, 'myId_good', 'b'], |
| [40, null, 'b'], |
| [50, null, null], |
| [60, undefined, null], |
| [70, NaN, null], |
| [80, '', null], |
| [90, null, 'b'], |
| [100, null, null], |
| [110, 'myId_better', null], |
| [120, 'myId_better', null] // duplicated id. |
| ], |
| { |
| sourceLayout: 'column', |
| sourceHeader: 0, |
| dimensions: null |
| }, |
| SOURCE_FORMAT_ARRAY_ROWS |
| ); |
| list.initData(source); |
| oneByOne.nextIdEqualsTo('myId_10'); |
| oneByOne.nextIdEqualsTo('555'); |
| oneByOne.nextIdEqualsTo('666%'); |
| oneByOne.nextIdEqualsTo('myId_good'); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| oneByOne.nextIdEqualsTo('NaN'); |
| oneByOne.nextIdEqualsTo(''); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| oneByOne.nextIdEqualsTo('myId_better'); |
| oneByOne.nextIdEqualsTo('myId_better'); |
| |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo('b'); |
| oneByOne.nextNameEqualsTo('b'); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo('b'); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| |
| list.appendData([ |
| [ 200, 'myId_best', null ], |
| [ 210, 999, null ], // numeric id. |
| [ 220, '777px', null], |
| [ 230, null, 'b' ], |
| [ 240, null, null ] |
| ]); |
| |
| oneByOne.nextIdEqualsTo('myId_best'); |
| oneByOne.nextIdEqualsTo('999'); |
| oneByOne.nextIdEqualsTo('777px'); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo(''); |
| oneByOne.nextNameEqualsTo('b'); |
| oneByOne.nextNameEqualsTo(''); |
| |
| list.appendValues([], ['b', 'c', null]); |
| |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| oneByOne.nextIdEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`); |
| |
| oneByOne.nextNameEqualsTo('b'); |
| oneByOne.nextNameEqualsTo('c'); |
| oneByOne.nextNameEqualsTo(''); |
| } |
| |
| }); |
| }); |
| }); |