add presubmit checks for sorting, support only checking changed files

BUG: https://github.com/dart-lang/sdk/issues/52064
Change-Id: Ie45f22cbfb9f97018ba2bf6256f1470abc95a220
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/476980
Commit-Queue: Jake Macdonald <jakemac@google.com>
Auto-Submit: Jake Macdonald <jakemac@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Jake Macdonald
2026-02-02 08:18:20 -08:00
committed by Commit Queue
parent 33d795b3a3
commit 39eba46d91
6 changed files with 336 additions and 30 deletions
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
# Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
"""Analysis server specific presubmit script.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
"""
import importlib.util
import importlib.machinery
import os.path
import subprocess
USE_PYTHON3 = True
PRESUBMIT_VERSION = '2.0.0'
def load_source(modname, filename):
loader = importlib.machinery.SourceFileLoader(modname, filename)
spec = importlib.util.spec_from_file_location(modname,
filename,
loader=loader)
module = importlib.util.module_from_spec(spec)
# The module is always executed and not cached in sys.modules.
# Uncomment the following line to cache the module.
# sys.modules[module.__name__] = module
loader.exec_module(module)
return module
def CheckSorted(input_api, output_api):
local_root = input_api.change.RepositoryRoot()
utils = load_source('utils', os.path.join(local_root, 'tools', 'utils.py'))
dart = os.path.join(utils.CheckedInSdkPath(), 'bin', 'dart')
windows = utils.GuessOS() == 'win32'
if windows:
dart += '.exe'
sourceArgs = [
arg for git_file in input_api.AffectedTestableFiles()
for arg in ('-s', git_file.AbsoluteLocalPath())
]
result = subprocess.run([
dart,
'run',
'-r',
os.path.join(local_root, 'pkg', 'analysis_server', 'test',
'verify_sorted_test.dart'),
] + sourceArgs,
capture_output=True)
if result.returncode != 0:
return [
output_api.PresubmitError('\n'.join([
line for line in result.stdout.decode('utf-8').splitlines()
if 'Unsorted file' in line
]))
]
return []
@@ -9,6 +9,7 @@ import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer_testing/package_root.dart';
import 'package:args/args.dart';
import 'package:test/test.dart';
/// The purpose of this test is to validate that all elements
@@ -19,40 +20,37 @@ import 'package:test/test.dart';
/// Pass `--update` as argument to this script to have the sorted files
/// written back.
void main([List<String> args = const <String>[]]) {
if (args.contains('--update')) {
var parsed = argParser.parse(args);
if (parsed.flag('update')) {
updateUnsorted = true;
}
var sources = parsed.multiOption('source');
group('analysis_server', () {
buildTestsForAnalysisServer();
});
group('analysis_server', () => buildTestsForAnalysisServer(sources));
group('analyzer', () {
buildTestsForAnalyzer();
});
group('analyzer', () => buildTestsForAnalyzer(sources));
group('analyzer_cli', () {
buildTestsForAnalyzerCli();
});
group('analyzer_cli', () => buildTestsForAnalyzerCli(sources));
group('analyzer_plugin', () {
buildTestsForAnalyzerPlugin();
});
group('analyzer_plugin', () => buildTestsForAnalyzerPlugin(sources));
group('analyzer_utilities', () {
buildTestsForAnalyzerPlugin();
});
group('linter', () {
buildTestsForLinter();
});
group('linter', () => buildTestsForLinter(sources));
}
final argParser = ArgParser()
..addFlag('update', help: 'Writes the sorted files')
..addMultiOption(
'source',
abbr: 's',
help: 'Absolute paths to sources to check',
);
bool updateUnsorted = false;
void buildTests({
required String packagePath,
required List<String> excludedPaths,
required List<String> explicitSources,
}) {
var provider = PhysicalResourceProvider.INSTANCE;
var pkgRootPath = provider.pathContext.normalize(packageRoot);
@@ -73,11 +71,12 @@ void buildTests({
packagePath,
excludedPaths,
provider.getFolder(packagePath),
explicitSources,
);
}
}
void buildTestsForAnalysisServer() {
void buildTestsForAnalysisServer(List<String> explicitSources) {
var excludedPaths = <String>[
// TODO(brianwilkerson): Fix the generator to sort the generated files and
// remove these exclusions.
@@ -90,10 +89,14 @@ void buildTestsForAnalysisServer() {
'lib/src/services/kythe/schema.dart',
];
buildTests(packagePath: 'analysis_server', excludedPaths: excludedPaths);
buildTests(
packagePath: 'analysis_server',
excludedPaths: excludedPaths,
explicitSources: explicitSources,
);
}
void buildTestsForAnalyzer() {
void buildTestsForAnalyzer(List<String> explicitSources) {
buildTests(
packagePath: 'analyzer',
excludedPaths: [
@@ -102,14 +105,19 @@ void buildTestsForAnalyzer() {
'lib/src/wolf/ir/ir.g.dart',
'test/generated/test_all.dart',
],
explicitSources: explicitSources,
);
}
void buildTestsForAnalyzerCli() {
buildTests(packagePath: 'analyzer_cli', excludedPaths: ['test/data']);
void buildTestsForAnalyzerCli(List<String> explicitSources) {
buildTests(
packagePath: 'analyzer_cli',
excludedPaths: ['test/data'],
explicitSources: explicitSources,
);
}
void buildTestsForAnalyzerPlugin() {
void buildTestsForAnalyzerPlugin(List<String> explicitSources) {
// TODO(brianwilkerson): Fix the generator to sort the generated files and
// remove these exclusions.
var excludedPaths = <String>[
@@ -119,11 +127,19 @@ void buildTestsForAnalyzerPlugin() {
'test/integration/support/protocol_matchers.dart',
];
buildTests(packagePath: 'analyzer_plugin', excludedPaths: excludedPaths);
buildTests(
packagePath: 'analyzer_plugin',
excludedPaths: excludedPaths,
explicitSources: explicitSources,
);
}
void buildTestsForLinter() {
buildTests(packagePath: 'linter', excludedPaths: ['test_data']);
void buildTestsForLinter(List<String> explicitSources) {
buildTests(
packagePath: 'linter',
excludedPaths: ['test_data'],
explicitSources: explicitSources,
);
}
void buildTestsIn(
@@ -131,6 +147,7 @@ void buildTestsIn(
String testDirPath,
List<String> excludedPath,
Folder directory,
List<String> explicitSources,
) {
var pathContext = session.resourceProvider.pathContext;
var children = directory.getChildren();
@@ -138,13 +155,22 @@ void buildTestsIn(
for (var child in children) {
if (child is Folder) {
if (!excludedPath.contains(child.path)) {
buildTestsIn(session, testDirPath, excludedPath, child);
buildTestsIn(
session,
testDirPath,
excludedPath,
child,
explicitSources,
);
}
} else if (child is File && child.shortName.endsWith('.dart')) {
var path = child.path;
if (excludedPath.contains(path)) {
continue;
}
if (explicitSources.isNotEmpty && !explicitSources.contains(child.path)) {
continue;
}
var relativePath = pathContext.relative(path, from: testDirPath);
test(relativePath, () {
var result = session.getParsedUnit(path);
+44
View File
@@ -8,13 +8,57 @@ See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
"""
import importlib.util
import importlib.machinery
import os.path
import re
import subprocess
USE_PYTHON3 = True
PRESUBMIT_VERSION = '2.0.0'
def load_source(modname, filename):
loader = importlib.machinery.SourceFileLoader(modname, filename)
spec = importlib.util.spec_from_file_location(modname,
filename,
loader=loader)
module = importlib.util.module_from_spec(spec)
# The module is always executed and not cached in sys.modules.
# Uncomment the following line to cache the module.
# sys.modules[module.__name__] = module
loader.exec_module(module)
return module
def CheckSorted(input_api, output_api):
local_root = input_api.change.RepositoryRoot()
utils = load_source('utils', os.path.join(local_root, 'tools', 'utils.py'))
dart = os.path.join(utils.CheckedInSdkPath(), 'bin', 'dart')
windows = utils.GuessOS() == 'win32'
if windows:
dart += '.exe'
sourceArgs = [
arg for git_file in input_api.AffectedTestableFiles()
for arg in ('-s', git_file.AbsoluteLocalPath())
]
result = subprocess.run([
dart,
'run',
'-r',
os.path.join(local_root, 'pkg', 'analysis_server', 'test',
'verify_sorted_test.dart'),
] + sourceArgs,
capture_output=True)
if result.returncode != 0:
return [
output_api.PresubmitError('\n'.join([
line for line in result.stdout.decode('utf-8').splitlines()
if 'Unsorted file' in line
]))
]
return []
def CheckNodeTextExpectationsCollectorUpdatingIsDisabled(input_api, output_api):
local_root = input_api.change.RepositoryRoot()
node_text_expectations_file = os.path.join(local_root, 'pkg', 'analyzer',
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
# Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
"""Analysis server specific presubmit script.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
"""
import importlib.util
import importlib.machinery
import os.path
import subprocess
USE_PYTHON3 = True
PRESUBMIT_VERSION = '2.0.0'
def load_source(modname, filename):
loader = importlib.machinery.SourceFileLoader(modname, filename)
spec = importlib.util.spec_from_file_location(modname,
filename,
loader=loader)
module = importlib.util.module_from_spec(spec)
# The module is always executed and not cached in sys.modules.
# Uncomment the following line to cache the module.
# sys.modules[module.__name__] = module
loader.exec_module(module)
return module
def CheckSorted(input_api, output_api):
local_root = input_api.change.RepositoryRoot()
utils = load_source('utils', os.path.join(local_root, 'tools', 'utils.py'))
dart = os.path.join(utils.CheckedInSdkPath(), 'bin', 'dart')
windows = utils.GuessOS() == 'win32'
if windows:
dart += '.exe'
sourceArgs = [
arg for git_file in input_api.AffectedTestableFiles()
for arg in ('-s', git_file.AbsoluteLocalPath())
]
result = subprocess.run([
dart,
'run',
'-r',
os.path.join(local_root, 'pkg', 'analysis_server', 'test',
'verify_sorted_test.dart'),
] + sourceArgs,
capture_output=True)
if result.returncode != 0:
return [
output_api.PresubmitError('\n'.join([
line for line in result.stdout.decode('utf-8').splitlines()
if 'Unsorted file' in line
]))
]
return []
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
# Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
"""Analysis server specific presubmit script.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
"""
import importlib.util
import importlib.machinery
import os.path
import subprocess
USE_PYTHON3 = True
PRESUBMIT_VERSION = '2.0.0'
def load_source(modname, filename):
loader = importlib.machinery.SourceFileLoader(modname, filename)
spec = importlib.util.spec_from_file_location(modname,
filename,
loader=loader)
module = importlib.util.module_from_spec(spec)
# The module is always executed and not cached in sys.modules.
# Uncomment the following line to cache the module.
# sys.modules[module.__name__] = module
loader.exec_module(module)
return module
def CheckSorted(input_api, output_api):
local_root = input_api.change.RepositoryRoot()
utils = load_source('utils', os.path.join(local_root, 'tools', 'utils.py'))
dart = os.path.join(utils.CheckedInSdkPath(), 'bin', 'dart')
windows = utils.GuessOS() == 'win32'
if windows:
dart += '.exe'
sourceArgs = [
arg for git_file in input_api.AffectedTestableFiles()
for arg in ('-s', git_file.AbsoluteLocalPath())
]
result = subprocess.run([
dart,
'run',
'-r',
os.path.join(local_root, 'pkg', 'analysis_server', 'test',
'verify_sorted_test.dart'),
] + sourceArgs,
capture_output=True)
if result.returncode != 0:
return [
output_api.PresubmitError('\n'.join([
line for line in result.stdout.decode('utf-8').splitlines()
if 'Unsorted file' in line
]))
]
return []
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
# Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
"""Analysis server specific presubmit script.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
"""
import importlib.util
import importlib.machinery
import os.path
import subprocess
USE_PYTHON3 = True
PRESUBMIT_VERSION = '2.0.0'
def load_source(modname, filename):
loader = importlib.machinery.SourceFileLoader(modname, filename)
spec = importlib.util.spec_from_file_location(modname,
filename,
loader=loader)
module = importlib.util.module_from_spec(spec)
# The module is always executed and not cached in sys.modules.
# Uncomment the following line to cache the module.
# sys.modules[module.__name__] = module
loader.exec_module(module)
return module
def CheckSorted(input_api, output_api):
local_root = input_api.change.RepositoryRoot()
utils = load_source('utils', os.path.join(local_root, 'tools', 'utils.py'))
dart = os.path.join(utils.CheckedInSdkPath(), 'bin', 'dart')
windows = utils.GuessOS() == 'win32'
if windows:
dart += '.exe'
sourceArgs = [
arg for git_file in input_api.AffectedTestableFiles()
for arg in ('-s', git_file.AbsoluteLocalPath())
]
result = subprocess.run([
dart,
'run',
'-r',
os.path.join(local_root, 'pkg', 'analysis_server', 'test',
'verify_sorted_test.dart'),
] + sourceArgs,
capture_output=True)
if result.returncode != 0:
return [
output_api.PresubmitError('\n'.join([
line for line in result.stdout.decode('utf-8').splitlines()
if 'Unsorted file' in line
]))
]
return []