Unverified Commit d2f889b4 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] Reduce context usage in analyze command and tests (#49589)

parent bafa03e5
......@@ -67,7 +67,14 @@ Future<void> main(List<String> args) async {
final bool verboseHelp = help && verbose;
await runner.run(args, <FlutterCommand>[
AnalyzeCommand(verboseHelp: verboseHelp),
AnalyzeCommand(
verboseHelp: verboseHelp,
fileSystem: globals.fs,
platform: globals.platform,
processManager: globals.processManager,
logger: globals.logger,
terminal: globals.terminal,
),
AssembleCommand(),
AttachCommand(verboseHelp: verboseHelp),
BuildCommand(verboseHelp: verboseHelp),
......
......@@ -4,14 +4,32 @@
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/terminal.dart';
import '../globals.dart' as globals;
import '../runner/flutter_command.dart';
import 'analyze_continuously.dart';
import 'analyze_once.dart';
class AnalyzeCommand extends FlutterCommand {
AnalyzeCommand({bool verboseHelp = false, this.workingDirectory}) {
AnalyzeCommand({
bool verboseHelp = false,
this.workingDirectory,
@required FileSystem fileSystem,
@required Platform platform,
@required AnsiTerminal terminal,
@required Logger logger,
@required ProcessManager processManager,
}) : _fileSystem = fileSystem,
_processManager = processManager,
_logger = logger,
_terminal = terminal,
_platform = platform {
argParser.addFlag('flutter-repo',
negatable: false,
help: 'Include all the examples and tests from the Flutter repository.',
......@@ -59,6 +77,12 @@ class AnalyzeCommand extends FlutterCommand {
/// The working directory for testing analysis using dartanalyzer.
final Directory workingDirectory;
final FileSystem _fileSystem;
final Logger _logger;
final AnsiTerminal _terminal;
final ProcessManager _processManager;
final Platform _platform;
@override
String get name => 'analyze';
......@@ -73,7 +97,7 @@ class AnalyzeCommand extends FlutterCommand {
}
// Or we're not in a project directory.
if (!globals.fs.file('pubspec.yaml').existsSync()) {
if (!_fileSystem.file('pubspec.yaml').existsSync()) {
return false;
}
......@@ -87,6 +111,13 @@ class AnalyzeCommand extends FlutterCommand {
argResults,
runner.getRepoRoots(),
runner.getRepoPackages(),
fileSystem: _fileSystem,
// TODO(jonahwilliams): determine a better way to inject the logger,
// since it is constructed on-demand.
logger: _logger ?? globals.logger,
platform: _platform,
processManager: _processManager,
terminal: _terminal,
).analyze();
} else {
await AnalyzeOnce(
......@@ -94,6 +125,13 @@ class AnalyzeCommand extends FlutterCommand {
runner.getRepoRoots(),
runner.getRepoPackages(),
workingDirectory: workingDirectory,
fileSystem: _fileSystem,
// TODO(jonahwilliams): determine a better way to inject the logger,
// since it is constructed on-demand.
logger: _logger ?? globals.logger,
platform: _platform,
processManager: _processManager,
terminal: _terminal,
).analyze();
}
return FlutterCommandResult.success();
......
......@@ -5,20 +5,47 @@
import 'dart:async';
import 'package:args/args.dart';
import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import 'package:yaml/yaml.dart' as yaml;
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/terminal.dart';
import '../base/utils.dart';
import '../cache.dart';
import '../globals.dart' as globals;
/// Common behavior for `flutter analyze` and `flutter analyze --watch`
abstract class AnalyzeBase {
AnalyzeBase(this.argResults);
AnalyzeBase(this.argResults, {
@required this.repoRoots,
@required this.repoPackages,
@required this.fileSystem,
@required this.logger,
@required this.platform,
@required this.processManager,
@required this.terminal,
});
/// The parsed argument results for execution.
final ArgResults argResults;
@protected
final List<String> repoRoots;
@protected
final List<Directory> repoPackages;
@protected
final FileSystem fileSystem;
@protected
final Logger logger;
@protected
final ProcessManager processManager;
@protected
final Platform platform;
@protected
final AnsiTerminal terminal;
/// Called by [AnalyzeCommand] to start the analysis process.
Future<void> analyze();
......@@ -26,7 +53,7 @@ abstract class AnalyzeBase {
void dumpErrors(Iterable<String> errors) {
if (argResults['write'] != null) {
try {
final RandomAccessFile resultsFile = globals.fs.file(argResults['write']).openSync(mode: FileMode.write);
final RandomAccessFile resultsFile = fileSystem.file(argResults['write']).openSync(mode: FileMode.write);
try {
resultsFile.lockSync();
resultsFile.writeStringSync(errors.join('\n'));
......@@ -34,7 +61,7 @@ abstract class AnalyzeBase {
resultsFile.close();
}
} catch (e) {
globals.printError('Failed to save output to "${argResults['write']}": $e');
logger.printError('Failed to save output to "${argResults['write']}": $e');
}
}
}
......@@ -46,30 +73,13 @@ abstract class AnalyzeBase {
'issues': errorCount,
'missingDartDocs': membersMissingDocumentation,
};
globals.fs.file(benchmarkOut).writeAsStringSync(toPrettyJson(data));
globals.printStatus('Analysis benchmark written to $benchmarkOut ($data).');
fileSystem.file(benchmarkOut).writeAsStringSync(toPrettyJson(data));
logger.printStatus('Analysis benchmark written to $benchmarkOut ($data).');
}
bool get isBenchmarking => argResults['benchmark'] as bool;
}
/// Return true if [fileList] contains a path that resides inside the Flutter repository.
/// If [fileList] is empty, then return true if the current directory resides inside the Flutter repository.
bool inRepo(List<String> fileList) {
if (fileList == null || fileList.isEmpty) {
fileList = <String>[globals.fs.path.current];
}
final String root = globals.fs.path.normalize(globals.fs.path.absolute(Cache.flutterRoot));
final String prefix = root + globals.fs.path.separator;
for (String file in fileList) {
file = globals.fs.path.normalize(globals.fs.path.absolute(file));
if (file == root || file.startsWith(prefix)) {
return true;
}
}
return false;
}
class PackageDependency {
// This is a map from dependency targets (lib directories) to a list
// of places that ask for that target (.packages or pubspec.yaml files)
......
......@@ -5,23 +5,38 @@
import 'dart:async';
import 'package:args/args.dart';
import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/terminal.dart';
import '../base/utils.dart';
import '../cache.dart';
import '../dart/analysis.dart';
import '../dart/sdk.dart' as sdk;
import '../globals.dart' as globals;
import 'analyze_base.dart';
class AnalyzeContinuously extends AnalyzeBase {
AnalyzeContinuously(ArgResults argResults, this.repoRoots, this.repoPackages) : super(argResults);
final List<String> repoRoots;
final List<Directory> repoPackages;
AnalyzeContinuously(ArgResults argResults, List<String> repoRoots, List<Directory> repoPackages, {
@required FileSystem fileSystem,
@required Logger logger,
@required AnsiTerminal terminal,
@required Platform platform,
@required ProcessManager processManager,
}) : super(
argResults,
repoPackages: repoPackages,
repoRoots: repoRoots,
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
processManager: processManager,
);
String analysisTarget;
bool firstAnalysis = true;
......@@ -42,18 +57,24 @@ class AnalyzeContinuously extends AnalyzeBase {
directories = repoRoots;
analysisTarget = 'Flutter repository';
globals.printTrace('Analyzing Flutter repository:');
logger.printTrace('Analyzing Flutter repository:');
for (final String projectPath in repoRoots) {
globals.printTrace(' ${globals.fs.path.relative(projectPath)}');
logger.printTrace(' ${fileSystem.path.relative(projectPath)}');
}
} else {
directories = <String>[globals.fs.currentDirectory.path];
analysisTarget = globals.fs.currentDirectory.path;
directories = <String>[fileSystem.currentDirectory.path];
analysisTarget = fileSystem.currentDirectory.path;
}
final String sdkPath = argResults['dart-sdk'] as String ?? sdk.dartSdkPath;
final AnalysisServer server = AnalysisServer(sdkPath, directories);
final AnalysisServer server = AnalysisServer(sdkPath, directories,
fileSystem: fileSystem,
logger: logger,
platform: platform,
processManager: processManager,
terminal: terminal,
);
server.onAnalyzing.listen((bool isAnalyzing) => _handleAnalysisStatus(server, isAnalyzing));
server.onErrors.listen(_handleAnalysisErrors);
......@@ -66,7 +87,7 @@ class AnalyzeContinuously extends AnalyzeBase {
if (exitCode != 0) {
throwToolExit(message, exitCode: exitCode);
}
globals.printStatus(message);
logger.printStatus(message);
if (server.didServerErrorOccur) {
throwToolExit('Server error(s) occurred.');
......@@ -77,9 +98,9 @@ class AnalyzeContinuously extends AnalyzeBase {
if (isAnalyzing) {
analysisStatus?.cancel();
if (!firstAnalysis) {
globals.printStatus('\n');
logger.printStatus('\n');
}
analysisStatus = globals.logger.startProgress('Analyzing $analysisTarget...', timeout: timeoutConfiguration.slowOperation);
analysisStatus = logger.startProgress('Analyzing $analysisTarget...', timeout: timeoutConfiguration.slowOperation);
analyzedPaths.clear();
analysisTimer = Stopwatch()..start();
} else {
......@@ -87,12 +108,12 @@ class AnalyzeContinuously extends AnalyzeBase {
analysisStatus = null;
analysisTimer.stop();
globals.logger.printStatus(globals.terminal.clearScreen(), newline: false);
logger.printStatus(terminal.clearScreen(), newline: false);
// Remove errors for deleted files, sort, and print errors.
final List<AnalysisError> errors = <AnalysisError>[];
for (final String path in analysisErrors.keys.toList()) {
if (globals.fs.isFileSync(path)) {
if (fileSystem.isFileSync(path)) {
errors.addAll(analysisErrors[path]);
} else {
analysisErrors.remove(path);
......@@ -113,9 +134,9 @@ class AnalyzeContinuously extends AnalyzeBase {
errors.sort();
for (final AnalysisError error in errors) {
globals.printStatus(error.toString());
logger.printStatus(error.toString());
if (error.code != null) {
globals.printTrace('error code: ${error.code}');
logger.printTrace('error code: ${error.code}');
}
}
......@@ -148,9 +169,9 @@ class AnalyzeContinuously extends AnalyzeBase {
final String files = '${analyzedPaths.length} ${pluralize('file', analyzedPaths.length)}';
final String seconds = (analysisTimer.elapsedMilliseconds / 1000.0).toStringAsFixed(2);
if (undocumentedMembers > 0) {
globals.printStatus('$errorsMessage$dartdocMessage • analyzed $files in $seconds seconds');
logger.printStatus('$errorsMessage$dartdocMessage • analyzed $files in $seconds seconds');
} else {
globals.printStatus('$errorsMessage • analyzed $files in $seconds seconds');
logger.printStatus('$errorsMessage • analyzed $files in $seconds seconds');
}
if (firstAnalysis && isBenchmarking) {
......
......@@ -5,15 +5,18 @@
import 'dart:async';
import 'package:args/args.dart';
import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/terminal.dart';
import '../base/utils.dart';
import '../cache.dart';
import '../dart/analysis.dart';
import '../dart/sdk.dart' as sdk;
import '../globals.dart' as globals;
import 'analyze.dart';
import 'analyze_base.dart';
......@@ -21,13 +24,24 @@ import 'analyze_base.dart';
class AnalyzeOnce extends AnalyzeBase {
AnalyzeOnce(
ArgResults argResults,
this.repoRoots,
this.repoPackages, {
List<String> repoRoots,
List<Directory> repoPackages, {
@required FileSystem fileSystem,
@required Logger logger,
@required Platform platform,
@required ProcessManager processManager,
@required AnsiTerminal terminal,
this.workingDirectory,
}) : super(argResults);
final List<String> repoRoots;
final List<Directory> repoPackages;
}) : super(
argResults,
repoRoots: repoRoots,
repoPackages: repoPackages,
fileSystem: fileSystem,
logger: logger,
platform: platform,
processManager: processManager,
terminal: terminal,
);
/// The working directory for testing analysis using dartanalyzer.
final Directory workingDirectory;
......@@ -35,14 +49,14 @@ class AnalyzeOnce extends AnalyzeBase {
@override
Future<void> analyze() async {
final String currentDirectory =
(workingDirectory ?? globals.fs.currentDirectory).path;
(workingDirectory ?? fileSystem.currentDirectory).path;
// find directories from argResults.rest
final Set<String> directories = Set<String>.from(argResults.rest
.map<String>((String path) => globals.fs.path.canonicalize(path)));
.map<String>((String path) => fileSystem.path.canonicalize(path)));
if (directories.isNotEmpty) {
for (final String directory in directories) {
final FileSystemEntityType type = globals.fs.typeSync(directory);
final FileSystemEntityType type = fileSystem.typeSync(directory);
if (type == FileSystemEntityType.notFound) {
throwToolExit("'$directory' does not exist");
......@@ -79,6 +93,11 @@ class AnalyzeOnce extends AnalyzeBase {
final AnalysisServer server = AnalysisServer(
sdkPath,
directories.toList(),
fileSystem: fileSystem,
platform: platform,
logger: logger,
processManager: processManager,
terminal: terminal,
);
StreamSubscription<bool> subscription;
......@@ -108,9 +127,9 @@ class AnalyzeOnce extends AnalyzeBase {
final Stopwatch timer = Stopwatch()..start();
final String message = directories.length > 1
? '${directories.length} ${directories.length == 1 ? 'directory' : 'directories'}'
: globals.fs.path.basename(directories.first);
: fileSystem.path.basename(directories.first);
final Status progress = argResults['preamble'] as bool
? globals.logger.startProgress('Analyzing $message...', timeout: timeoutConfiguration.slowOperation)
? logger.startProgress('Analyzing $message...', timeout: timeoutConfiguration.slowOperation)
: null;
await analysisCompleter.future;
......@@ -135,11 +154,11 @@ class AnalyzeOnce extends AnalyzeBase {
// report errors
if (errors.isNotEmpty && (argResults['preamble'] as bool)) {
globals.printStatus('');
logger.printStatus('');
}
errors.sort();
for (final AnalysisError error in errors) {
globals.printStatus(error.toString(), hangingIndent: 7);
logger.printStatus(error.toString(), hangingIndent: 7);
}
final String seconds = (timer.elapsedMilliseconds / 1000.0).toStringAsFixed(1);
......@@ -154,7 +173,7 @@ class AnalyzeOnce extends AnalyzeBase {
// We consider any level of error to be an error exit (we don't report different levels).
if (errors.isNotEmpty) {
final int errorCount = errors.length;
globals.printStatus('');
logger.printStatus('');
if (undocumentedMembers > 0) {
throwToolExit('$errorCount ${pluralize('issue', errorCount)} found. (ran in ${seconds}s; $dartdocMessage)');
} else {
......@@ -168,9 +187,9 @@ class AnalyzeOnce extends AnalyzeBase {
if (argResults['congratulate'] as bool) {
if (undocumentedMembers > 0) {
globals.printStatus('No issues found! (ran in ${seconds}s; $dartdocMessage)');
logger.printStatus('No issues found! (ran in ${seconds}s; $dartdocMessage)');
} else {
globals.printStatus('No issues found! (ran in ${seconds}s)');
logger.printStatus('No issues found! (ran in ${seconds}s)');
}
}
}
......
......@@ -5,18 +5,39 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/terminal.dart';
import '../base/utils.dart';
import '../convert.dart';
import '../globals.dart' as globals;
/// An interface to the Dart analysis server.
class AnalysisServer {
AnalysisServer(this.sdkPath, this.directories);
AnalysisServer(this.sdkPath, this.directories, {
@required FileSystem fileSystem,
@required ProcessManager processManager,
@required Logger logger,
@required Platform platform,
@required AnsiTerminal terminal,
}) : _fileSystem = fileSystem,
_processManager = processManager,
_logger = logger,
_platform = platform,
_terminal = terminal;
final String sdkPath;
final List<String> directories;
final FileSystem _fileSystem;
final ProcessManager _processManager;
final Logger _logger;
final Platform _platform;
final AnsiTerminal _terminal;
Process _process;
final StreamController<bool> _analyzingController =
......@@ -29,9 +50,9 @@ class AnalysisServer {
Future<void> start() async {
final String snapshot =
globals.fs.path.join(sdkPath, 'bin/snapshots/analysis_server.dart.snapshot');
_fileSystem.path.join(sdkPath, 'bin/snapshots/analysis_server.dart.snapshot');
final List<String> command = <String>[
globals.fs.path.join(sdkPath, 'bin', 'dart'),
_fileSystem.path.join(sdkPath, 'bin', 'dart'),
snapshot,
'--disable-server-feature-completion',
'--disable-server-feature-search',
......@@ -39,14 +60,14 @@ class AnalysisServer {
sdkPath,
];
globals.printTrace('dart ${command.skip(1).join(' ')}');
_process = await globals.processManager.start(command);
_logger.printTrace('dart ${command.skip(1).join(' ')}');
_process = await _processManager.start(command);
// This callback hookup can't throw.
unawaited(_process.exitCode.whenComplete(() => _process = null));
final Stream<String> errorStream =
_process.stderr.transform<String>(utf8.decoder).transform<String>(const LineSplitter());
errorStream.listen(globals.printError);
errorStream.listen(_logger.printError);
final Stream<String> inStream =
_process.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter());
......@@ -73,11 +94,11 @@ class AnalysisServer {
'params': params,
});
_process.stdin.writeln(message);
globals.printTrace('==> $message');
_logger.printTrace('==> $message');
}
void _handleServerResponse(String line) {
globals.printTrace('<== $line');
_logger.printTrace('<== $line');
final dynamic response = json.decode(line);
......@@ -98,10 +119,10 @@ class AnalysisServer {
} else if (response['error'] != null) {
// Fields are 'code', 'message', and 'stackTrace'.
final Map<String, dynamic> error = castStringKeyedMap(response['error']);
globals.printError(
_logger.printError(
'Error response from the server: ${error['code']} ${error['message']}');
if (error['stackTrace'] != null) {
globals.printError(error['stackTrace'] as String);
_logger.printError(error['stackTrace'] as String);
}
}
}
......@@ -117,9 +138,9 @@ class AnalysisServer {
void _handleServerError(Map<String, dynamic> error) {
// Fields are 'isFatal', 'message', and 'stackTrace'.
globals.printError('Error from the analysis server: ${error['message']}');
_logger.printError('Error from the analysis server: ${error['message']}');
if (error['stackTrace'] != null) {
globals.printError(error['stackTrace'] as String);
_logger.printError(error['stackTrace'] as String);
}
_didServerErrorOccur = true;
}
......@@ -130,7 +151,13 @@ class AnalysisServer {
final List<dynamic> errorsList = issueInfo['errors'] as List<dynamic>;
final List<AnalysisError> errors = errorsList
.map<Map<String, dynamic>>(castStringKeyedMap)
.map<AnalysisError>((Map<String, dynamic> json) => AnalysisError(json))
.map<AnalysisError>((Map<String, dynamic> json) {
return AnalysisError(json,
fileSystem: _fileSystem,
platform: _platform,
terminal: _terminal,
);
})
.toList();
if (!_errorsController.isClosed) {
_errorsController.add(FileAnalysisErrors(file, errors));
......@@ -152,7 +179,17 @@ enum _AnalysisSeverity {
}
class AnalysisError implements Comparable<AnalysisError> {
AnalysisError(this.json);
AnalysisError(this.json, {
@required Platform platform,
@required AnsiTerminal terminal,
@required FileSystem fileSystem,
}) : _platform = platform,
_terminal = terminal,
_fileSystem = fileSystem;
final Platform _platform;
final AnsiTerminal _terminal;
final FileSystem _fileSystem;
static final Map<String, _AnalysisSeverity> _severityMap = <String, _AnalysisSeverity>{
'INFO': _AnalysisSeverity.info,
......@@ -160,7 +197,7 @@ class AnalysisError implements Comparable<AnalysisError> {
'ERROR': _AnalysisSeverity.error,
};
static final String _separator = globals.platform.isWindows ? '-' : '•';
String get _separator => _platform.isWindows ? '-' : '•';
// "severity":"INFO","type":"TODO","location":{
// "file":"/Users/.../lib/test.dart","offset":362,"length":72,"startLine":15,"startColumn":4
......@@ -171,9 +208,9 @@ class AnalysisError implements Comparable<AnalysisError> {
String get colorSeverity {
switch(_severityLevel) {
case _AnalysisSeverity.error:
return globals.terminal.color(severity, TerminalColor.red);
return _terminal.color(severity, TerminalColor.red);
case _AnalysisSeverity.warning:
return globals.terminal.color(severity, TerminalColor.yellow);
return _terminal.color(severity, TerminalColor.yellow);
case _AnalysisSeverity.info:
case _AnalysisSeverity.none:
return severity;
......@@ -224,7 +261,7 @@ class AnalysisError implements Comparable<AnalysisError> {
final String padding = ' ' * math.max(0, 7 - severity.length);
return '$padding${colorSeverity.toLowerCase()} $_separator '
'$messageSentenceFragment $_separator '
'${globals.fs.path.relative(file)}:$startLine:$startColumn $_separator '
'${_fileSystem.path.relative(file)}:$startLine:$startColumn $_separator '
'$code';
}
......
......@@ -5,12 +5,15 @@
import 'dart:async';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/dart/analysis.dart';
import 'package:flutter_tools/src/dart/pub.dart';
import 'package:flutter_tools/src/dart/sdk.dart';
import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import '../../src/common.dart';
import '../../src/context.dart';
......@@ -18,10 +21,21 @@ import '../../src/context.dart';
void main() {
AnalysisServer server;
Directory tempDir;
FileSystem fileSystem;
Platform platform;
ProcessManager processManager;
AnsiTerminal terminal;
Logger logger;
setUp(() {
platform = const LocalPlatform();
fileSystem = const LocalFileSystem();
platform = const LocalPlatform();
processManager = const LocalProcessManager();
terminal = AnsiTerminal(platform: platform, stdio: Stdio());
logger = BufferLogger(outputPreferences: OutputPreferences.test(), terminal: terminal);
FlutterCommandRunner.initFlutterRoot();
tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_analysis_test.');
tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analysis_test.');
});
tearDown(() {
......@@ -29,13 +43,36 @@ void main() {
return server?.dispose();
});
void _createSampleProject(Directory directory, { bool brokenCode = false }) {
final File pubspecFile = fileSystem.file(fileSystem.path.join(directory.path, 'pubspec.yaml'));
pubspecFile.writeAsStringSync('''
name: foo_project
''');
final File dartFile = fileSystem.file(fileSystem.path.join(directory.path, 'lib', 'main.dart'));
dartFile.parent.createSync();
dartFile.writeAsStringSync('''
void main() {
print('hello world');
${brokenCode ? 'prints("hello world");' : ''}
}
''');
}
group('analyze --watch', () {
testUsingContext('AnalysisServer success', () async {
_createSampleProject(tempDir);
await pub.get(context: PubContext.flutterTests, directory: tempDir.path);
await const Pub().get(context: PubContext.flutterTests, directory: tempDir.path);
server = AnalysisServer(dartSdkPath, <String>[tempDir.path]);
server = AnalysisServer(dartSdkPath, <String>[tempDir.path],
fileSystem: fileSystem,
platform: platform,
processManager: processManager,
logger: logger,
terminal: terminal,
);
int errorCount = 0;
final Future<bool> onDone = server.onAnalyzing.where((bool analyzing) => analyzing == false).first;
......@@ -45,18 +82,21 @@ void main() {
await onDone;
expect(errorCount, 0);
}, overrides: <Type, Generator>{
OperatingSystemUtils: () => globals.os,
Pub: () => const Pub(),
});
});
testUsingContext('AnalysisServer errors', () async {
_createSampleProject(tempDir, brokenCode: true);
await pub.get(context: PubContext.flutterTests, directory: tempDir.path);
await const Pub().get(context: PubContext.flutterTests, directory: tempDir.path);
server = AnalysisServer(dartSdkPath, <String>[tempDir.path]);
server = AnalysisServer(dartSdkPath, <String>[tempDir.path],
fileSystem: fileSystem,
platform: platform,
processManager: processManager,
logger: logger,
terminal: terminal,
);
int errorCount = 0;
final Future<bool> onDone = server.onAnalyzing.where((bool analyzing) => analyzing == false).first;
......@@ -68,15 +108,18 @@ void main() {
await onDone;
expect(errorCount, greaterThan(0));
}, overrides: <Type, Generator>{
OperatingSystemUtils: () => globals.os,
Pub: () => const Pub(),
});
testUsingContext('Returns no errors when source is error-free', () async {
const String contents = "StringBuffer bar = StringBuffer('baz');";
tempDir.childFile('main.dart').writeAsStringSync(contents);
server = AnalysisServer(dartSdkPath, <String>[tempDir.path]);
server = AnalysisServer(dartSdkPath, <String>[tempDir.path],
fileSystem: fileSystem,
platform: platform,
processManager: processManager,
logger: logger,
terminal: terminal,
);
int errorCount = 0;
final Future<bool> onDone = server.onAnalyzing.where((bool analyzing) => analyzing == false).first;
......@@ -86,24 +129,5 @@ void main() {
await server.start();
await onDone;
expect(errorCount, 0);
}, overrides: <Type, Generator>{
OperatingSystemUtils: () => globals.os,
Pub: () => const Pub(),
});
}
void _createSampleProject(Directory directory, { bool brokenCode = false }) {
final File pubspecFile = globals.fs.file(globals.fs.path.join(directory.path, 'pubspec.yaml'));
pubspecFile.writeAsStringSync('''
name: foo_project
''');
final File dartFile = globals.fs.file(globals.fs.path.join(directory.path, 'lib', 'main.dart'));
dartFile.parent.createSync();
dartFile.writeAsStringSync('''
void main() {
print('hello world');
${brokenCode ? 'prints("hello world");' : ''}
}
''');
}
......@@ -5,7 +5,6 @@
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/analyze_base.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import '../../src/common.dart';
......@@ -13,6 +12,23 @@ import '../../src/context.dart';
const String _kFlutterRoot = '/data/flutter';
/// Return true if [fileList] contains a path that resides inside the Flutter repository.
/// If [fileList] is empty, then return true if the current directory resides inside the Flutter repository.
bool inRepo(List<String> fileList) {
if (fileList == null || fileList.isEmpty) {
fileList = <String>[globals.fs.path.current];
}
final String root = globals.fs.path.normalize(globals.fs.path.absolute(Cache.flutterRoot));
final String prefix = root + globals.fs.path.separator;
for (String file in fileList) {
file = globals.fs.path.normalize(globals.fs.path.absolute(file));
if (file == root || file.startsWith(prefix)) {
return true;
}
}
return false;
}
void main() {
FileSystem fs;
Directory tempDir;
......
// Copyright 2014 The Flutter 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 'dart:async';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/dart/analysis.dart';
import 'package:flutter_tools/src/dart/pub.dart';
import 'package:flutter_tools/src/dart/sdk.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import '../../src/common.dart';
import '../../src/context.dart';
void main() {
testSampleProject('ui', 'Window');
testSampleProject('html', 'HttpStatus');
testSampleProject('js', 'allowInterop');
testSampleProject('js_util', 'jsify');
}
void testSampleProject(String lib, String member) {
testUsingContext('contains dart:$lib', () async {
Cache.disableLocking();
final Directory projectDirectory = globals.fs.systemTempDirectory.createTempSync('flutter_sdk_validation_${lib}_test.').absolute;
try {
final File pubspecFile = globals.fs.file(globals.fs.path.join(projectDirectory.path, 'pubspec.yaml'));
pubspecFile.writeAsStringSync('''
name: ${lib}_project
dependencies:
flutter:
sdk: flutter
''');
final File dartFile = globals.fs.file(globals.fs.path.join(projectDirectory.path, 'lib', 'main.dart'));
dartFile.parent.createSync();
dartFile.writeAsStringSync('''
import 'dart:$lib' as $lib;
void main() {
// ignore: unnecessary_statements
$lib.$member;
}
''');
await pub.get(context: PubContext.flutterTests, directory: projectDirectory.path);
final AnalysisServer server = AnalysisServer(dartSdkPath, <String>[projectDirectory.path]);
try {
final int errorCount = await analyze(server);
expect(errorCount, 0);
} finally {
await server.dispose();
}
} finally {
tryToDelete(projectDirectory);
Cache.enableLocking();
}
}, skip: true);
}
Future<int> analyze(AnalysisServer server) async {
int errorCount = 0;
final Future<bool> onDone = server.onAnalyzing.where((bool analyzing) => analyzing == false).first;
server.onErrors.listen((FileAnalysisErrors result) {
for (final AnalysisError error in result.errors) {
print(error.toString().trim());
}
errorCount += result.errors.length;
});
await server.start();
await onDone;
return errorCount;
}
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