blob: d200b5830425c361d7891009a8efd4956d3d4200 [file] [log] [blame]
/*
* 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('');
}
});
});
});