Skip to content

Commit

Permalink
use file sizes of all dependencies in TestSequencer
Browse files Browse the repository at this point in the history
  • Loading branch information
jeysal committed Dec 26, 2018
1 parent 570af86 commit 88dc8c1
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@

### Performance

- `[jest-cli]` Better test order heuristics on first run without cache ([#7553](https://github.com/facebook/jest/pull/7553))
- `[jest-mock]` Improve `getType` function performance. ([#7159](https://github.com/facebook/jest/pull/7159))

## 23.6.0
Expand Down
34 changes: 31 additions & 3 deletions packages/jest-cli/src/TestSequencer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
import type {AggregatedResult} from 'types/TestResult';
import type {Context} from 'types/Context';
import type {Test} from 'types/TestRunner';
import type {Path} from 'types/Config';

import fs from 'fs';
import HasteMap from 'jest-haste-map';
import DependencyResolver from 'jest-resolve-dependencies';
import {buildSnapshotResolver} from 'jest-snapshot';

const FAIL = 0;
const SUCCESS = 1;
Expand Down Expand Up @@ -59,6 +62,30 @@ export default class TestSequencer {
return cache;
}

_fileSizeRecurseDependencies(
test: Test,
stats: {[path: Path]: number},
): number {
const {
path,
context: {resolver, hasteFS, config},
} = test;

const fileSize = path =>
stats[path] || (stats[path] = fs.statSync(path).size);

const dependencyResolver = new DependencyResolver(
resolver,
hasteFS,
buildSnapshotResolver(config),
);
return (
Array.from(dependencyResolver.resolveRecursive(path))
.map(file => fileSize(file))
.reduce((sum, size) => sum + size, 0) + fileSize(path)
);
}

// When running more tests than we have workers available, sort the tests
// by size - big test files usually take longer to complete, so we run
// them first in an effort to minimize worker idle time at the end of a
Expand All @@ -69,8 +96,6 @@ export default class TestSequencer {
// fastest results.
sort(tests: Array<Test>): Array<Test> {
const stats = {};
const fileSize = test =>
stats[test.path] || (stats[test.path] = fs.statSync(test.path).size);
const hasFailed = (cache, test) =>
cache[test.path] && cache[test.path][0] === FAIL;
const time = (cache, test) => cache[test.path] && cache[test.path][1];
Expand All @@ -90,7 +115,10 @@ export default class TestSequencer {
} else if (testA.duration != null && testB.duration != null) {
return testA.duration < testB.duration ? 1 : -1;
} else {
return fileSize(testA) < fileSize(testB) ? 1 : -1;
return this._fileSizeRecurseDependencies(testA, stats) <
this._fileSizeRecurseDependencies(testB, stats)
? 1
: -1;
}
});
}
Expand Down
44 changes: 34 additions & 10 deletions packages/jest-cli/src/__tests__/test_sequencer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ import path from 'path';
import TestSequencer from '../TestSequencer';

jest.mock('fs');
jest.mock(
'jest-resolve-dependencies',
() =>
class {
constructor() {
this.resolveRecursive = jest.fn().mockImplementation(
path =>
({
'/test-a.js': ['/sut-a.js', 'test-util.js'],
'/test-ab.js': ['/sut-ab.js'],
'/test-b.js': ['/sut-b0.js'],
'/test-e.js': ['/sut-e.js'],
}[path] || []),
);
}
},
);

const FAIL = 0;
const SUCCESS = 1;
Expand Down Expand Up @@ -49,10 +66,17 @@ beforeEach(() => {
fs.writeFileSync = jest.fn();
});

test('sorts by file size if there is no timing information', () => {
expect(sequencer.sort(toTests(['/test-a.js', '/test-ab.js']))).toEqual([
{context, duration: undefined, path: '/test-ab.js'},
test('sorts by dependency file sizes if there is no timing information', () => {
expect(sequencer.sort(toTests(['/test-ab.js', '/test-a.js']))).toEqual([
{context, duration: undefined, path: '/test-a.js'},
{context, duration: undefined, path: '/test-ab.js'},
]);
});

test('includes the test file itself during size calculation', () => {
expect(sequencer.sort(toTests(['/test-b.js', '/test-ab.js']))).toEqual([
{context, duration: undefined, path: '/test-ab.js'},
{context, duration: undefined, path: '/test-b.js'},
]);
});

Expand Down Expand Up @@ -90,29 +114,29 @@ test('sorts based on failures and timing information', () => {
]);
});

test('sorts based on failures, timing information and file size', () => {
test('sorts based on failures, timing information and dependency file sizes', () => {
fs.readFileSync = jest.fn(() =>
JSON.stringify({
'/test-a.js': [SUCCESS, 5],
'/test-ab.js': [FAIL, 1],
'/test-c.js': [FAIL],
'/test-cd.js': [FAIL],
'/test-d.js': [SUCCESS, 2],
'/test-efg.js': [FAIL],
'/test-e.js': [FAIL],
}),
);
expect(
sequencer.sort(
toTests([
'/test-a.js',
'/test-ab.js',
'/test-c.js',
'/test-cd.js',
'/test-d.js',
'/test-efg.js',
'/test-e.js',
]),
),
).toEqual([
{context, duration: undefined, path: '/test-efg.js'},
{context, duration: undefined, path: '/test-c.js'},
{context, duration: undefined, path: '/test-e.js'},
{context, duration: undefined, path: '/test-cd.js'},
{context, duration: 1, path: '/test-ab.js'},
{context, duration: 5, path: '/test-a.js'},
{context, duration: 2, path: '/test-d.js'},
Expand Down

0 comments on commit 88dc8c1

Please sign in to comment.