Skip to content

Commit 91bad46

Browse files
jv3ndgp1130
authored andcommitted
fix(@angular/build): sort discovered unit-test files for deterministic builds
(cherry picked from commit 01afbf4)
1 parent bd09b98 commit 91bad46

2 files changed

Lines changed: 37 additions & 3 deletions

File tree

‎packages/angular/build/src/builders/unit-test/test-discovery.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,10 @@ export async function findTests(
7474
}
7575
}
7676

77-
// 3. Combine and de-duplicate results
78-
return [...resolvedTestFiles];
77+
// 3. Combine, de-duplicate and sort the results. The glob crawls directories asynchronously and
78+
// returns matches in a varying order; the order feeds the build entry points, so an unsorted list
79+
// makes the output (chunk contents and names) differ between two builds of the same sources.
80+
return [...resolvedTestFiles].sort();
7981
}
8082

8183
interface TestEntrypointsOptions {

‎packages/angular/build/src/builders/unit-test/test-discovery_spec.ts‎

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,40 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9+
import assert from 'node:assert';
10+
import { promises as fs } from 'node:fs';
11+
import { join } from 'node:path';
912
import { initializeHash } from '../../utils/hash';
10-
import { generateNameFromPath, getTestEntrypoints } from './test-discovery';
13+
import { toPosixPath } from '../../utils/path';
14+
import { findTests, generateNameFromPath, getTestEntrypoints } from './test-discovery';
15+
16+
describe('findTests', () => {
17+
let projectSourceRoot: string;
18+
19+
beforeEach(async () => {
20+
const baseTmpDir = process.env['TEST_TMPDIR'];
21+
assert(baseTmpDir, 'TEST_TMPDIR is not set');
22+
projectSourceRoot = await fs.mkdtemp(join(baseTmpDir, 'angular-find-tests-'));
23+
});
24+
25+
afterEach(async () => {
26+
await fs.rm(projectSourceRoot, { recursive: true, force: true });
27+
});
28+
29+
it('should return the test files sorted, regardless of the crawl order', async () => {
30+
const files = ['z/z.spec.ts', 'a/b/c.spec.ts', 'm.spec.ts', 'a/a.spec.ts', 'b/x/y.spec.ts'];
31+
for (const file of files) {
32+
const path = join(projectSourceRoot, file);
33+
await fs.mkdir(join(path, '..'), { recursive: true });
34+
await fs.writeFile(path, '');
35+
}
36+
37+
const result = await findTests(['**/*.spec.ts'], [], projectSourceRoot, projectSourceRoot);
38+
39+
const expected = files.map((file) => toPosixPath(join(projectSourceRoot, file))).sort();
40+
expect(result).toEqual(expected);
41+
});
42+
});
1143

1244
describe('getTestEntrypoints', () => {
1345
beforeAll(async () => {

0 commit comments

Comments
 (0)