Unverified Commit 07148525 authored by Devon Carew's avatar Devon Carew Committed by GitHub

Revert "re-write flutter analyze (the single-shot and --flutter-repo) to use...

Revert "re-write flutter analyze (the single-shot and --flutter-repo) to use the analysis server (#16281)" (#16482)

This reverts commit 2f41ea54.
parent 2f41ea54
...@@ -79,7 +79,7 @@ analyzer. There are two main ways to run it. In either case you will ...@@ -79,7 +79,7 @@ analyzer. There are two main ways to run it. In either case you will
want to run `flutter update-packages` first, or you will get bogus want to run `flutter update-packages` first, or you will get bogus
error messages about core classes like Offset from `dart:ui`. error messages about core classes like Offset from `dart:ui`.
For a one-off, use `flutter analyze --flutter-repo`. This uses the `analysis_options.yaml` file For a one-off, use `flutter analyze --flutter-repo`. This uses the `analysis_options_repo.yaml` file
at the root of the repository for its configuration. at the root of the repository for its configuration.
For continuous analysis, use `flutter analyze --flutter-repo --watch`. This uses normal For continuous analysis, use `flutter analyze --flutter-repo --watch`. This uses normal
......
...@@ -9,18 +9,22 @@ ...@@ -9,18 +9,22 @@
# #
# There are four similar analysis options files in the flutter repos: # There are four similar analysis options files in the flutter repos:
# - analysis_options.yaml (this file) # - analysis_options.yaml (this file)
# - analysis_options_repo.yaml
# - packages/flutter/lib/analysis_options_user.yaml # - packages/flutter/lib/analysis_options_user.yaml
# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml # - https://github.com/flutter/plugins/blob/master/analysis_options.yaml
# - https://github.com/flutter/engine/blob/master/analysis_options.yaml
# #
# This file contains the analysis options used by Flutter tools, such as IntelliJ, # This file contains the analysis options used by Flutter tools, such as IntelliJ,
# Android Studio, and the `flutter analyze` command. # Android Studio, and the `flutter analyze` command.
# It is very similar to the analysis_options_repo.yaml file in this same directory;
# the only difference (currently) is the public_member_api_docs option,
# which triggers too many messages to be used in editors.
# #
# The flutter/plugins repo contains a copy of this file, which should be kept # The flutter/plugins repo contains a copy of this file, which should be kept
# in sync with this file. # in sync with this file.
analyzer: analyzer:
language: language:
enableStrictCallChecks: true
enableSuperMixins: true enableSuperMixins: true
strong-mode: strong-mode:
implicit-dynamic: false implicit-dynamic: false
...@@ -127,6 +131,7 @@ linter: ...@@ -127,6 +131,7 @@ linter:
- prefer_is_not_empty - prefer_is_not_empty
- prefer_single_quotes - prefer_single_quotes
- prefer_typing_uninitialized_variables - prefer_typing_uninitialized_variables
# - public_member_api_docs # this is the only difference from analysis_options_repo.yaml
- recursive_getters - recursive_getters
- slash_for_doc_comments - slash_for_doc_comments
- sort_constructors_first - sort_constructors_first
......
# Specify analysis options.
#
# Until there are meta linter rules, each desired lint must be explicitly enabled.
# See: https://github.com/dart-lang/linter/issues/288
#
# For a list of lints, see: http://dart-lang.github.io/linter/lints/
# See the configuration guide for more
# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer
#
# There are three similar analysis options files in the flutter repo:
# - analysis_options.yaml
# - analysis_options_repo.yaml (this file)
# - packages/flutter/lib/analysis_options_user.yaml
#
# This file contains the analysis options used by 'flutter analyze' when analyzing
# the flutter repository. It is very similar to analysis_options.yaml;
# the only difference (currently) is the public_member_api_docs option,
# which is turned on and programmatically reduced to a single output line
# indicating the # of violations for that rule.
analyzer:
language:
enableStrictCallChecks: true
enableSuperMixins: true
strong-mode:
implicit-dynamic: false
errors:
# treat missing required parameters as a warning (not a hint)
missing_required_param: warning
# treat missing returns as a warning (not a hint)
missing_return: warning
# allow having TODOs in the code
todo: ignore
# `flutter analyze` (without `--watch`) just ignores directories
# that contain a .dartignore file, and this file does not have any
# effect on what files are actually analyzed.
linter:
rules:
# these rules are documented on and in the same order as
# the Dart Lint rules page to make maintenance easier
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
- always_declare_return_types
- always_put_control_body_on_new_line
# - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
- always_require_non_null_named_parameters
- always_specify_types
- annotate_overrides
# - avoid_annotating_with_dynamic # conflicts with always_specify_types
- avoid_as
# - avoid_bool_literals_in_conditional_expressions # not yet tested
# - avoid_catches_without_on_clauses # we do this commonly
# - avoid_catching_errors # we do this commonly
- avoid_classes_with_only_static_members
- avoid_empty_else
- avoid_function_literals_in_foreach_calls
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
# - avoid_positional_boolean_parameters # not yet tested
# - avoid_private_typedef_functions # not yet tested
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
# - avoid_returning_null # we do this commonly
# - avoid_returning_this # https://github.com/dart-lang/linter/issues/842
# - avoid_setters_without_getters # not yet tested
# - avoid_single_cascade_in_expression_statements # not yet tested
- avoid_slow_async_io
# - avoid_types_as_parameter_names # https://github.com/dart-lang/linter/pull/954/files
# - avoid_types_on_closure_parameters # conflicts with always_specify_types
# - avoid_unused_constructor_parameters # https://github.com/dart-lang/linter/pull/847
- await_only_futures
- camel_case_types
- cancel_subscriptions
# - cascade_invocations # not yet tested
# - close_sinks # https://github.com/flutter/flutter/issues/5789
# - comment_references # blocked on https://github.com/dart-lang/dartdoc/issues/1153
# - constant_identifier_names # https://github.com/dart-lang/linter/issues/204
- control_flow_in_finally
- directives_ordering
- empty_catches
- empty_constructor_bodies
- empty_statements
- hash_and_equals
- implementation_imports
# - invariant_booleans # https://github.com/flutter/flutter/issues/5790
- iterable_contains_unrelated_type
# - join_return_with_assignment # not yet tested
- library_names
- library_prefixes
- list_remove_unrelated_type
# - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791
- no_adjacent_strings_in_list
- no_duplicate_case_values
- non_constant_identifier_names
# - omit_local_variable_types # opposite of always_specify_types
# - one_member_abstracts # too many false positives
# - only_throw_errors # https://github.com/flutter/flutter/issues/5792
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
# - parameter_assignments # we do this commonly
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_bool_in_asserts
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
# - prefer_constructors_over_static_methods # not yet tested
- prefer_contains
# - prefer_equal_for_default_values # not yet tested
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
- prefer_final_fields
- prefer_final_locals
- prefer_foreach
# - prefer_function_declarations_over_variables # not yet tested
- prefer_initializing_formals
# - prefer_interpolation_to_compose_strings # not yet tested
- prefer_is_empty
- prefer_is_not_empty
- prefer_single_quotes
- prefer_typing_uninitialized_variables
- public_member_api_docs # this is the only difference from analysis_options.yaml
- recursive_getters
- slash_for_doc_comments
- sort_constructors_first
- sort_unnamed_constructors_first
- super_goes_last
- test_types_in_equals
- throw_in_finally
# - type_annotate_public_apis # subset of always_specify_types
- type_init_formals
# - unawaited_futures # https://github.com/flutter/flutter/issues/5793
- unnecessary_brace_in_string_interps
- unnecessary_getters_setters
# - unnecessary_lambdas # https://github.com/dart-lang/linter/issues/498
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
- unnecessary_overrides
- unnecessary_parenthesis
# - unnecessary_statements # not yet tested
- unnecessary_this
- unrelated_type_equality_checks
- use_rethrow_when_possible
# - use_setters_to_change_properties # not yet tested
# - use_string_buffers # https://github.com/dart-lang/linter/pull/664
# - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
- valid_regexps
...@@ -185,8 +185,6 @@ Future<Null> main() async { ...@@ -185,8 +185,6 @@ Future<Null> main() async {
} }
} }
buffer.add(''); buffer.add('');
buffer.add('// ignore_for_file: unused_element');
buffer.add('');
final List<Line> lines = new List<Line>.filled(buffer.length, null, growable: true); final List<Line> lines = new List<Line>.filled(buffer.length, null, growable: true);
for (Section section in sections) { for (Section section in sections) {
buffer.addAll(section.strings); buffer.addAll(section.strings);
...@@ -205,7 +203,7 @@ dependencies: ...@@ -205,7 +203,7 @@ dependencies:
print('Found $sampleCodeSections sample code sections.'); print('Found $sampleCodeSections sample code sections.');
final Process process = await Process.start( final Process process = await Process.start(
_flutter, _flutter,
<String>['analyze', '--no-preamble', '--no-congratulate', mainDart.parent.path], <String>['analyze', '--no-preamble', mainDart.path],
workingDirectory: temp.path, workingDirectory: temp.path,
); );
stderr.addStream(process.stderr); stderr.addStream(process.stderr);
...@@ -214,6 +212,10 @@ dependencies: ...@@ -214,6 +212,10 @@ dependencies:
errors.removeAt(0); errors.removeAt(0);
if (errors.first.startsWith('Running "flutter packages get" in ')) if (errors.first.startsWith('Running "flutter packages get" in '))
errors.removeAt(0); errors.removeAt(0);
if (errors.first.startsWith('Analyzing '))
errors.removeAt(0);
if (errors.last.endsWith(' issues found.') || errors.last.endsWith(' issue found.'))
errors.removeLast();
int errorCount = 0; int errorCount = 0;
for (String error in errors) { for (String error in errors) {
final String kBullet = Platform.isWindows ? ' - ' : ' • '; final String kBullet = Platform.isWindows ? ' - ' : ' • ';
......
...@@ -21,24 +21,21 @@ Future<Null> main() async { ...@@ -21,24 +21,21 @@ Future<Null> main() async {
int publicMembers = 0; int publicMembers = 0;
int otherErrors = 0; int otherErrors = 0;
int otherLines = 0; int otherLines = 0;
await for (String entry in analysis.stdout.transform(utf8.decoder).transform(const LineSplitter())) { await for (String entry in analysis.stderr.transform(utf8.decoder).transform(const LineSplitter())) {
entry = entry.trim(); print('analyzer stderr: $entry');
print('analyzer stdout: $entry'); if (entry.startsWith('[lint] Document all public members')) {
if (entry == 'Building flutter tool...') {
// ignore this line
} else if (entry.startsWith('info • Document all public members •')) {
publicMembers += 1; publicMembers += 1;
} else if (entry.startsWith('info •') || entry.startsWith('warning •') || entry.startsWith('error •')) { } else if (entry.startsWith('[')) {
otherErrors += 1; otherErrors += 1;
} else if (entry.contains(' (ran in ')) { } else if (entry.startsWith('(Ran in ')) {
// ignore this line // ignore this line
} else if (entry.isNotEmpty) { } else {
otherLines += 1; otherLines += 1;
} }
} }
await for (String entry in analysis.stderr.transform(utf8.decoder).transform(const LineSplitter())) { await for (String entry in analysis.stdout.transform(utf8.decoder).transform(const LineSplitter())) {
print('analyzer stderr: $entry'); print('analyzer stdout: $entry');
if (entry.startsWith('[lint] ')) { if (entry == 'Building flutter tool...') {
// ignore this line // ignore this line
} else { } else {
otherLines += 1; otherLines += 1;
......
# Take our settings from the repo's main analysis_options.yaml file, but include
# an additional rule to validate that public members are documented.
include: ../analysis_options.yaml
linter:
rules:
- public_member_api_docs
...@@ -7,21 +7,21 @@ ...@@ -7,21 +7,21 @@
# See the configuration guide for more # See the configuration guide for more
# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer # https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer
# #
# There are four similar analysis options files in the flutter repos: # There are three similar analysis options files in the flutter repo:
# - analysis_options.yaml # - analysis_options.yaml
# - analysis_options_repo.yaml
# - packages/flutter/lib/analysis_options_user.yaml (this file) # - packages/flutter/lib/analysis_options_user.yaml (this file)
# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml
# - https://github.com/flutter/engine/blob/master/analysis_options.yaml
# #
# This file contains the analysis options used by "flutter analyze" and the # This file contains the analysis options used by "flutter analyze"
# dartanalyzer when analyzing code outside the flutter repository. It isn't named # and the dartanalyzer when analyzing code outside the flutter repository.
# 'analysis_options.yaml' because otherwise editors would use it when analyzing # It isn't named 'analysis_options.yaml' because otherwise editors like Atom
# the flutter tool itself. # would use it when analyzing the flutter tool itself.
# #
# When editing, make sure you keep this and /analysis_options.yaml consistent. # When editing, make sure you keep /analysis_options.yaml consistent.
analyzer: analyzer:
language: language:
enableStrictCallChecks: true
enableSuperMixins: true enableSuperMixins: true
strong-mode: true strong-mode: true
errors: errors:
......
# Use the analysis options settings from the top level of the repo (not
# the ones from above, which include the `public_member_api_docs` rule).
include: ../../analysis_options.yaml
...@@ -9,47 +9,28 @@ import '../runner/flutter_command.dart'; ...@@ -9,47 +9,28 @@ import '../runner/flutter_command.dart';
import 'analyze_continuously.dart'; import 'analyze_continuously.dart';
import 'analyze_once.dart'; import 'analyze_once.dart';
bool isDartFile(FileSystemEntity entry) => entry is File && entry.path.endsWith('.dart');
typedef bool FileFilter(FileSystemEntity entity);
class AnalyzeCommand extends FlutterCommand { class AnalyzeCommand extends FlutterCommand {
AnalyzeCommand({bool verboseHelp: false, this.workingDirectory}) { AnalyzeCommand({ bool verboseHelp: false, this.workingDirectory }) {
argParser.addFlag('flutter-repo', argParser.addFlag('flutter-repo', help: 'Include all the examples and tests from the Flutter repository.', defaultsTo: false);
negatable: false, argParser.addFlag('current-package', help: 'Include the lib/main.dart file from the current directory, if any.', defaultsTo: true);
help: 'Include all the examples and tests from the Flutter repository.', argParser.addFlag('dartdocs', help: 'List every public member that is lacking documentation (only works with --flutter-repo and without --watch).', defaultsTo: false, hide: !verboseHelp);
defaultsTo: false, argParser.addFlag('watch', help: 'Run analysis continuously, watching the filesystem for changes.', negatable: false);
hide: !verboseHelp); argParser.addFlag('preview-dart-2', defaultsTo: true, help: 'Preview Dart 2.0 functionality.');
argParser.addFlag('current-package', argParser.addOption('write', valueHelp: 'file', help: 'Also output the results to a file. This is useful with --watch if you want a file to always contain the latest results.');
help: 'Analyze the current project, if applicable.', defaultsTo: true); argParser.addOption('dart-sdk', valueHelp: 'path-to-sdk', help: 'The path to the Dart SDK.', hide: !verboseHelp);
argParser.addFlag('dartdocs',
negatable: false,
help: 'List every public member that is lacking documentation '
'(only works with --flutter-repo).',
hide: !verboseHelp);
argParser.addFlag('watch',
help: 'Run analysis continuously, watching the filesystem for changes.',
negatable: false);
argParser.addFlag('preview-dart-2',
defaultsTo: true, help: 'Preview Dart 2.0 functionality.');
argParser.addOption('write',
valueHelp: 'file',
help: 'Also output the results to a file. This is useful with --watch '
'if you want a file to always contain the latest results.');
// Hidden option to enable a benchmarking mode. // Hidden option to enable a benchmarking mode.
argParser.addFlag('benchmark', argParser.addFlag('benchmark', negatable: false, hide: !verboseHelp, help: 'Also output the analysis time.');
negatable: false,
hide: !verboseHelp,
help: 'Also output the analysis time.');
usesPubOption(); usesPubOption();
// Not used by analyze --watch // Not used by analyze --watch
argParser.addFlag('congratulate', argParser.addFlag('congratulate', help: 'When analyzing the flutter repository, show output even when there are no errors, warnings, hints, or lints.', defaultsTo: true);
help: 'When analyzing the flutter repository, show output even when ' argParser.addFlag('preamble', help: 'When analyzing the flutter repository, display the number of files that will be analyzed.', defaultsTo: true);
'there are no errors, warnings, hints, or lints.',
defaultsTo: true);
argParser.addFlag('preamble',
defaultsTo: true,
help: 'When analyzing the flutter repository, display the number of '
'files that will be analyzed.');
} }
/// The working directory for testing analysis using dartanalyzer. /// The working directory for testing analysis using dartanalyzer.
...@@ -59,19 +40,17 @@ class AnalyzeCommand extends FlutterCommand { ...@@ -59,19 +40,17 @@ class AnalyzeCommand extends FlutterCommand {
String get name => 'analyze'; String get name => 'analyze';
@override @override
String get description => "Analyze the project's Dart code."; String get description => 'Analyze the project\'s Dart code.';
@override @override
bool get shouldRunPub { bool get shouldRunPub {
// If they're not analyzing the current project. // If they're not analyzing the current project.
if (!argResults['current-package']) { if (!argResults['current-package'])
return false; return false;
}
// Or we're not in a project directory. // Or we're not in a project directory.
if (!fs.file('pubspec.yaml').existsSync()) { if (!fs.file('pubspec.yaml').existsSync())
return false; return false;
}
return super.shouldRunPub; return super.shouldRunPub;
} }
...@@ -80,15 +59,11 @@ class AnalyzeCommand extends FlutterCommand { ...@@ -80,15 +59,11 @@ class AnalyzeCommand extends FlutterCommand {
Future<Null> runCommand() { Future<Null> runCommand() {
if (argResults['watch']) { if (argResults['watch']) {
return new AnalyzeContinuously( return new AnalyzeContinuously(
argResults, argResults, runner.getRepoPackages(), previewDart2: argResults['preview-dart-2']
runner.getRepoRoots(),
runner.getRepoPackages(),
previewDart2: argResults['preview-dart-2'],
).analyze(); ).analyze();
} else { } else {
return new AnalyzeOnce( return new AnalyzeOnce(
argResults, argResults,
runner.getRepoRoots(),
runner.getRepoPackages(), runner.getRepoPackages(),
workingDirectory: workingDirectory, workingDirectory: workingDirectory,
previewDart2: argResults['preview-dart-2'], previewDart2: argResults['preview-dart-2'],
......
...@@ -383,19 +383,12 @@ class FlutterCommandRunner extends CommandRunner<Null> { ...@@ -383,19 +383,12 @@ class FlutterCommandRunner extends CommandRunner<Null> {
Cache.flutterRoot ??= _defaultFlutterRoot; Cache.flutterRoot ??= _defaultFlutterRoot;
} }
/// Get the root directories of the repo - the directories containing Dart packages.
List<String> getRepoRoots() {
final String root = fs.path.absolute(Cache.flutterRoot);
// not bin, and not the root
return <String>['dev', 'examples', 'packages'].map((String item) {
return fs.path.join(root, item);
}).toList();
}
/// Get all pub packages in the Flutter repo. /// Get all pub packages in the Flutter repo.
List<Directory> getRepoPackages() { List<Directory> getRepoPackages() {
return getRepoRoots() final String root = fs.path.absolute(Cache.flutterRoot);
.expand<String>((String root) => _gatherProjectPaths(root)) // not bin, and not the root
return <String>['dev', 'examples', 'packages']
.expand<String>((String path) => _gatherProjectPaths(fs.path.join(root, path)))
.map((String dir) => fs.directory(dir)) .map((String dir) => fs.directory(dir))
.toList(); .toList();
} }
......
...@@ -6,7 +6,7 @@ import 'dart:async'; ...@@ -6,7 +6,7 @@ import 'dart:async';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/dart/analysis.dart'; import 'package:flutter_tools/src/commands/analyze_continuously.dart';
import 'package:flutter_tools/src/dart/pub.dart'; import 'package:flutter_tools/src/dart/pub.dart';
import 'package:flutter_tools/src/dart/sdk.dart'; import 'package:flutter_tools/src/dart/sdk.dart';
import 'package:flutter_tools/src/runner/flutter_command_runner.dart'; import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
......
// Copyright 2016 The Chromium Authors. 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:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/commands/analyze.dart';
import 'package:test/test.dart';
import '../src/common.dart';
import '../src/context.dart';
import '../src/mocks.dart';
void main() {
Directory tempDir;
setUpAll(() {
Cache.disableLocking();
});
setUp(() {
tempDir = fs.systemTempDirectory.createTempSync('analysis_duplicate_names_test');
});
tearDown(() {
tempDir?.deleteSync(recursive: true);
});
group('analyze', () {
testUsingContext('flutter analyze with two files with the same name', () async {
final File dartFileA = fs.file(fs.path.join(tempDir.path, 'a.dart'));
dartFileA.parent.createSync();
dartFileA.writeAsStringSync('library test;');
final File dartFileB = fs.file(fs.path.join(tempDir.path, 'b.dart'));
dartFileB.writeAsStringSync('library test;');
final AnalyzeCommand command = new AnalyzeCommand();
applyMocksToCommand(command);
return createTestCommandRunner(command).run(
<String>['analyze', '--no-current-package', dartFileA.path, dartFileB.path]
).then<Null>((Null value) {
expect(testLogger.statusText, contains('Analyzing'));
expect(testLogger.statusText, contains('No issues found!'));
});
});
});
}
...@@ -17,6 +17,7 @@ import '../src/common.dart'; ...@@ -17,6 +17,7 @@ import '../src/common.dart';
import '../src/context.dart'; import '../src/context.dart';
void main() { void main() {
final String analyzerSeparator = platform.isWindows ? '-' : '•'; final String analyzerSeparator = platform.isWindows ? '-' : '•';
group('analyze once', () { group('analyze once', () {
...@@ -32,11 +33,7 @@ void main() { ...@@ -32,11 +33,7 @@ void main() {
}); });
tearDownAll(() { tearDownAll(() {
try { tempDir?.deleteSync(recursive: true);
tempDir?.deleteSync(recursive: true);
} catch (e) {
// ignore errors deleting the temporary directory
}
}); });
// Create a project to be analyzed // Create a project to be analyzed
...@@ -53,7 +50,7 @@ void main() { ...@@ -53,7 +50,7 @@ void main() {
}, timeout: allowForRemotePubInvocation); }, timeout: allowForRemotePubInvocation);
// Analyze in the current directory - no arguments // Analyze in the current directory - no arguments
testUsingContext('working directory', () async { testUsingContext('flutter analyze working directory', () async {
await runCommand( await runCommand(
command: new AnalyzeCommand(workingDirectory: fs.directory(projectPath)), command: new AnalyzeCommand(workingDirectory: fs.directory(projectPath)),
arguments: <String>['analyze'], arguments: <String>['analyze'],
...@@ -62,17 +59,17 @@ void main() { ...@@ -62,17 +59,17 @@ void main() {
}); });
// Analyze a specific file outside the current directory // Analyze a specific file outside the current directory
testUsingContext('passing one file throws', () async { testUsingContext('flutter analyze one file', () async {
await runCommand( await runCommand(
command: new AnalyzeCommand(), command: new AnalyzeCommand(),
arguments: <String>['analyze', libMain.path], arguments: <String>['analyze', libMain.path],
toolExit: true, statusTextContains: <String>['No issues found!'],
exitMessageContains: 'is not a directory',
); );
}); });
// Analyze in the current directory - no arguments // Analyze in the current directory - no arguments
testUsingContext('working directory with errors', () async { testUsingContext('flutter analyze working directory with errors', () async {
// Break the code to produce the "The parameter 'onPressed' is required" hint // Break the code to produce the "The parameter 'onPressed' is required" hint
// that is upgraded to a warning in package:flutter/analysis_options_user.yaml // that is upgraded to a warning in package:flutter/analysis_options_user.yaml
// to assert that we are using the default Flutter analysis options. // to assert that we are using the default Flutter analysis options.
...@@ -96,7 +93,22 @@ void main() { ...@@ -96,7 +93,22 @@ void main() {
statusTextContains: <String>[ statusTextContains: <String>[
'Analyzing', 'Analyzing',
'warning $analyzerSeparator The parameter \'onPressed\' is required', 'warning $analyzerSeparator The parameter \'onPressed\' is required',
'info $analyzerSeparator The method \'_incrementCounter\' isn\'t used', 'hint $analyzerSeparator The method \'_incrementCounter\' isn\'t used',
'2 issues found.',
],
toolExit: true,
);
});
// Analyze a specific file outside the current directory
testUsingContext('flutter analyze one file with errors', () async {
await runCommand(
command: new AnalyzeCommand(),
arguments: <String>['analyze', libMain.path],
statusTextContains: <String>[
'Analyzing',
'warning $analyzerSeparator The parameter \'onPressed\' is required',
'hint $analyzerSeparator The method \'_incrementCounter\' isn\'t used',
'2 issues found.', '2 issues found.',
], ],
toolExit: true, toolExit: true,
...@@ -104,7 +116,8 @@ void main() { ...@@ -104,7 +116,8 @@ void main() {
}); });
// Analyze in the current directory - no arguments // Analyze in the current directory - no arguments
testUsingContext('working directory with local options', () async { testUsingContext('flutter analyze working directory with local options', () async {
// Insert an analysis_options.yaml file in the project // Insert an analysis_options.yaml file in the project
// which will trigger a lint for broken code that was inserted earlier // which will trigger a lint for broken code that was inserted earlier
final File optionsFile = fs.file(fs.path.join(projectPath, 'analysis_options.yaml')); final File optionsFile = fs.file(fs.path.join(projectPath, 'analysis_options.yaml'));
...@@ -122,15 +135,15 @@ void main() { ...@@ -122,15 +135,15 @@ void main() {
statusTextContains: <String>[ statusTextContains: <String>[
'Analyzing', 'Analyzing',
'warning $analyzerSeparator The parameter \'onPressed\' is required', 'warning $analyzerSeparator The parameter \'onPressed\' is required',
'info $analyzerSeparator The method \'_incrementCounter\' isn\'t used', 'hint $analyzerSeparator The method \'_incrementCounter\' isn\'t used',
'info $analyzerSeparator Only throw instances of classes extending either Exception or Error', 'lint $analyzerSeparator Only throw instances of classes extending either Exception or Error',
'3 issues found.', '3 issues found.',
], ],
toolExit: true, toolExit: true,
); );
}); });
testUsingContext('no duplicate issues', () async { testUsingContext('flutter analyze no duplicate issues', () async {
final Directory tempDir = fs.systemTempDirectory.createTempSync('analyze_once_test_').absolute; final Directory tempDir = fs.systemTempDirectory.createTempSync('analyze_once_test_').absolute;
try { try {
...@@ -164,6 +177,22 @@ void bar() { ...@@ -164,6 +177,22 @@ void bar() {
} }
}); });
// Analyze a specific file outside the current directory
testUsingContext('flutter analyze one file with local options', () async {
await runCommand(
command: new AnalyzeCommand(),
arguments: <String>['analyze', libMain.path],
statusTextContains: <String>[
'Analyzing',
'warning $analyzerSeparator The parameter \'onPressed\' is required',
'hint $analyzerSeparator The method \'_incrementCounter\' isn\'t used',
'lint $analyzerSeparator Only throw instances of classes extending either Exception or Error',
'3 issues found.',
],
toolExit: true,
);
});
testUsingContext('--preview-dart-2', () async { testUsingContext('--preview-dart-2', () async {
const String contents = ''' const String contents = '''
StringBuffer bar = StringBuffer('baz'); StringBuffer bar = StringBuffer('baz');
...@@ -221,23 +250,18 @@ Future<Null> runCommand({ ...@@ -221,23 +250,18 @@ Future<Null> runCommand({
List<String> statusTextContains, List<String> statusTextContains,
List<String> errorTextContains, List<String> errorTextContains,
bool toolExit: false, bool toolExit: false,
String exitMessageContains,
}) async { }) async {
try { try {
arguments.insert(0, '--flutter-root=${Cache.flutterRoot}'); arguments.insert(0, '--flutter-root=${Cache.flutterRoot}');
await createTestCommandRunner(command).run(arguments); await createTestCommandRunner(command).run(arguments);
expect(toolExit, isFalse, reason: 'Expected ToolExit exception'); expect(toolExit, isFalse, reason: 'Expected ToolExit exception');
} on ToolExit catch (e) { } on ToolExit {
if (!toolExit) { if (!toolExit) {
testLogger.clear(); testLogger.clear();
rethrow; rethrow;
} }
if (exitMessageContains != null) {
expect(e.message, contains(exitMessageContains));
}
} }
assertContains(testLogger.statusText, statusTextContains); assertContains(testLogger.statusText, statusTextContains);
assertContains(testLogger.errorText, errorTextContains); assertContains(testLogger.errorText, errorTextContains);
testLogger.clear(); testLogger.clear();
} }
...@@ -431,13 +431,14 @@ Future<Null> _createAndAnalyzeProject( ...@@ -431,13 +431,14 @@ Future<Null> _createAndAnalyzeProject(
{ List<String> unexpectedPaths = const <String>[], bool plugin = false }) async { { List<String> unexpectedPaths = const <String>[], bool plugin = false }) async {
await _createProject(dir, createArgs, expectedPaths, unexpectedPaths: unexpectedPaths, plugin: plugin); await _createProject(dir, createArgs, expectedPaths, unexpectedPaths: unexpectedPaths, plugin: plugin);
if (plugin) { if (plugin) {
await _analyzeProject(dir.path); await _analyzeProject(dir.path, target: fs.path.join(dir.path, 'lib', 'flutter_project.dart'));
await _analyzeProject(fs.path.join(dir.path, 'example'));
} else { } else {
await _analyzeProject(dir.path); await _analyzeProject(dir.path);
} }
} }
Future<Null> _analyzeProject(String workingDir) async { Future<Null> _analyzeProject(String workingDir, {String target}) async {
final String flutterToolsPath = fs.path.absolute(fs.path.join( final String flutterToolsPath = fs.path.absolute(fs.path.join(
'bin', 'bin',
'flutter_tools.dart', 'flutter_tools.dart',
...@@ -447,6 +448,8 @@ Future<Null> _analyzeProject(String workingDir) async { ...@@ -447,6 +448,8 @@ Future<Null> _analyzeProject(String workingDir) async {
..addAll(dartVmFlags) ..addAll(dartVmFlags)
..add(flutterToolsPath) ..add(flutterToolsPath)
..add('analyze'); ..add('analyze');
if (target != null)
args.add(target);
final ProcessResult exec = await Process.run( final ProcessResult exec = await Process.run(
'$dartSdkPath/bin/dart', '$dartSdkPath/bin/dart',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment