Rebuild context on bazel BUILD file change.

Bug: https://buganizer.corp.google.com/issues/195398835
Change-Id: I80a659a8ab6a3162703e6442970438af9b19be60
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/209020
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Michal Terepeta <michalt@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2021-08-05 12:47:40 +00:00 committed by commit-bot@chromium.org
parent 95164f40f4
commit 6f917d00af
2 changed files with 131 additions and 84 deletions

View file

@ -501,7 +501,7 @@ class ContextManagerImpl implements ContextManager {
}
}
List<String> _getPossibelBazelBinPaths(_BazelWatchedFiles watched) => [
List<String> _getPossibleBazelBinPaths(_BazelWatchedFiles watched) => [
pathContext.join(watched.workspace, 'bazel-bin'),
pathContext.join(watched.workspace, 'blaze-bin'),
];
@ -530,7 +530,7 @@ class ContextManagerImpl implements ContextManager {
// we do, we'll simply recreate all contexts to make sure that we follow the
// correct paths.
var bazelSymlinkPaths = bazelWatchedPathsPerFolder.values
.expand((watched) => _getPossibelBazelBinPaths(watched))
.expand((watched) => _getPossibleBazelBinPaths(watched))
.toSet();
if (allEvents.any((event) => bazelSymlinkPaths.contains(event.path))) {
refresh();
@ -572,15 +572,16 @@ class ContextManagerImpl implements ContextManager {
_instrumentationService.logWatchEvent('<unknown>', path, type.toString());
final isPubpsec = file_paths.isPubspecYaml(pathContext, path);
final isPubspec = file_paths.isPubspecYaml(pathContext, path);
if (file_paths.isAnalysisOptionsYaml(pathContext, path) ||
file_paths.isBazelBuild(pathContext, path) ||
file_paths.isDotPackages(pathContext, path) ||
file_paths.isPackageConfigJson(pathContext, path) ||
isPubpsec ||
isPubspec ||
false) {
_createAnalysisContexts();
if (isPubpsec) {
if (isPubspec) {
if (type == ChangeType.REMOVE) {
callbacks.pubspecRemoved(path);
} else {
@ -684,7 +685,7 @@ class ContextManagerImpl implements ContextManager {
void _startWatchingBazelBinPaths(_BazelWatchedFiles watched) {
var watcherService = bazelWatcherService;
if (watcherService == null) return;
var paths = _getPossibelBazelBinPaths(watched);
var paths = _getPossibleBazelBinPaths(watched);
watcherService.startWatching(
watched.workspace, BazelSearchInfo(paths[0], paths));
}
@ -693,7 +694,7 @@ class ContextManagerImpl implements ContextManager {
void _stopWatchingBazelBinPaths(_BazelWatchedFiles watched) {
var watcherService = bazelWatcherService;
if (watcherService == null) return;
var paths = _getPossibelBazelBinPaths(watched);
var paths = _getPossibleBazelBinPaths(watched);
watcherService.stopWatching(watched.workspace, paths[0]);
}

View file

@ -26,12 +26,56 @@ import 'mocks.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(AnalysisDomainTest);
defineReflectiveTests(AnalysisDomainBazelTest);
defineReflectiveTests(AnalysisDomainPubTest);
defineReflectiveTests(AnalysisDomainHandlerTest);
defineReflectiveTests(SetSubscriptionsTest);
});
}
@reflectiveTest
class AnalysisDomainBazelTest extends _AnalysisDomainTest {
String get myPackageLibPath => '$myPackageRootPath/lib';
String get myPackageRootPath => '$workspaceRootPath/dart/my';
String get myPackageTestFilePath => '$myPackageLibPath/test.dart';
String get workspaceRootPath => '/workspace';
@override
void setUp() {
super.setUp();
newFile('$workspaceRootPath/WORKSPACE');
}
Future<void> test_fileSystem_changeFile_buildFile() async {
// This BUILD file does not enable null safety.
newBazelBuildFile(myPackageRootPath, '');
newFile(myPackageTestFilePath, content: '''
void f(int? a) {}
''');
setRoots(included: [myPackageRootPath], excluded: []);
await server.onAnalysisComplete;
// Cannot use `int?` without enabling null safety.
assertHasErrors(myPackageTestFilePath);
// Enable null safety.
newBazelBuildFile(myPackageRootPath, '''
dart_package(null_safety = True)
''');
await pumpEventQueue();
await server.onAnalysisComplete;
// We have null safety enabled, so no errors.
assertNoErrors(myPackageTestFilePath);
}
}
@reflectiveTest
class AnalysisDomainHandlerTest extends AbstractAnalysisTest {
Future<void> outOfRangeTest(SourceEdit edit) async {
@ -302,12 +346,7 @@ class AnalysisDomainHandlerTest extends AbstractAnalysisTest {
}
@reflectiveTest
class AnalysisDomainTest extends AbstractAnalysisTest {
final Map<String, List<AnalysisError>> filesErrors = {};
/// The files for which `analysis.flushResults` was received.
final List<String> flushResults = [];
class AnalysisDomainPubTest extends _AnalysisDomainTest {
String get testFilePath => '$testPackageLibPath/test.dart';
String get testPackageLibPath => '$testPackageRootPath/lib';
@ -316,38 +355,6 @@ class AnalysisDomainTest extends AbstractAnalysisTest {
String get workspaceRootPath => '/home';
void assertHasErrors(String path) {
path = convertPath(path);
expect(filesErrors[path], isNotEmpty, reason: path);
}
void assertNoErrors(String path) {
path = convertPath(path);
expect(filesErrors[path], isEmpty, reason: path);
}
void assertNoErrorsNotification(String path) {
path = convertPath(path);
expect(filesErrors[path], isNull, reason: path);
}
void forgetReceivedErrors() {
filesErrors.clear();
}
@override
void processNotification(Notification notification) {
if (notification.event == ANALYSIS_NOTIFICATION_FLUSH_RESULTS) {
var decoded = AnalysisFlushResultsParams.fromNotification(notification);
flushResults.addAll(decoded.files);
decoded.files.forEach(filesErrors.remove);
}
if (notification.event == ANALYSIS_NOTIFICATION_ERRORS) {
var decoded = AnalysisErrorsParams.fromNotification(notification);
filesErrors[decoded.file] = decoded.errors;
}
}
Future<void> test_fileSystem_addFile_analysisOptions() async {
var a_path = '$testPackageLibPath/a.dart';
var b_path = '$testPackageLibPath/b.dart';
@ -1408,44 +1415,6 @@ void f(A a) {}
// errors are not reported for packages
assertNoErrorsNotification(a_path);
}
void writePackageConfig(String path, PackageConfigFileBuilder config) {
newFile(path, content: config.toContent(toUriStr: toUriStr));
}
void _assertAnalyzedFiles({
required List<String> hasErrors,
List<String> noErrors = const [],
required List<String> notAnalyzed,
}) {
for (var path in hasErrors) {
assertHasErrors(path);
}
for (var path in noErrors) {
assertNoErrors(path);
}
for (var path in notAnalyzed) {
assertNoErrorsNotification(path);
}
filesErrors.clear();
}
void _assertFlushedResults(List<String> paths) {
var convertedPaths = paths.map(convertPath).toList();
expect(flushResults, unorderedEquals(convertedPaths));
flushResults.clear();
}
/// Create files with a content that has a compile time error.
/// So, when analyzed, these files will satisfy [assertHasErrors].
void _createFilesWithErrors(List<String> paths) {
for (var path in paths) {
newFile(path, content: 'error');
}
}
}
/// A helper to test 'analysis.*' requests.
@ -1776,3 +1745,80 @@ class A {}
expect(files, [testFile]);
}
}
class _AnalysisDomainTest extends AbstractAnalysisTest {
final Map<String, List<AnalysisError>> filesErrors = {};
/// The files for which `analysis.flushResults` was received.
final List<String> flushResults = [];
void assertHasErrors(String path) {
path = convertPath(path);
expect(filesErrors[path], isNotEmpty, reason: path);
}
void assertNoErrors(String path) {
path = convertPath(path);
expect(filesErrors[path], isEmpty, reason: path);
}
void assertNoErrorsNotification(String path) {
path = convertPath(path);
expect(filesErrors[path], isNull, reason: path);
}
void forgetReceivedErrors() {
filesErrors.clear();
}
@override
void processNotification(Notification notification) {
if (notification.event == ANALYSIS_NOTIFICATION_FLUSH_RESULTS) {
var decoded = AnalysisFlushResultsParams.fromNotification(notification);
flushResults.addAll(decoded.files);
decoded.files.forEach(filesErrors.remove);
}
if (notification.event == ANALYSIS_NOTIFICATION_ERRORS) {
var decoded = AnalysisErrorsParams.fromNotification(notification);
filesErrors[decoded.file] = decoded.errors;
}
}
void writePackageConfig(String path, PackageConfigFileBuilder config) {
newFile(path, content: config.toContent(toUriStr: toUriStr));
}
void _assertAnalyzedFiles({
required List<String> hasErrors,
List<String> noErrors = const [],
required List<String> notAnalyzed,
}) {
for (var path in hasErrors) {
assertHasErrors(path);
}
for (var path in noErrors) {
assertNoErrors(path);
}
for (var path in notAnalyzed) {
assertNoErrorsNotification(path);
}
filesErrors.clear();
}
void _assertFlushedResults(List<String> paths) {
var convertedPaths = paths.map(convertPath).toList();
expect(flushResults, unorderedEquals(convertedPaths));
flushResults.clear();
}
/// Create files with a content that has a compile time error.
/// So, when analyzed, these files will satisfy [assertHasErrors].
void _createFilesWithErrors(List<String> paths) {
for (var path in paths) {
newFile(path, content: 'error');
}
}
}