afcfbbeba8
(Part of https://github.com/dart-lang/sdk/issues/63288) This change migrates the packages owned by the developer experience team to use the new constructor declaration syntax, described in https://github.com/dart-lang/language/blob/main/accepted/future-releases/primary-constructors/feature-specification.md#abbreviations-of-in-body-constructor-declarations. This change was performed in an automated fashion, by (a) bumping the packages' SDK constraints to `3.13.0-0`, (b) enabling the lints `unnecessary_type_name_in_constructor` and `unnecessary_const_in_enum_constructor`, (c) fixing the resulting lint failures using `dart fix`, and then (d) reformatting the affected files. To ease code review, I've reverted unrelated formatting changes. Since this change requires bumping SDK constaints to `3.13.0-0`, it was only performed on packages that are *not* published on pub. (Packages that *are* published on pub should remain on lower language versions until at least after the stable version of 3.13 is released, so that we don't block users on the stable channel from receiving updates to those packages.) Change-Id: Ibb4daebafd239da58251e838ea6a3f336a6a6964 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/505046 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
192 lines
6.2 KiB
Dart
192 lines
6.2 KiB
Dart
// Copyright (c) 2015, 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 'dart:async';
|
|
|
|
import 'package:analysis_server/protocol/protocol_generated.dart';
|
|
import 'package:logging/logging.dart';
|
|
|
|
import 'driver.dart';
|
|
import 'input_converter.dart';
|
|
|
|
/// An [Operation] represents an action such as sending a request to the server.
|
|
abstract class Operation {
|
|
Future<void>? perform(Driver driver);
|
|
}
|
|
|
|
/// A [RequestOperation] sends a JSON request to the server.
|
|
class RequestOperation extends Operation {
|
|
final CommonInputConverter converter;
|
|
final Map<String, dynamic> json;
|
|
|
|
new(this.converter, this.json);
|
|
|
|
@override
|
|
Future<void>? perform(Driver driver) {
|
|
var stopwatch = Stopwatch();
|
|
var originalId = json['id'] as String;
|
|
var method = json['method'] as String;
|
|
json['clientRequestTime'] = DateTime.now().millisecondsSinceEpoch;
|
|
driver.logger.log(Level.FINE, 'Sending request: $method\n $json');
|
|
stopwatch.start();
|
|
|
|
void recordResult(bool success, result) {
|
|
var elapsed = stopwatch.elapsed;
|
|
driver.results.record(method, elapsed, success: success);
|
|
driver.logger.log(
|
|
Level.FINE,
|
|
'Response received: $method : $elapsed\n $result',
|
|
);
|
|
}
|
|
|
|
driver
|
|
.send(method, converter.asMap(json['params']))
|
|
.then((result) {
|
|
recordResult(true, result);
|
|
processResult(originalId, result!, stopwatch);
|
|
})
|
|
.catchError((exception) {
|
|
recordResult(false, exception);
|
|
converter.processErrorResponse(originalId, exception);
|
|
});
|
|
return null;
|
|
}
|
|
|
|
void processResult(
|
|
String id,
|
|
Map<String, Object?> result,
|
|
Stopwatch stopwatch,
|
|
) {
|
|
converter.processResponseResult(id, result);
|
|
}
|
|
}
|
|
|
|
/// A [ResponseOperation] waits for a JSON response from the server.
|
|
class ResponseOperation extends Operation {
|
|
static final Duration responseTimeout = Duration(seconds: 60);
|
|
final CommonInputConverter converter;
|
|
final Map<String, Object?> requestJson;
|
|
final Map<String, Object?> responseJson;
|
|
final Completer<void> completer = Completer();
|
|
late Driver driver;
|
|
|
|
new(this.converter, this.requestJson, this.responseJson) {
|
|
completer.future.then(_processResult).timeout(responseTimeout);
|
|
}
|
|
|
|
@override
|
|
Future<void>? perform(Driver driver) {
|
|
this.driver = driver;
|
|
var id = responseJson['id'] as String;
|
|
return converter.processExpectedResponse(id, completer);
|
|
}
|
|
|
|
bool _equal(Object? expectedResult, Object? actualResult) {
|
|
if (expectedResult is Map && actualResult is Map) {
|
|
if (expectedResult.length == actualResult.length) {
|
|
return expectedResult.keys.every((key) {
|
|
return key ==
|
|
'fileStamp' || // fileStamp values will not be the same across runs
|
|
_equal(expectedResult[key], actualResult[key]);
|
|
});
|
|
}
|
|
} else if (expectedResult is List && actualResult is List) {
|
|
if (expectedResult.length == actualResult.length) {
|
|
for (var i = 0; i < expectedResult.length; ++i) {
|
|
if (!_equal(expectedResult[i], actualResult[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return expectedResult == actualResult;
|
|
}
|
|
|
|
/// Compare the expected and actual server response result.
|
|
void _processResult(Object? actualResult) {
|
|
var expectedResult = responseJson['result'];
|
|
if (!_equal(expectedResult, actualResult)) {
|
|
var expectedError = responseJson['error'];
|
|
String format(value) {
|
|
var text = '\n$value';
|
|
if (text.endsWith('\n')) {
|
|
text = text.substring(0, text.length - 1);
|
|
}
|
|
return text.replaceAll('\n', '\n ');
|
|
}
|
|
|
|
var message =
|
|
'Request:${format(requestJson)}\n'
|
|
'expected result:${format(expectedResult)}\n'
|
|
'expected error:${format(expectedError)}\n'
|
|
'but received:${format(actualResult)}';
|
|
var method = requestJson['method'] as String;
|
|
driver.results.recordUnexpectedResults(method);
|
|
converter.logOverlayContent();
|
|
if (expectedError == null) {
|
|
converter.logger.log(Level.SEVERE, message);
|
|
} else {
|
|
throw message;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class StartServerOperation extends Operation {
|
|
@override
|
|
Future<void> perform(Driver driver) {
|
|
return driver.startServer();
|
|
}
|
|
}
|
|
|
|
class WaitForAnalysisCompleteOperation extends Operation {
|
|
@override
|
|
Future<void> perform(Driver driver) {
|
|
var start = DateTime.now();
|
|
driver.logger.log(Level.FINE, 'waiting for analysis to complete');
|
|
late StreamSubscription<ServerStatusParams> subscription;
|
|
late Timer timer;
|
|
var completer = Completer();
|
|
var isAnalyzing = false;
|
|
subscription = driver.onServerStatus.listen((ServerStatusParams params) {
|
|
var analysisStatus = params.analysis;
|
|
if (analysisStatus != null) {
|
|
if (analysisStatus.isAnalyzing) {
|
|
isAnalyzing = true;
|
|
} else {
|
|
subscription.cancel();
|
|
timer.cancel();
|
|
var end = DateTime.now();
|
|
var delta = end.difference(start);
|
|
driver.logger.log(Level.FINE, 'analysis complete after $delta');
|
|
completer.complete();
|
|
driver.results.record('analysis complete', delta, notification: true);
|
|
}
|
|
}
|
|
});
|
|
timer = Timer.periodic(Duration(milliseconds: 20), (_) {
|
|
if (!isAnalyzing) {
|
|
// TODO(danrubel): revisit this once source change requests are implemented
|
|
subscription.cancel();
|
|
timer.cancel();
|
|
driver.logger.log(Level.INFO, 'analysis never started');
|
|
completer.complete();
|
|
return;
|
|
}
|
|
// Timeout if no communication received within the last 60 seconds.
|
|
var currentTime = driver.server.currentElapseTime;
|
|
var lastTime = driver.server.lastCommunicationTime!;
|
|
if (currentTime - lastTime > 60) {
|
|
subscription.cancel();
|
|
timer.cancel();
|
|
var message = 'gave up waiting for analysis to complete';
|
|
driver.logger.log(Level.WARNING, message);
|
|
completer.completeError(message);
|
|
}
|
|
});
|
|
return completer.future;
|
|
}
|
|
}
|