Version 2.10.0-96.0.dev
Merge commit '5b956bd8a0eb2012f55d3e98fc0d3d8cc08f4dca' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index 27e4114..09dd636 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -37,8 +37,10 @@
import 'package:analysis_server/src/services/correction/dart/remove_this_expression.dart';
import 'package:analysis_server/src/services/correction/dart/remove_type_annotation.dart';
import 'package:analysis_server/src/services/correction/dart/remove_unnecessary_new.dart';
+import 'package:analysis_server/src/services/correction/dart/rename_to_camel_case.dart';
import 'package:analysis_server/src/services/correction/dart/replace_cascade_with_dot.dart';
import 'package:analysis_server/src/services/correction/dart/replace_colon_with_equals.dart';
+import 'package:analysis_server/src/services/correction/dart/replace_final_with_const.dart';
import 'package:analysis_server/src/services/correction/dart/replace_null_with_closure.dart';
import 'package:analysis_server/src/services/correction/dart/replace_with_conditional_assignment.dart';
import 'package:analysis_server/src/services/correction/dart/replace_with_is_empty.dart';
@@ -80,11 +82,13 @@
LintNames.empty_statements: RemoveEmptyStatement.newInstance,
LintNames.hash_and_equals: CreateMethod.equalsOrHashCode,
LintNames.no_duplicate_case_values: RemoveDuplicateCase.newInstance,
+ LintNames.non_constant_identifier_names: RenameToCamelCase.newInstance,
LintNames.null_closures: ReplaceNullWithClosure.newInstance,
LintNames.omit_local_variable_types: ReplaceWithVar.newInstance,
LintNames.prefer_adjacent_string_concatenation: RemoveOperator.newInstance,
LintNames.prefer_conditional_assignment:
ReplaceWithConditionalAssignment.newInstance,
+ LintNames.prefer_const_declarations: ReplaceFinalWithConst.newInstance,
LintNames.prefer_contains: ConvertToContains.newInstance,
LintNames.prefer_equal_for_default_values:
ReplaceColonWithEquals.newInstance,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
index f5e89d2..19d4808 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
@@ -2,12 +2,15 @@
// 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/services/correction/fix/data_driven/add_type_parameter.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/change.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/parameter_reference.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/rename.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/value_extractor.dart';
import 'package:analysis_server/src/utilities/extensions/yaml.dart';
import 'package:analyzer/error/listener.dart';
import 'package:yaml/yaml.dart';
@@ -15,51 +18,31 @@
/// A parser used to read a transform set from a file.
class TransformSetParser {
static const String _changesKey = 'changes';
-
static const String _classKey = 'class';
-
static const String _constructorKey = 'constructor';
-
static const String _elementKey = 'element';
-
static const String _enumConstantKey = 'constant';
-
static const String _enumKey = 'enum';
-
static const String _extensionKey = 'extension';
-
static const String _fieldKey = 'field';
-
static const String _functionKey = 'function';
-
static const String _getterKey = 'getter';
-
static const String _inClassKey = 'inClass';
-
static const String _inEnumKey = 'inEnum';
-
static const String _inExtensionKey = 'inExtension';
-
+ static const String _indexKey = 'index';
static const String _inMixinKey = 'inMixin';
-
static const String _kindKey = 'kind';
-
static const String _methodKey = 'method';
-
static const String _mixinKey = 'mixin';
-
+ static const String _nameKey = 'name';
static const String _newNameKey = 'newName';
-
static const String _setterKey = 'setter';
-
static const String _titleKey = 'title';
-
static const String _transformsKey = 'transforms';
-
static const String _typedefKey = 'typedef';
-
static const String _urisKey = 'uris';
-
+ static const String _valueKey = 'value';
static const String _versionKey = 'version';
/// A table mapping top-level keys for member elements to the list of keys for
@@ -73,6 +56,8 @@
_setterKey: [_inClassKey, _inExtensionKey, _inMixinKey],
};
+ static const String _addTypeParameterKind = 'addTypeParameter';
+ static const String _argumentKind = 'argument';
static const String _renameKind = 'rename';
static const int currentVersion = 1;
@@ -160,13 +145,63 @@
return foundKeys[0];
}
+ /// Translate the [node] into an add-type-parameter change. Return the
+ /// resulting change, or `null` if the [node] does not represent a valid
+ /// add-type-parameter change.
+ Change _translateAddTypeParameterChange(YamlMap node) {
+ _reportUnsupportedKeys(
+ node, const {_indexKey, _kindKey, _nameKey, _valueKey});
+ var index = _translateInteger(node.valueAt(_indexKey));
+ if (index == null) {
+ return null;
+ }
+ var name = _translateString(node.valueAt(_nameKey));
+ if (name == null) {
+ return null;
+ }
+ var value = _translateValueExtractor(node.valueAt(_valueKey));
+ if (value == null) {
+ return null;
+ }
+ return AddTypeParameter(index: index, name: name, value: value);
+ }
+
+ /// Translate the [node] into a value extractor. Return the resulting
+ /// extractor, or `null` if the [node] does not represent a valid value
+ /// extractor.
+ ValueExtractor _translateArgumentExtractor(YamlMap node) {
+ var indexNode = node.valueAt(_indexKey);
+ if (indexNode != null) {
+ _reportUnsupportedKeys(node, const {_indexKey, _kindKey});
+ var index = _translateInteger(indexNode);
+ if (index == null) {
+ // The error has already been reported.
+ return null;
+ }
+ return ArgumentExtractor(PositionalParameterReference(index));
+ }
+ var nameNode = node.valueAt(_nameKey);
+ if (nameNode != null) {
+ _reportUnsupportedKeys(node, const {_nameKey, _kindKey});
+ var name = _translateString(nameNode);
+ if (name == null) {
+ // The error has already been reported.
+ return null;
+ }
+ return ArgumentExtractor(NamedParameterReference(name));
+ }
+ // TODO(brianwilkerson) Report the missing YAML.
+ return null;
+ }
+
/// Translate the [node] into a change. Return the resulting change, or `null`
/// if the [node] does not represent a valid change.
Change _translateChange(YamlNode node) {
if (node is YamlMap) {
var kind = _translateString(node.valueAt(_kindKey));
- // TODO(brianwilkerson) Implement additional change kinds.
- if (kind == _renameKind) {
+ if (kind == _addTypeParameterKind) {
+ return _translateAddTypeParameterChange(node);
+ } else if (kind == _renameKind) {
return _translateRenameChange(node);
}
// TODO(brianwilkerson) Report the invalid change kind.
@@ -365,4 +400,24 @@
return null;
}
}
+
+ /// Translate the [node] into a value extractor. Return the resulting
+ /// extractor, or `null` if the [node] does not represent a valid value
+ /// extractor.
+ ValueExtractor _translateValueExtractor(YamlNode node) {
+ if (node is YamlMap) {
+ var kind = _translateString(node.valueAt(_kindKey));
+ if (kind == _argumentKind) {
+ return _translateArgumentExtractor(node);
+ }
+ // TODO(brianwilkerson) Report the invalid extractor kind.
+ return null;
+ } else if (node == null) {
+ // TODO(brianwilkerson) Report the missing YAML.
+ return null;
+ } else {
+ // TODO(brianwilkerson) Report the invalid YAML.
+ return null;
+ }
+ }
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/rename_to_camel_case_test.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/rename_to_camel_case_test.dart
new file mode 100644
index 0000000..8e717a0
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/rename_to_camel_case_test.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2020, 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/services/linter/lint_names.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'bulk_fix_processor.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(RenameToCamelCaseTest);
+ });
+}
+
+@reflectiveTest
+class RenameToCamelCaseTest extends BulkFixProcessorTest {
+ @override
+ String get lintCode => LintNames.non_constant_identifier_names;
+
+ Future<void> test_singleFile() async {
+ await resolveTestUnit('''
+main() {
+ int my_integer_variable = 42;
+ int foo;
+ print(my_integer_variable);
+ print(foo);
+ [0, 1, 2].forEach((my_integer_variable) {
+ print(my_integer_variable);
+ });
+}
+''');
+ await assertHasFix('''
+main() {
+ int myIntegerVariable = 42;
+ int foo;
+ print(myIntegerVariable);
+ print(foo);
+ [0, 1, 2].forEach((myIntegerVariable) {
+ print(myIntegerVariable);
+ });
+}
+''');
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/replace_final_with_const_test.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/replace_final_with_const_test.dart
new file mode 100644
index 0000000..1eefe84
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/replace_final_with_const_test.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2020, 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/services/linter/lint_names.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'bulk_fix_processor.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ReplaceFinalWithConstTest);
+ });
+}
+
+@reflectiveTest
+class ReplaceFinalWithConstTest extends BulkFixProcessorTest {
+ @override
+ String get lintCode => LintNames.prefer_const_declarations;
+
+ Future<void> test_singleFile() async {
+ await resolveTestUnit('''
+final int a = 1;
+final b = 1;
+''');
+ await assertHasFix('''
+const int a = 1;
+const b = 1;
+''');
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart
index 36033cf..169e7b5 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart
@@ -39,7 +39,9 @@
import 'remove_type_annotation_test.dart' as remove_type_annotation;
import 'remove_unnecessary_const_test.dart' as remove_unnecessary_const;
import 'remove_unnecessary_new_test.dart' as remove_unnecessary_new;
+import 'rename_to_camel_case_test.dart' as rename_to_camel_case;
import 'replace_colon_with_equals_test.dart' as replace_colon_with_equals;
+import 'replace_final_with_const_test.dart' as replace_final_with_const;
import 'replace_null_with_closure_test.dart' as replace_null_with_closure;
import 'replace_with_conditional_assignment_test.dart'
as replace_with_conditional_assignment;
@@ -83,8 +85,10 @@
remove_type_annotation.main();
remove_unnecessary_const.main();
remove_unnecessary_new.main();
+ rename_to_camel_case.main();
replace_with_conditional_assignment.main();
replace_colon_with_equals.main();
+ replace_final_with_const.main();
replace_null_with_closure.main();
replace_with_is_empty.main();
replace_with_tear_off.main();
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart
index 58dd53a..1f0eb58 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart
@@ -14,6 +14,45 @@
@reflectiveTest
class EndToEndTest extends DataDrivenFixProcessorTest {
+ Future<void> test_addTypeParameter() async {
+ setPackageContent('''
+class C {
+ void m<S, T>(Type t) {}
+}
+''');
+ addPackageDataFile('''
+version: 1
+transforms:
+- title: 'Add type argument'
+ element:
+ uris:
+ - '$importUri'
+ method: 'm'
+ inClass: 'C'
+ changes:
+ - kind: 'addTypeParameter'
+ index: 1
+ name: 'T'
+ value:
+ kind: 'argument'
+ index: 0
+''');
+ await resolveTestUnit('''
+import '$importUri';
+
+void f(C c) {
+ c.m<int>(String);
+}
+''');
+ await assertHasFix('''
+import '$importUri';
+
+void f(C c) {
+ c.m<int, String>(String);
+}
+''');
+ }
+
Future<void> test_rename() async {
setPackageContent('''
class New {}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
index f36fef5..b60500a 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
@@ -2,8 +2,11 @@
// 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/services/correction/fix/data_driven/add_type_parameter.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/parameter_reference.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/rename.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/value_extractor.dart';
import 'package:matcher/matcher.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -18,6 +21,66 @@
@reflectiveTest
class TransformSetParserTest extends AbstractTransformSetParserTest {
+ void test_addTypeParameter_fromNamedArgument() {
+ parse('''
+version: 1
+transforms:
+- title: 'Add'
+ element:
+ uris:
+ - 'test.dart'
+ class: 'A'
+ changes:
+ - kind: 'addTypeParameter'
+ index: 0
+ name: 'T'
+ value:
+ kind: 'argument'
+ name: 'p'
+''');
+ var transforms = result.transformsFor('A', ['test.dart']);
+ expect(transforms, hasLength(1));
+ var transform = transforms[0];
+ expect(transform.title, 'Add');
+ expect(transform.changes, hasLength(1));
+ var change = transform.changes[0] as AddTypeParameter;
+ expect(change.index, 0);
+ expect(change.name, 'T');
+ var value = change.value as ArgumentExtractor;
+ var parameter = value.parameter as NamedParameterReference;
+ expect(parameter.name, 'p');
+ }
+
+ void test_addTypeParameter_fromPositionalArgument() {
+ parse('''
+version: 1
+transforms:
+- title: 'Add'
+ element:
+ uris:
+ - 'test.dart'
+ class: 'A'
+ changes:
+ - kind: 'addTypeParameter'
+ index: 0
+ name: 'T'
+ value:
+ kind: 'argument'
+ index: 2
+''');
+ var transforms = result.transformsFor('A', ['test.dart']);
+ expect(transforms, hasLength(1));
+ var transform = transforms[0];
+ expect(transform.title, 'Add');
+ expect(transform.changes, hasLength(1));
+ var change = transform.changes[0] as AddTypeParameter;
+ expect(change.index, 0);
+ expect(change.name, 'T');
+ var value = change.value as ArgumentExtractor;
+ var parameter = value.parameter as PositionalParameterReference;
+ expect(parameter.index, 2);
+ }
+
void test_element_getter_inMixin() {
parse('''
version: 1
diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart
index 4cc2964..a258612 100644
--- a/pkg/dartdev/lib/dartdev.dart
+++ b/pkg/dartdev/lib/dartdev.dart
@@ -20,7 +20,7 @@
import 'src/commands/fix.dart';
import 'src/commands/pub.dart';
import 'src/commands/run.dart';
-import 'src/commands/test.dart' hide Runtime;
+import 'src/commands/test.dart';
import 'src/core.dart';
import 'src/experiments.dart';
import 'src/sdk.dart';
diff --git a/pkg/dartdev/lib/src/commands/test.dart b/pkg/dartdev/lib/src/commands/test.dart
index f655ed4..dcb5a7f 100644
--- a/pkg/dartdev/lib/src/commands/test.dart
+++ b/pkg/dartdev/lib/src/commands/test.dart
@@ -2,13 +2,7 @@
// 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.
-// ignore_for_file: avoid_redundant_argument_values
-// ignore_for_file: lines_longer_than_80_chars
-// ignore_for_file: prefer_initializing_formals
-
import 'dart:async';
-import 'dart:io';
-import 'dart:math' as math;
import 'package:args/args.dart';
@@ -17,35 +11,51 @@
import '../sdk.dart';
import '../vm_interop_handler.dart';
+/// Implement `dart test`.
+///
+/// This command largely delegates to `pub run test`.
class TestCommand extends DartdevCommand<int> {
- TestCommand() : super('test', 'Run tests in this package.') {
- generateParser(argParser);
- }
+ TestCommand() : super('test', 'Run tests in this package.');
+
+ @override
+ final ArgParser argParser = ArgParser.allowAnything();
@override
void printUsage() {
- if (!Sdk.checkArtifactExists(sdk.pubSnapshot)) {
- return;
- }
- super.printUsage();
+ _runImpl(['-h']);
}
@override
FutureOr<int> run() async {
+ return _runImpl(argResults.arguments.toList());
+ }
+
+ int _runImpl(List<String> testArgs) {
if (!Sdk.checkArtifactExists(sdk.pubSnapshot)) {
return 255;
}
- // "Could not find package "test". Did you forget to add a dependency?"
- if (project.hasPackageConfigFile) {
- if ((project.packageConfig != null) &&
- !project.packageConfig.hasDependency('test')) {
- _printPackageTestInstructions();
- return 65;
- }
+
+ final pubSnapshot = sdk.pubSnapshot;
+
+ bool isHelpCommand = testArgs.contains('--help') || testArgs.contains('-h');
+
+ // Check for no pubspec.yaml file.
+ if (!project.hasPubspecFile) {
+ _printNoPubspecMessage(isHelpCommand);
+ return 65;
}
- final command = sdk.pubSnapshot;
- final testArgs = argResults.arguments.toList();
+ // Handle the case of no .dart_tool/package_config.json file.
+ if (!project.hasPackageConfigFile) {
+ _printRunPubGetInstructions(isHelpCommand);
+ return 65;
+ }
+
+ // "Could not find package "test". Did you forget to add a dependency?"
+ if (!project.packageConfig.hasDependency('test')) {
+ _printMissingDepInstructions(isHelpCommand);
+ return 65;
+ }
final args = [
'run',
@@ -55,327 +65,59 @@
...testArgs,
];
- log.trace('$command ${args.join(' ')}');
- VmInteropHandler.run(command, args);
+ log.trace('$pubSnapshot ${args.join(' ')}');
+ VmInteropHandler.run(pubSnapshot, args);
return 0;
}
- void _printPackageTestInstructions() {
- log.stdout('');
+ void _printNoPubspecMessage(bool wasHelpCommand) {
+ log.stdout('''
+No pubspec.yaml file found; please run this command from the root of your project.
+''');
+ if (wasHelpCommand) {
+ log.stdout(_terseHelp);
+ log.stdout('');
+ }
+
+ log.stdout(_usageHelp);
+ }
+
+ void _printRunPubGetInstructions(bool wasHelpCommand) {
+ log.stdout('''
+No .dart_tool/package_config.json file found, please run 'dart pub get'.
+''');
+
+ if (wasHelpCommand) {
+ log.stdout(_terseHelp);
+ log.stdout('');
+ }
+
+ log.stdout(_usageHelp);
+ }
+
+ void _printMissingDepInstructions(bool wasHelpCommand) {
final ansi = log.ansi;
log.stdout('''
-In order to run tests, you need to add a dependency on package:test in your
-pubspec.yaml file:
+No dependency on package:test found. In order to run tests, you need to add a dependency
+on package:test in your pubspec.yaml file:
${ansi.emphasized('dev_dependencies:\n test: ^1.0.0')}
-See https://pub.dev/packages/test#-installing-tab- for more information on
-adding package:test, and https://dart.dev/guides/testing for general
-information on testing.''');
- }
+See https://pub.dev/packages/test/install for more information on adding package:test,
+and https://dart.dev/guides/testing for general information on testing.
+''');
- /// This content has been copied from and kept in sync with
- /// https://github.com/dart-lang/test, by having a copy in dartdev itself,
- /// help is faster and more robust, see
- /// https://github.com/dart-lang/sdk/issues/42014.
- void generateParser(ArgParser parser) {
- // Set in test/pkgs/test_core/lib/src/runner/configuration/values.dart:
- final defaultConcurrency = math.max(1, Platform.numberOfProcessors ~/ 2);
+ if (wasHelpCommand) {
+ log.stdout(_terseHelp);
+ log.stdout('');
+ }
- /// The parser used to parse the command-line arguments.
- var allRuntimes = Runtime.builtIn.toList()..remove(Runtime.vm);
- if (!Platform.isMacOS) allRuntimes.remove(Runtime.safari);
- if (!Platform.isWindows) allRuntimes.remove(Runtime.internetExplorer);
-
-// parser.addFlag('help',
-// abbr: 'h', negatable: false, help: 'Shows this usage information.');
- parser.addFlag('version',
- negatable: false, help: "Shows the package's version.");
-
- // Note that defaultsTo declarations here are only for documentation
- // purposes.
- // We pass null instead of the default so that it merges properly with the
- // config file.
-
- parser.addSeparator('======== Selecting Tests');
- parser.addMultiOption('name',
- abbr: 'n',
- help: 'A substring of the name of the test to run.\n'
- 'Regular expression syntax is supported.\n'
- 'If passed multiple times, tests must match all substrings.',
- splitCommas: false);
- parser.addMultiOption('plain-name',
- abbr: 'N',
- help: 'A plain-text substring of the name of the test to run.\n'
- 'If passed multiple times, tests must match all substrings.',
- splitCommas: false);
- parser.addMultiOption('tags',
- abbr: 't',
- help: 'Run only tests with all of the specified tags.\n'
- 'Supports boolean selector syntax.');
- parser.addMultiOption('tag', hide: true);
- parser.addMultiOption('exclude-tags',
- abbr: 'x',
- help: "Don't run tests with any of the specified tags.\n"
- 'Supports boolean selector syntax.');
- parser.addMultiOption('exclude-tag', hide: true);
- parser.addFlag('run-skipped',
- help: 'Run skipped tests instead of skipping them.');
-
- parser.addSeparator('======== Running Tests');
-
- // The UI term "platform" corresponds with the implementation term "runtime".
- // The [Runtime] class used to be called [TestPlatform], but it was changed to
- // avoid conflicting with [SuitePlatform]. We decided not to also change the
- // UI to avoid a painful migration.
- parser.addMultiOption('platform',
- abbr: 'p',
- help: 'The platform(s) on which to run the tests.\n'
- '[vm (default), '
- '${allRuntimes.map((runtime) => runtime.identifier).join(", ")}]');
- parser.addMultiOption('preset',
- abbr: 'P', help: 'The configuration preset(s) to use.');
- parser.addOption('concurrency',
- abbr: 'j',
- help: 'The number of concurrent test suites run.',
- defaultsTo: defaultConcurrency.toString(),
- valueHelp: 'threads');
- parser.addOption('total-shards',
- help: 'The total number of invocations of the test runner being run.');
- parser.addOption('shard-index',
- help: 'The index of this test runner invocation (of --total-shards).');
- parser.addOption('pub-serve',
- help: 'The port of a pub serve instance serving "test/".',
- valueHelp: 'port');
- parser.addOption('timeout',
- help: 'The default test timeout. For example: 15s, 2x, none',
- defaultsTo: '30s');
- parser.addFlag('pause-after-load',
- help: 'Pauses for debugging before any tests execute.\n'
- 'Implies --concurrency=1, --debug, and --timeout=none.\n'
- 'Currently only supported for browser tests.',
- negatable: false);
- parser.addFlag('debug',
- help: 'Runs the VM and Chrome tests in debug mode.', negatable: false);
- parser.addOption('coverage',
- help: 'Gathers coverage and outputs it to the specified directory.\n'
- 'Implies --debug.',
- valueHelp: 'directory');
- parser.addFlag('chain-stack-traces',
- help: 'Chained stack traces to provide greater exception details\n'
- 'especially for asynchronous code. It may be useful to disable\n'
- 'to provide improved test performance but at the cost of\n'
- 'debuggability.',
- defaultsTo: true);
- parser.addFlag('no-retry',
- help: "Don't re-run tests that have retry set.",
- defaultsTo: false,
- negatable: false);
- parser.addOption('test-randomize-ordering-seed',
- help: 'The seed to randomize the execution order of test cases.\n'
- 'Must be a 32bit unsigned integer or "random".\n'
- 'If "random", pick a random seed to use.\n'
- 'If not passed, do not randomize test case execution order.');
-
- var defaultReporter = 'compact';
- var reporterDescriptions = <String, String>{
- 'compact': 'A single line, updated continuously.',
- 'expanded': 'A separate line for each update.',
- 'json': 'A machine-readable format (see https://goo.gl/gBsV1a).'
- };
-
- parser.addSeparator('======== Output');
- parser.addOption('reporter',
- abbr: 'r',
- help: 'The runner used to print test results.',
- defaultsTo: defaultReporter,
- allowed: reporterDescriptions.keys.toList(),
- allowedHelp: reporterDescriptions);
- parser.addOption('file-reporter',
- help: 'The reporter used to write test results to a file.\n'
- 'Should be in the form <reporter>:<filepath>, '
- 'e.g. "json:reports/tests.json"');
- parser.addFlag('verbose-trace',
- negatable: false,
- help: 'Whether to emit stack traces with core library frames.');
- parser.addFlag('js-trace',
- negatable: false,
- help: 'Whether to emit raw JavaScript stack traces for browser tests.');
- parser.addFlag('color',
- help: 'Whether to use terminal colors.\n(auto-detected by default)');
-
- /// The following options are used only by the internal Google test runner.
- /// They're hidden and not supported as stable API surface outside Google.
- parser.addOption('configuration',
- help: 'The path to the configuration file.', hide: true);
- parser.addOption('dart2js-path',
- help: 'The path to the dart2js executable.', hide: true);
- parser.addMultiOption('dart2js-args',
- help: 'Extra arguments to pass to dart2js.', hide: true);
-
- // If we're running test/dir/my_test.dart, we'll look for
- // test/dir/my_test.dart.html in the precompiled directory.
- parser.addOption('precompiled',
- help: 'The path to a mirror of the package directory containing HTML '
- 'that points to precompiled JS.',
- hide: true);
+ log.stdout(_usageHelp);
}
}
-/// An enum of all Dart runtimes supported by the test runner.
-class Runtime {
- // When adding new runtimes, be sure to update the baseline and derived
- // variable tests in test/backend/platform_selector/evaluate_test.
+const String _terseHelp = 'Run tests in this package.';
- /// The command-line Dart VM.
- static const Runtime vm = Runtime('VM', 'vm', isDartVM: true);
-
- /// Google Chrome.
- static const Runtime chrome =
- Runtime('Chrome', 'chrome', isBrowser: true, isJS: true, isBlink: true);
-
- /// PhantomJS.
- static const Runtime phantomJS = Runtime('PhantomJS', 'phantomjs',
- isBrowser: true, isJS: true, isBlink: true, isHeadless: true);
-
- /// Mozilla Firefox.
- static const Runtime firefox =
- Runtime('Firefox', 'firefox', isBrowser: true, isJS: true);
-
- /// Apple Safari.
- static const Runtime safari =
- Runtime('Safari', 'safari', isBrowser: true, isJS: true);
-
- /// Microsoft Internet Explorer.
- static const Runtime internetExplorer =
- Runtime('Internet Explorer', 'ie', isBrowser: true, isJS: true);
-
- /// The command-line Node.js VM.
- static const Runtime nodeJS = Runtime('Node.js', 'node', isJS: true);
-
- /// The platforms that are supported by the test runner by default.
- static const List<Runtime> builtIn = [
- Runtime.vm,
- Runtime.chrome,
- Runtime.phantomJS,
- Runtime.firefox,
- Runtime.safari,
- Runtime.internetExplorer,
- Runtime.nodeJS
- ];
-
- /// The human-friendly name of the platform.
- final String name;
-
- /// The identifier used to look up the platform.
- final String identifier;
-
- /// The parent platform that this is based on, or `null` if there is no
- /// parent.
- final Runtime parent;
-
- /// Returns whether this is a child of another platform.
- bool get isChild => parent != null;
-
- /// Whether this platform runs the Dart VM in any capacity.
- final bool isDartVM;
-
- /// Whether this platform is a browser.
- final bool isBrowser;
-
- /// Whether this platform runs Dart compiled to JavaScript.
- final bool isJS;
-
- /// Whether this platform uses the Blink rendering engine.
- final bool isBlink;
-
- /// Whether this platform has no visible window.
- final bool isHeadless;
-
- /// Returns the platform this is based on, or [this] if it's not based on
- /// anything.
- ///
- /// That is, returns [parent] if it's non-`null` or [this] if it's `null`.
- Runtime get root => parent ?? this;
-
- const Runtime(this.name, this.identifier,
- {this.isDartVM = false,
- this.isBrowser = false,
- this.isJS = false,
- this.isBlink = false,
- this.isHeadless = false})
- : parent = null;
-
- Runtime._child(this.name, this.identifier, Runtime parent)
- : isDartVM = parent.isDartVM,
- isBrowser = parent.isBrowser,
- isJS = parent.isJS,
- isBlink = parent.isBlink,
- isHeadless = parent.isHeadless,
- parent = parent;
-
- /// Converts a JSON-safe representation generated by [serialize] back into a
- /// [Runtime].
- factory Runtime.deserialize(Object serialized) {
- if (serialized is String) {
- return builtIn
- .firstWhere((platform) => platform.identifier == serialized);
- }
-
- var map = serialized as Map;
- var parent = map['parent'];
- if (parent != null) {
- // Note that the returned platform's [parent] won't necessarily be `==` to
- // a separately-deserialized parent platform. This should be fine, though,
- // since we only deserialize platforms in the remote execution context
- // where they're only used to evaluate platform selectors.
- return Runtime._child(map['name'] as String, map['identifier'] as String,
- Runtime.deserialize(parent as Object));
- }
-
- return Runtime(map['name'] as String, map['identifier'] as String,
- isDartVM: map['isDartVM'] as bool,
- isBrowser: map['isBrowser'] as bool,
- isJS: map['isJS'] as bool,
- isBlink: map['isBlink'] as bool,
- isHeadless: map['isHeadless'] as bool);
- }
-
- /// Converts [this] into a JSON-safe object that can be converted back to a
- /// [Runtime] using [Runtime.deserialize].
- Object serialize() {
- if (builtIn.contains(this)) return identifier;
-
- if (parent != null) {
- return {
- 'name': name,
- 'identifier': identifier,
- 'parent': parent.serialize()
- };
- }
-
- return {
- 'name': name,
- 'identifier': identifier,
- 'isDartVM': isDartVM,
- 'isBrowser': isBrowser,
- 'isJS': isJS,
- 'isBlink': isBlink,
- 'isHeadless': isHeadless
- };
- }
-
- /// Returns a child of [this] that counts as both this platform's identifier
- /// and the new [identifier].
- ///
- /// This may not be called on a platform that's already a child.
- Runtime extend(String name, String identifier) {
- if (parent == null) return Runtime._child(name, identifier, this);
- throw StateError('A child platform may not be extended.');
- }
-
- @override
- String toString() => name;
-}
+const String _usageHelp = 'Usage: dart test [files or directories...]';
diff --git a/pkg/dartdev/lib/src/core.dart b/pkg/dartdev/lib/src/core.dart
index 9a05bee..cc3a505 100644
--- a/pkg/dartdev/lib/src/core.dart
+++ b/pkg/dartdev/lib/src/core.dart
@@ -55,7 +55,7 @@
/// Return whether any Dart experiments were specified by the user.
bool get wereExperimentsSpecified =>
- globalResults.wasParsed(experimentFlagName);
+ globalResults?.wasParsed(experimentFlagName) ?? false;
/// Return the list of Dart experiment flags specified by the user.
List<String> get specifiedExperiments => globalResults[experimentFlagName];
@@ -121,6 +121,9 @@
Project.fromDirectory(this.dir);
+ bool get hasPubspecFile =>
+ FileSystemEntity.isFileSync(path.join(dir.path, 'pubspec.yaml'));
+
bool get hasPackageConfigFile => packageConfig != null;
PackageConfig get packageConfig {
diff --git a/pkg/dartdev/test/commands/test_test.dart b/pkg/dartdev/test/commands/test_test.dart
index 304f598..ef2ae06 100644
--- a/pkg/dartdev/test/commands/test_test.dart
+++ b/pkg/dartdev/test/commands/test_test.dart
@@ -2,6 +2,9 @@
// 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 path;
import 'package:test/test.dart';
import '../utils.dart';
@@ -17,32 +20,69 @@
test('--help', () {
p = project();
- var result = p.runSync('test', ['--help']);
+
+ var result = p.runSync('pub', ['get']);
expect(result.exitCode, 0);
+
+ result = p.runSync('test', ['--help']);
+
+ expect(result.exitCode, 0);
+ expect(result.stdout, contains(' tests in this package'));
expect(result.stderr, isEmpty);
- expect(result.stdout, contains('Run tests in this package.'));
- expect(result.stdout, contains('Usage: dart test [arguments]'));
- expect(result.stdout, contains('======== Selecting Tests'));
});
- test('no dependency', () {
+ test('dart help test', () {
+ p = project();
+
+ var result = p.runSync('pub', ['get']);
+ expect(result.exitCode, 0);
+
+ result = p.runSync('help', ['test']);
+
+ expect(result.exitCode, 0);
+ expect(result.stdout, contains(' tests in this package'));
+ expect(result.stderr, isEmpty);
+ });
+
+ test('no pubspec.yaml', () {
+ p = project();
+ var pubspec = File(path.join(p.dirPath, 'pubspec.yaml'));
+ pubspec.deleteSync();
+
+ var result = p.runSync('help', ['test']);
+
+ expect(result.exitCode, 0);
+ expect(result.stdout, contains('No pubspec.yaml file found'));
+ expect(result.stderr, isEmpty);
+ });
+
+ test('no .dart_tool/package_config.json', () {
+ p = project();
+
+ var result = p.runSync('help', ['test']);
+
+ expect(result.exitCode, 0);
+ expect(result.stdout,
+ contains('No .dart_tool/package_config.json file found'));
+ expect(result.stderr, isEmpty);
+ });
+
+ test('no package:test dependency', () {
p = project(mainSrc: 'int get foo => 1;\n');
p.file('pubspec.yaml', 'name: ${p.name}\n');
- var result = p.runSync('pub', ['get', '--offline']);
+ var result = p.runSync('pub', ['get']);
expect(result.exitCode, 0);
result = p.runSync('test', []);
expect(result.exitCode, 65);
expect(
result.stdout,
- contains(
- 'In order to run tests, you need to add a dependency on package:test',
- ),
+ contains('In order to run tests, you need to add a dependency'),
);
- }, skip: 'https://github.com/dart-lang/sdk/issues/40854');
+ });
- test('has dependency', () {
+ test('has package:test dependency', () {
p = project(mainSrc: 'int get foo => 1;\n');
p.file('test/foo_test.dart', '''
import 'package:test/test.dart';
@@ -54,14 +94,14 @@
}
''');
- var result = p.runSync('pub', ['get', '--offline']);
+ var result = p.runSync('pub', ['get']);
expect(result.exitCode, 0);
result = p.runSync('test', ['--no-color', '--reporter', 'expanded']);
expect(result.exitCode, 0);
expect(result.stdout, contains('All tests passed!'));
expect(result.stderr, isEmpty);
- }, skip: 'https://github.com/dart-lang/sdk/issues/40854');
+ });
test('--enable-experiment', () {
p = project(mainSrc: 'int get foo => 1;\n');
@@ -77,11 +117,11 @@
}
''');
- var result = p.runSync('pub', ['get', '--offline']);
+ var result = p.runSync('pub', ['get']);
expect(result.exitCode, 0);
result = p.runSync('--enable-experiment=non-nullable',
['test', '--no-color', '--reporter', 'expanded']);
expect(result.exitCode, 1);
- }, skip: 'https://github.com/dart-lang/sdk/issues/40854');
+ });
}
diff --git a/pkg/dartdev/test/core_test.dart b/pkg/dartdev/test/core_test.dart
index 6384d9b..bc10cb9 100644
--- a/pkg/dartdev/test/core_test.dart
+++ b/pkg/dartdev/test/core_test.dart
@@ -3,8 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:convert';
+import 'dart:io';
import 'package:dartdev/src/core.dart';
+import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'utils.dart';
@@ -32,10 +34,19 @@
tearDown(() => p?.dispose());
- test('hasPackageConfigFile negative', () {
+ test('hasPubspecFile positive', () {
p = project();
Project coreProj = Project.fromDirectory(p.dir);
- expect(coreProj.hasPackageConfigFile, isFalse);
+ expect(coreProj.hasPubspecFile, isTrue);
+ });
+
+ test('hasPubspecFile negative', () {
+ p = project();
+ var pubspec = File(path.join(p.dirPath, 'pubspec.yaml'));
+ pubspec.deleteSync();
+
+ Project coreProj = Project.fromDirectory(p.dir);
+ expect(coreProj.hasPubspecFile, isFalse);
});
test('hasPackageConfigFile positive', () {
@@ -46,6 +57,12 @@
expect(coreProj.packageConfig, isNotNull);
expect(coreProj.packageConfig.packages, isNotEmpty);
});
+
+ test('hasPackageConfigFile negative', () {
+ p = project();
+ Project coreProj = Project.fromDirectory(p.dir);
+ expect(coreProj.hasPackageConfigFile, isFalse);
+ });
}
const String _packageData = '''{
diff --git a/pkg/dds/CHANGELOG.md b/pkg/dds/CHANGELOG.md
index 52b7282..96848ba 100644
--- a/pkg/dds/CHANGELOG.md
+++ b/pkg/dds/CHANGELOG.md
@@ -1,5 +1,6 @@
-# 1.3.2-dev
+# 1.3.2
+- Add IPv6 hosting support.
- Fix handling of requests that are outstanding when a client channel is closed.
# 1.3.1
diff --git a/pkg/dds/lib/dds.dart b/pkg/dds/lib/dds.dart
index f9c057c..aeb39a9 100644
--- a/pkg/dds/lib/dds.dart
+++ b/pkg/dds/lib/dds.dart
@@ -51,7 +51,8 @@
/// development service will communicate with.
///
/// If provided, [serviceUri] will determine the address and port of the
- /// spawned Dart Development Service.
+ /// spawned Dart Development Service. The format of [serviceUri] must be
+ /// consistent with the protocol determined by [ipv6].
///
/// [enableAuthCodes] controls whether or not an authentication code must
/// be provided by clients when communicating with this instance of
@@ -59,10 +60,14 @@
/// encoded string provided as the first element of the DDS path and is meant
/// to make it more difficult for unintended clients to connect to this
/// service. Authentication codes are enabled by default.
+ ///
+ /// [ipv6] controls whether or not DDS is served via IPv6. IPv4 is enabled by
+ /// default.
static Future<DartDevelopmentService> startDartDevelopmentService(
Uri remoteVmServiceUri, {
Uri serviceUri,
bool enableAuthCodes = true,
+ bool ipv6 = false,
}) async {
if (remoteVmServiceUri == null) {
throw ArgumentError.notNull('remoteVmServiceUri');
@@ -72,15 +77,33 @@
'remoteVmServiceUri must have an HTTP scheme. Actual: ${remoteVmServiceUri.scheme}',
);
}
- if (serviceUri != null && serviceUri.scheme != 'http') {
- throw ArgumentError(
- 'serviceUri must have an HTTP scheme. Actual: ${serviceUri.scheme}',
+ if (serviceUri != null) {
+ if (serviceUri.scheme != 'http') {
+ throw ArgumentError(
+ 'serviceUri must have an HTTP scheme. Actual: ${serviceUri.scheme}',
+ );
+ }
+
+ // If provided an address to bind to, ensure it uses a protocol consistent
+ // with that used to spawn DDS.
+ final addresses = await InternetAddress.lookup(serviceUri.host);
+ final address = addresses.firstWhere(
+ (a) => (a.type ==
+ (ipv6 ? InternetAddressType.IPv6 : InternetAddressType.IPv4)),
+ orElse: () => null,
);
+ if (address == null) {
+ throw ArgumentError(
+ "serviceUri '$serviceUri' is not an IPv${ipv6 ? "6" : "4"} address.",
+ );
+ }
}
+
final service = _DartDevelopmentService(
remoteVmServiceUri,
serviceUri,
enableAuthCodes,
+ ipv6,
);
await service.startService();
return service;
diff --git a/pkg/dds/lib/src/dds_impl.dart b/pkg/dds/lib/src/dds_impl.dart
index 55e9e64..192e9ad5 100644
--- a/pkg/dds/lib/src/dds_impl.dart
+++ b/pkg/dds/lib/src/dds_impl.dart
@@ -6,10 +6,7 @@
class _DartDevelopmentService implements DartDevelopmentService {
_DartDevelopmentService(
- this._remoteVmServiceUri,
- this._uri,
- this._authCodesEnabled,
- ) {
+ this._remoteVmServiceUri, this._uri, this._authCodesEnabled, this._ipv6) {
_clientManager = _ClientManager(this);
_expressionEvaluator = _ExpressionEvaluator(this);
_isolateManager = _IsolateManager(this);
@@ -38,8 +35,9 @@
Future<void> _startDDSServer() async {
// No provided address, bind to an available port on localhost.
- // TODO(bkonyi): handle case where there's no IPv4 loopback.
- final host = uri?.host ?? InternetAddress.loopbackIPv4.host;
+ final host = uri?.host ??
+ (_ipv6 ? InternetAddress.loopbackIPv6 : InternetAddress.loopbackIPv4)
+ .host;
final port = uri?.port ?? 0;
// Start the DDS server.
@@ -226,6 +224,8 @@
Uri get wsUri => _toWebSocket(_uri);
Uri _uri;
+ final bool _ipv6;
+
bool get isRunning => _uri != null;
Future<void> get done => _done.future;
diff --git a/pkg/dds/pubspec.yaml b/pkg/dds/pubspec.yaml
index e1d0d3d..447170c 100644
--- a/pkg/dds/pubspec.yaml
+++ b/pkg/dds/pubspec.yaml
@@ -3,7 +3,7 @@
A library used to spawn the Dart Developer Service, used to communicate with
a Dart VM Service instance.
-version: 1.3.2-dev
+version: 1.3.2
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/dds
diff --git a/pkg/dds/test/smoke_test.dart b/pkg/dds/test/smoke_test.dart
index 0da26db..0a7347c 100644
--- a/pkg/dds/test/smoke_test.dart
+++ b/pkg/dds/test/smoke_test.dart
@@ -27,16 +27,26 @@
process = null;
});
- void createSmokeTest(bool useAuthCodes) {
+ void createSmokeTest(bool useAuthCodes, bool ipv6) {
+ final protocol = ipv6 ? 'IPv6' : 'IPv4';
test(
- 'Smoke Test with ${useAuthCodes ? "" : "no "} authentication codes',
+ 'Smoke Test with ${useAuthCodes ? "" : "no"} authentication codes '
+ 'with $protocol',
() async {
dds = await DartDevelopmentService.startDartDevelopmentService(
remoteVmServiceUri,
enableAuthCodes: useAuthCodes,
+ ipv6: ipv6,
);
expect(dds.isRunning, true);
+ try {
+ Uri.parseIPv6Address(dds.uri.host);
+ expect(ipv6, true);
+ } on FormatException {
+ expect(ipv6, false);
+ }
+
// Ensure basic websocket requests are forwarded correctly to the VM service.
final service = await vmServiceConnectUri(dds.wsUri.toString());
final version = await service.getVersion();
@@ -69,8 +79,9 @@
);
}
- createSmokeTest(true);
- createSmokeTest(false);
+ createSmokeTest(true, false);
+ createSmokeTest(false, false);
+ createSmokeTest(true, true);
test('startup fails when VM service has existing clients', () async {
Uri httpToWebSocketUri(Uri httpUri) {
@@ -121,5 +132,23 @@
serviceUri: Uri.parse('dart-lang://localhost:2345'),
),
throwsA(TypeMatcher<ArgumentError>()));
+
+ // Protocol mismatch
+ expect(
+ () async => await DartDevelopmentService.startDartDevelopmentService(
+ Uri.parse('http://localhost:1234'),
+ serviceUri: Uri.parse('http://127.0.0.1:2345'),
+ ipv6: true,
+ ),
+ throwsA(TypeMatcher<ArgumentError>()));
+
+ // Protocol mismatch
+ expect(
+ () async => await DartDevelopmentService.startDartDevelopmentService(
+ Uri.parse('http://localhost:1234'),
+ serviceUri: Uri.parse('http://[::1]:2345'),
+ ipv6: false,
+ ),
+ throwsA(TypeMatcher<ArgumentError>()));
});
}
diff --git a/runtime/vm/service_event.cc b/runtime/vm/service_event.cc
index 18e06f9..00d7ba3 100644
--- a/runtime/vm/service_event.cc
+++ b/runtime/vm/service_event.cc
@@ -34,11 +34,18 @@
bytes_(NULL),
bytes_length_(0),
timestamp_(OS::GetCurrentTimeMillis()) {
- // We should never generate events for the vm or service isolates.
+ // We should never generate events for the vm isolate as it is never reported over the service.
ASSERT(isolate_ != Dart::vm_isolate());
+
+ // System isolates should never post service events. However, the Isolate service object uses a
+ // service event to represent the current running state of the isolate, so we need to allow for
+ // system isolates to create resume and none events for this purpose. The resume event represents
+ // a running isolate and the none event is returned for an isolate that has not yet been marked as
+ // runnable (see "pauseEvent" in Isolate::PrintJSON).
ASSERT(isolate == NULL || !Isolate::IsSystemIsolate(isolate) ||
(Isolate::IsSystemIsolate(isolate) &&
- event_kind == ServiceEvent::kResume));
+ (event_kind == ServiceEvent::kResume ||
+ event_kind == ServiceEvent::kNone)));
if ((event_kind == ServiceEvent::kPauseStart) ||
(event_kind == ServiceEvent::kPauseExit)) {
diff --git a/tools/VERSION b/tools/VERSION
index be4759b..59af991 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 10
PATCH 0
-PRERELEASE 95
+PRERELEASE 96
PRERELEASE_PATCH 0
\ No newline at end of file