Skip to content

Commit

Permalink
Add performance unit test for timeline-chart
Browse files Browse the repository at this point in the history
This commit adds unit tests that measure the performance of
the bottlenecks of the timeline chart while zooming in. With these
unit tests, developers now can measure the effects of their patches
on the performance of the timeline chart.

Signed-off-by: Hoang Thuan Pham <hoang.pham@calian.ca>
  • Loading branch information
hoangphamEclipse committed Jun 7, 2023
1 parent 5d40c37 commit e69ac71
Show file tree
Hide file tree
Showing 21 changed files with 1,875,608 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ jobs:
shell: bash
run: |
yarn test
env:
NODE_OPTIONS: "--max_old_space_size=4096"

publish:
needs: build
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,22 @@ cd timeline-chart
yarn test --verbose --watch
```

### Performance tests

Developers can use unit tests to measure the performance improvement of their changes. The following section is a quick guide on how to set up unit tests.

#### Set up

The `TimeGraphPerformanceTest` is a class that conveniently creates a timeline chart for performance tests. To construct a chart for testing, trace data and its corresponding view range is required. The class provides some methods that return important test data, such as the total length of a trace for assertions. The code is taken from the timeline chart example.

You can view an example [here](./timeline-chart/src/layer/__tests__/time-graph-chart-long-removal-test.ts). In this example, test data are 'packaged' into a single object for better readability.

#### Useful tips

1. The `getTimeGraphChart()` method returns the constructed timeline chart. From here, developers can call any methods that they want to test on the returned chart. If the methods are protected or private, using the `@ts-ignore` annotation will bypass typescript checks.
2. It is important to re-construct the timeline chart before each test to make sure the results are consistent.
3. The tests themselves only print out the results and don't compare them to an actual value, because the threshold depends on the environment it runs. Therefore, make sure to output the performance measurement to the output console, so that it can be viewed once the tests finished running.

## Test coverage

The following command prints a coverage report to the terminal. As of now it covers all typescript files of the project, including those that are not supposed to have tests.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

import { TimeGraphPerformanceTest } from "../unitTest/time-graph-performance-test";
import { LongRemovalTestData } from '../unitTest/data/longRemoval/settings';

describe('TImeGraphChart performance test with long states removal', () => {
let timeGraph: TimeGraphPerformanceTest;

beforeEach(() => {
// Initiating the test
timeGraph = new TimeGraphPerformanceTest(
LongRemovalTestData.data,
LongRemovalTestData.viewRange);

timeGraph.toNextDataSet();
timeGraph.setViewRange(LongRemovalTestData.zoomRange.start, LongRemovalTestData.zoomRange.end);
});

it ('addOrUpdateRow() test with long removal time', () => {
const data = timeGraph.getData();
const timeGraphChart = timeGraph.getTimeGraphChart();

const start = performance.now();
// @ts-ignore
timeGraphChart.addOrUpdateRows(data);
const end = performance.now();
const time = end - start;
console.log("addOrUpdateRow() - with long removal time", time, "ms");
expect(time).toBeGreaterThan(0);
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

import { TimeGraphPerformanceTest } from "../unitTest/time-graph-performance-test";
import { WithLabelTestData } from "../unitTest/data/withLabels/settings";

describe('TImeGraphChart performance test with labels', () => {
let timeGraph: TimeGraphPerformanceTest;

beforeEach(() => {
// Initiating the test
timeGraph = new TimeGraphPerformanceTest(
WithLabelTestData.data,
WithLabelTestData.viewRange);

});

it ('onScaleFactorChanged test', () => {
// Making sure that the set up is good
expect(timeGraph.getAbsoluteStart()).toEqual(WithLabelTestData.traceStart);
expect(timeGraph.getTotalLength()).toEqual(WithLabelTestData.totalLength);

// Set the zoom range so that we don't have to call adjustZoom()
const start = performance.now();
timeGraph.scaleChart(1.2);
const end = performance.now();
const time = end - start;
console.log("onScaleFactorChanged - with labels running time", time, "ms");
expect(time).toBeGreaterThan(0);
})

it ('addOrUpdateRow() test', () => {
const timeGraphChart = timeGraph.getTimeGraphChart();
const data = timeGraph.getData();

const start = performance.now();
// We don't need to fetch any new data at all, the function should wipe out all existing rows
// and rerender them.
// @ts-ignore
timeGraphChart.addOrUpdateRows(data);
const end = performance.now();
const time = end - start;
console.log("addOrUpdateRow() - with labels running time", time, "ms");
expect(time).toBeGreaterThan(0);
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

import { TimeGraphPerformanceTest } from "../unitTest/time-graph-performance-test";
import { NoLabelTestData } from "../unitTest/data/noLabels/settings";

describe('TImeGraphChart performance test without labels', () => {
let timeGraph: TimeGraphPerformanceTest;

beforeEach(() => {
// Initiating the test
timeGraph = new TimeGraphPerformanceTest(
NoLabelTestData.data,
NoLabelTestData.viewRange);
});

it ('onScaleFactorChanged test', () => {
const start = performance.now();
// Set the view range so that the on view range handlers are triggered
timeGraph.scaleChart(1.2); // Zoom in
const end = performance.now();
const time = end - start;
console.log("onScaleFactorChanged - without labels running time", time, "ms");
expect(time).toBeGreaterThan(0);
})

it ('addOrUpdateRow() test', () => {
const timeGraphChart = timeGraph.getTimeGraphChart();
const data = timeGraph.getData();

const start = performance.now();
// We don't need to fetch any new data at all, the function should wipe out all existing rows
// and rerender them.
// @ts-ignore
timeGraphChart.addOrUpdateRows(data);
const end = performance.now();
const time = end - start;
console.log("addOrUpdateRow() - without labels running time", time, "ms");
expect(time).toBeGreaterThan(0);
})
})
31 changes: 31 additions & 0 deletions timeline-chart/src/layer/unitTest/data/longRemoval/settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { timeGraphEntries } from "./test-entries";
import { timeGraphRowIds } from "./test-ids";
import { timeGraphStates } from "./test-states";
import { timeGraphStates2 } from "./test-states-2";

export const LongRemovalTestData = {
traceStart: BigInt("1673902683695025022"),
totalLength: BigInt("3027146240"),
// The view range to set the time graph chart to before the zoom in absolute value
viewRange: {
start: BigInt("1673902684100000000") - BigInt("1673902683695025022"),
end: BigInt("1673902684200000000") - BigInt("1673902683695025022")
},
// The view range post zooming is applied, logical time
zoomRange: {
start: BigInt("414974978"),
end: BigInt("424974978")
},
data: [
{
timeGraphEntries: timeGraphEntries,
timeGraphRowIds: timeGraphRowIds,
timeGraphStates: timeGraphStates
},
{
timeGraphEntries: timeGraphEntries,
timeGraphRowIds: timeGraphRowIds,
timeGraphStates: timeGraphStates2
}
]
}
Loading

0 comments on commit e69ac71

Please sign in to comment.