Sort files in analysis_server_client and analyzer_plugin and add tests to keep them that way

I didn't update the generators to generate sorted output, I just
whitelisted those files for now.

Change-Id: Ia1d233ee978691f15bce06b2556aa3371c54183f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/141209
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Brian Wilkerson 2020-03-27 13:27:17 +00:00 committed by commit-bot@chromium.org
parent b59217721b
commit 050c7e1ac7
9 changed files with 258 additions and 64 deletions

View file

@ -5,6 +5,6 @@
export 'package:analysis_server_client/src/protocol/protocol_base.dart';
export 'package:analysis_server_client/src/protocol/protocol_common.dart';
export 'package:analysis_server_client/src/protocol/protocol_constants.dart';
export 'package:analysis_server_client/src/protocol/protocol_generated.dart';
export 'package:analysis_server_client/src/protocol/protocol_internal.dart'
show ResponseDecoder;
export 'package:analysis_server_client/src/protocol/protocol_generated.dart';

View file

@ -6,10 +6,11 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:analysis_server_client/src/server_base.dart';
import 'package:analysis_server_client/listener/server_listener.dart';
import 'package:analysis_server_client/protocol.dart';
import 'package:analysis_server_client/src/server_base.dart';
import 'package:path/path.dart';
export 'package:analysis_server_client/src/server_base.dart'
show NotificationProcessor;

View file

