blob: c7ee08956530b8026159edb1a9efddd98d6da9b3 [file] [log] [blame]
// Copyright (c) 2023, 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.
import 'package:analysis_server/src/lsp/temporary_overlay_operation.dart';
import 'package:analysis_server/src/protocol_server.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'server_abstract.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(TemporaryOverlayOperationTest);
});
}
@reflectiveTest
class TemporaryOverlayOperationTest extends AbstractLspAnalysisServerTest {
void expectFsStateContent(String path, String expected) {
var driver = server.getAnalysisDriver(path)!;
var actual = driver.fsState.getFileForPath(path).content;
expect(actual, expected);
}
void expectOverlayContent(String path, String expected) {
expect(server.resourceProvider.hasOverlay(path), isTrue);
var actual = server.resourceProvider.getFile(path).readAsStringSync();
expect(actual, expected);
}
Future<void> test_noIntermediateAnalysisResults() async {
newFile(mainFilePath, '');
await initialize();
await initialAnalysis;
// Modify the overlays to have invalid code, which will then be reverted.
// At no point should diagnostics or closing labels be transmitted for the
// intermediate invalid code.
await _TestTemporaryOverlayOperation(server, () async {
server.onOverlayCreated(mainFilePath, 'INVALID1');
server.onOverlayUpdated(mainFilePath, [], newContent: 'INVALID2');
}).doWork();
await pumpEventQueue(times: 5000);
expect(diagnostics[mainFileUri], isNull);
}
Future<void> test_pausesRequestQueue() async {
await initialize();
await initialAnalysis;
await openFile(mainFileUri, '// ORIGINAL');
await _TestTemporaryOverlayOperation(server, () async {
// Simulate changes from the client.
await replaceFile(2, mainFileUri, '// CHANGED');
// Ensure we still have the original content.
await pumpEventQueue(times: 5000);
expectFsStateContent(mainFilePath, '// ORIGINAL');
expectOverlayContent(mainFilePath, '// ORIGINAL');
}).doWork();
// Ensure we processed the update afterwards.
await pumpEventQueue(times: 5000);
expectFsStateContent(mainFilePath, '// CHANGED');
expectOverlayContent(mainFilePath, '// CHANGED');
}
Future<void> test_pausesWatcherEvents() async {
newFile(mainFilePath, '// ORIGINAL');
await initialize();
await initialAnalysis;
await _TestTemporaryOverlayOperation(server, () async {
// Modify the file to trigger watcher events
modifyFile(mainFilePath, '// CHANGED');
// Ensure we still have the original content.
await pumpEventQueue(times: 5000);
expectFsStateContent(mainFilePath, '// ORIGINAL');
}).doWork();
// Ensure we processed the update afterwards.
await pumpEventQueue(times: 5000);
expectFsStateContent(mainFilePath, '// CHANGED');
}
Future<void> test_restoresOverlays() async {
newFile(mainFilePath, '// DISK');
await initialize();
await initialAnalysis;
await openFile(mainFileUri, '// ORIGINAL OVERLAY');
late _TestTemporaryOverlayOperation operation;
operation = _TestTemporaryOverlayOperation(server, () async {
operation.applyTemporaryOverlayEdits(SourceFileEdit(mainFilePath, -1,
edits: [SourceEdit(3, 8, 'CHANGED')]));
expectOverlayContent(mainFilePath, '// CHANGED OVERLAY');
});
await operation.doWork();
await pumpEventQueue(times: 5000);
expectOverlayContent(mainFilePath, '// ORIGINAL OVERLAY');
}
Future<void> test_temporarilyRemovesAddedFiles() async {
newFile(mainFilePath, '');
await initialize();
await initialAnalysis;
expect(server.driverMap.values.single.addedFiles, isNotEmpty);
await _TestTemporaryOverlayOperation(server, () async {
expect(server.driverMap.values.single.addedFiles, isEmpty);
}).doWork();
expect(server.driverMap.values.single.addedFiles, isNotEmpty);
}
}
/// A [TemporaryOverlayOperation] that accepts an implementation in its
/// constructor.
class _TestTemporaryOverlayOperation extends TemporaryOverlayOperation {
final Future<void> Function() operation;
_TestTemporaryOverlayOperation(super.server, this.operation);
Future<void> doWork() => lockRequestsWithTemporaryOverlays(operation);
}