blob: 1f8ae9f6c91640e4829ce837b26eade0e6098fce [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {assert} from 'chai';
import {afterEach, describe, it} from 'mocha';
import * as fs from 'node:fs';
import * as path from 'node:path';
import {GnAstExtractor} from '../extractors/gn_ast_extractor.ts';
import {TypeScriptAnalyzer} from '../extractors/typescript_analyzer.ts';
import {checkDepsGn} from '../validator.ts';
const FIXTURES_DIR = path.join(import.meta.dirname, 'fixtures');
describe('validator', () => {
afterEach(async () => {
GnAstExtractor.clearCacheForTesting();
TypeScriptAnalyzer.clearCacheForTesting();
// Clean up any remaining temp dirs
const files = await fs.promises.readdir(import.meta.dirname);
for (const file of files) {
if (file.startsWith('gn-test-')) {
await fs.promises.rm(path.join(import.meta.dirname, file), {recursive: true, force: true});
}
}
});
it('warns correctly for missing targets even when files are relative and cwd is different', async () => {
const tempDir = await fs.promises.mkdtemp(
path.join(import.meta.dirname, 'gn-test-'),
);
await fs.promises.cp(FIXTURES_DIR, tempDir, {recursive: true});
const relFile = 'missing_target_file.ts';
const absPathInFixtures = path.join(tempDir, relFile);
await fs.promises.writeFile(absPathInFixtures, 'console.log("hello");');
const originalCwd = process.cwd();
const originalConsoleWarn = console.warn;
const warnings: string[] = [];
console.warn = (msg: string) => {
warnings.push(msg);
};
try {
process.chdir(path.join(import.meta.dirname, 'utils'));
await checkDepsGn(tempDir, [relFile]);
assert.isNotEmpty(
warnings,
'Validator warned correctly about missing target',
);
} finally {
process.chdir(originalCwd);
console.warn = originalConsoleWarn;
await fs.promises.rm(tempDir, {recursive: true, force: true});
}
});
it('throws on first missing target when dryRun is true', async () => {
const tempDir = await fs.promises.mkdtemp(
path.join(import.meta.dirname, 'gn-test-'),
);
await fs.promises.cp(FIXTURES_DIR, tempDir, {recursive: true});
const relFile = 'missing_target_file.ts';
const absPathInFixtures = path.join(tempDir, relFile);
await fs.promises.writeFile(absPathInFixtures, 'console.log("hello");');
try {
let thrownError: Error|undefined;
try {
await checkDepsGn(tempDir, [relFile], true);
} catch (e) {
thrownError = e as Error;
}
assert.isDefined(thrownError);
assert.include(
thrownError.message,
'Could not find target for file missing_target_file.ts in project BUILD.gn ASTs',
);
} finally {
await fs.promises.rm(tempDir, {recursive: true, force: true});
}
});
it('does not warn for BUILD.gn files and throws on dependency mismatch in dryRun mode', async () => {
const tempDir = await fs.promises.mkdtemp(
path.join(import.meta.dirname, 'gn-test-'),
);
await fs.promises.cp(FIXTURES_DIR, tempDir, {recursive: true});
const originalConsoleWarn = console.warn;
const warnings: string[] = [];
console.warn = (msg: string) => {
warnings.push(msg);
};
try {
let thrownError: Error|undefined;
try {
await checkDepsGn(tempDir, [path.join(tempDir, 'BUILD.gn')], true);
} catch (e) {
thrownError = e as Error;
}
assert.isFalse(
warnings.some(w => w.includes('Could not find target for file')),
);
assert.isDefined(thrownError);
assert.include(thrownError.message, 'Mismatch in //:animation');
} finally {
console.warn = originalConsoleWarn;
await fs.promises.rm(tempDir, {recursive: true, force: true});
}
});
it('succeeds in targeted dry-run mode when source is declared in ancestor BUILD.gn', async () => {
const tempDir = await fs.promises.mkdtemp(
path.join(import.meta.dirname, 'gn-test-'),
);
try {
const subDir = path.join(tempDir, 'sub');
await fs.promises.mkdir(subDir, {recursive: true});
await fs.promises.writeFile(path.join(subDir, 'child.ts'), 'export const x = 1;\n');
await fs.promises.writeFile(path.join(subDir, 'other.ts'), 'export const y = 2;\n');
await fs.promises.writeFile(
path.join(tempDir, 'BUILD.gn'),
'group("ancestor_target") {\n sources = [ "sub/child.ts" ]\n}\n',
);
await fs.promises.writeFile(
path.join(subDir, 'BUILD.gn'),
'group("child_target") {\n sources = [ "other.ts" ]\n}\n',
);
await checkDepsGn(tempDir, [path.join(subDir, 'child.ts')], true);
} finally {
await fs.promises.rm(tempDir, {recursive: true, force: true});
}
});
});