@ -17,6 +17,17 @@
/// same operations as the performance critical variant, but allocates an extra
/// object.
class JenkinsSmiHash {
int _hash = 0;
/// Finalizes the hash and return the resulting hashcode.
@override
int get hashCode => finish(_hash);
/// Accumulates the object [o] into the hash.
void add(Object o) {
_hash = combine(_hash, o.hashCode);
}
/// Accumulates the hash code [value] into the running hash [hash].
static int combine(int hash, int value) {
hash = 0x1fffffff & (hash + value);
@ -41,15 +52,4 @@ class JenkinsSmiHash {
/// Combines together four hash codes.
static int hash4(int a, int b, int c, int d) =>
finish(combine(combine(combine(combine(0, a), b), c), d));
int _hash = 0;
/// Accumulates the object [o] into the hash.
void add(Object o) {
_hash = combine(_hash, o.hashCode);
}
/// Finalizes the hash and return the resulting hashcode.
@override
int get hashCode => finish(_hash);
}

View file

@ -9,12 +9,6 @@ import 'dart:io';
import 'package:analysis_server_client/listener/server_listener.dart';
import 'package:analysis_server_client/protocol.dart';
/// Type of callbacks used to process notifications.
typedef NotificationProcessor = void Function(Notification notification);
/// A function via which data can be sent to a started server.
typedef CommandSender = void Function(List<int> utf8bytes);
///
/// Add server arguments.
///
@ -58,6 +52,12 @@ List<String> getServerArguments({
return arguments;
}
/// A function via which data can be sent to a started server.
typedef CommandSender = void Function(List<int> utf8bytes);
/// Type of callbacks used to process notifications.
typedef NotificationProcessor = void Function(Notification notification);
/// Implementations of the class [ServerBase] manage an analysis server,
/// and facilitate communication to and from the server.
///
@ -74,53 +74,16 @@ abstract class ServerBase {
/// about interactions with the server.
final ServerListener _listener;
ServerListener get listener => _listener;
ServerBase({ServerListener listener, bool stdioPassthrough = false})
: _listener = listener,
_stdioPassthrough = stdioPassthrough;
/// Commands that have been sent to the server but not yet acknowledged,
/// and the [Completer] objects which should be completed
/// when acknowledgement is received.
final _pendingCommands = <String, Completer<Map<String, dynamic>>>{};
/// Force kill the server. Returns a future that completes when the server
/// stops.
Future kill({String reason = 'none'});
ServerBase({ServerListener listener, bool stdioPassthrough = false})
: _listener = listener,
_stdioPassthrough = stdioPassthrough;
/// Start listening to output from the server,
/// and deliver notifications to [notificationProcessor].
void listenToOutput({NotificationProcessor notificationProcessor});
/// Send a command to the server. An 'id' will be automatically assigned.
/// The returned [Future] will be completed when the server acknowledges
/// the command with a response.
/// If the server acknowledges the command with a normal (non-error) response,
/// the future will be completed with the 'result' field from the response.
/// If the server acknowledges the command with an error response,
/// the future will be completed with an error.
Future<Map<String, dynamic>> send(String method, Map<String, dynamic> params);
/// Encodes a request for transmission and sends it as a utf8 encoded byte
/// string with [sendWith].
Future<Map<String, dynamic>> sendCommandWith(
String method, Map<String, dynamic> params, CommandSender sendWith) {
String id = '${_nextId++}';
Map<String, dynamic> command = <String, dynamic>{
Request.ID: id,
Request.METHOD: method
};
if (params != null) {
command[Request.PARAMS] = params;
}
final completer = Completer<Map<String, dynamic>>();
_pendingCommands[id] = completer;
String line = json.encode(command);
listener?.requestSent(line);
sendWith(utf8.encoder.convert('$line\n'));
return completer.future;
}
ServerListener get listener => _listener;
/// If the implementation of [ServerBase] captures an error stream,
/// it can use this to forward the errors to [listener] and [stderr] if
@ -132,6 +95,14 @@ abstract class ServerBase {
listener?.errorMessage(trimmedLine);
}
/// Force kill the server. Returns a future that completes when the server
/// stops.
Future kill({String reason = 'none'});
/// Start listening to output from the server,
/// and deliver notifications to [notificationProcessor].
void listenToOutput({NotificationProcessor notificationProcessor});
/// Handle a (possibly) json encoded object, completing the [Completer] in
/// [_pendingCommands] corresponding to the response. Reports problems in
/// decoding or message synchronization using [listener], and replicates
@ -189,6 +160,35 @@ abstract class ServerBase {
}
}
/// Send a command to the server. An 'id' will be automatically assigned.
/// The returned [Future] will be completed when the server acknowledges
/// the command with a response.
/// If the server acknowledges the command with a normal (non-error) response,
/// the future will be completed with the 'result' field from the response.
/// If the server acknowledges the command with an error response,
/// the future will be completed with an error.
Future<Map<String, dynamic>> send(String method, Map<String, dynamic> params);
/// Encodes a request for transmission and sends it as a utf8 encoded byte
/// string with [sendWith].
Future<Map<String, dynamic>> sendCommandWith(
String method, Map<String, dynamic> params, CommandSender sendWith) {
String id = '${_nextId++}';
Map<String, dynamic> command = <String, dynamic>{
Request.ID: id,
Request.METHOD: method
};
if (params != null) {
command[Request.PARAMS] = params;
}
final completer = Completer<Map<String, dynamic>>();
_pendingCommands[id] = completer;
String line = json.encode(command);
listener?.requestSent(line);
sendWith(utf8.encoder.convert('$line\n'));
return completer.future;
}
/// Start the server. The returned future completes when the server
/// is started and it is valid to call [listenToOutput].
Future start({

View file

@ -2,10 +2,12 @@
// 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 'live_test.dart' as live_test;
import 'server_test.dart' as server_test;
import 'live_test.dart' as live;
import 'server_test.dart' as server;
import 'verify_sorted_test.dart' as verify_sorted;
void main() {
server_test.main();
live_test.main();
live.main();
server.main();
verify_sorted.main();
}

View file

@ -0,0 +1,29 @@
// Copyright (c) 2017, 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:io';
import 'package:path/path.dart' as pathos;
/// Returns a path to the directory containing source code for packages such as
/// kernel, front_end, and analyzer.
String get packageRoot {
// If the package root directory is specified on the command line using
// -DpkgRoot=..., use it.
const String pkgRootVar =
bool.hasEnvironment('pkgRoot') ? String.fromEnvironment('pkgRoot') : null;
if (pkgRootVar != null) {
String path = pathos.join(Directory.current.path, pkgRootVar);
if (!path.endsWith(pathos.separator)) path += pathos.separator;
return path;
}
// Otherwise try to guess based on the script path.
String scriptPath = pathos.fromUri(Platform.script);
List<String> parts = pathos.split(scriptPath);
int pkgIndex = parts.indexOf('pkg');
if (pkgIndex != -1) {
return pathos.joinAll(parts.sublist(0, pkgIndex + 1)) + pathos.separator;
}
throw StateError('Unable to find sdk/pkg/ in $scriptPath');
}

View file

@ -0,0 +1,79 @@
// Copyright (c) 2019, 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:io';
import 'package:analysis_server/src/services/correction/sort_members.dart';
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/results.dart';
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:test/test.dart';
import 'utils/package_root.dart';
void main() {
var provider = PhysicalResourceProvider.INSTANCE;
var normalizedRoot = provider.pathContext.normalize(packageRoot);
var packagePath =
provider.pathContext.join(normalizedRoot, 'analysis_server_client');
// TODO(brianwilkerson) Fix the generator to sort the generated files and
// remove these exclusions.
var generatedFilePaths = [
provider.pathContext
.join(packagePath, 'lib', 'src', 'protocol', 'protocol_common.dart'),
provider.pathContext
.join(packagePath, 'lib', 'src', 'protocol', 'protocol_constants.dart'),
provider.pathContext
.join(packagePath, 'lib', 'src', 'protocol', 'protocol_generated.dart'),
];
var collection = AnalysisContextCollection(
includedPaths: <String>[packagePath],
excludedPaths: generatedFilePaths,
resourceProvider: provider);
var contexts = collection.contexts;
if (contexts.length != 1) {
fail('The directory $packagePath contains multiple analysis contexts.');
}
buildTestsIn(contexts[0].currentSession, packagePath, generatedFilePaths,
provider.getFolder(packagePath));
}
void buildTestsIn(AnalysisSession session, String testDirPath,
List<String> generatedFilePaths, Folder directory) {
var pathContext = session.resourceProvider.pathContext;
var children = directory.getChildren();
children.sort((first, second) => first.shortName.compareTo(second.shortName));
for (var child in children) {
if (child is Folder) {
buildTestsIn(session, testDirPath, generatedFilePaths, child);
} else if (child is File && child.shortName.endsWith('.dart')) {
var path = child.path;
if (generatedFilePaths.contains(path)) {
continue;
}
var relativePath = pathContext.relative(path, from: testDirPath);
test(relativePath, () {
var result = session.getParsedUnit(path);
if (result.state != ResultState.VALID) {
fail('Could not parse $path');
}
var code = result.content;
var unit = result.unit;
var errors = result.errors;
if (errors.isNotEmpty) {
fail('Errors found when parsing $path');
}
var sorter = MemberSorter(code, unit);
var edits = sorter.sort();
if (edits.isNotEmpty) {
fail('Unsorted file $path');
}
});
}
}
}

View file

@ -8,6 +8,7 @@ import '../tool/spec/check_all_test.dart' as check_spec;
import 'plugin/test_all.dart' as plugin;
import 'src/test_all.dart' as src;
import 'utilities/test_all.dart' as utilities;
import 'verify_sorted_test.dart' as verify_sorted;
import 'verify_tests_test.dart' as verify_tests;
void main() {
@ -16,6 +17,7 @@ void main() {
src.main();
utilities.main();
verify_tests.main();
verify_sorted.main();
defineReflectiveSuite(() {
defineReflectiveTests(SpecTest);
}, name: 'spec');

View file

@ -0,0 +1,81 @@
// Copyright (c) 2019, 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:io';
import 'package:analysis_server/src/services/correction/sort_members.dart';
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/results.dart';
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:test/test.dart';
import 'utils/package_root.dart';
void main() {
var provider = PhysicalResourceProvider.INSTANCE;
var normalizedRoot = provider.pathContext.normalize(packageRoot);
var packagePath =
provider.pathContext.join(normalizedRoot, 'analyzer_plugin');
// TODO(brianwilkerson) Fix the generator to sort the generated files and
// remove these exclusions.
var generatedFilePaths = [
provider.pathContext
.join(packagePath, 'lib', 'protocol', 'protocol_common.dart'),
provider.pathContext
.join(packagePath, 'lib', 'protocol', 'protocol_generated.dart'),
provider.pathContext.join(packagePath, 'test', 'integration', 'support',
'integration_test_methods.dart'),
provider.pathContext.join(packagePath, 'test', 'integration', 'support',
'protocol_matchers.dart'),
];
var collection = AnalysisContextCollection(
includedPaths: <String>[packagePath],
excludedPaths: generatedFilePaths,
resourceProvider: provider);
var contexts = collection.contexts;
if (contexts.length != 1) {
fail('The directory $packagePath contains multiple analysis contexts.');
}
buildTestsIn(contexts[0].currentSession, packagePath, generatedFilePaths,
provider.getFolder(packagePath));
}
void buildTestsIn(AnalysisSession session, String testDirPath,
List<String> generatedFilePaths, Folder directory) {
var pathContext = session.resourceProvider.pathContext;
var children = directory.getChildren();
children.sort((first, second) => first.shortName.compareTo(second.shortName));
for (var child in children) {
if (child is Folder) {
buildTestsIn(session, testDirPath, generatedFilePaths, child);
} else if (child is File && child.shortName.endsWith('.dart')) {
var path = child.path;
if (generatedFilePaths.contains(path)) {
continue;
}
var relativePath = pathContext.relative(path, from: testDirPath);
test(relativePath, () {
var result = session.getParsedUnit(path);
if (result.state != ResultState.VALID) {
fail('Could not parse $path');
}
var code = result.content;
var unit = result.unit;
var errors = result.errors;
if (errors.isNotEmpty) {
fail('Errors found when parsing $path');
}
var sorter = MemberSorter(code, unit);
var edits = sorter.sort();
if (edits.isNotEmpty) {
fail('Unsorted file $path');
}
});
}
}
}