blob: 25e9f69ed5cb65d7f44018cadabb2228a33d4fe7 [file] [log] [blame]
/* eslint-disable dot-notation */
import { assert } from 'chai';
import fetchMock from 'fetch-mock';
import {
ColumnHeader,
DataFrame,
FrameRequest,
FrameResponse,
QueryConfig,
progress,
} from '../json';
import { deepCopy } from '../../../infra-sk/modules/object';
import {
calculateRangeChange,
defaultPointSelected,
ExploreSimpleSk,
isValidSelection,
PointSelected,
selectionToEvent,
} from './explore-simple-sk';
import { timestampBounds, buildParamSet } from '../dataframe';
import { toParamSet, fromParamSet } from '../../../infra-sk/modules/query';
import { setUpElementUnderTest } from '../../../infra-sk/modules/test_util';
fetchMock.config.overwriteRoutes = true;
describe('calculateRangeChange', () => {
const offsets: [number, number] = [100, 120];
it('finds a left range increase', () => {
const zoom: [number, number] = [-1, 10];
const clampedZoom: [number, number] = [0, 10];
const ret = calculateRangeChange(zoom, clampedZoom, offsets);
assert.isTrue(ret.rangeChange);
// We shift the beginning of the range by RANGE_CHANGE_ON_ZOOM_PERCENT of
// the total range.
assert.deepEqual(ret.newOffsets, [90, 120]);
});
it('finds a right range increase', () => {
const zoom: [number, number] = [0, 12];
const clampedZoom: [number, number] = [0, 10];
const ret = calculateRangeChange(zoom, clampedZoom, offsets);
assert.isTrue(ret.rangeChange);
// We shift the end of the range by RANGE_CHANGE_ON_ZOOM_PERCENT of the
// total range.
assert.deepEqual(ret.newOffsets, [100, 130]);
});
it('find an increase in the range in both directions', () => {
const zoom: [number, number] = [-1, 11];
const clampedZoom: [number, number] = [0, 10];
const ret = calculateRangeChange(zoom, clampedZoom, offsets);
assert.isTrue(ret.rangeChange);
// We shift both the begin and end of the range by
// RANGE_CHANGE_ON_ZOOM_PERCENT of the total range.
assert.deepEqual(ret.newOffsets, [90, 130]);
});
it('find an increase in the range in both directions and clamps the offset', () => {
const zoom: [number, number] = [-1, 11];
const clampedZoom: [number, number] = [0, 10];
const widerOffsets: [number, number] = [0, 100];
const ret = calculateRangeChange(zoom, clampedZoom, widerOffsets);
assert.isTrue(ret.rangeChange);
// We shift both the begin and end of the range by
// RANGE_CHANGE_ON_ZOOM_PERCENT of the total range.
assert.deepEqual(ret.newOffsets, [0, 150]);
});
it('does not find a range change', () => {
const zoom: [number, number] = [0, 10];
const clampedZoom: [number, number] = [0, 10];
const ret = calculateRangeChange(zoom, clampedZoom, offsets);
assert.isFalse(ret.rangeChange);
});
});
describe('applyFuncToTraces', () => {
window.perf = {
radius: 2,
key_order: null,
num_shift: 50,
interesting: 2,
step_up_only: false,
commit_range_url: '',
demo: true,
display_group_by: false,
hide_list_of_commits_on_explore: false,
notifications: 'none',
fetch_chrome_perf_anomalies: false,
feedback_url: '',
chat_url: '',
help_url_override: '',
trace_format: '',
};
// Create a common element-sk to be used by all the tests.
const explore = document.createElement(
'explore-simple-sk'
) as ExploreSimpleSk;
document.body.appendChild(explore);
const finishedBody: progress.SerializedProgress = {
status: 'Finished',
messages: [],
results: {},
url: '',
};
it('applies the func to existing formulas', async () => {
const startURL = '/_/frame/start';
// We mock out a response that returns a Progress that is Finished
// so we don't have to mock out any more responses, we are just checking
// on what explore-sk sends in the POST request.
fetchMock.post(startURL, finishedBody);
// Add a formula we expect to be wrapped.
explore['state'].formulas = ['shortcut("Xfoo")'];
await explore['applyFuncToTraces']('iqrr');
// Confirm we hit the mock.
assert.isTrue(fetchMock.done());
// Confirm the formula is wrapped in iqrr().
const body = JSON.parse(
fetchMock.lastOptions(startURL)?.body as unknown as string
) as any;
assert.deepEqual(body.formulas, ['iqrr(shortcut("Xfoo"))']);
fetchMock.restore();
});
});
describe('PointSelected', () => {
it('defaults to not having a name', () => {
const p = defaultPointSelected();
assert.isEmpty(p.name);
});
it('defaults to being invalid', () => {
const p = defaultPointSelected();
assert.isFalse(isValidSelection(p));
});
it('becomes a valid event if the commit appears in the header', () => {
const header: ColumnHeader[] = [
{
offset: 99,
timestamp: 0,
},
{
offset: 100,
timestamp: 0,
},
{
offset: 101,
timestamp: 0,
},
];
const p: PointSelected = {
commit: 100,
name: 'foo',
};
// selectionToEvent will look up the commit (aka offset) in header and
// should return an event where the 'x' value is the index of the matching
// ColumnHeader in 'header', i.e. 1.
const e = selectionToEvent(p, header);
assert.equal(e.detail.x, 1);
});
it('becomes an invalid event if the commit does not appear in the header', () => {
const header: ColumnHeader[] = [
{
offset: 99,
timestamp: 0,
},
{
offset: 100,
timestamp: 0,
},
{
offset: 101,
timestamp: 0,
},
];
const p: PointSelected = {
commit: 102,
name: 'foo',
};
// selectionToEvent will look up the commit (aka offset) in header and
// should return an event where the 'x' value is -1 since the matching
// ColumnHeader in 'header' doesn't exist.
const e = selectionToEvent(p, header);
assert.equal(e.detail.x, -1);
});
});
describe('Default values', () => {
beforeEach(() => {
fetchMock.get('/_/login/status', {
email: 'someone@example.org',
roles: ['editor'],
});
fetchMock.post('/_/count/', {
count: 117,
paramset: {},
});
fetchMock.get(/_\/initpage\/.*/, () => ({
dataframe: {
traceset: null,
header: null,
paramset: {},
skip: 0,
},
ticks: [],
skps: [],
msg: '',
}));
});
it('Checks no default values', async () => {
const defaultConfig: QueryConfig = {
default_param_selections: null,
default_url_values: null,
include_params: null,
};
const defaultBody = JSON.stringify(defaultConfig);
fetchMock.get('path:/_/defaults/', {
status: 200,
body: defaultBody,
});
var explore = await setUpElementUnderTest<ExploreSimpleSk>(
'explore-simple-sk'
)();
await fetchMock.flush(true);
const originalState = deepCopy(explore!.state);
await explore['applyQueryDefaultsIfMissing']();
const newState = explore.state;
assert.deepEqual(newState, originalState);
});
it('Checks for default summary value', async () => {
const defaultConfig: QueryConfig = {
default_param_selections: null,
default_url_values: {
summary: 'true',
},
include_params: null,
};
const defaultBody = JSON.stringify(defaultConfig);
fetchMock.get('path:/_/defaults/', {
status: 200,
body: defaultBody,
});
var explore = setUpElementUnderTest<ExploreSimpleSk>('explore-simple-sk')();
await fetchMock.flush(true);
var actualConfig = explore['defaults'];
assert.deepEqual(
actualConfig,
defaultConfig,
'actual and default configs are the same'
);
const originalState = deepCopy(explore.state);
await explore['applyQueryDefaultsIfMissing']();
assert.isTrue(fetchMock.done());
const newState = explore.state;
assert.notDeepEqual(
newState,
originalState,
'new state should not equal original state'
);
assert.isTrue(newState.summary);
});
});
describe('requestFrameBodyDeltaFromState', () => {
function fakeDataFrame(): DataFrame {
const ret: DataFrame = {
header: [
{ offset: 11, timestamp: 1100 },
{ offset: 12, timestamp: 1200 },
{ offset: 13, timestamp: 1300 },
{ offset: 14, timestamp: 1400 },
],
traceset: {
',config=8888,arch=x86,': [0.1, 0.2, 0.0, 0.4],
',config=8888,arch=arm,': [1.1, 1.2, 0.0, 1.4],
',config=565,arch=x86,': [0.0, 0.0, 3.3, 3.4],
',config=565,arch=arm,': [0.0, 0.0, 4.3, 4.4],
},
paramset: {},
skip: 0,
};
buildParamSet(ret);
return ret;
}
it('fetches only missing older data when panning left with overlap', async () => {
// dataframe: [1100, 1400]
// state: [300, 1200]
// request: [300, 1100]
const explore = await setUpElementUnderTest<ExploreSimpleSk>(
'explore-simple-sk'
)();
// Setup is that we have the timestamp range [1100, 1400] loaded in the current dataframe.
// User does something to cause this._state's timestamp range to become [300, 1200],
// e.g. 'pan left'.
const existingDataFrame = fakeDataFrame();
explore['_dataframe'] = existingDataFrame;
const state = deepCopy(explore.state);
state._incremental = true;
state.begin = 300;
state.end = 1200;
state.queries = fromParamSet(existingDataFrame.paramset).split('&');
explore['_state'] = state;
const frameBodyDeltaRequest: FrameRequest =
explore['requestFrameBodyDeltaFromState']();
// It should return a request for just the missing range, [300, 1100].
assert.deepEqual(
[frameBodyDeltaRequest.begin, frameBodyDeltaRequest.end],
[300, 1100]
);
assert.includeMembers(frameBodyDeltaRequest.queries!, [
'config=8888',
'arch=x86',
'config=565',
'arch=arm',
]);
});
it('fetches entire range when _incremental is false ', async () => {
// dataframe: [1100, 1400]
// state: [300, 1400]
// request: [300, 1400]
const explore = await setUpElementUnderTest<ExploreSimpleSk>(
'explore-simple-sk'
)();
// Setup is that we have the timestamp range [1100, 1400] loaded in the current dataframe.
// User does something to cause this._state's timestamp range to become [300, 1400],
// e.g. 'pan left'.
const existingDataFrame = fakeDataFrame();
explore['_dataframe'] = existingDataFrame;
const state = deepCopy(explore.state);
state._incremental = false;
state.begin = 300;
state.end = 1400;
explore['_state'] = state;
fetchMock.post('/_/shift/', {
begin: 0,
end: 1200,
});
explore['zoomLeftKey']();
assert.isTrue(fetchMock.done());
const frameBodyDeltaRequest: FrameRequest =
explore['requestFrameBodyDeltaFromState']();
// It should return a request for just the missing range, [300, 1100].
assert.deepEqual(
[frameBodyDeltaRequest.begin, frameBodyDeltaRequest.end],
[300, 1400]
);
});
it('fetches only missing new data when panning right with overlap', async () => {
// dataframe: [1100, 1400]
// state: [1200, 2100]
// request: [1400, 2100]
const explore = await setUpElementUnderTest<ExploreSimpleSk>(
'explore-simple-sk'
)();
// Setup is that we have the timestamp range [1100, 1400] loaded in the current dataframe.
// User does something to cause this._state's timestamp range to become [1200, 2100],
// e.g. 'pan right'.
const existingDataFrame = fakeDataFrame();
explore['_dataframe'] = existingDataFrame;
const state = deepCopy(explore.state);
state._incremental = true;
state.begin = 1100;
state.end = 2100;
state.queries = fromParamSet(existingDataFrame.paramset).split('&');
explore['_state'] = state;
const frameBodyDeltaRequest: FrameRequest =
explore['requestFrameBodyDeltaFromState']();
// It should return a request for just the missing range, [1400, 2100].
assert.deepEqual(
[frameBodyDeltaRequest.begin, frameBodyDeltaRequest.end],
[1400, 2100]
);
});
it('fetches full range if zoomed out both left and right', async () => {
// dataframe: [1100, 1400]
// state: [700, 2100]
// request: [700, 2100]
const explore = await setUpElementUnderTest<ExploreSimpleSk>(
'explore-simple-sk'
)();
// Setup is that we have the timestamp range [1100, 1400] loaded in the current dataframe.
// User does something to cause this._state's timestamp range to become [700, 2100],
// e.g. 'zoom out past both edges'.
const existingDataFrame = fakeDataFrame();
explore['_dataframe'] = existingDataFrame;
const state = deepCopy(explore.state);
state._incremental = true;
state.begin = 700;
state.end = 2100;
explore['_state'] = state;
const frameBodyDeltaRequest: FrameRequest =
explore['requestFrameBodyDeltaFromState']();
// Even though incremental fetches are enabled, it should still return a
// request for the full range, [700, 2100].
assert.deepEqual(
[frameBodyDeltaRequest.begin, frameBodyDeltaRequest.end],
[700, 2100]
);
});
it('fetches full range if state and dataframe ranges do not overlap', async () => {
// dataframe: [1100, 1400]
// state: [1900, 2100]
// request: [1900, 2100]
const explore = await setUpElementUnderTest<ExploreSimpleSk>(
'explore-simple-sk'
)();
// Setup is that we have the timestamp range [1100, 1400] loaded in the current dataframe.
// User does something to cause this._state's timestamp range to become [700, 2100],
// e.g. 'zoom out past both edges'.
const existingDataFrame = fakeDataFrame();
explore['_dataframe'] = existingDataFrame;
const state = deepCopy(explore.state);
state._incremental = true;
state.begin = 1900;
state.end = 2100;
explore['_state'] = state;
const frameBodyDeltaRequest: FrameRequest =
explore['requestFrameBodyDeltaFromState']();
// Even though incremental fetches are enabled, it should still return a
// request for the full range, [700, 2100].
assert.deepEqual(
[frameBodyDeltaRequest.begin, frameBodyDeltaRequest.end],
[1900, 2100]
);
});
it('fetches full range when _incremental is false', async () => {
// dataframe: [1100, 1400]
// state: [300, 1400]
// request: [300, 1400]
const explore = await setUpElementUnderTest<ExploreSimpleSk>(
'explore-simple-sk'
)();
// Setup is that we have the timestamp range [1100, 1400] loaded in the current dataframe.
// User does something to cause this._state's timestamp range to become [300, 1400],
// e.g. 'pan left'.
const existingDataFrame = fakeDataFrame();
explore['_dataframe'] = existingDataFrame;
const state = deepCopy(explore.state);
state._incremental = false;
state.begin = 300;
state.end = 1400;
explore['_state'] = state;
const frameBodyDeltaRequest: FrameRequest =
explore['requestFrameBodyDeltaFromState']();
// It should return a request for the full _state.begin/end range, [300, 1400].
assert.deepEqual(
[frameBodyDeltaRequest.begin, frameBodyDeltaRequest.end],
[300, 1400]
);
});
it('handles pan and zoom requests when _incremental is false', async () => {
// dataframe: [1100, 1400]
// state: [900, 1200]
// request: [900, 1200]
const explore = await setUpElementUnderTest<ExploreSimpleSk>(
'explore-simple-sk'
)();
const existingDataFrame = fakeDataFrame();
explore['_dataframe'] = existingDataFrame;
let currentBounds = timestampBounds(existingDataFrame);
let prePanZoom = explore['getCurrentZoom']();
const state = deepCopy(explore.state);
state.queries = ['name=IDK'];
state._incremental = false;
explore['_state'] = state;
const shiftedDataFrame = deepCopy(existingDataFrame);
shiftedDataFrame.header = [
{ offset: 9, timestamp: 900 },
{ offset: 10, timestamp: 1000 },
{ offset: 11, timestamp: 1100 },
{ offset: 12, timestamp: 1200 },
];
const shiftResponse = {
begin: shiftedDataFrame!.header[0]!.timestamp,
end: shiftedDataFrame!.header[3]!.timestamp,
};
const finishedBody: progress.SerializedProgress = {
status: 'Finished',
messages: [],
results: {
anomalymap: null,
skps: [],
dataframe: shiftedDataFrame,
display_mode: 'display_plot',
msg: '',
},
url: '',
};
const startURL = '/_/frame/start';
fetchMock.reset();
fetchMock.post('/_/shift/', shiftResponse);
fetchMock.post(startURL, finishedBody);
// Simulate user hitting a key to pan left.
explore['zoomLeftKey']();
await fetchMock.flush(true);
assert.isTrue(fetchMock.done(), 'made the expected fetch calls');
const postPanZoom = explore['getCurrentZoom']();
assert.deepEqual(
prePanZoom,
postPanZoom,
'panning should not affect the zoom'
);
const newState = explore['_state'];
assert.equal(newState.requestType, 0);
const frameBodyDeltaRequest: FrameRequest =
explore['requestFrameBodyDeltaFromState']();
currentBounds = timestampBounds(explore['_dataframe']);
// It should return a request for the full _state.begin/end range.
assert.deepEqual(
[frameBodyDeltaRequest.begin, frameBodyDeltaRequest.end],
currentBounds,
'frame req should match current bounds'
);
fetchMock.restore();
});
it('fetch full dataframe if queries change', async () => {
// run a query, test=A, turn on _incremental,
// then do another search with test=B and also change the date range on that second query.
// dataframe (paramset: ['test=A']): [1100, 1400]
// state (queries: ['test=B']): [900, 1200]
// request (queries: ['test=B']): [900, 1200]
const explore = await setUpElementUnderTest<ExploreSimpleSk>(
'explore-simple-sk'
)();
// run a query, test=A
const queryTestADataFrame: DataFrame = {
header: [
{ offset: 11, timestamp: 1100 },
{ offset: 12, timestamp: 1200 },
{ offset: 13, timestamp: 1300 },
{ offset: 14, timestamp: 1400 },
],
traceset: {
'test=A': [0.1, 0.2, 0.0, 0.4],
},
paramset: {},
skip: 0,
};
buildParamSet(queryTestADataFrame);
explore['_dataframe'] = queryTestADataFrame;
const state = deepCopy(explore.state);
// turn on _incremental
state._incremental = true;
// then do another search with test=B
state.queries = ['test=B'];
// and also change the date range on that second query
state.begin = 900;
state.end = 1200;
explore['_state'] = state;
const frameBodyDeltaRequest: FrameRequest =
explore['requestFrameBodyDeltaFromState']();
assert.deepEqual(
frameBodyDeltaRequest.queries,
['test=B'],
'fetch the new query rather than the existing query'
);
assert.deepEqual(
[frameBodyDeltaRequest.begin, frameBodyDeltaRequest.end],
[900, 1200],
'fetch entire range if the query changed'
);
});
});