Construct PackageGraphs and run tools asynchronously (#1849)
* asynchronous PackageGraph construction and precaching
* Run tools more asynchronously
* Enhacements and dropping of Future.wait
* dartfmt
* Rate-limit the number of tools in flight
* dartfmt and review comments
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 055e5e7..e472853 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -17,4 +17,5 @@
- package_api_docs
- slash_for_doc_comments
- prefer_final_fields
+ - unawaited_futures
# - unnecessary_brace_in_string_interps
diff --git a/lib/src/dartdoc_options.dart b/lib/src/dartdoc_options.dart
index e523843..2d77708 100644
--- a/lib/src/dartdoc_options.dart
+++ b/lib/src/dartdoc_options.dart
@@ -20,6 +20,7 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:args/args.dart';
import 'package:dartdoc/dartdoc.dart';
+import 'package:dartdoc/src/tuple.dart';
import 'package:path/path.dart' as pathLib;
import 'package:yaml/yaml.dart';
@@ -226,22 +227,23 @@
/// to run. If no snapshot file existed, then create one and modify the args
/// so that if they are executed with dart, will result in the snapshot being
/// built.
- String createSnapshotIfNeeded(List<String> args) {
- assert(ToolDefinition.isDartExecutable(args[0]));
- // Generate a new snapshot, if needed, and use the first run as the training
+ Future<Tuple2<String, Function()>> modifyArgsToCreateSnapshotIfNeeded(
+ List<String> args) async {
+ assert(args[0] == command.first);
+ // Set up flags to create a new snapshot, if needed, and use the first run as the training
// run.
- File snapshotPath = _snapshotPath;
- snapshotPath ??= SnapshotCache.instance.getSnapshot(args[0]);
- if (snapshotPath.existsSync()) {
+ File snapshotFile = await getSnapshotFile();
+ if (snapshotFile.existsSync()) {
// replace the first argument with the path to the snapshot.
- args[0] = snapshotPath.absolute.path;
+ args[0] = snapshotFile.absolute.path;
} else {
args.insertAll(0, [
- '--snapshot=${snapshotPath.absolute.path}',
+ '--snapshot=${snapshotFile.absolute.path}',
'--snapshot_kind=app-jit'
]);
}
- return Platform.resolvedExecutable;
+ return new Tuple2(Platform.resolvedExecutable,
+ _snapshotCompleter.isCompleted ? null : _snapshotCompleter.complete);
}
DartToolDefinition(
@@ -250,11 +252,23 @@
// If the dart tool is already a snapshot, then we just use that.
if (command[0].endsWith('.snapshot')) {
_snapshotPath = File(command[0]);
+ _snapshotCompleter.complete();
}
}
+ final Completer _snapshotCompleter = new Completer();
+
/// If the tool has a pre-built snapshot, it will be stored here.
File _snapshotPath;
+
+ Future<File> getSnapshotFile() async {
+ if (_snapshotPath == null) {
+ _snapshotPath = SnapshotCache.instance.getSnapshot(command.first);
+ } else {
+ await _snapshotCompleter.future;
+ }
+ return _snapshotPath;
+ }
}
/// A configuration class that can interpret [ToolDefinition]s from a YAML map.
diff --git a/lib/src/io_utils.dart b/lib/src/io_utils.dart
index 3ff3a32..913a0cd 100644
--- a/lib/src/io_utils.dart
+++ b/lib/src/io_utils.dart
@@ -75,18 +75,6 @@
MultiFutureTracker(this.parallel);
- /// Adds a Future to the queue of outstanding Futures, and returns a Future
- /// that completes only when the number of Futures outstanding is < [parallel]
- /// (and so it is OK to start another).
- ///
- /// That can be extremely brief and there's no longer a guarantee after that
- /// point that another async task has not added a Future to the list.
- Future<void> addFuture(Future<T> future) async {
- _queue.add(future);
- future.then((f) => _queue.remove(future));
- await _waitUntil(parallel - 1);
- }
-
/// Wait until fewer or equal to this many Futures are outstanding.
Future<void> _waitUntil(int max) async {
while (_queue.length > max) {
@@ -94,6 +82,18 @@
}
}
+ /// Generates a [Future] from the given closure and adds it to the queue,
+ /// once the queue is sufficiently empty.
+ Future<void> addFutureFromClosure(Future<T> Function() closure) async {
+ while (_queue.length > parallel - 1) {
+ await Future.any(_queue);
+ }
+ Future future = closure();
+ _queue.add(future);
+ // ignore: unawaited_futures
+ future.then((f) => _queue.remove(future));
+ }
+
/// Wait until all futures added so far have completed.
Future<void> wait() async => await _waitUntil(0);
}
diff --git a/lib/src/model.dart b/lib/src/model.dart
index 7dec3eb..1c92fc1 100644
--- a/lib/src/model.dart
+++ b/lib/src/model.dart
@@ -627,7 +627,8 @@
.toList(growable: false);
}
- Iterable<Method> get allInstanceMethods => quiverIterables.concat([instanceMethods, inheritedMethods]);
+ Iterable<Method> get allInstanceMethods =>
+ quiverIterables.concat([instanceMethods, inheritedMethods]);
Iterable<Method> get allPublicInstanceMethods =>
filterNonPublic(allInstanceMethods);
@@ -635,9 +636,13 @@
bool get allPublicInstanceMethodsInherited =>
instanceMethods.every((f) => f.isInherited);
- Iterable<Field> get allInstanceFields => quiverIterables.concat([instanceProperties, inheritedProperties]);
+ Iterable<Field> get allInstanceFields =>
+ quiverIterables.concat([instanceProperties, inheritedProperties]);
- Iterable<Accessor> get allAccessors => quiverIterables.concat([allInstanceFields.expand((f) => f.allAccessors), constants.map((c) => c.getter)]);
+ Iterable<Accessor> get allAccessors => quiverIterables.concat([
+ allInstanceFields.expand((f) => f.allAccessors),
+ constants.map((c) => c.getter)
+ ]);
Iterable<Field> get allPublicInstanceProperties =>
filterNonPublic(allInstanceFields);
@@ -645,7 +650,8 @@
bool get allPublicInstancePropertiesInherited =>
allPublicInstanceProperties.every((f) => f.isInherited);
- Iterable<Operator> get allOperators => quiverIterables.concat([operators, inheritedOperators]);
+ Iterable<Operator> get allOperators =>
+ quiverIterables.concat([operators, inheritedOperators]);
Iterable<Operator> get allPublicOperators => filterNonPublic(allOperators);
@@ -1295,11 +1301,8 @@
/// Mixin implementing dartdoc categorization for ModelElements.
abstract class Categorization implements ModelElement {
@override
- String _buildDocumentationLocal() {
- _rawDocs = _buildDocumentationBase();
- _rawDocs = _stripAndSetDartdocCategories(_rawDocs);
- return _rawDocs;
- }
+ String _buildDocumentationAddition(String rawDocs) =>
+ _stripAndSetDartdocCategories(rawDocs);
/// Parse {@category ...} and related information in API comments, stripping
/// out that information from the given comments and returning the stripped
@@ -3227,23 +3230,48 @@
return docFrom;
}
- String _buildDocumentationLocal() => _buildDocumentationBase();
+ String _buildDocumentationLocal() => _buildDocumentationBaseSync();
+
+ /// Override this to add more features to the documentation builder in a
+ /// subclass.
+ String _buildDocumentationAddition(String docs) => docs;
/// Separate from _buildDocumentationLocal for overriding.
- String _buildDocumentationBase() {
+ String _buildDocumentationBaseSync() {
assert(_rawDocs == null);
+ // Do not use the sync method if we need to evaluate tools or templates.
+ assert(packageGraph._localDocumentationBuilt);
+ if (config.dropTextFrom.contains(element.library.name)) {
+ _rawDocs = '';
+ } else {
+ _rawDocs = documentationComment ?? '';
+ _rawDocs = stripComments(_rawDocs) ?? '';
+ _rawDocs = _injectExamples(_rawDocs);
+ _rawDocs = _injectAnimations(_rawDocs);
+ _rawDocs = _stripHtmlAndAddToIndex(_rawDocs);
+ }
+ _rawDocs = _buildDocumentationAddition(_rawDocs);
+ return _rawDocs;
+ }
+
+ /// Separate from _buildDocumentationLocal for overriding. Can only be
+ /// used as part of [PackageGraph.setUpPackageGraph].
+ Future<String> _buildDocumentationBase() async {
+ assert(_rawDocs == null);
+ // Do not use the sync method if we need to evaluate tools or templates.
if (config.dropTextFrom.contains(element.library.name)) {
_rawDocs = '';
} else {
_rawDocs = documentationComment ?? '';
_rawDocs = stripComments(_rawDocs) ?? '';
// Must evaluate tools first, in case they insert any other directives.
- _rawDocs = _evaluateTools(_rawDocs);
+ _rawDocs = await _evaluateTools(_rawDocs);
_rawDocs = _injectExamples(_rawDocs);
_rawDocs = _injectAnimations(_rawDocs);
_rawDocs = _stripMacroTemplatesAndAddToIndex(_rawDocs);
_rawDocs = _stripHtmlAndAddToIndex(_rawDocs);
}
+ _rawDocs = _buildDocumentationAddition(_rawDocs);
return _rawDocs;
}
@@ -3674,11 +3702,11 @@
return _documentationComment;
}
- /// Call this method to precache docs for this object if it might possibly
- /// have a macro template or a tool definition.
- void precacheLocalDocsIfNeeded() {
- if (documentationComment != null &&
- needsPrecacheRegExp.hasMatch(documentationComment)) documentationLocal;
+ /// Unconditionally precache local documentation.
+ ///
+ /// Use only in factory for [PackageGraph].
+ Future _precacheLocalDocs() async {
+ _documentationLocal = await _buildDocumentationBase();
}
Documentation get _documentation {
@@ -3895,6 +3923,19 @@
});
}
+ static Future<String> _replaceAllMappedAsync(
+ String string, Pattern exp, Future<String> replace(Match match)) async {
+ StringBuffer replaced = new StringBuffer();
+ int currentIndex = 0;
+ for (Match match in exp.allMatches(string)) {
+ String prefix = match.input.substring(currentIndex, match.start);
+ currentIndex = match.end;
+ replaced..write(prefix)..write(await replace(match));
+ }
+ replaced.write(string.substring(currentIndex));
+ return replaced.toString();
+ }
+
/// Replace {@tool ...}{@end-tool} in API comments with the
/// output of an external tool.
///
@@ -3946,43 +3987,39 @@
///
/// ## Content to send to tool.
/// 2018-09-18T21:15+00:00
- String _evaluateTools(String rawDocs) {
- var runner = new ToolRunner(config.tools, (String message) {
+ Future<String> _evaluateTools(String rawDocs) async {
+ var runner = new ToolRunner(config.tools, (String message) async {
warn(PackageWarning.toolError, message: message);
});
int invocationIndex = 0;
- try {
- return rawDocs.replaceAllMapped(basicToolRegExp, (basicMatch) {
- List<String> args = _splitUpQuotedArgs(basicMatch[1]).toList();
- // Tool name must come first.
- if (args.isEmpty) {
- warn(PackageWarning.toolError,
- message:
- 'Must specify a tool to execute for the @tool directive.');
- return '';
- }
- // Count the number of invocations of tools in this dartdoc block,
- // so that tools can differentiate different blocks from each other.
- invocationIndex++;
- return runner.run(args,
- content: basicMatch[2],
- environment: {
- 'SOURCE_LINE': lineAndColumn?.item1?.toString(),
- 'SOURCE_COLUMN': lineAndColumn?.item2?.toString(),
- 'SOURCE_PATH': (sourceFileName == null ||
- package?.packagePath == null)
- ? null
- : pathLib.relative(sourceFileName, from: package.packagePath),
- 'PACKAGE_PATH': package?.packagePath,
- 'PACKAGE_NAME': package?.name,
- 'LIBRARY_NAME': library?.fullyQualifiedName,
- 'ELEMENT_NAME': fullyQualifiedNameWithoutLibrary,
- 'INVOCATION_INDEX': invocationIndex.toString(),
- }..removeWhere((key, value) => value == null));
- });
- } finally {
- runner.dispose();
- }
+ return await _replaceAllMappedAsync(rawDocs, basicToolRegExp,
+ (basicMatch) async {
+ List<String> args = _splitUpQuotedArgs(basicMatch[1]).toList();
+ // Tool name must come first.
+ if (args.isEmpty) {
+ warn(PackageWarning.toolError,
+ message: 'Must specify a tool to execute for the @tool directive.');
+ return Future.value('');
+ }
+ // Count the number of invocations of tools in this dartdoc block,
+ // so that tools can differentiate different blocks from each other.
+ invocationIndex++;
+ return await runner.run(args,
+ content: basicMatch[2],
+ environment: {
+ 'SOURCE_LINE': lineAndColumn?.item1?.toString(),
+ 'SOURCE_COLUMN': lineAndColumn?.item2?.toString(),
+ 'SOURCE_PATH': (sourceFileName == null ||
+ package?.packagePath == null)
+ ? null
+ : pathLib.relative(sourceFileName, from: package.packagePath),
+ 'PACKAGE_PATH': package?.packagePath,
+ 'PACKAGE_NAME': package?.name,
+ 'LIBRARY_NAME': library?.fullyQualifiedName,
+ 'ELEMENT_NAME': fullyQualifiedNameWithoutLibrary,
+ 'INVOCATION_INDEX': invocationIndex.toString(),
+ }..removeWhere((key, value) => value == null));
+ }).whenComplete(runner.dispose);
}
/// Replace {@animation ...} in API comments with some HTML to manage an
@@ -4554,21 +4591,24 @@
// building Libraries and adding them to Packages, then adding Packages
// to this graph.
- /// Construct a package graph.
- /// [libraryElements] - Libraries to be documented.
- /// [specialLibraryElements] - Any libraries that may not be documented, but
- /// contain required [SpecialClass]es.
- PackageGraph(
- Iterable<LibraryElement> libraryElements,
- Iterable<LibraryElement> specialLibraryElements,
- this.config,
- this.packageMeta,
- this._packageWarningOptions,
- this.driver,
- this.sdk) {
- assert(_allConstructedModelElements.isEmpty);
- assert(allLibraries.isEmpty);
- _packageWarningCounter = new PackageWarningCounter(_packageWarningOptions);
+ PackageGraph._(this.config, this.packageMeta, this._packageWarningOptions,
+ this.driver, this.sdk) {}
+
+ static Future<PackageGraph> setUpPackageGraph(
+ Iterable<LibraryElement> libraryElements,
+ Iterable<LibraryElement> specialLibraryElements,
+ DartdocOptionContext config,
+ PackageMeta packageMeta,
+ packageWarningOptions,
+ driver,
+ sdk,
+ ) async {
+ PackageGraph newGraph =
+ PackageGraph._(config, packageMeta, packageWarningOptions, driver, sdk);
+ assert(newGraph._allConstructedModelElements.isEmpty);
+ assert(newGraph.allLibraries.isEmpty);
+ newGraph._packageWarningCounter =
+ new PackageWarningCounter(newGraph._packageWarningOptions);
// Build [Package] objects.
libraryElements.forEach((element) {});
@@ -4576,45 +4616,57 @@
// Build [Library] objects, and link them to [Package]s.
libraryElements.forEach((element) {
var packageMeta = new PackageMeta.fromElement(element, config);
- var lib = new Library._(
- element, this, new Package.fromPackageMeta(packageMeta, this));
- packageMap[packageMeta.name]._libraries.add(lib);
- allLibraries[element] = lib;
+ var lib = new Library._(element, newGraph,
+ new Package.fromPackageMeta(packageMeta, newGraph));
+ newGraph.packageMap[packageMeta.name]._libraries.add(lib);
+ newGraph.allLibraries[element] = lib;
});
// Make sure the default package exists, even if it has no libraries.
// This can happen for packages that only contain embedder SDKs.
- new Package.fromPackageMeta(packageMeta, this);
- allLibrariesAdded = true;
+ new Package.fromPackageMeta(packageMeta, newGraph);
+ newGraph.allLibrariesAdded = true;
// [findOrCreateLibraryFor] already adds to the proper structures.
specialLibraryElements.forEach((element) {
- findOrCreateLibraryFor(element);
+ newGraph.findOrCreateLibraryFor(element);
});
// From here on in, we might find special objects. Initialize the
// specialClasses handler so when we find them, they get added.
- specialClasses = new SpecialClasses();
+ newGraph.specialClasses = new SpecialClasses();
// Go through docs of every ModelElement in package to pre-build the macros
// index.
- allModelElements.forEach((m) => m.precacheLocalDocsIfNeeded());
- _localDocumentationBuilt = true;
+ List<Future> precacheFutures = newGraph.precacheLocalDocs().toList();
+ for (Future f in precacheFutures) await f;
+ newGraph._localDocumentationBuilt = true;
// Scan all model elements to insure that interceptor and other special
// objects are found.
// After the allModelElements traversal to be sure that all packages
// are picked up.
- documentedPackages.toList().forEach((package) {
+ newGraph.documentedPackages.toList().forEach((package) {
package._libraries.sort((a, b) => compareNatural(a.name, b.name));
package._libraries.forEach((library) {
- library._allClasses.forEach(_addToImplementors);
+ library._allClasses.forEach(newGraph._addToImplementors);
});
});
- _implementors.values.forEach((l) => l.sort());
- allImplementorsAdded = true;
+ newGraph._implementors.values.forEach((l) => l.sort());
+ newGraph.allImplementorsAdded = true;
// We should have found all special classes by now.
- specialClasses.assertSpecials();
+ newGraph.specialClasses.assertSpecials();
+ return newGraph;
+ }
+
+ /// Generate a list of futures for any docs that actually require precaching.
+ Iterable<Future> precacheLocalDocs() sync* {
+ for (ModelElement m in allModelElements) {
+ if (m.documentationComment != null &&
+ needsPrecacheRegExp.hasMatch(m.documentationComment)) {
+ yield m._precacheLocalDocs();
+ }
+ }
}
SpecialClasses specialClasses;
@@ -6396,8 +6448,8 @@
}
await getLibraries(libraries, specialLibraries, getFiles,
specialLibraryFiles(findSpecialsSdk).toSet());
- return new PackageGraph(libraries, specialLibraries, config,
- config.topLevelPackageMeta, getWarningOptions(), driver, sdk);
+ return await PackageGraph.setUpPackageGraph(libraries, specialLibraries,
+ config, config.topLevelPackageMeta, getWarningOptions(), driver, sdk);
}
DartSdk _sdk;
diff --git a/lib/src/tool_runner.dart b/lib/src/tool_runner.dart
index aa79d4f..c31a7ae 100644
--- a/lib/src/tool_runner.dart
+++ b/lib/src/tool_runner.dart
@@ -7,6 +7,7 @@
import 'dart:async';
import 'dart:io';
+import 'package:dartdoc/src/io_utils.dart';
import 'package:path/path.dart' as pathLib;
import 'dartdoc_options.dart';
@@ -14,6 +15,10 @@
typedef FakeResultCallback = String Function(String tool,
{List<String> args, String content});
+/// Set a ceiling on how many tool instances can be in progress at once,
+/// limiting both parallelization and the number of open temporary files.
+final MultiFutureTracker _toolTracker = new MultiFutureTracker(4);
+
/// A helper class for running external tools.
class ToolRunner {
/// Creates a new ToolRunner.
@@ -27,11 +32,10 @@
final ToolErrorCallback _errorCallback;
int _temporaryFileCount = 0;
- Directory _temporaryDirectory;
- Directory get temporaryDirectory {
+ Future<Directory> _temporaryDirectory;
+ Future<Directory> get temporaryDirectory {
if (_temporaryDirectory == null) {
- _temporaryDirectory =
- Directory.systemTemp.createTempSync('dartdoc_tools_');
+ _temporaryDirectory = Directory.systemTemp.createTemp('dartdoc_tools_');
}
return _temporaryDirectory;
}
@@ -42,11 +46,13 @@
}
}
- File _createTemporaryFile() {
+ Future<File> _createTemporaryFile() async {
_temporaryFileCount++;
- return new File(pathLib.join(
- temporaryDirectory.absolute.path, 'input_$_temporaryFileCount'))
- ..createSync(recursive: true);
+ File tempFile = new File(pathLib.join(
+ (await temporaryDirectory).absolute.path,
+ 'input_$_temporaryFileCount'));
+ await tempFile.create(recursive: true);
+ return tempFile;
}
/// Must be called when the ToolRunner is no longer needed. Ideally, this is
@@ -58,14 +64,15 @@
}
/// Avoid blocking on I/O for cleanups.
- static Future<void> disposeAsync(Directory temporaryDirectory) async {
- temporaryDirectory.exists().then((bool exists) {
- if (exists) return temporaryDirectory.delete(recursive: true);
- });
+ static Future<void> disposeAsync(Future<Directory> temporaryDirectory) async {
+ Directory tempDir = await temporaryDirectory;
+ if (await tempDir.exists()) {
+ return tempDir.delete(recursive: true);
+ }
}
void _runSetup(
- String name, ToolDefinition tool, Map<String, String> environment) {
+ String name, ToolDefinition tool, Map<String, String> environment) async {
bool isDartSetup = ToolDefinition.isDartExecutable(tool.setupCommand[0]);
var args = tool.setupCommand.toList();
String commandPath;
@@ -75,15 +82,16 @@
} else {
commandPath = args.removeAt(0);
}
- _runProcess(name, '', commandPath, args, environment);
+ await _runProcess(name, '', commandPath, args, environment);
tool.setupComplete = true;
}
- String _runProcess(String name, String content, String commandPath,
- List<String> args, Map<String, String> environment) {
+ Future<String> _runProcess(String name, String content, String commandPath,
+ List<String> args, Map<String, String> environment) async {
String commandString() => ([commandPath] + args).join(' ');
try {
- var result = Process.runSync(commandPath, args, environment: environment);
+ ProcessResult result =
+ await Process.run(commandPath, args, environment: environment);
if (result.exitCode != 0) {
_error('Tool "$name" returned non-zero exit code '
'(${result.exitCode}) when run as '
@@ -110,8 +118,19 @@
///
/// The [args] must not be null, and it must have at least one member (the name
/// of the tool).
- String run(List<String> args,
- {String content, Map<String, String> environment}) {
+ Future<String> run(List<String> args,
+ {String content, Map<String, String> environment}) async {
+ Future runner;
+ // Prevent too many tools from running simultaneously.
+ await _toolTracker.addFutureFromClosure(() {
+ runner = _run(args, content: content, environment: environment);
+ return runner;
+ });
+ return runner;
+ }
+
+ Future<String> _run(List<String> args,
+ {String content, Map<String, String> environment}) async {
assert(args != null);
assert(args.isNotEmpty);
content ??= '';
@@ -133,8 +152,8 @@
// file before running the tool synchronously.
// Write the content to a temp file.
- var tmpFile = _createTemporaryFile();
- tmpFile.writeAsStringSync(content);
+ var tmpFile = await _createTemporaryFile();
+ await tmpFile.writeAsString(content);
// Substitute the temp filename for the "$INPUT" token, and all of the other
// environment variables. Variables are allowed to either be in $(VAR) form,
@@ -169,15 +188,26 @@
}
if (toolDefinition.setupCommand != null && !toolDefinition.setupComplete)
- _runSetup(tool, toolDefinition, envWithInput);
+ await _runSetup(tool, toolDefinition, envWithInput);
argsWithInput = toolArgs + argsWithInput;
var commandPath;
+ void Function() callCompleter;
if (toolDefinition is DartToolDefinition) {
- commandPath = toolDefinition.createSnapshotIfNeeded(argsWithInput);
+ var modified = await toolDefinition
+ .modifyArgsToCreateSnapshotIfNeeded(argsWithInput);
+ commandPath = modified.item1;
+ callCompleter = modified.item2;
} else {
commandPath = argsWithInput.removeAt(0);
}
- return _runProcess(tool, content, commandPath, argsWithInput, envWithInput);
+ if (callCompleter != null) {
+ return _runProcess(
+ tool, content, commandPath, argsWithInput, envWithInput)
+ .whenComplete(callCompleter);
+ } else {
+ return _runProcess(
+ tool, content, commandPath, argsWithInput, envWithInput);
+ }
}
}
diff --git a/test/model_test.dart b/test/model_test.dart
index 5b6004f..3dfe7db 100644
--- a/test/model_test.dart
+++ b/test/model_test.dart
@@ -1459,8 +1459,8 @@
});
test(('Verify non-overridden members have right canonical classes'), () {
- final Field member =
- TypeInferenceMixedIn.allInstanceFields.firstWhere((f) => f.name == 'member');
+ final Field member = TypeInferenceMixedIn.allInstanceFields
+ .firstWhere((f) => f.name == 'member');
final Field modifierMember = TypeInferenceMixedIn.allInstanceFields
.firstWhere((f) => f.name == 'modifierMember');
final Field mixinMember = TypeInferenceMixedIn.allInstanceFields
@@ -2420,8 +2420,8 @@
documentedPartialFieldInSubclassOnly = UnusualProperties.allModelElements
.firstWhere((e) => e.name == 'documentedPartialFieldInSubclassOnly');
- isEmpty = CatString.allInstanceFields
- .firstWhere((p) => p.name == 'isEmpty');
+ isEmpty =
+ CatString.allInstanceFields.firstWhere((p) => p.name == 'isEmpty');
dynamicGetter = LongFirstLine.instanceProperties
.firstWhere((p) => p.name == 'dynamicGetter');
onlySetter = LongFirstLine.instanceProperties
diff --git a/test/tool_runner_test.dart b/test/tool_runner_test.dart
index 8044187..361d3e2 100644
--- a/test/tool_runner_test.dart
+++ b/test/tool_runner_test.dart
@@ -97,8 +97,8 @@
});
// This test must come first, to verify that the first run creates
// a snapshot.
- test('can invoke a Dart tool, and second run is a snapshot.', () {
- var result = runner.run(
+ test('can invoke a Dart tool, and second run is a snapshot.', () async {
+ var result = await runner.run(
['drill', r'--file=$INPUT'],
content: 'TEST INPUT',
);
@@ -107,7 +107,7 @@
expect(result, contains('## `TEST INPUT`'));
expect(result, contains('Script location is in dartdoc tree.'));
expect(setupFile.existsSync(), isFalse);
- result = runner.run(
+ result = await runner.run(
['drill', r'--file=$INPUT'],
content: 'TEST INPUT 2',
);
@@ -117,8 +117,8 @@
expect(result, contains('Script location is in snapshot cache.'));
expect(setupFile.existsSync(), isFalse);
});
- test('can invoke a Dart tool', () {
- var result = runner.run(
+ test('can invoke a Dart tool', () async {
+ var result = await runner.run(
['drill', r'--file=$INPUT'],
content: 'TEST INPUT',
);
@@ -128,16 +128,16 @@
expect(result, contains('## `TEST INPUT`'));
expect(setupFile.existsSync(), isFalse);
});
- test('can invoke a non-Dart tool', () {
- String result = runner.run(
+ test('can invoke a non-Dart tool', () async {
+ String result = await runner.run(
['non_dart', '--version'],
content: 'TEST INPUT',
);
expect(errors, isEmpty);
expect(result, isEmpty); // Output is on stderr.
});
- test('can invoke a pre-snapshotted tool', () {
- var result = runner.run(
+ test('can invoke a pre-snapshotted tool', () async {
+ var result = await runner.run(
['snapshot_drill', r'--file=$INPUT'],
content: 'TEST INPUT',
);
@@ -145,8 +145,8 @@
expect(result, contains('--file=<INPUT_FILE>'));
expect(result, contains('## `TEST INPUT`'));
});
- test('can invoke a tool with a setup action', () {
- var result = runner.run(
+ test('can invoke a tool with a setup action', () async {
+ var result = await runner.run(
['setup_drill', r'--file=$INPUT'],
content: 'TEST INPUT',
);
@@ -155,8 +155,8 @@
expect(result, contains('## `TEST INPUT`'));
expect(setupFile.existsSync(), isTrue);
});
- test('fails if tool not in tool map', () {
- String result = runner.run(
+ test('fails if tool not in tool map', () async {
+ String result = await runner.run(
['hammer', r'--file=$INPUT'],
content: 'TEST INPUT',
);
@@ -165,8 +165,8 @@
errors[0], contains('Unable to find definition for tool "hammer"'));
expect(result, isEmpty);
});
- test('fails if tool returns non-zero status', () {
- String result = runner.run(
+ test('fails if tool returns non-zero status', () async {
+ String result = await runner.run(
['drill', r'--file=/a/missing/file'],
content: 'TEST INPUT',
);
@@ -174,8 +174,8 @@
expect(errors[0], contains('Tool "drill" returned non-zero exit code'));
expect(result, isEmpty);
});
- test("fails if tool in tool map doesn't exist", () {
- String result = runner.run(
+ test("fails if tool in tool map doesn't exist", () async {
+ String result = await runner.run(
['missing'],
content: 'TEST INPUT',
);
diff --git a/tool/grind.dart b/tool/grind.dart
index 895119d..e33c0f5 100644
--- a/tool/grind.dart
+++ b/tool/grind.dart
@@ -731,7 +731,7 @@
@Task(
'Serve an arbitrary pub package based on PACKAGE_NAME and PACKAGE_VERSION environment variables')
Future<void> servePubPackage() async {
- _serveDocsFrom(await buildPubPackage(), 9000, 'serve-pub-package');
+ await _serveDocsFrom(await buildPubPackage(), 9000, 'serve-pub-package');
}
@Task('Checks that CHANGELOG mentions current version')
@@ -842,7 +842,7 @@
List<String> parameters = ['--enable-asserts'];
for (File dartFile in testFiles) {
- await testFutures.addFuture(
+ await testFutures.addFutureFromClosure(() =>
new SubprocessLauncher('dart2-${pathLib.basename(dartFile.path)}')
.runStreamed(
Platform.resolvedExecutable,
@@ -852,7 +852,7 @@
}
for (File dartFile in binFiles) {
- await testFutures.addFuture(new SubprocessLauncher(
+ await testFutures.addFutureFromClosure(() => new SubprocessLauncher(
'dart2-bin-${pathLib.basename(dartFile.path)}-help')
.runStreamed(
Platform.resolvedExecutable,