blob: 3b6dcacada357a4e16fc48602bbd17c687a39beb [file] [log] [blame]
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed 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.
import {TimeSpan} from '../common/time';
import {getStepSize, Tick, TickGenerator, TickType} from './gridline_helper';
import {TimeScale} from './time_scale';
const pattern1 = '|....:....';
const pattern2 = '|.:.';
const pattern5 = '|....';
const timeScale = new TimeScale(new TimeSpan(0, 1), [1, 2]);
test('gridline helper to have sensible step sizes', () => {
expect(getStepSize(10, 14)).toEqual([1, pattern1]);
expect(getStepSize(30, 14)).toEqual([5, pattern5]);
expect(getStepSize(60, 14)).toEqual([5, pattern5]);
expect(getStepSize(100, 14)).toEqual([10, pattern1]);
expect(getStepSize(10, 21)).toEqual([0.5, pattern5]);
expect(getStepSize(30, 21)).toEqual([2, pattern2]);
expect(getStepSize(60, 21)).toEqual([5, pattern5]);
expect(getStepSize(100, 21)).toEqual([5, pattern5]);
expect(getStepSize(10, 3)).toEqual([5, pattern5]);
expect(getStepSize(30, 3)).toEqual([10, pattern1]);
expect(getStepSize(60, 3)).toEqual([20, pattern2]);
expect(getStepSize(100, 3)).toEqual([50, pattern5]);
expect(getStepSize(800, 4)).toEqual([200, pattern2]);
});
test('gridline helper to scale to very small and very large values', () => {
expect(getStepSize(.01, 14)).toEqual([.001, pattern1]);
expect(getStepSize(10000, 14)).toEqual([1000, pattern1]);
});
test('gridline helper to always return a reasonable number of steps', () => {
for (let i = 1; i <= 1000; i++) {
const [stepSize, _] = getStepSize(i, 14);
expect(Math.round(i / stepSize)).toBeGreaterThanOrEqual(6);
expect(Math.round(i / stepSize)).toBeLessThanOrEqual(14);
}
});
describe('TickGenerator with range 0.0-1.0 and room for 2 labels', () => {
let tickGen: TickGenerator|undefined = undefined;
beforeAll(() => {
const timeSpan = new TimeSpan(0.0, 1.0);
const timeScale = new TimeScale(timeSpan, [0, 200]);
tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
});
it('should produce major ticks at 0.5s and minor ticks at 0.1s starting at 0',
() => {
const expected = [
{type: TickType.MAJOR, time: 0.0},
{type: TickType.MINOR, time: 0.1},
{type: TickType.MINOR, time: 0.2},
{type: TickType.MINOR, time: 0.3},
{type: TickType.MINOR, time: 0.4},
{type: TickType.MAJOR, time: 0.5},
{type: TickType.MINOR, time: 0.6},
{type: TickType.MINOR, time: 0.7},
{type: TickType.MINOR, time: 0.8},
{type: TickType.MINOR, time: 0.9},
];
const actual = Array.from(tickGen!);
expectTicksEqual(actual, expected);
});
it('should tell us to use 1 decimal place for labels', () => {
expect(tickGen!.digits).toEqual(1);
});
});
describe('TickGenerator with range 0.3-1.3 and room for 2 labels', () => {
let tickGen: TickGenerator|undefined = undefined;
beforeAll(() => {
const timeSpan = new TimeSpan(0.3, 1.3);
const timeScale = new TimeScale(timeSpan, [0, 200]);
tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
});
it('should produce major ticks at 0.5s and minor ticks at 0.1s starting at 0',
() => {
const expected = [
{type: TickType.MINOR, time: 0.3},
{type: TickType.MINOR, time: 0.4},
{type: TickType.MAJOR, time: 0.5},
{type: TickType.MINOR, time: 0.6},
{type: TickType.MINOR, time: 0.7},
{type: TickType.MINOR, time: 0.8},
{type: TickType.MINOR, time: 0.9},
{type: TickType.MAJOR, time: 1.0},
{type: TickType.MINOR, time: 1.1},
{type: TickType.MINOR, time: 1.2},
];
const actual = Array.from(tickGen!);
expectTicksEqual(actual, expected);
});
it('should tell us to use 1 decimal place for labels', () => {
expect(tickGen!.digits).toEqual(1);
});
});
describe('TickGenerator with range 0.0-0.2 and room for 1 label', () => {
let tickGen: TickGenerator|undefined = undefined;
beforeAll(() => {
const timeSpan = new TimeSpan(0.0, 0.2);
const timeScale = new TimeScale(timeSpan, [0, 100]);
tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
});
it('should produce major ticks at 0.2s and minor ticks at 0.1s starting at 0',
() => {
const expected = [
{type: TickType.MAJOR, time: 0.0},
{type: TickType.MINOR, time: 0.05},
{type: TickType.MEDIUM, time: 0.1},
{type: TickType.MINOR, time: 0.15},
];
const actual = Array.from(tickGen!);
expectTicksEqual(actual, expected);
});
it('should tell us to use 1 decimal place for labels', () => {
expect(tickGen!.digits).toEqual(1);
});
});
describe('TickGenerator with range 0.0-0.1 and room for 1 label', () => {
let tickGen: TickGenerator|undefined = undefined;
beforeAll(() => {
const timeSpan = new TimeSpan(0.0, 0.1);
const timeScale = new TimeScale(timeSpan, [0, 100]);
tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
});
it('should produce major ticks at 0.1s & minor ticks at 0.02s starting at 0',
() => {
const expected = [
{type: TickType.MAJOR, time: 0.0},
{type: TickType.MINOR, time: 0.01},
{type: TickType.MINOR, time: 0.02},
{type: TickType.MINOR, time: 0.03},
{type: TickType.MINOR, time: 0.04},
{type: TickType.MEDIUM, time: 0.05},
{type: TickType.MINOR, time: 0.06},
{type: TickType.MINOR, time: 0.07},
{type: TickType.MINOR, time: 0.08},
{type: TickType.MINOR, time: 0.09},
];
const actual = Array.from(tickGen!);
expect(tickGen!.digits).toEqual(1);
expectTicksEqual(actual, expected);
});
it('should tell us to use 1 decimal place for labels', () => {
expect(tickGen!.digits).toEqual(1);
});
});
describe('TickGenerator with a very small timespan', () => {
let tickGen: TickGenerator|undefined = undefined;
beforeAll(() => {
const timeSpan = new TimeSpan(0.0, 1e-9);
const timeScale = new TimeScale(timeSpan, [0, 100]);
tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
});
it('should generate minor ticks at 2e-10s and one major tick at the start',
() => {
const expected = [
{type: TickType.MAJOR, time: 0.0},
{type: TickType.MINOR, time: 1e-10},
{type: TickType.MINOR, time: 2e-10},
{type: TickType.MINOR, time: 3e-10},
{type: TickType.MINOR, time: 4e-10},
{type: TickType.MEDIUM, time: 5e-10},
{type: TickType.MINOR, time: 6e-10},
{type: TickType.MINOR, time: 7e-10},
{type: TickType.MINOR, time: 8e-10},
{type: TickType.MINOR, time: 9e-10},
];
const actual = Array.from(tickGen!);
expectTicksEqual(actual, expected);
});
it('should tell us to use 9 decimal places for labels', () => {
expect(tickGen!.digits).toEqual(9);
});
});
describe('TickGenerator with a very large timespan', () => {
let tickGen: TickGenerator|undefined = undefined;
beforeAll(() => {
const timeSpan = new TimeSpan(0.0, 1e9);
const timeScale = new TimeScale(timeSpan, [0, 100]);
tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
});
it('should generate minor ticks at 2e8 and one major tick at the start',
() => {
const expected = [
{type: TickType.MAJOR, time: 0.0},
{type: TickType.MINOR, time: 1e8},
{type: TickType.MINOR, time: 2e8},
{type: TickType.MINOR, time: 3e8},
{type: TickType.MINOR, time: 4e8},
{type: TickType.MEDIUM, time: 5e8},
{type: TickType.MINOR, time: 6e8},
{type: TickType.MINOR, time: 7e8},
{type: TickType.MINOR, time: 8e8},
{type: TickType.MINOR, time: 9e8},
];
const actual = Array.from(tickGen!);
expectTicksEqual(actual, expected);
});
it('should tell us to use 0 decimal places for labels', () => {
expect(tickGen!.digits).toEqual(0);
});
});
describe('TickGenerator where the timespan has a dynamic range of 1e12', () => {
// This is the equivalent of zooming in to the nanosecond level, 1000 seconds
// into a trace Note: this is about the limit of what this generator can
// handle.
let tickGen: TickGenerator|undefined = undefined;
beforeAll(() => {
const timeSpan = new TimeSpan(1000, 1000.000000001);
const timeScale = new TimeScale(timeSpan, [0, 100]);
tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
});
it('should generate minor ticks at 1e-10s and one major tick at the start',
() => {
const expected = [
{type: TickType.MAJOR, time: 1000.0000000000},
{type: TickType.MINOR, time: 1000.0000000001},
{type: TickType.MINOR, time: 1000.0000000002},
{type: TickType.MINOR, time: 1000.0000000003},
{type: TickType.MINOR, time: 1000.0000000004},
{type: TickType.MEDIUM, time: 1000.0000000005},
{type: TickType.MINOR, time: 1000.0000000006},
{type: TickType.MINOR, time: 1000.0000000007},
{type: TickType.MINOR, time: 1000.0000000008},
{type: TickType.MINOR, time: 1000.0000000009},
];
const actual = Array.from(tickGen!);
expectTicksEqual(actual, expected);
});
it('should tell us to use 9 decimal places for labels', () => {
expect(tickGen!.digits).toEqual(9);
});
});
describe(
'TickGenerator where the timespan has a ridiculously huge dynamic range',
() => {
// We don't expect this to work, just wanna make sure it doesn't crash or
// get stuck
it('should not crash or get stuck in an infinite loop', () => {
const timeSpan = new TimeSpan(1000, 1000.000000000001);
const timeScale = new TimeScale(timeSpan, [0, 100]);
new TickGenerator(timeScale);
});
});
describe(
'TickGenerator where the timespan has a ridiculously huge dynamic range',
() => {
// We don't expect this to work, just wanna make sure it doesn't crash or
// get stuck
it('should not crash or get stuck in an infinite loop', () => {
const timeSpan = new TimeSpan(1000, 1000.000000000001);
const timeScale = new TimeScale(timeSpan, [0, 100]);
new TickGenerator(timeScale);
});
});
test('TickGenerator constructed with a 0 width throws an error', () => {
expect(() => {
const timeScale = new TimeScale(new TimeSpan(0.0, 1.0), [0, 0]);
new TickGenerator(timeScale);
}).toThrow(Error);
});
test(
'TickGenerator constructed with desiredPxPerStep of 0 throws an error',
() => {
expect(() => {
new TickGenerator(timeScale, {minLabelPx: 0});
}).toThrow(Error);
});
test('TickGenerator constructed with a 0 duration throws an error', () => {
expect(() => {
const timeScale = new TimeScale(new TimeSpan(0.0, 0.0), [0, 1]);
new TickGenerator(timeScale);
}).toThrow(Error);
});
function expectTicksEqual(actual: Tick[], expected: any[]) {
// TODO(stevegolton) We could write a custom matcher for this; this approach
// produces cryptic error messages.
expect(actual.length).toEqual(expected.length);
for (let i = 0; i < actual.length; ++i) {
const ex = expected[i];
const ac = actual[i];
expect(ac.type).toEqual(ex.type);
expect(ac.time).toBeCloseTo(ex.time, 9);
}
}